ssize_t XPlat_SocketUtils_send(XPlat_Socket s, const void *buf, size_t count) { if( count == 0 ) return 0; xplat_dbg(5, xplat_printf(FLF, stderr, "writing %"PRIszt" bytes to fd=%d)\n", count, s)); int flags = 0; #if defined(os_linux) // don't generate SIGPIPE flags = MSG_NOSIGNAL; #endif ssize_t bytes_written = 0; while( bytes_written != (ssize_t) count ) { ssize_t ret = send( s, ((const char*)buf) + bytes_written, count - bytes_written, flags ); int err = errno; if( ret == -1 ) { if( (err == EINTR) || (err == EAGAIN) || (err == EWOULDBLOCK) ) { continue; } else { xplat_dbg(3, xplat_printf(FLF, stderr, "Warning: premature return from send(). " "Wrote %"PRIsszt" of %"PRIszt" bytes ('%s')\n", bytes_written, count, strerror(err))); return bytes_written; } } else { bytes_written += ret; if( bytes_written < (ssize_t) count ) { continue; } else { xplat_dbg(5, xplat_printf(FLF, stderr, "returning %"PRIsszt"\n", bytes_written)); return bytes_written; } } } assert(!"XPlat_SocketUtils_send - invalid code path"); return -1; }
ssize_t XPlat_SocketUtils_recv(XPlat_Socket s, void *buf, size_t count) { if( count == 0 ) return 0; ssize_t bytes_recvd = 0; while( bytes_recvd != (ssize_t) count ) { ssize_t ret = recv( s, ((char*)buf) + bytes_recvd, count - bytes_recvd, 0 ); int err = errno; if( ret == -1 ) { if( (err == EINTR) || (err == EAGAIN) ) { continue; } else { xplat_dbg(3, xplat_printf(FLF, stderr, "Warning: premature return from recv(). " "Got %" PRIsszt " of %" PRIszt " bytes ('%s')\n", bytes_recvd, count, strerror(err))); return bytes_recvd; } } else if( ret == 0 ) { // the remote endpoint has gone away xplat_dbg(3, xplat_printf(FLF, stderr, "recv() returned 0 (peer likely gone)\n")); return -1; } else { bytes_recvd += ret; if( bytes_recvd < (ssize_t) count ) { continue; } else { xplat_dbg(5, xplat_printf(FLF, stderr, "returning %" PRIsszt "\n", bytes_recvd)); return bytes_recvd; } } } assert(!"XPlat_SocketUtils_recv - invalid code path"); return -1; }
int PthreadMonitorData::Unlock( void ) { int rc = pthread_mutex_unlock( &mutex ); if( rc ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "Error: pthread_mutex_unlock() returned '%s'\n", strerror( rc ))); fflush(stderr); } return rc; }
static bool_t SetTcpNoDelay( XPlat_Socket sock ) { #if defined(TCP_NODELAY) // turn off Nagle algorithm for coalescing packets int optval = 1; bool_t soret = XPlat_SocketUtils_SetOption( sock, IPPROTO_TCP, TCP_NODELAY, (void*) &optval, (socklen_t) sizeof(optval) ); if( ! soret ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set option\n") ); return false; } #else xplat_dbg(1, xplat_printf(FLF, stderr, "WARNING: TCP_NODELAY not found! Performance WILL suffer.\n")); #endif return true; }
static bool_t SetCloseOnExec( XPlat_Socket sock ) { #ifndef os_windows int fdflag, fret; fdflag = fcntl( sock, F_GETFD ); if( fdflag == -1 ) { // failed to retrieve the socket descriptor flags xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to get flags\n") ); return false; } else { fret = fcntl( sock, F_SETFD, fdflag | FD_CLOEXEC ); if( fret == -1 ) { // failed to set the socket descriptor flags xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set flags\n") ); return false; } } #endif return true; }
static bool_t ClearFlag( XPlat_Socket sock, int flag ) { int fdflag = fcntl( sock, F_GETFL ); if( -1 == fdflag ) { // failed to retrieve the socket status flags xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to get flags\n") ); return false; } else { int fret; fret = fcntl( sock, F_SETFL, fdflag & ~flag ); if( -1 == fret ) { // failed to set the socket status flags return false; } } return true; }
bool_t XPlat_SocketUtils_GetPort( const XPlat_Socket sock, XPlat_Port* port ) { const char* err_str; int err; struct sockaddr_in local_addr; socklen_t sockaddr_len = sizeof( local_addr ); if( -1 == getsockname(sock, (struct sockaddr*) &local_addr, &sockaddr_len) ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "getsockname(%d) failed with %s\n", sock, err_str) ); return false; } *port = ntohs( local_addr.sin_port ); return 0; }
Monitor::Monitor( void ) { static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; int ret; bool failed = false; pthread_mutex_lock( &init_mutex ); data = new PthreadMonitorData; if(data == NULL) { failed = true; goto ctor_fail; } ctor_fail: pthread_mutex_unlock( &init_mutex ); if(failed) { xplat_dbg(1, xplat_printf(FLF, stderr, "Error: Failed to construct Monitor\n")); data = NULL; } }
int WinMonitorData::ConditionVariable::TimedWait( int milliseconds ) { int ret = -1; DWORD dwret; // used for return value from Win32 calls DWORD msecs; // used for time limit if(milliseconds <= 0) { xplat_dbg(1, xplat_printf(FLF, stderr, "Error: TimedWait received %d ms limit\n", milliseconds)); return ret; } msecs = (DWORD)milliseconds; // We're going to be waiting, so bump the number of waiters. // Even though we currently have our owning Monitor's mutex locked, // we need to protect access to this variable because our access to // this variable later in this file occur when we do *not* have it locked. EnterCriticalSection( &nWaitersMutex ); nWaiters++; LeaveCriticalSection( &nWaitersMutex ); // Atomically release our owning object's mutex and // wait on our semaphore. ("Signalling" a Win32 Mutex releases it.) dwret = SignalObjectAndWait( hMutex, // obj to signal hWaitSemaphore, // obj to wait on msecs, // timeout FALSE ); // alertable? if( dwret == WAIT_OBJECT_0 ) { // We have been released from the semaphore. // At this point, we do *not* have the critical section mutex. EnterCriticalSection( &nWaitersMutex ); nWaiters--; // check if we were released via a broadcast and we are // the last thread to be released bool signalBroadcaster = (waitReleasedByBroadcast && (nWaiters == 0)); LeaveCriticalSection( &nWaitersMutex ); if( signalBroadcaster ) { // We were unblocked by a broadcast and we were the last // waiter to run. We need to: // // 1. Signal the broadcaster that it should continue (and // release the owning Monitor's mutex lock) // 2. Try to acquire the owning Monitor's mutex lock. // // We need to do both in the same atomic operation. dwret = SignalObjectAndWait( hAllWaitersReleasedEvent, // obj to signal hMutex, // obj to wait on INFINITE, // timeout FALSE ); // alertable? } else { // We were unblocked by a signal operation, or we were not // the last waiter to run. We just need to try to reacquire the // owning Monitor's mutex lock. dwret = WaitForSingleObject( hMutex, INFINITE ); } if( dwret == WAIT_OBJECT_0 ) { ret = 0; } else { // the wait or signalandwait failed // TODO how to indicate this to the user? } } else { // SignalObjectAndWait failed // TODO how to indicate the failure? } return ret; }
// Returns -1 for a fatal error, 0 on success, and 1 if the time given in // milliseconds has expired before the condition variable has been signalled. int PthreadMonitorData::TimedWaitOnCondition( unsigned int cvid, int milliseconds ) { int gt_ret, ret = -1; if( milliseconds < 0 ) { return ret; } pthread_cond_t *cv = NULL; ConditionVariableMap::iterator iter = cvmap.find( cvid ); if( iter != cvmap.end() ) { cv = cvmap[cvid]; if( NULL == cv ) { xplat_dbg(1, xplat_printf(FLF, stderr, "NULL condition variable\n")); return ret; } } else { return ret; } // Get time of day for 'abstime' arg of pthread_cond_timedwait struct timeval tv; gt_ret = gettimeofday( &tv, NULL ); if( -1 == gt_ret ) { int err = errno; xplat_dbg(1, xplat_printf(FLF, stderr, "gettimeofday() failed - %s\n", strerror(err))); return ret; } time_t sec = milliseconds / 1000; long msec = milliseconds % 1000; long nanosec = msec * 1000000L; // ms -> ns sec += tv.tv_sec; nanosec += (tv.tv_usec * 1000L); if( nanosec >= 1000000000L ) { nanosec -= 1000000000L; sec++; } struct timespec ts; ts.tv_sec = sec; ts.tv_nsec = nanosec; ret = pthread_cond_timedwait( cv, &mutex, &ts ); if( ETIMEDOUT == ret ) { xplat_dbg(3, xplat_printf(FLF, stderr, "time out!\n")); } else if( ret ) { xplat_dbg(1, xplat_printf(FLF, stderr, "pthread_cond_timedwait() failed - %s\n", strerror(ret))); ret = -1; } return ret; }
bool_t XPlat_SocketUtils_Connect( const char* host, const XPlat_Port port, XPlat_Socket* sock, unsigned num_retry ) { unsigned nConnectTries = 0; int err, cret = -1; XPlat_Socket _sock = *sock; const char* err_str = NULL; struct hostent *server_hostent = NULL; struct sockaddr_in server_addr; xplat_dbg( 3, xplat_printf(FLF, stderr, "host=%s port=%hu sock=%d\n", host, port, _sock) ); if( InvalidSocket == _sock ) { _sock = socket( AF_INET, SOCK_STREAM, 0 ); if ( InvalidSocket == _sock ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "socket() failed with '%s'\n", err_str) ); return false; } xplat_dbg( 5, xplat_printf(FLF, stderr, "socket() => %d\n", _sock) ); } server_hostent = gethostbyname( host ); if ( NULL == server_hostent ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "gethostbyname() failed with '%s'\n", err_str) ); return false; } memset( &server_addr, 0, sizeof(server_addr) ); server_addr.sin_family = AF_INET; server_addr.sin_port = htons( port ); memcpy( &server_addr.sin_addr, server_hostent->h_addr_list[0], sizeof(struct in_addr) ); do { cret = connect( _sock, (struct sockaddr *) &server_addr, (socklen_t) sizeof(server_addr) ); if( -1 == cret ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 5, xplat_printf(FLF, stderr, "connect() failed with '%s'\n", err_str) ); if( ! (XPlat_Error_ETimedOut(err) || XPlat_Error_EConnRefused(err)) ) { xplat_dbg(1, xplat_printf(FLF, stderr, "connect() to %s:%hu failed with '%s'\n", host, port, err_str)); return false; } nConnectTries++; xplat_dbg( 3, xplat_printf(FLF, stderr, "timed out %d times\n", nConnectTries) ); if( (num_retry > 0) && (nConnectTries >= num_retry) ) break; // delay before trying again (more each time) sleep( nConnectTries ); } } while( -1 == cret ); if( -1 == cret ) { err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "connect() to %s:%d failed with '%s'\n", host, port, err_str) ); return false; } xplat_dbg( 5, xplat_printf(FLF, stderr, "connected to %s:%d \n", host, port) ); // Close socket on exec if( ! SetCloseOnExec(_sock) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "XPlat_SocketUtils_Connect - " "failed to set close-on-exec\n") ); } // Turn off Nagle algorithm if( ! SetTcpNoDelay(_sock) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set TCP_NODELAY\n") ); } xplat_dbg( 3, xplat_printf(FLF, stderr, "returning socket=%d\n", _sock) ); *sock = _sock; return true; }
bool_t XPlat_SocketUtils_AcceptConnection( XPlat_Socket listen_sock, XPlat_Socket* connected_sock, int timeout_sec, bool_t nonblock ) { int err, maxfd, retval; XPlat_Socket connection; const char* err_str; fd_set readfds; struct timeval tv; xplat_dbg( 3, xplat_printf(FLF, stderr, "listening on socket=%d\n", listen_sock) ); *connected_sock = InvalidSocket; if( nonblock && (0 == timeout_sec) ) timeout_sec = 1; do { if( timeout_sec > 0 ) { // use select with timeout FD_ZERO( &readfds ); FD_SET( listen_sock, &readfds ); tv.tv_sec = timeout_sec; tv.tv_usec = 0; maxfd = listen_sock + 1; retval = select( maxfd, &readfds, NULL, NULL, &tv ); err = XPlat_NetUtils_GetLastError(); if( retval == 0 ) { // timed-out return false; // let caller decide what's next } else if( retval < 0 ) { err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "select() failed with '%s'\n", err_str) ); return false; } } // now try to accept a connection connection = accept( listen_sock, NULL, NULL ); if( -1 == connection ) { err = XPlat_NetUtils_GetLastError(); if( nonblock && (EWOULDBLOCK == err) ) return false; // let caller decide what's next if( EWOULDBLOCK != err ) { err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "accept() failed with '%s'\n", err_str) ); } if( EINTR != err ) return false; } } while( -1 == connection ); // Set the socket to be blocking if( ! XPlat_SocketUtils_SetBlockingMode(connection, true) ) xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set blocking\n") ); // Close socket on exec if( ! SetCloseOnExec(connection) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set close-on-exec\n") ); } // Turn off Nagle algorithm if( ! SetTcpNoDelay(connection) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set TCP_NODELAY\n") ); } xplat_dbg( 3, xplat_printf(FLF, stderr, "returning socket=%d\n", connection) ); *connected_sock = connection; return true; }
bool_t XPlat_SocketUtils_CreateListening( XPlat_Socket* sock, XPlat_Port* port, bool_t nonblock ) { static int backlog = 128; int err, optval; bool_t soret, success; XPlat_Socket _sock; XPlat_Port _port = *port; const char* err_str = NULL; struct sockaddr_in local_addr; if( InvalidPort == _port ) _port = 0; _sock = socket( AF_INET, SOCK_STREAM, 0 ); if( -1 == _sock ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "socket() failed with '%s'\n", err_str) ); return false; } xplat_dbg( 3, xplat_printf(FLF, stderr, "sock:%d, port:%d\n", _sock, _port) ); // Close socket on exec if( ! SetCloseOnExec(_sock) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set close-on-exec\n") ); } // Set listening socket to non-blocking if requested if( nonblock ) { if( ! XPlat_SocketUtils_SetBlockingMode(_sock, false) ) xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set non-blocking\n") ); } #ifndef os_windows /* Set the socket so that it does not hold onto its port after * the process exits (needed because on at least some platforms we * use well-known ports when connecting sockets) */ optval = 1; soret = XPlat_SocketUtils_SetOption( _sock, SOL_SOCKET, SO_REUSEADDR, (void*) &optval, (socklen_t) sizeof(optval) ); if( ! soret ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "setsockopt() failed with '%s'\n", err_str) ); } #endif memset( &local_addr, 0, sizeof(local_addr) ); local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = htonl( INADDR_ANY ); if( 0 != _port ) { // try to bind and listen using the supplied port local_addr.sin_port = htons( _port ); if( -1 == bind(_sock, (struct sockaddr*)&local_addr, sizeof(local_addr)) ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "bind() to static port %d failed with '%s'\n", _port, err_str) ); XPlat_SocketUtils_Close( _sock ); return false; } } #ifndef os_windows // else, the system will assign a port for us in listen if( -1 == listen(_sock, backlog) ) { err = XPlat_NetUtils_GetLastError(); err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "listen() failed with '%s'\n", err_str) ); XPlat_SocketUtils_Close( _sock ); return false; } // determine which port we were actually assigned to if( ! XPlat_SocketUtils_GetPort(_sock, &_port) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to obtain port from socket\n" ) ); XPlat_SocketUtils_Close( _sock ); return false; } #else // try binding ports, starting from 1st dynamic port _port = 49152; success = false; do { local_addr.sin_port = htons( _port ); if( -1 == bind(_sock, (struct sockaddr*)&local_addr, sizeof(local_addr)) ) { err = XPlat_NetUtils_GetLastError(); if( XPlat_Error_EAddrInUse( err ) ) { ++_port; continue; } else { err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "bind() to dynamic port %d failed with '%s'\n", _port, err_str) ); XPlat_SocketUtils_Close( _sock ); return false; } } else { if( -1 == listen(_sock, backlog) ) { err = XPlat_NetUtils_GetLastError(); if( XPlat_Error_EAddrInUse( err ) ) { ++_port; continue; } else { err_str = XPlat_Error_GetErrorString(err); xplat_dbg( 1, xplat_printf(FLF, stderr, "listen() failed with '%s'\n", err_str) ); XPlat_SocketUtils_Close( _sock ); return false; } } success = true; } } while( ! success ); #endif // Turn off Nagle algorithm if( ! SetTcpNoDelay(_sock) ) { xplat_dbg( 1, xplat_printf(FLF, stderr, "failed to set TCP_NODELAY\n") ); } *port = _port; *sock = _sock; xplat_dbg( 3, xplat_printf(FLF, stderr, "returning socket=%d, port=%hu\n", _sock, _port) ); return true; }