Poll based implementation of the SocketCollection

Serguei Kolos <[email protected]> Fri, 26 Mar 2004 09:46:06 +0100
Newsgroups gmane.comp.corba.omniorb.user,gmane.comp.corba.omniorb.devel
Message-ID <[email protected]>
Hello

Concerning the issue with the large number of concurrent clients for the
same server. I have had this problem and end up with reimplementation
of the SocketCollection class using the poll function instead of the select.
I have tested my implementation very extensively during the last month.
I'm pretty sure that it works as it has to. My server can easily work with
3000 concurrent clients (of course in ThreadPool mode).
I have also compared the CPU consumption overhead of the two implementations
and have found that it is pretty much the same. I have tested it on 
Solaris 5.8 with
SunPro 5.4 and also on Linux RedHat 7.3.4 (2.4.20-30.7) with gcc-2.95.2 and
gcc-3.2 and gcc-3.2.3. Unfortunately I have now idea either it will work 
for Windows.

Two notes about the implementation:
1. I have tried to make it efficient, that's why it uses pre allocated 
array of the
    pollfd structures. The size of the array is defined at compilation 
time. This
    produces some extra memory allocation, but for the modern machines I 
believe
    this is negligible
2. Again for the efficiency reasons I have cheated a bit: I have used 
the high bits
    of the pollfd.event attribute to mark somehow the file descriptors, 
which require
    some actions to be done over them. This is necessary because some 
actions
    (for example removing fd from the set) can not be done if the poll 
function is using
    the set. In this case I marked such fd-s and then after exiting from 
poll make
    the necessary actions.

I have attached my implementation to this message. My question is - could
it be possible to integrate it somehow to the official omniORB release? 
For example
one might think about integrating it to the original SocketCollection 
file with some
#ifdefs with the possibility to choose the desired implementation via 
configure script.

Cheers,
Sergei

PS: If anybody wants it is very simple to try this out:
1. cp SocketCollection.h include/omniORB4/internal/SocketCollection.h
2. cp SocketCollection.cc src/lib/omniORB/orbcore/SocketCollection.cc
3. recompile

Kamaldeep Singh Khanuja wrote:

> Hi Duncan,
>
> Please refer to the posting below.
>
> We have increased the  value of FD_SETSIZE from 64 to 2048, which 
> actually
> delayed the problem of ORB thread hanging. I have the following 
> suggestion
> regarding the same to eliminate the ORB thread hanging, incase of no of
> concurrent clients exceed the above limit, please see the following code
> from SocketCollection.cc with the comments inline:
>
> /////////////////////////////////////////////////////////////////////////
> void
> SocketCollection::setSelectable(SocketHandle_t sock,
>                                 CORBA::Boolean now,
>                                 CORBA::Boolean data_in_buffer,
>                                 CORBA::Boolean hold_lock) {
>
> ...
> ...
>     if (!FD_ISSET(sock,&pd_fdset_2)) {
>       pd_n_fdset_2++; // Here I see the problem that if something goes 
> wrong
> in the FD_SET, then we have already incremented the pd_n_fdset_2
>                         // which cause problem in the next function. 
> (This
> happen when no concurrent clients increases the above said limit.
>                        // This can be easily reproduced by reducing the
> FD_SETSIZE to lets say 32. Now if the 33 client tries to connect while
> remaing 32 are still in the call.
>                        // then FD_SET fails and pd_n_fdset_2 is 
> incremented.
>       FD_SET(sock,&pd_fdset_2);
>     }
> ...
> ...
> }
>
> Now lets see where the problem actually occurs:
>
> /////////////////////////////////////////////////////////////////////////
> CORBA::Boolean
> SocketCollection::Select() {
>
> ...
> ...
>   if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
>
>     omni_thread::get_time(&pd_abs_sec,&pd_abs_nsec,
>                           scan_interval_sec,scan_interval_nsec);
>     timeout.tv_sec  = scan_interval_sec;
>     timeout.tv_usec = scan_interval_nsec / 1000;
>
>     omni_tracedmutex_lock sync(pd_fdset_lock);
>     rfds  = pd_fdset_2;
>    
>     // I have changed the following statement from "total = 
> pd_n_fdset_2;"
> to "total = rfds.fd_count;"
>     // the reason of doing this is because the value of total would 
> one more
> then the required and
>     // while loop at bottom would become a infinite loop and thread 
> hangs as
> total will not decrement to ZERO!!! And using rfds.fd_count would
>     // always give the right size.
>        
>     total = rfds.fd_count;// total = pd_n_fdset_2;
>     pd_fdset_2 = pd_fdset_1;
>     pd_n_fdset_2 = pd_n_fdset_1;
>   }
>   else {
>     omni_tracedmutex_lock sync(pd_fdset_lock);
>     rfds  = pd_fdset_2;
>    
>     //Same reason as the previous one
>     total = rfds.fd_count;// total = pd_n_fdset_2;
>   }
>
>   int maxfd = 0;
>   int fd = 0;
>   while (total) {
>     if (FD_ISSET(fd,&rfds)) {
>       maxfd = fd;
>       total--;
>     }
>     fd++;
>   }
> ...
> ..
> }
>
>
>
> -----Original Message-----
> From: Kamaldeep Singh Khanuja [mailto:[email protected]]
> Sent: Tuesday, November 18, 2003 4:06 PM
> To: [email protected]
> Subject: [omniORB] RE: Major concurrency issue on Windows (OmniORB 4.0.1)
>
> Hi Duncan,
>
> My following posting remained unanswered.
>
> Regards,
> --Kamal
>
> -----Original Message-----
> From: Kamaldeep Singh Khanuja
> Sent: Thursday, September 11, 2003 11:01 AM
> To: '[email protected]'
> Subject: Major concurrency issue on Windows (OmniORB 4.0.1)
>
>
> Hi Duncan,
> Our application is a server application (uses OmniORB 4.0.1) where large
> number of clients (usually greater than 300) connect to our server. On 
> the
> Windows version of this server, we have noticed that if there are more 
> than
> 64 concurrent IDL (i.e. 64 socket connections opened with server) calls
> pending on the server, the 65th IDL call from any other client 
> application
> blocks and the server stops responding there after. Upon investigating we
> found that it is due to the limit specified in winsock2.h by using the
> preprocessor macro FD_SETSIZE. By default it is 64, so I introduced the
> following before winsock2.h is included in the OmniORB code:
>
> // Should be moved to some Common place
> // This is to support the Winsock implementation to support non-default
> number of
> // open sockets for an application. [Default limit is 64] #undef 
> FD_SETSIZE
> #define FD_SETSIZE 2048
>
> The above was done at two places:
> 1) src\lib\omniORB\orbcore\tcp\tcpTransportImpl.cc
> 2) include\omniORB4\internal\libcWrapper.h
>
> After this I compiled the ORB and it worked fine for us!!!
>
> I think it would make sense to include this change for Windows in the ORB
> distribution. This could remain hard-coded to 2048 or be made 
> configurable
> using a makefile macro.
>
> Best Regards,
> --Kamal
>
> _______________________________________________
> omniORB-list mailing list
> [email protected]
> http://www.omniorb-support.com/mailman/listinfo/omniorb-list
>
> _______________________________________________
> omniORB-list mailing list
> [email protected]
> http://www.omniorb-support.com/mailman/listinfo/omniorb-list
>

