/* Copyright (C) 2007-2008 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program 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 General Public License for more details. */ #include "sysdeps.h" #include <assert.h> #include <unistd.h> #include <sys/select.h> #include <errno.h> #include <memory.h> #include <stdio.h> #ifndef HAVE_WINSOCK #include <fcntl.h> #include <sys/socket.h> #include <sys/select.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <netdb.h> #endif /** QUEUE **/ #define SYS_MAX_QUEUE 16 typedef struct { int start; int end; void* pending[ SYS_MAX_QUEUE ]; } SysQueueRec, *SysQueue; static void sys_queue_reset( SysQueue queue ) { queue->start = queue->end = 0; } static void sys_queue_add( SysQueue queue, void* item ) { assert( queue->end - queue->start < SYS_MAX_QUEUE ); assert( queue->start == 0 ); assert( item != NULL ); queue->pending[ queue->end++ ] = item; } #if 0 static void sys_queue_remove( SysQueue queue, void* item ) { int nn, count; assert( queue->end > queue->start ); assert( item != NULL ); count = queue->end - queue->start; for ( nn = queue->start; count > 0; ++nn, --count ) { if ( queue->pending[nn] == item ) { queue->pending[nn] = queue->pending[nn+count-1]; queue->end -= 1; break; } } assert( 0 && "sys_queue_remove: item not found" ); } #endif static void* sys_queue_get( SysQueue queue ) { if (queue->end > queue->start) { return queue->pending[ queue->start++ ]; } return NULL; } /** CHANNELS **/ typedef struct SysChannelRec_ { SysChannel next; int fd; char active; char pending; char closed; int wanted; int ready; SysChannelCallback callback; void* opaque; } SysChannelRec; /*** channel allocation ***/ #define SYS_EVENT_MAX 3 #define SYS_MAX_CHANNELS 16 static SysChannelRec _s_channels0[ SYS_MAX_CHANNELS ]; static SysChannel _s_free_channels; static SysChannel sys_channel_alloc( void ) { SysChannel channel = _s_free_channels; assert( channel != NULL && "out of free channels" ); _s_free_channels = channel->next; channel->next = NULL; channel->active = 0; channel->closed = 0; channel->pending = 0; channel->wanted = 0; return channel; } static void sys_channel_free( SysChannel channel ) { if (channel->fd >= 0) { #ifdef _WIN32 shutdown( channel->fd, SD_BOTH ); #else shutdown( channel->fd, SHUT_RDWR ); #endif close(channel->fd); channel->fd = -1; } channel->wanted = 0; channel->ready = 0; channel->callback = NULL; channel->next = _s_free_channels; _s_free_channels = channel; } /* list of active channels */ static SysChannel _s_channels; /* used by select to wait on channel events */ static fd_set _s_fdsets[SYS_EVENT_MAX]; static int _s_maxfd; static void sys_channel_deactivate( SysChannel channel ) { assert( channel->active != 0 ); SysChannel *pnode = &_s_channels; for (;;) { SysChannel node = *pnode; assert( node != NULL ); if (node == channel) break; pnode = &node->next; } *pnode = channel->next; channel->next = NULL; channel->active = 0; } static void sys_channel_activate( SysChannel channel ) { assert( channel->active == 0 ); channel->next = _s_channels; _s_channels = channel; channel->active = 1; if (channel->fd > _s_maxfd) _s_maxfd = channel->fd; } /* queue of pending channels */ static SysQueueRec _s_pending_channels[1]; static void sys_init_channels( void ) { int nn; for (nn = 0; nn < SYS_MAX_CHANNELS-1; nn++) _s_channels0[nn].next = &_s_channels0[nn+1]; _s_free_channels = &_s_channels0[0]; for (nn = 0; nn < SYS_EVENT_MAX; nn++) FD_ZERO( &_s_fdsets[nn] ); _s_maxfd = -1; sys_queue_reset( _s_pending_channels ); } void sys_channel_on( SysChannel channel, int events, SysChannelCallback callback, void* opaque ) { int adds = events & ~channel->wanted; int removes = channel->wanted & ~events; channel->wanted = events; channel->callback = callback; channel->opaque = opaque; /* update global fdsets */ if (adds) { int ee; for (ee = 0; ee < SYS_EVENT_MAX; ee++) if (adds & (1 << ee)) FD_SET( channel->fd, &_s_fdsets[ee] ); } if (removes) { int ee; for (ee = 0; ee < SYS_EVENT_MAX; ee++) if (removes & (1 << ee)) FD_CLR( channel->fd, &_s_fdsets[ee] ); } if (events && !channel->active) { sys_channel_activate( channel ); } else if (!events && channel->active) { sys_channel_deactivate( channel ); } } int sys_channel_read( SysChannel channel, void* buffer, int size ) { char* buff = buffer; int count = 0; assert( !channel->closed ); while (size > 0) { int len = read(channel->fd, buff, size); if (len < 0) { if (errno == EINTR) continue; if (count == 0) count = -1; break; } buff += len; size -= len; count += len; } return count; } int sys_channel_write( SysChannel channel, const void* buffer, int size ) { const char* buff = buffer; int count = 0; assert( !channel->closed ); while (size > 0) { int len = write(channel->fd, buff, size); if (len < 0) { if (errno == EINTR) continue; if (count == 0) count = -1; break; } buff += len; size -= len; count += len; } return count; } void sys_channel_close( SysChannel channel ) { if (channel->active) { sys_channel_on( channel, 0, NULL, NULL ); } if (channel->pending) { /* we can't free the channel right now because it */ /* is in the pending list, set a flag */ channel->closed = 1; return; } if (!channel->closed) { channel->closed = 1; } sys_channel_free( channel ); } /** time measurement **/ SysTime sys_time_ms( void ) { struct timeval tv; gettimeofday( &tv, NULL ); return (SysTime)(tv.tv_usec / 1000) + (SysTime)tv.tv_sec * 1000; } /** timers **/ typedef struct SysTimerRec_ { SysTimer next; SysTime when; SysCallback callback; void* opaque; } SysTimerRec; #define SYS_MAX_TIMERS 16 static SysTimerRec _s_timers0[ SYS_MAX_TIMERS ]; static SysTimer _s_free_timers; static SysTimer _s_timers; static SysQueueRec _s_pending_timers[1]; static void sys_init_timers( void ) { int nn; for (nn = 0; nn < SYS_MAX_TIMERS-1; nn++) { _s_timers0[nn].next = & _s_timers0[nn+1]; } _s_free_timers = &_s_timers0[0]; sys_queue_reset( _s_pending_timers ); } SysTimer sys_timer_create( void ) { SysTimer timer = _s_free_timers; assert( timer != NULL && "too many timers allocated" ); _s_free_timers = timer->next; timer->next = NULL; return timer; } void sys_timer_unset( SysTimer timer ) { if (timer->callback != NULL) { SysTimer *pnode, node; pnode = &_s_timers; for (;;) { node = *pnode; if (node == NULL) break; if (node == timer) { *pnode = node->next; break; } pnode = &node->next; } timer->next = NULL; timer->callback = NULL; timer->opaque = NULL; } } void sys_timer_set( SysTimer timer, SysTime when, SysCallback callback, void* opaque ) { if (timer->callback != NULL) sys_timer_unset(timer); if (callback != NULL) { SysTime now = sys_time_ms(); if (now >= when) { callback( opaque ); } else { SysTimer *pnode, node; pnode = &_s_timers; for (;;) { node = *pnode; if (node == NULL || node->when >= when) { break; } pnode = &node->next; } timer->next = *pnode; *pnode = timer; timer->when = when; timer->callback = callback; timer->opaque = opaque; } } } void sys_timer_destroy( SysTimer timer ) { assert( timer != NULL && "sys_timer_destroy: bad argument" ); if (timer->callback != NULL) sys_timer_unset(timer); timer->next = _s_free_timers; _s_free_timers = timer; } static void sys_single_loop( void ) { fd_set rfd, wfd, efd; struct timeval timeout_tv, *timeout = NULL; int n; memcpy(&rfd, &_s_fdsets[0], sizeof(fd_set)); memcpy(&wfd, &_s_fdsets[1], sizeof(fd_set)); memcpy(&efd, &_s_fdsets[2], sizeof(fd_set)); if ( _s_timers != NULL ) { SysTime now = sys_time_ms(); SysTimer first = _s_timers; timeout = &timeout_tv; if (first->when <= now) { timeout->tv_sec = 0; timeout->tv_usec = 0; } else { SysTime diff = first->when - now; timeout->tv_sec = diff / 1000; timeout->tv_usec = (diff - timeout->tv_sec*1000) * 1000; } } n = select( _s_maxfd+1, &rfd, &wfd, &efd, timeout); if(n < 0) { if(errno == EINTR) return; perror("select"); return; } /* enqueue pending channels */ { int i; sys_queue_reset( _s_pending_channels ); for(i = 0; (i <= _s_maxfd) && (n > 0); i++) { int events = 0; if(FD_ISSET(i, &rfd)) events |= SYS_EVENT_READ; if(FD_ISSET(i, &wfd)) events |= SYS_EVENT_WRITE; if(FD_ISSET(i, &efd)) events |= SYS_EVENT_ERROR; if (events) { SysChannel channel; n--; for (channel = _s_channels; channel; channel = channel->next) { if (channel->fd != i) continue; channel->ready = events; channel->pending = 1; sys_queue_add( _s_pending_channels, channel ); break; } } } } /* enqueue pending timers */ { SysTimer timer = _s_timers; SysTime now = sys_time_ms(); sys_queue_reset( _s_pending_timers ); while (timer != NULL) { if (timer->when > now) break; sys_queue_add( _s_pending_timers, timer ); _s_timers = timer = timer->next; } } } void sys_main_init( void ) { sys_init_channels(); sys_init_timers(); } int sys_main_loop( void ) { for (;;) { SysTimer timer; SysChannel channel; /* exit if we have nothing to do */ if (_s_channels == NULL && _s_timers == NULL) break; sys_single_loop(); while ((timer = sys_queue_get( _s_pending_timers )) != NULL) { timer->callback( timer->opaque ); } while ((channel = sys_queue_get( _s_pending_channels )) != NULL) { int events; channel->pending = 0; if (channel->closed) { /* the channel was closed by a previous callback */ sys_channel_close(channel); } events = channel->ready; channel->ready = 0; channel->callback( channel->opaque, events ); } } return 0; } SysChannel sys_channel_create_tcp_server( int port ) { SysChannel channel; int on = 1; const int BACKLOG = 4; channel = sys_channel_alloc(); if (-1==(channel->fd=socket(AF_INET, SOCK_STREAM, 0))) { perror("socket"); sys_channel_free( channel ); return NULL; } /* Enable address re-use for server mode */ if ( -1==setsockopt( channel->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) )) { perror("setsockopt(SO_REUSEADDR)"); } { struct sockaddr_in servname; long in_addr = INADDR_ANY; servname.sin_family = AF_INET; servname.sin_port = htons(port); servname.sin_addr.s_addr=in_addr; if (-1==bind(channel->fd, (struct sockaddr*)&servname, sizeof(servname))) { perror("bind"); sys_channel_close(channel); return NULL; } /* Listen but don't accept */ if ( listen(channel->fd, BACKLOG) < 0 ) { perror("listen"); sys_channel_close(channel); return NULL; } } return channel; } SysChannel sys_channel_create_tcp_handler( SysChannel server_channel ) { int on = 1; SysChannel channel = sys_channel_alloc(); channel->fd = accept( server_channel->fd, NULL, 0 ); if (channel->fd < 0) { perror( "accept" ); sys_channel_free( channel ); return NULL; } /* set to non-blocking and disable TCP Nagle algorithm */ fcntl(channel->fd, F_SETFL, O_NONBLOCK); setsockopt(channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); return channel; } SysChannel sys_channel_create_tcp_client( const char* hostname, int port ) { struct hostent* hp; struct sockaddr_in addr; SysChannel channel = sys_channel_alloc(); int on = 1; hp = gethostbyname(hostname); if(hp == 0) { fprintf(stderr, "unknown host: %s\n", hostname); sys_channel_free(channel); return NULL; }; memset(&addr, 0, sizeof(addr)); addr.sin_family = hp->h_addrtype; addr.sin_port = htons(port); memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); channel->fd = socket(hp->h_addrtype, SOCK_STREAM, 0); if(channel->fd < 0) { sys_channel_free(channel); return NULL; } if(connect( channel->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror( "connect" ); sys_channel_free(channel); return NULL; } /* set to non-blocking and disable Nagle algorithm */ fcntl(channel->fd, F_SETFL, O_NONBLOCK); setsockopt( channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) ); return channel; }