Simple coroutine library integrated with IO event loop (libevent) / objects in plain C Snapshot
evthread.h
Go to the documentation of this file.
00001 #ifndef __EVTHREAD_H_
00002 #define __EVTHREAD_H_
00003 
00004 #include <corothread/cthread.h>
00005 #include <corothread/stacks.h>
00006 
00007 #include <sys/socket.h>
00008 #include <sys/types.h>
00009 #include <cutils/dlist.h>
00010 #include <nutils/addrutil.h>
00011 
00012 #include <event.h> 
00013 
00014 // ---------------------------------------------------------------------------
00015 
00016 /**
00017  * @defgroup EVLOOP
00018  * @brief event loop object.
00019  * The event loop  wraps a small user mode threading library where a user mode thread is created per connection.
00020  *
00021  * When a new connection is received via accept(2), then an EVSOCKET object is created that encapsulates this socket, 
00022  * and the socket is set to non blocking mode, and a user mode thread EVTHREAD is created that owns the new EVSOCKET object.
00023  * TCPACCEPTOR is the class that does all that.
00024  *
00025  * A user mode thread can own one or more EVSOCKET objects, it can read or write to an EVSOCKET object which has the API of a blocking socket.
00026  * when the socket blocks, (i.e. recv or send returns -1 and errno == EWOULDBLOCK) then the attached user mode thread is suspended, and control
00027  * returns to the event loop, the event loop schedules a different user mode thread, when an IO event occurs on a socket owned by that thread.
00028  *
00029  * A user mode thread (EVTHREAD) can create other sockets (EVSOCKET) that are then attached to the current thread.
00030  *
00031  * A use mode thread also owns a set of timer objects (EVTIMER), when the thread exits, all the timer objects are cleaned up.
00032  *
00033  * The event loop is implemented by libevent.
00034  *
00035  * Unlike other packages, where one can create several user mode thread and then has to cope with the problem of their synchronization, we keep things simple here.
00036  * There is one thread per connection, that is supposed to do protocol handling and processing, all on condition that processing does not involve heavy CPU intensive tasks.
00037  * If your problem does have such task, then there is also support for the Half-sync/Half-async pattern.
00038  * 
00039  * @{
00040  */
00041 typedef struct tagEVLOOP {
00042   struct event_base *ev_base;
00043   STACKS *stacks;
00044 } EVLOOP;
00045 
00046 EVLOOP * EVLOOP_init(STACKS *stacks );
00047 
00048 int EVLOOP_run( EVLOOP *loop );
00049 
00050 int EVLOOP_break( EVLOOP *loop );
00051 
00052 /**
00053  * @}
00054  */
00055  
00056 // ---------------------------------------------------------------------------
00057 struct tagEVSOCKET;
00058 struct tagEVTHREAD;
00059 
00060 /**
00061  * @defgroup EVTHREAD
00062  * @brief user mode thread attached to an event loop
00063  * The thread owns one or more EVSOCKETS, it initially activated when the thread is created, and is futher activated when an IO event occurs on a EVSOCKET object 
00064  * owned by this thread.
00065  *
00066  * @{
00067  */
00068 
00069 typedef void (*EVTHREAD_PROC) ( struct tagEVTHREAD *thread, struct tagEVSOCKET *socket, void *user_ctx);
00070 
00071 typedef struct tagEVTHREAD {
00072   EVLOOP *loop;
00073   EVTHREAD_PROC thread_proc;
00074   void *user_context;
00075   CTHREAD *cthread;
00076   struct tagEVSOCKET *socket; 
00077   struct event timer_event;
00078   DLIST   object_list;
00079 } EVTHREAD;
00080 
00081 EVTHREAD *EVTHREAD_init(EVLOOP *loop, EVTHREAD_PROC thread_proc,  void *user_ctx);
00082 
00083 
00084 int EVTHREAD_start( EVTHREAD *thread, struct tagEVSOCKET *socket );
00085 
00086 int EVTHREAD_delay( EVTHREAD *thread, struct timeval delay );
00087 
00088 #if 0
00089 int EVTHREAD_dns_lookup ( EVTHREAD *thread, int  addr_family, const char *dns_name );
00090 
00091 int EVTHREAD_dns_lookup ( EVTHREAD *thread, int  addr_family, const char *dns_name )
00092 {
00093   CTHREAD *thread;
00094 
00095   switch( addr_family ) {
00096     case AF_INET:
00097       evdns_resolve_ipv6( dns_name, 0, evdns_callback_type callback, void *ptr);
00098       break;
00099     case AF_INET6:
00100       evdns_resolve_ipv6( dns_name, 0, evdns_callback_type callback, void *ptr);
00101       break;
00102   }
00103 
00104   CHREAD_yield();
00105 }
00106 #endif
00107 
00108 
00109 /**
00110  * @}
00111  */
00112 
00113 // ---------------------------------------------------------------------------
00114 
00115 typedef enum { 
00116   EVTHREAD_OBJECT_SOCKET,
00117   EVTHREAD_OBJECT_TIMER,
00118 } EVTHREAD_OBJECT_TYPE;
00119 
00120 /**
00121  * @brief base class of sockets and timers. API of this class is not not called directly by the user of this library.
00122  */
00123 typedef struct tagEVTHREAD_OBJECT {
00124   DLIST_entry entry;
00125   int object_type;
00126   EVTHREAD *owner;
00127 } EVTHREAD_OBJECT;
00128 
00129 // ---------------------------------------------------------------------------
00130 
00131 /**
00132  * @defgroup EVTIMER
00133  * @brief timer object atached to event loop, when the timer fires, the timer id is sent to the thread that has set the timer
00134  * @{
00135  */
00136 struct tagEVTIMER;
00137 
00138 typedef  enum {
00139   EVTIMER_STATE_INIT,
00140   EVTIMER_STATE_SCHEDULED,
00141 } EVTIMERSTATE;
00142 
00143 typedef struct tagEVTIMER {
00144   EVTHREAD_OBJECT object_base;
00145   
00146   EVLOOP *loop;
00147   int    timer_id;
00148   struct timeval tm;
00149   struct event timer_event;
00150   
00151   EVTIMERSTATE state;
00152 } EVTIMER;
00153 
00154 
00155 EVTIMER *EVTIMER_init(EVTHREAD *thread, int timer_id, struct timeval tm );
00156 
00157 int  EVTIMER_start( EVTIMER *ret);
00158 
00159 int  EVTIMER_cancel( EVTIMER *timer );
00160 
00161 int  EVTIMER_free( EVTIMER *timer );
00162 
00163 /**
00164  * @}
00165  */
00166 
00167 // ---------------------------------------------------------------------------
00168 /**
00169  * @defgroup EVSOCKET
00170  * @brief a socket attached to user mode thread.
00171  *
00172  *  - a thread can either read from or write to a socket, not both at the same time.
00173  *  - a thread can read or write from one socket at a time.
00174  *
00175  * @{
00176  */
00177 
00178 typedef enum {
00179   EVSOCKET_STATE_INIT,
00180   EVSOCKET_STATE_CONNECTING,
00181   EVSOCKET_STATE_CONNECTED,
00182   EVSOCKET_STATE_READING,
00183   EVSOCKET_STATE_WRITING,
00184   EVSOCKET_STATE_CLOSED,
00185   EVSOCKET_STATE_ERROR,
00186 } EVSOCKET_STATE;
00187 
00188 
00189 typedef struct tagEVSOCKET {
00190   EVTHREAD_OBJECT object_base;
00191   
00192   int fd;
00193   struct event read_event;
00194   struct event write_event;
00195   
00196   EVSOCKET_STATE state;
00197   EVTIMER  *timer_idle_timeout; // idle timeout.
00198   EVTIMER  *timer_io_timeout; // timeout of current operation (read or write).
00199   struct timeval idle_timeout;
00200    
00201   EVLOOP *loop;
00202   EVTHREAD *thread;
00203 } 
00204   EVSOCKET;
00205 
00206 EVSOCKET *EVSOCKET_init(EVTHREAD *thread, int fd, int is_connected);
00207 
00208 int EVSOCKET_close(EVSOCKET *socket);
00209 
00210 int EVSOCKET_connect( EVSOCKET *socket, struct sockaddr *address, socklen_t socklen, struct timeval timeout);
00211 
00212 void EVSOCKET_set_idle_timeout(EVSOCKET *socket, struct timeval timeout );
00213 
00214 int EVSOCKET_recv( EVSOCKET *socket, void *buf, size_t buf_size, int flags, struct timeval timeout );
00215 
00216 int EVSOCKET_recv_all( EVSOCKET *socket, void *buf, size_t buf_size, int flags, struct timeval timeout );
00217 
00218 int EVSOCKET_send( EVSOCKET *socket, void *buf, size_t buf_size, int flags, struct timeval timeout );
00219 
00220 /**
00221  * @}
00222  */
00223 
00224 // ---------------------------------------------------------------------------
00225 
00226 /**
00227  * @defgroup TCPACCEPTOR
00228  * @brief listener for tcp connections, a usermode thread is created for each new connection.
00229  *
00230  * @{
00231  */
00232 
00233 typedef int (*EVTHREAD_FACTORY) (int fd, EVTHREAD_PROC *proc, void **ctx, void *factory_ctx );
00234 
00235 
00236 typedef struct tagTCPACCEPTOR {
00237   EVLOOP *loop; 
00238   int fd;
00239   struct event read_event;
00240   int read_buffer_size,send_buffer_size;
00241   EVTHREAD_FACTORY factory;
00242   void *ctx;
00243 
00244 } EVTCPACCEPTOR;
00245 
00246 EVTCPACCEPTOR * EVTCPACCEPTOR_init_ex( EVLOOP *loop,  SOCKADDR *addr, int listener_backlog,  EVTHREAD_FACTORY factory, int read_buffer_size, int write_buffer_size, void *ctx );
00247 
00248 EVTCPACCEPTOR * EVTCPACCEPTOR_init( EVLOOP *loop, int listener_fd, EVTHREAD_FACTORY factory, int read_buffer_size, int write_buffer_size, void *ctx );
00249 
00250 void EVTCPACCEPTOR_close(EVTCPACCEPTOR *);
00251 
00252 
00253 /**
00254  * @}
00255  */
00256 
00257 #endif
00258 
00259