_______________________________________________
omniORB-list mailing list
[email protected]
http://www.omniorb-support.com/mailman/listinfo/omniorb-list
SocketCollection.cc (text/plain, 19.9 KB)
// -*- Mode: C++; -*-
//                      Package   : omniORB
// SocketCollection.cc	Created on: 23 Jul 2003
//                      Author    : Serguei Kolos
//
//    Copyright (C) 2001 AT&T Laboratories Cambridge
//
//    This file is part of the omniORB library
//
//    The omniORB library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Library General Public
//    License as published by the Free Software Foundation; either
//    version 2 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//    Library General Public License for more details.
//
//    You should have received a copy of the GNU Library General Public
//    License along with this library; if not, write to the Free
//    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
//    02111-1307, USA
//
//
// Description:
//	*** PROPRIETORY INTERFACE ***
// 

/*
  $Log: SocketCollection.cc,v $
  Revision 1.3  2004/02/20 17:39:21  kolos
  Apply the omniORB patch: now FD_CLOEXEC flag is set for the TCP sockets, used by the omniORB.

  Revision 1.2  2004/02/19 17:10:21  kolos
  Implement new connection multiplexor, which uses the poll function.

  Revision 1.1.2.15  2003/07/25 16:04:57  dgrisby
  vxWorks patches.

  Revision 1.1.2.14  2003/02/17 10:39:52  dgrisby
  Fix inevitable Windows problem.

  Revision 1.1.2.13  2003/02/17 01:46:23  dgrisby
  Pipe to kick select thread (on Unix).

  Revision 1.1.2.12  2003/01/28 12:17:09  dgrisby
  Bug with Select() ignoring data in buffer indications.

  Revision 1.1.2.11  2002/10/14 15:27:41  dgrisby
  Typo in fcntl error check.

  Revision 1.1.2.10  2002/08/21 06:23:15  dgrisby
  Properly clean up bidir connections and ropes. Other small tweaks.

  Revision 1.1.2.9  2002/03/18 16:50:18  dpg1
  New threadPoolWatchConnection parameter.

  Revision 1.1.2.8  2002/03/14 12:21:49  dpg1
  Undo accidental scavenger period change, remove invalid assertion.

  Revision 1.1.2.7  2002/03/13 16:05:38  dpg1
  Transport shutdown fixes. Reference count SocketCollections to avoid
  connections using them after they are deleted. Properly close
  connections when in thread pool mode.

  Revision 1.1.2.6  2002/02/26 14:06:45  dpg1
  Recent changes broke Windows.

  Revision 1.1.2.5  2002/02/13 16:02:38  dpg1
  Stability fixes thanks to Bastiaan Bakker, plus threading
  optimisations inspired by investigating Bastiaan's bug reports.

  Revision 1.1.2.4  2001/08/24 15:56:44  sll
  Fixed code which made the wrong assumption about the semantics of
  do { ...; continue; } while(0)

  Revision 1.1.2.3  2001/08/02 13:00:53  sll
  Do not use select(0,0,0,0,&timeout), it doesn't work on win32.

  Revision 1.1.2.2  2001/08/01 15:56:07  sll
  Workaround MSVC++ bug. It generates wrong code with FD_ISSET and FD_SET
  under certain conditions.

  Revision 1.1.2.1  2001/07/31 16:16:26  sll
  New transport interface to support the monitoring of active connections.

*/

#include <omniORB4/CORBA.h>
#include <omniORB4/giopEndpoint.h>
#include <SocketCollection.h>

#if defined(__vxWorks__)
#  include "pipeDrv.h"
#  include "selectLib.h"
#  include "iostream.h"
#endif

#  include "iostream"
#  include "stdio.h"

OMNI_NAMESPACE_BEGIN(omni)

#define GDB_DEBUG

/////////////////////////////////////////////////////////////////////////
void
SocketSetTimeOut(unsigned long abs_sec,
		 unsigned long abs_nsec,struct timeval& t)
{
  unsigned long now_sec, now_nsec;
  omni_thread::get_time(&now_sec,&now_nsec);

  if ((abs_sec <= now_sec) && ((abs_sec < now_sec) || (abs_nsec < now_nsec))) {
    t.tv_sec = t.tv_usec = 0;
  }
  else {
    t.tv_sec = abs_sec - now_sec;
    if (abs_nsec >= now_nsec) {
      t.tv_usec = (abs_nsec - now_nsec) / 1000;
    }
    else {
      t.tv_usec = (1000000000 + abs_nsec - now_nsec) / 1000;
      t.tv_sec -= 1;
    }
  }
}

/////////////////////////////////////////////////////////////////////////
int
SocketSetnonblocking(SocketHandle_t sock) {
# if defined(__vxWorks__)
  int fl = TRUE;
  if (ioctl(sock, FIONBIO, (int)&fl) == ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# elif defined(__WIN32__)
  u_long v = 1;
  if (ioctlsocket(sock,FIONBIO,&v) == RC_SOCKET_ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# else
  int fl = O_NONBLOCK;
  if (fcntl(sock,F_SETFL,fl) == RC_SOCKET_ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# endif
}

/////////////////////////////////////////////////////////////////////////
int
SocketSetblocking(SocketHandle_t sock) {
# if defined(__vxWorks__)
  int fl = FALSE;
  if (ioctl(sock, FIONBIO, (int)&fl) == ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# elif defined(__WIN32__)
  u_long v = 0;
  if (ioctlsocket(sock,FIONBIO,&v) == RC_SOCKET_ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# else
  int fl = 0;
  if (fcntl(sock,F_SETFL,fl) == RC_SOCKET_ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# endif
}

/////////////////////////////////////////////////////////////////////////
// This bit is set in the events field of the pollfd 
// structure to indicate that this fd has not to be 
// taken into account while processing the poll result
#define FDCLRD		0x1000
// This bit is set in the events field of the pollfd 
// structure to indicate that this fd has to be 
// moved to the end of the pollfd array
#define FDMVD		0x2000
// This bit is set in the events field of the pollfd 
// structure to indicate that this fd has data to be read
#define FDDIB		0x4000

/////////////////////////////////////////////////////////////////////////
// Defines the maximum number of connections 
#define MAX_FD_SIZE	4096

#define SWAP( data, index, i1, i2 ) { \
    pollfd tmp = data[i1];	\
    data[i1] = data[i2];	\
    index[data[i1].fd] = i1;	\
    data[i2] = tmp;		\
    index[data[i2].fd] = i2;	\
} \


/////////////////////////////////////////////////////////////////////////
SocketHandleSet::SocketHandleSet() 
  : pd_length_full( 0 ),
    pd_length_now( 0 ),
    pd_used_by_poll_n( 0 ),
    pd_to_be_removed_n( 0 ),
    pd_to_be_moved_n( 0 ),
    pd_dib_n( 0 )
{
    pd_data = new pollfd[MAX_FD_SIZE];
    pd_index = new short[MAX_FD_SIZE];
    memset( pd_data, 0, sizeof( pollfd ) * MAX_FD_SIZE );
    memset( pd_index, -1, sizeof( short ) * MAX_FD_SIZE );
}

SocketHandleSet::~SocketHandleSet()
{
    delete[] pd_data;
    delete[] pd_index;
}

/////////////////////////////////////////////////////////////////////////
void
SocketHandleSet::Add( SocketHandle_t fd, CORBA::Boolean now, CORBA::Boolean data_in_buffer )
{
    short i = pd_index[fd];
	    
    if ( i != -1 ) {
	// fd is already in the set
	
	CORBA::Boolean was_removed = pd_data[i].events & FDCLRD;
	
	// fd was marked to be removed while we were in the poll
	// we will set it again
	if (was_removed) {
	    pd_to_be_removed_n--;
	    pd_data[i].events &= ~FDCLRD;
	}
	
	if (data_in_buffer && !(pd_data[i].events & FDDIB)) {
	    pd_data[i].events |= FDDIB;
	    pd_dib_n++;
	}
	
	if (now || data_in_buffer) {
	// fd has to be in the part of the set, which will 
	// be scheduled for the next poll invocation
	    if (i >= pd_length_now) {
		// it is not there,
		// so we will move it to that part
		SWAP( pd_data, pd_index, pd_length_now, i )
		pd_length_now++;
	    }
	    return;
	}
	
	// fd has to be in the part of the set, which will NOT
	// be scheduled for the next poll invocation
	if ( was_removed && i < pd_length_now) {
	// fd is now in the part of the set, which will 
	// be scheduled for the next poll invocation
	// and it is marked as removed
	    
	    if (i < pd_used_by_poll_n) {
	    // the poll is running 
	    // we mark the fd as to be moved to another part of the fd set
		pd_data[i].events |= FDMVD;
    		pd_to_be_moved_n++;
	    }
	    else {
	    // the poll is NOT running 
	    // we move the fd to another part of the fd set
	   	if (pd_data[i].events & FDMVD) {
		    pd_data[i].events &= ~FDMVD;
		    pd_to_be_moved_n--;
		}
			
		pd_length_now--;
		if (i != pd_length_now) {
		    SWAP( pd_data, pd_index, pd_length_now, i )
		}
	    }
	}
	return;
    }
    
    // fd is NOT in the set
    // lets put it there
    if (now || data_in_buffer) {
    	// put fd to the first part of the array
	// it will be used for the next poll invocation
	if (pd_length_full != pd_length_now) {
	    pd_data[pd_length_full] = pd_data[pd_length_now];
	    pd_index[pd_data[pd_length_full].fd] = pd_length_full;
	}
    	pd_data[pd_length_now].fd = fd;
	pd_data[pd_length_now].events = POLLIN;
    	if (data_in_buffer) {
	    pd_data[pd_length_now].events |= FDDIB;
	    pd_dib_n++;
	}
    	pd_data[pd_length_now].revents = 0;
	pd_index[fd] = pd_length_now;
	pd_length_full++;
	pd_length_now++;
    }
    else {
    	// put fd to the last part of the array
	// it will not be used for the next poll invocation
    	pd_data[pd_length_full].fd = fd;
	pd_data[pd_length_full].events = POLLIN;
    	pd_data[pd_length_full].revents = 0;
	pd_index[fd] = pd_length_full;
	pd_length_full++;
    }	
}

/////////////////////////////////////////////////////////////////////////
void
SocketHandleSet::Remove( SocketHandle_t fd )
{
    short i = pd_index[fd];

    if ( i == -1 )
    	return;
    
    if (pd_data[i].events & FDDIB) {
	pd_data[i].events &= ~FDDIB;
	pd_dib_n--;
    }
    if (pd_data[i].events & FDMVD) {
	pd_data[i].events &= ~FDMVD;
	pd_to_be_moved_n--;
    }
    if (pd_data[i].events & FDCLRD) {
	pd_data[i].events &= ~FDCLRD;
	pd_to_be_removed_n--;
    }
    
    if ( i < pd_used_by_poll_n ) {
	// this fd is used by the poll function - be careful
	// don't modify array, which is now used by poll
	// just mark the fd by setting the FDCLRD bit in the events field
	// it will be removed later in the SocketCollection::Select
	pd_data[i].events |= FDCLRD;
	pd_to_be_removed_n++;
    }
    else {
	// fd is NOT used by the poll function - simply remove fd
	pd_index[fd] = -1;
		
    	pd_length_full--;
	if (i < pd_length_now) {
	   pd_length_now--;
	   if (i != pd_length_now) {
		pd_data[i] = pd_data[pd_length_now];
		pd_index[pd_data[i].fd] = i;
	   }
	   if (pd_length_now != pd_length_full) {
		pd_data[pd_length_now] = pd_data[pd_length_full];
		pd_index[pd_data[pd_length_now].fd] = pd_length_now;
	   }
	}
	else {
	   if (i != pd_length_full) {
		pd_data[i] = pd_data[pd_length_full];
		pd_index[pd_data[i].fd] = i;
	   }
	}
    }
}

/////////////////////////////////////////////////////////////////////////
void
SocketHandleSet::Move( SocketHandle_t fd )
{
    int i = pd_index[fd];
    
    if (!(pd_data[i].events & FDMVD))
    	return; 
    
    // socket has been marked as moved when the poll was running
    // now move it to the end of the array
    pd_data[i].events &= ~FDMVD;
			
    pd_to_be_moved_n--;
    pd_length_now--;
    if (i != pd_length_now) {
	SWAP( pd_data, pd_index, pd_length_now, i )
    }
}

/////////////////////////////////////////////////////////////////////////
CORBA::Boolean
SocketHandleSet::isSet( SocketHandle_t fd, CORBA::Boolean data_in_buffer )
{
    int i = pd_index[fd];
    
    if (      ( i != -1 )
	  &&  ( data_in_buffer == 0 || (pd_data[i].events & FDDIB) ) 
	  && !( pd_data[i].events & FDCLRD )
	  && !( pd_data[i].events & FDMVD ) )
	return 1;
    else
	return 0;
}

/////////////////////////////////////////////////////////////////////////
int
SocketSetCloseOnExec(SocketHandle_t sock) {
# if defined(__vxWorks__)
  // Not supported on vxWorks
  return 0;
# elif defined(__WIN32__)
  SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0);
  return 0;
# else
  int fl = FD_CLOEXEC;
  if (fcntl(sock,F_SETFD,fl) == RC_SOCKET_ERROR) {
    return RC_INVALID_SOCKET;
  }
  return 0;
# endif
}

/////////////////////////////////////////////////////////////////////////
unsigned long SocketCollection::scan_interval_sec  = 0;
unsigned long SocketCollection::scan_interval_nsec = 50*1000*1000;
CORBA::ULong  SocketCollection::hashsize           = 103;

/////////////////////////////////////////////////////////////////////////
SocketCollection::SocketCollection() :
  pd_poll_cond(&pd_fdset_lock),
  pd_abs_sec(0), pd_abs_nsec(0),
  pd_pipe_read(-1), pd_pipe_write(-1), pd_pipe_full(0),
  pd_refcount(1)
{

#ifdef UnixArchitecture
#  ifdef __vxWorks__
    if (pipeDrv() == OK) {
      if (pipeDevCreate("/pipe/SocketCollection",10,sizeof(int)) == OK) {
	pd_pipe_read = pd_pipe_write = open("/pipe/SocketCollection",
					    O_RDWR,0);
      }
    }
    if (pd_pipe_read <= 0) {
      omniORB::logs(5, "Unable to create pipe for SocketCollection.");
    }
#  else
    int filedes[2];
    int r = pipe(filedes);
    if (r != -1) {
      pd_pipe_read  = filedes[0];
      pd_pipe_write = filedes[1];
    }
    else {
      omniORB::logs(5, "Unable to create pipe for SocketCollection.");
    }
#  endif
#endif

  if (pd_pipe_read > 0) {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    pd_fdset.Add( pd_pipe_read, 1, 0 );
  }

  pd_hash_table = new SocketLink* [hashsize];
  for (CORBA::ULong i=0; i < hashsize; i++)
    pd_hash_table[i] = 0;
}


/////////////////////////////////////////////////////////////////////////
SocketCollection::~SocketCollection()
{
  pd_refcount = -1;
  delete [] pd_hash_table;

#ifdef UnixArchitecture
#  ifdef __vxWorks__
  // *** How do we clean up on vxWorks?
#  else
  close(pd_pipe_read);
  close(pd_pipe_write);
#  endif
#endif
}


/////////////////////////////////////////////////////////////////////////
void
SocketCollection::setSelectable(SocketHandle_t sock, 
				CORBA::Boolean now,
				CORBA::Boolean data_in_buffer,
				CORBA::Boolean hold_lock) {

  ASSERT_OMNI_TRACEDMUTEX_HELD(pd_fdset_lock, hold_lock);

  if (!hold_lock) pd_fdset_lock.lock();

  pd_fdset.Add( sock, now, data_in_buffer );
  
  if (now || data_in_buffer) {
    // Wake up the thread blocked in select() if we can.
    if (pd_pipe_write > 0) {
#ifdef UnixArchitecture
      if (!pd_pipe_full) {
	char data = '\0';
	pd_pipe_full = 1;
	write(pd_pipe_write, &data, 1);
      }
#endif
    }
    else {
      pd_poll_cond.signal();
    }
  }
  if (!hold_lock) pd_fdset_lock.unlock();
}

/////////////////////////////////////////////////////////////////////////
void
SocketCollection::clearSelectable(SocketHandle_t sock) {

  omni_tracedmutex_lock sync(pd_fdset_lock);
  pd_fdset.Remove( sock );
}

#ifdef GDB_DEBUG

static
int
do_poll(pollfd * r, unsigned int l, int t) {
  return poll(r,l,t);
}

#endif

/////////////////////////////////////////////////////////////////////////
CORBA::Boolean
SocketCollection::Select() {

  struct timeval timeout;
  unsigned int	 total;
  pollfd *	 rfds;

 again:

  // (pd_abs_sec,pd_abs_nsec) define the absolute time when we switch fdset
  SocketSetTimeOut(pd_abs_sec,pd_abs_nsec,timeout);

  if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {

    omni_thread::get_time(&pd_abs_sec,&pd_abs_nsec,
			  scan_interval_sec,scan_interval_nsec);
    timeout.tv_sec  = scan_interval_sec;
    timeout.tv_usec = scan_interval_nsec / 1000;

    omni_tracedmutex_lock sync(pd_fdset_lock);
    rfds = pd_fdset.enterPoll(total);
    pd_fdset.Reschedule();
  }
  else {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    rfds = pd_fdset.enterPoll(total);
  }

  int nready;

  if (total != 0) {
#ifndef GDB_DEBUG
    nready = poll(rfds,total,timeout.tv_sec*1000+(timeout.tv_usec/1000));
#else
    nready = do_poll(rfds,total,timeout.tv_sec*1000+(timeout.tv_usec/1000));
#endif
  }
  else {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    pd_poll_cond.timedwait(pd_abs_sec,pd_abs_nsec);
    // The condition variable should be poked so we are woken up
    // immediately when there is something to monitor.  We cannot use
    // select(0,0,0,0,&timeout) because win32 doesn't like it.
    nready = 0; // simulate a timeout
  }

  if (nready == RC_SOCKET_ERROR) {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    pd_fdset.exitPoll();
    if (ERRNO == RC_EBADF) {
      omniORB::logs(20, "poll() returned EBADF, retrying");
      goto again;
    }
    else if (ERRNO != RC_EINTR) {
      return 0;
    }
    else {
      return 1;
    }
  }

  if (total != 0) {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    pd_fdset.exitPoll();

    while (pd_fdset.needsProcessing() || nready) {
	if (rfds->revents) {
	    nready--;
	}
		
	if (rfds->events & FDCLRD) {
	    pd_fdset.Remove(rfds->fd);
	    continue;	
	}
	
	if (rfds->events & FDMVD) {
	    rfds->revents = 0;
	    pd_fdset.Move(rfds->fd);
	    continue;	
	}
	
	if (rfds->revents || (rfds->events & FDDIB)) {
	    if (rfds->fd == pd_pipe_read) {
#ifdef UnixArchitecture
		char data;
		read(pd_pipe_read, &data, 1);
		pd_pipe_full = 0;
#endif
            }
	    else {
		int fd = rfds->fd;
		pd_fdset.Remove(fd);

		if (!notifyReadable(fd)) return 0;
	      
		continue;
	    }
	}
	rfds++;
    }
  }  
  return 1;

}

/////////////////////////////////////////////////////////////////////////
CORBA::Boolean
SocketCollection::Peek(SocketHandle_t sock) {

  {
    omni_tracedmutex_lock sync(pd_fdset_lock);
   
    // Do nothing if this socket is not set to be monitored.
    if (!pd_fdset.isSet(sock,0))
      return 0;

    // If data in buffer is set, do callback straight away.
    if (pd_fdset.isSet(sock,1)) {
      pd_fdset.Remove(sock);
      return 1;
    }
  }

  struct timeval timeout;
  // select on the socket for half the time of scan_interval, if no request
  // arrives in this interval, we just let AcceptAndMonitor take care
  // of it.
  timeout.tv_sec  = scan_interval_sec / 2;
  timeout.tv_usec = scan_interval_nsec / 1000 / 2;
  if (scan_interval_sec % 2) timeout.tv_usec += 500000;
  
  pollfd rfds;

  do {
    rfds.fd = sock;
    rfds.events = POLLIN;
#ifndef GDB_DEBUG
    int nready = poll(&rfds,1,timeout.tv_sec*1000+(timeout.tv_usec/1000));
#else
    int nready = do_poll(&rfds,1,timeout.tv_sec*1000+(timeout.tv_usec/1000));
#endif

    if (nready == RC_SOCKET_ERROR) {
      if (ERRNO != RC_EINTR) {
	break;
      }
      else {
	continue;
      }
    }

    // Reach here if nready >= 0

    if (rfds.revents) {
      omni_tracedmutex_lock sync(pd_fdset_lock);

      // Are we still interested?
      if (pd_fdset.isSet(sock,0)) {
	pd_fdset.Remove(sock);
	return 1;
      }
    }
    break;

  } while(1);

  return 0;
}


/////////////////////////////////////////////////////////////////////////
void
SocketCollection::incrRefCount()
{
  omni_tracedmutex_lock sync(pd_fdset_lock);
  OMNIORB_ASSERT(pd_refcount > 0);
  pd_refcount++;
}

/////////////////////////////////////////////////////////////////////////
void
SocketCollection::decrRefCount()
{
  int refcount;
  {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    OMNIORB_ASSERT(pd_refcount > 0);
    refcount = --pd_refcount;
  }
  if (refcount == 0) delete this;
}

/////////////////////////////////////////////////////////////////////////
void
SocketCollection::addSocket(SocketLink* conn)
{
  omni_tracedmutex_lock sync(pd_fdset_lock);
  SocketLink** head = &(pd_hash_table[conn->pd_socket % hashsize]);
  conn->pd_next = *head;
  *head = conn;
  OMNIORB_ASSERT(pd_refcount > 0);
  pd_refcount++;
}

/////////////////////////////////////////////////////////////////////////
SocketLink*
SocketCollection::removeSocket(SocketHandle_t sock)
{
  int refcount  = 0; // Initialise to stop over-enthusiastic compiler warnings
  SocketLink* l = 0;
  {
    omni_tracedmutex_lock sync(pd_fdset_lock);
    SocketLink** head = &(pd_hash_table[sock % hashsize]);
    while (*head) {
      if ((*head)->pd_socket == sock) {
	l = *head;
	*head = (*head)->pd_next;
	OMNIORB_ASSERT(pd_refcount > 0);
	refcount = --pd_refcount;
	break;
      }
      head = &((*head)->pd_next);
    }
  }
  if (l && refcount == 0) delete this;
  return l;
}

/////////////////////////////////////////////////////////////////////////
SocketLink*
SocketCollection::findSocket(SocketHandle_t sock,
				CORBA::Boolean hold_lock) {

  if (!hold_lock) pd_fdset_lock.lock();

  SocketLink* l = 0;
  SocketLink** head = &(pd_hash_table[sock % hashsize]);
  while (*head) {
    if ((*head)->pd_socket == sock) {
      l = *head;
      break;
    }
    head = &((*head)->pd_next);
  }

  if (!hold_lock) pd_fdset_lock.unlock();

  return l;
}

OMNI_NAMESPACE_END(omni)
SocketCollection.h (text/plain, 11 KB)
// -*- Mode: C++; -*-
//                            	Package   : omniORB
// SocketCollection.h		Created on: 23 Jul 2003
//				Author    : Serguei Kolos
//
//    Copyright (C) 2001 AT&T Laboratories Cambridge
//
//    This file is part of the omniORB library
//
//    The omniORB library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Library General Public
//    License as published by the Free Software Foundation; either
//    version 2 of the License, or (at your option) any later version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//    Library General Public License for more details.
//
//    You should have received a copy of the GNU Library General Public
//    License along with this library; if not, write to the Free
//    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//    02111-1307, USA
//
//
// Description:
//	*** PROPRIETORY INTERFACE ***
//

/*
*/

#ifndef __SOCKETCOLLECTION_H__
#define __SOCKETCOLLECTION_H__

////////////////////////////////////////////////////////////////////////
//  Platform feature selection

#define SOCKNAME_SIZE_T OMNI_SOCKNAME_SIZE_T

#define USE_NONBLOCKING_CONNECT

#if defined(__linux__)
#   define USE_POLL
#endif

#if defined(__sunos__)
#   define USE_POLL
#endif

#if defined(__hpux__)
#   if __OSVERSION__ >= 11
#       define USE_POLL
#   endif
#   define USE_FAKE_INTERRUPTABLE_RECV
#endif

#if defined(__freebsd__)
#  define USE_POLL
#endif

#if defined(__WIN32__)
#   define USE_FAKE_INTERRUPTABLE_RECV
#endif

#if defined(__irix__)
#   define USE_POLL
#endif

////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//             win32 API
//
#if defined(__WIN32__)

#  include <sys/types.h>
#  include <libcWrapper.h>

#  define RC_INADDR_NONE     INADDR_NONE
#  define RC_INVALID_SOCKET  INVALID_SOCKET
#  define RC_SOCKET_ERROR    SOCKET_ERROR
#  define INETSOCKET         PF_INET
#  define CLOSESOCKET(sock)  closesocket(sock)
#  define SHUTDOWNSOCKET(sock) ::shutdown(sock,2)
#  define ERRNO              ::WSAGetLastError()
#  define EINPROGRESS        WSAEWOULDBLOCK
#  define RC_EINTR           WSAEINTR
#  define RC_EBADF           WSAENOTSOCK
#  define NEED_SOCKET_SHUTDOWN_FLAG 1

OMNI_NAMESPACE_BEGIN(omni)

typedef SOCKET SocketHandle_t;

OMNI_NAMESPACE_END(omni)

#else

////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//             unix(ish)
//
#  if defined(__vxWorks__)
#    include <sockLib.h>
#    include <hostLib.h>
#    include <ioLib.h>
#    include <netinet/tcp.h>
#  else
#    include <sys/time.h>
#  endif
#  include <sys/socket.h>
#  include <netinet/in.h>
#  include <netinet/tcp.h>
#  include <arpa/inet.h>
#  include <unistd.h>
#  include <sys/types.h>
#  include <errno.h>
#  include <libcWrapper.h>

#  if defined(USE_POLL)
#    include <poll.h>
#  endif

#  if !defined(__VMS)
#    include <fcntl.h>
#  endif

#  if defined (__uw7__)
#    ifdef shutdown
#      undef shutdown
#    endif
#  endif

#  if defined(__VMS) && defined(USE_tcpSocketVaxRoutines)
#    include "tcpSocketVaxRoutines.h"
#    undef accept
#    undef recv
#    undef send
#    define accept(a,b,c) tcpSocketVaxAccept(a,b,c)
#    define recv(a,b,c,d) tcpSocketVaxRecv(a,b,c,d)
#    define send(a,b,c,d) tcpSocketVaxSend(a,b,c,d)
#  endif

#  ifdef __rtems__
extern "C" int select (int,fd_set*,fd_set*,fd_set*,struct timeval *);
#  endif

#  define RC_INADDR_NONE     ((CORBA::ULong)-1)
#  define RC_INVALID_SOCKET  (-1)
#  define RC_SOCKET_ERROR    (-1)
#  define INETSOCKET         AF_INET
#  define CLOSESOCKET(sock)  close(sock)

#  if defined(__sunos__) && defined(__sparc__) && __OSVERSION__ >= 5
#    define SHUTDOWNSOCKET(sock)  ::shutdown(sock,2)
#  elif defined(__osf1__) && defined(__alpha__)
#    define SHUTDOWNSOCKET(sock)  ::shutdown(sock,2)
#  else
     // XXX none of the above, calling shutdown() may not have the
     // desired effect.
#    define SHUTDOWNSOCKET(sock)  ::shutdown(sock,2)
#  endif

#  define ERRNO              errno
#  define RC_EINTR           EINTR
#  define RC_EBADF           EBADF

OMNI_NAMESPACE_BEGIN(omni)

typedef int    SocketHandle_t;

OMNI_NAMESPACE_END(omni)

#endif

#if defined(NEED_GETHOSTNAME_PROTOTYPE)
extern "C" int gethostname(char *name, int namelen);
#endif

OMNI_NAMESPACE_BEGIN(omni)

// This class is a replacement for the two fd sets used
// by the select-based SocketCollection implementation
// It holds an array of pollfd structures, which consits
// from two parts:
// 1. elements [0..length_now-1] will be used 
//    for the next poll invocation
// 2. elements [length_now..length_full-1] will be passed 
//    to poll only if the Reschedule method has been called
class SocketHandleSet
{
public:
  
  SocketHandleSet();
  ~SocketHandleSet();
	
  // Adds socket to the array of pollfd structures 
  // If now == 1, this socket is set to the first part of the 
  //   array and will be used for the next poll invocation
  // If now == 0 the socket will be stored in the last part and will
  //   be passed to poll only if the Reschedule method is called
  // If data_in_buffer == 1 the FDDTNBFFR bit will be set in the
  //   events field of the corresponding pollfd structure
  void Add( SocketHandle_t fd, CORBA::Boolean now, CORBA::Boolean data_in_buffer );
  
  // Removes socket from the array of pollfd structures 
  void Remove( SocketHandle_t fd );

  // Moves socket from the current position to the end of array of pollfd structures 
  void Move( SocketHandle_t fd );

  // Checks if socket exist in the array of pollfd structures 
  // If data_in_buffer == 1, function returns 1 only if socket has the FDDTNBFFR
  //   bit set in the events field of the corresponding pollfd structure
  // If data_in_buffer == 0, function returns 1 if socket is in 
  //   the array of pollfd structures
  CORBA::Boolean isSet( SocketHandle_t fd, CORBA::Boolean data_in_buffer );
	  
  // Must be called before entering the poll
  inline pollfd * enterPoll( unsigned int & length ) {
    length = pd_length_now;
    pd_used_by_poll_n = pd_length_now;
    return pd_data;
  }

  // Must be called after exiting the poll
  inline void exitPoll( ) {
    pd_used_by_poll_n = 0;
  }
  
  // Schedule all the sockets for the next poll invocation
  inline void Reschedule( ) {
    pd_length_now = pd_length_full;
  }
  
  // Returns 1 if there are sockets in the array, which were 
  // requested to be moved or removed, or if there are 
  // unprocessed sockets marked as having data in buffer
  inline CORBA::Boolean needsProcessing( ) {
    return (    pd_to_be_removed_n
	     || pd_to_be_moved_n
	     || pd_dib_n );
  }
	
private:	
  pollfd *		pd_data;
  short	*		pd_index;	// takes more memory, but allows to find
  					// sockets int the data array very efficiently
  unsigned short	pd_length_full;
  unsigned short	pd_length_now;
  unsigned short	pd_used_by_poll_n;
  unsigned short	pd_to_be_removed_n;
  unsigned short	pd_to_be_moved_n;
  unsigned short	pd_dib_n;
};


class SocketCollection;

extern void SocketSetTimeOut(unsigned long abs_sec,
			     unsigned long abs_nsec,struct timeval& t);

extern int SocketSetnonblocking(SocketHandle_t sock);

extern int SocketSetblocking(SocketHandle_t sock);

extern int SocketSetCloseOnExec(SocketHandle_t sock);

class SocketLink {

public:
  SocketLink(SocketHandle_t s)
    : pd_socket(s),
#ifdef NEED_SOCKET_SHUTDOWN_FLAG
      pd_shutdown(0),
#endif
      pd_next(0) {}

  ~SocketLink() {}

  friend class SocketCollection;

protected:
  SocketHandle_t pd_socket;

#ifdef NEED_SOCKET_SHUTDOWN_FLAG
  // select() on Windows does not return an error after the socket has
  // shutdown, so we have to store an extra flag here.
  CORBA::Boolean pd_shutdown;
#endif

private:
  SocketLink*    pd_next;
};

class SocketCollection {
public:

  SocketCollection();

protected:
  virtual ~SocketCollection();

  virtual CORBA::Boolean notifyReadable(SocketHandle_t) = 0;
  // Callback used by Select(). This method is called while holding
  // pd_fdset_lock.

public:
  void setSelectable(SocketHandle_t sock, CORBA::Boolean now,
		     CORBA::Boolean data_in_buffer,
		     CORBA::Boolean hold_lock=0);
  // Indicates that this socket should be watched by a poll()
  // so that any new data arriving on the connection will be noted.
  // If now == 1, immediately make this socket part of the poll
  // set.
  // If data_in_buffer == 1, treat this socket as if there are
  // data available from the connection already.
  // If hold_lock == 1, pd_fdset_lock is already held.

  void clearSelectable(SocketHandle_t);
  // Indicates that this connection need not be watched any more.

  CORBA::Boolean Select();
  // Returns TRUE(1) if the Select() has successfully done a scan.
  // otherwise returns false(0) to indicate that an error has been
  // detected and this function should not be called again.
  //
  // For each of the sockets that has been marked watchable and indeed
  // has become readable, call notifyReadable() with the socket no.
  // as the argument.

  CORBA::Boolean Peek(SocketHandle_t sock);
  // Do nothing and returns immediately if the socket has not been
  // set to be watched by a previous setSelectable().
  // Otherwise, monitor the socket's status for a short time.
  // Returns TRUE(1) if the socket becomes readable.
  // otherwise returns FALSE(0).

  void incrRefCount();
  void decrRefCount();

  void addSocket(SocketLink* conn);
  // Add this socket to the collection. <conn> is associated with the
  // socket and should be added to the table hashed by the socket number.
  // Increments this collection's refcount.

  SocketLink* removeSocket(SocketHandle_t sock);
  // Remove the socket from this collection. Return the socket which has
  // been removed. Return 0 if the socket is not found.
  // Decrements this collection's refcount if a socket is removed.

  SocketLink* findSocket(SocketHandle_t sock,
			 CORBA::Boolean hold_lock=0);
  // Returns the connection that is associated with this socket.
  // Return 0 if this socket cannot be found in the hash table.
  // if hold_lock == 1, the caller has already got the lock on pd_fdset_lock.
  // (use purely by member functions.)

  static unsigned long scan_interval_sec;
  static unsigned long scan_interval_nsec;

  static CORBA::ULong  hashsize;

private:
  SocketHandleSet      pd_fdset;
  omni_tracedmutex     pd_fdset_lock;
  omni_tracedcondition pd_poll_cond; // timedwait on if nothing to select
  unsigned long        pd_abs_sec;
  unsigned long        pd_abs_nsec;
  int                  pd_pipe_read;
  int                  pd_pipe_write;
  CORBA::Boolean       pd_pipe_full;
  int                  pd_refcount;

protected:
  SocketLink**         pd_hash_table;

};

OMNI_NAMESPACE_END(omni)

#endif // __SOCKETCOLLECTION_H__