ircd/api/events
(Created page with '== Overview == The IRC server is built around an event loop. Until the u2.10.11 release (which is what ircd-darenet 1.x is based off of), this event loop has been rather ad-hoc;…') |
(→Types, Enumerations, Structures, Macros and Functions) |
||
(12 intermediate revisions not shown) | |||
Line 4: | Line 4: | ||
The primary concepts that the events subsystem works with are the "event," represented by a <code>struct Event</code>, and the "generator." There are three types of generators: sockets, represented by a <code>struct Socket</code>; signals, represented by a <code>struct Signal</code>; and timers, represented by <code>struct Timer</code>. Each of these generators will be described in turn. | The primary concepts that the events subsystem works with are the "event," represented by a <code>struct Event</code>, and the "generator." There are three types of generators: sockets, represented by a <code>struct Socket</code>; signals, represented by a <code>struct Signal</code>; and timers, represented by <code>struct Timer</code>. Each of these generators will be described in turn. | ||
+ | |||
+ | == Signals == | ||
+ | |||
+ | The signal is perhaps the simplest generator in the entire events subsystem. Basically, instead of setting a signal handler, the function <code>signal_add()</code> is called, specifying a function to be called when a given signal is detected. Most importantly, that call-back function is called ''outside'' the context of a signal handler, permitting the call-back to use more exotic functions that are anathema within a signal handler, such as <code>MyMalloc()</code>. Once a call-back for a signal has been established, it cannot be deleted; this design decision was driven by the fact that ircd never changes its signal handlers. | ||
+ | |||
+ | Whenever a signal is received, an event of type <code>ET_SIGNAL</code> is generated, and that event is passed to the event call-back function specified in the <code>signal_add()</code> call. | ||
+ | |||
+ | == Timers == | ||
+ | |||
+ | Execution of the call-back functions for a timer occur when that timer ''expires''; when a timer expires depends on the type of timer and the expiration time that was used for that timer. A <code>TT_ABSOLUTE</code> timer, for instance, expires at exactly the time given as the expiration time. This time is a standard UNIX <code>time_t</code> value, measuring seconds since the UNIX epoch. The <code>TT_ABSOLUTE</code> timer type is complemented by the <code>TT_RELATIVE</code> timer; the time passed as its expiration time is relative to the current time. If a <code>TT_RELATIVE</code> is given an expiration time of 5, for instance, it will expire 5 seconds after the present time. Internally, <code>TT_RELATIVE</code> timers are converted into <code>TT_ABSOLUTE</code> timers, with the expiration time adjusted by addition of the current time. | ||
+ | |||
+ | These two types of timers, <code>TT_ABSOLUTE</code> and <code>TT_RELATIVE</code>, are single-shot timers. Once they expire, they are removed from the timer list unless re-added by the event call-back or through some other mechanism. There is another type of timer, however, the <code>TT_PERIODIC</code> timer, that is not removed from the timer list. <code>TT_PERIODIC</code> timers are similar to <code>TT_RELATIVE</code> timers, in that one passes in the expire time as a relative number of seconds, but when they expire, they are re-added to the timer list with the same relative expire time. This means that a <code>TT_PERIODIC</code> timer with an expire time of 5 seconds that is set at 11:50:00 will have its call-back called at 11:50:05, 11:50:10, 11:50:15, and so on. | ||
+ | |||
+ | Timers have to be run by the event engines explicitly by calling <code>timer_run()</code> on the generator list passed to the engine event loop. In addition, engines may determine the next (absolute) time that a timer needs to be run by calling the <code>time_next()</code> macro; this may be used to set a timeout on the engine's network activity monitoring function. Engines are described in detail below. | ||
+ | |||
+ | When a timer expires, an event of <code>ET_EXPIRE</code> is generated, and the call-back function is called. When a timer is destroyed, either as the result of an expiration or as result of an explicit <code>timer_del()</code> call, am event of <code>ET_DESTROY</code> is generated, notifying the call-back that the <code>struct Timer</code> can be deallocated. | ||
+ | |||
+ | == Sockets == | ||
+ | |||
+ | Perhaps the most complicated event generator in all of the event subsystem is the socket, as described by <code>struct Socket</code>. This single classification covers datagram sockets and stream sockets. To differentiate the different kinds of sockets, there is a socket state associated with each socket. The available states are <code>SS_CONNECTING</code>, which indicates that a particular socket is in the process of completing a non-blocking <code>connect()</code>; <code>SS_LISTENING</code>, which indicates that a particular socket is a listening socket; <code>SS_CONNECTED</code>, which is the state of every other stream socket; <code>SS_DATAGRAM</code>, which is an ordinary datagram socket, and <code>SS_CONNECTDG</code>, which describes a connected datagram socket. The <code>SS_NOTSOCK</code> state is for the internal use of the event subsystem and will not be described here. | ||
+ | |||
+ | In addition to the socket states, there is also an event mask for each socket; this set of flags is used to tell the events subsystem what events the application is interested in for the socket. For <code>SS_CONNECTING</code> and <code>SS_LISTENING</code> sockets, this events mask has no meaning, but on the other socket states, the event mask is used to determine if the application is interested in readable (<code>SOCK_EVENT_READABLE</code>) or writable (<code>SOCK_EVENT_WRITABLE</code>) indications. | ||
+ | |||
+ | Most of the defined event types have to do with socket generators. When a socket turns up readable, for instance, an event type <code>ET_READ</code> is generated. Similarly, <code>ET_WRITE</code> is generated when a socket can be written to. The <code>ET_ACCEPT</code> event is generated when a listening socket indicates that there is a connection to be accepted; <code>ET_CONNECT</code> is generated when a non-blocking connect is completed. Finally, if an end-of-file indication is detected, <code>ET_EOF</code> is generated, whereas if an error has occurred on the socket, <code>ET_ERROR</code> is generated. Of course, when a socket has been deleted by the <code>socket_del()</code> function, an event of <code>ET_DESTROY</code> is generated when it is safe for the memory used by the <code>struct Socket</code> to be reclaimed. | ||
+ | |||
+ | == Events == | ||
+ | |||
+ | An event, represented by a <code>struct Event</code>, describes in detail all of the particulars of an event. Each event has a type, and an optional integer piece of data may be passed with some events -- in particular, <code>ET_SIGNAL</code> events pass the signal number, and <code>ET_ERROR</code> events pass the <code>errno</code> value. The <code>struct Event</code> also contains a pointer to the structure describing the generated event -- although it should be noted that the only way to disambiguate which type of generator is contained within the <code>struct Event</code> is by which call-back function has been called. | ||
+ | |||
+ | All generators have a void pointer which can be used to pass important information to the call-back, such as a pointer to a <code>struct Client</code>. Additionally, generators have a reference count, and a union of a void pointer and an integer that should only be utilized by the event engine. Finally, there is also a field for flags, although the only flag of concern to the application (or the engine) is the active flag, which may be tested using the test macros described below. | ||
+ | |||
+ | Whatever the generator, the call-back function is a function returning nothing (void) and taking as its sole argument a pointer to <code>struct Event</code>. This call-back function may be implemented as a single switch statement that calls out to appropriate external functions as needed. | ||
+ | |||
+ | == Engines == | ||
+ | |||
+ | Engines implement the actual socket event loop, and may also have some means of receiving signal events. Each engine has a name, which should describe what its core function is; for instance, the engine based on the standard <code>select()</code> function is named, simply, "<code>select()</code>." Each engine must implement several call-backs which are used to initialize the engine, notify the engine of sockets the application is interested in, etc. All of this data is described by a single <code>struct Engine</code>, which should be the only non-static variable or function in the engine's source file. | ||
+ | |||
+ | The engine's event loop, pointed to by the <code>eng_loop</code> field of the <code>struct Engine</code>, must consist of a single while loop predicated on the global variable <code>running</code>. Additionally, this loop's final statement must be a call to <code>timer_run()</code>, to execute all timers that have become due. Ideally, this construction should be pulled out of each engine's | ||
+ | <code>eng_loop</code> and put in the <code>event_loop()</code> function of the events subsystem. | ||
+ | |||
+ | == Reference Counts == | ||
+ | |||
+ | As mentioned previously, all generators keep a reference count. Should <code>timer_del()</code> or <code>socket_del()</code> be called on a generator with a non-zero reference count, for whatever reason, the actual destruction of the generator will be delayed until the reference count again reaches zero. This is used by the event loop to keep sockets that it is currently referencing from being deallocated before it is done checking all pending events on them. To increment the reference count by one, call <code>gen_ref_inc()</code> on the generator; the corresponding macro <code>gen_ref_dec()</code> decrements the reference counts, and will automatically destroy the generator if the appropriate conditions are met. | ||
+ | |||
+ | == Debugging Functions == | ||
+ | |||
+ | It can be difficult to debug an engine if, say, a socket state can only be expressed as a meaningless number; therefore, when <code>DEBUGMODE</code> is <code>#define</code>'d, five number-to-name functions are also defined to make the debugging data more meaningful. These functions must only be called when <code>DEBUGMODE</code> is <code>#define</code>'d. Calling them from within <code>Debug()</code> macro calls is safe; calling them from <code>log_write()</code> calls is not. | ||
+ | |||
+ | == Types, Enumerations, Structures, Macros and Functions == | ||
+ | |||
+ | <c>typedef void (*EventCallBack)(struct Event*);</c> | ||
+ | |||
+ | The <code>EventCallBack</code> type is used to simplify declaration of event call-back functions. It is used in <code>timer_add()</code>, <code>signal_add()</code>, and <code>socket_add()</code>. The event call-back should process the event, taking whatever actions are necessary. The function should be declared as returning void. | ||
+ | |||
+ | <c>typedef int (*EngineInit)(int);</c> | ||
+ | |||
+ | The <code>EngineInit</code> function takes an integer specifying the maximum number of sockets the event system is expecting to handle. This number may be used by the engine initialization function for memory allocation computations. If initialization succeeds, this function must return 1. If initialization fails, the function should clean up after itself and return 0. The events subsystem has the ability to fall back upon another engine, should an engine initialization fail. Needless to say, the engines based upon <code>poll()</code> and <code>select()</code> should never fail in this way. | ||
+ | |||
+ | <c>typedef void (*EngineSignal)(struct Signal*);</c> | ||
+ | |||
+ | If an engine has the capability to directly detect signals, it should set the <code>eng_signal</code> field of <code>struct Engine</code> non-zero. When the application indicates interest in a particular signal, the <code>EngineSignal</code> function will be called with the filled-in <code>struct Signal</code>, in order to register interest in that signal with the engine. | ||
+ | |||
+ | <c>typedef int (*EngineAdd)(struct Socket*);</c> | ||
+ | |||
+ | All engines must define an <code>EngineAdd</code> function, which is used to inform the engine of the application's interest in the socket. If the new socket cannot be accommodated by the engine for whatever reason, this function must return 0. Otherwise, the function must return 1, informing the events subsystem that the interest has been noted. | ||
+ | |||
+ | <c>typedef void (*EngineState)(struct Socket*, enum SocketState new_state);</c> | ||
+ | |||
+ | Sockets can change state. <code>SS_CONNECTING</code> sockets, for instance, can become <code>SS_CONNECTED</code>. Whenever a socket state changes, the engine is informed, since some states require different notification procedures than others. This is accomplished by calling the <code>EngineState</code> function with the new state. The <code>struct Socket</code> passed to the engine will still have the old state, if the engine must reference that. | ||
+ | |||
+ | <c>typedef void (*EngineEvents)(struct Socket*, unsigned int new_events);</c> | ||
+ | |||
+ | Applications may only be interested in given events on a socket for a limited time. When the application's interest shifts, a new events mask is set for the socket. The engine is informed of this change by a call to its <code>EngineEvents</code> function. | ||
+ | |||
+ | <c>typedef void (*EngineDelete)(struct Socket*);</c> | ||
+ | |||
+ | Eventually, an application will close all the sockets it has opened. When a socket is closed, and the corresponding <code>struct Socket</code> deleted with a call to <code>socket_del()</code>, the <code>EngineDelete</code> function will be called to notify the engine of the change. | ||
+ | |||
+ | <c>typedef void (*EngineLoop)(struct Generators*);</c> | ||
+ | |||
+ | The workhorse of the entire events subsystem is the event loop, implemented by each engine as the <code>EngineLoop</code> function. This function is called with a single argument that may be passed to <code>timer_next()</code> to calculate the next time a timer will expire. | ||
+ | |||
+ | <c>enum SocketState { | ||
+ | SS_CONNECTING, /* Connection in progress on socket */ | ||
+ | SS_LISTENING, /* Socket is a listening socket */ | ||
+ | SS_CONNECTED, /* Socket is a connected socket */ | ||
+ | SS_DATAGRAM, /* Socket is a datagram socket */ | ||
+ | SS_CONNECTDG, /* Socket is a connected datagram socket */ | ||
+ | SS_NOTSOCK /* Socket isn't a socket at all */ | ||
+ | };</c> | ||
+ | |||
+ | This enumeration contains a list of all possible states a socket can be in. Applications should not use <code>SS_NOTSOCK</code>; engines should treat it as a special socket state for non-sockets. The only event that should be watched for on a <code>struct Socket</code> in the <code>SS_NOTSOCK</code> state is readability. This socket state is used to implement the fall-back signal event generation. | ||
+ | |||
+ | <c>enum TimerType { | ||
+ | TT_ABSOLUTE, /* timer that runs at a specific time */ | ||
+ | TT_RELATIVE, /* timer that runs so many seconds in the future */ | ||
+ | TT_PERIODIC /* timer that runs periodically */ | ||
+ | };</c> | ||
+ | |||
+ | The three possible timer types are defined by the <code>TimerType</code> enumeration. More details can be found in the "Timers" section, above. | ||
+ | |||
+ | <c>enum EventType { | ||
+ | ET_READ, /* Readable event detected */ | ||
+ | ET_WRITE, /* Writable event detected */ | ||
+ | ET_ACCEPT, /* Connection can be accepted */ | ||
+ | ET_CONNECT, /* Connection completed */ | ||
+ | ET_EOF, /* End-of-file on connection */ | ||
+ | ET_ERROR, /* Error condition detected */ | ||
+ | ET_SIGNAL, /* A signal was received */ | ||
+ | ET_EXPIRE, /* A timer expired */ | ||
+ | ET_DESTROY /* The generator is being destroyed */ | ||
+ | };</c> | ||
+ | |||
+ | This enumeration contains all the types of events that can be generated by the events subsystem. The first 6 are generated by socket generators, the next by signal generators, and the next by timer generators. <code>ET_DESTROY</code> is generated by both socket and timer generators when the events subsystem is finished with the memory allocated by both. | ||
+ | |||
+ | <c>struct Socket { | ||
+ | struct GenHeader s_header; /* Generator information */ | ||
+ | enum SocketState s_state; /* State socket's in */ | ||
+ | unsigned int s_events; /* Events socket is interested in */ | ||
+ | int s_fd; /* File descriptor for socket */ | ||
+ | #ifdef USE_SSL | ||
+ | SSL* ssl; /* If not NULL, use SSL routines on socket */ | ||
+ | #endif /* USE_SSL */ | ||
+ | };</c> | ||
+ | |||
+ | This structure describes everything the events subsystem knows about a given socket. All of its fields may be accessed through the s_* macros described below. | ||
+ | |||
+ | <c>struct Timer { | ||
+ | struct GenHeader t_header; /* Generator information */ | ||
+ | enum TimerType t_type; /* What type of timer this is */ | ||
+ | time_t t_value; /* Value timer was added with */ | ||
+ | time_t t_expire; /* Time at which timer expires */ | ||
+ | };</c> | ||
+ | |||
+ | The <code>struct Timer</code> structure describes everything the events subsystem knows about a given timer. Again, all of its fields may be accessed through the t_* macros described below. | ||
+ | |||
+ | <c>struct Signal { | ||
+ | struct GenHeader sig_header; /* Generator information */ | ||
+ | int sig_signal; /* Signal number */ | ||
+ | };</c> | ||
+ | |||
+ | Signal generators are described by a <code>struct Signal</code>. All of the fields of a <code>struct Signal</code> may be accessed by the sig_* macros described below. | ||
+ | |||
+ | <c>struct Event { | ||
+ | struct Event* ev_next; /* Linked list of events on queue */ | ||
+ | struct Event** ev_prev_p; /* Previous pointer to this event */ | ||
+ | enum EventType ev_type; /* Event type */ | ||
+ | int ev_data; /* Extra data, like errno value */ | ||
+ | union { | ||
+ | struct GenHeader* gen_header; /* Generator header */ | ||
+ | struct Socket* gen_socket; /* Socket generating event */ | ||
+ | struct Signal* gen_signal; /* Signal generating event */ | ||
+ | struct Timer* gen_timer; /* Timer generating event */ | ||
+ | } ev_gen; /* Object generating event */ | ||
+ | };</c> | ||
+ | |||
+ | Each event is described by a <code>struct Event</code>. Its fields may be examined using the ev_* macros described below. | ||
+ | |||
+ | <c>struct Generators { | ||
+ | struct GenHeader* g_socket; /* List of socket generators */ | ||
+ | struct GenHeader* g_signal; /* List of signal generators */ | ||
+ | struct GenHeader* g_timer; /* List of timer generators */ | ||
+ | };</c> | ||
+ | |||
+ | Each engine is passed a list of all generators when the engine's <code>EngineLoop</code> function is called. The only valid way to access this structure is via the <code>timer_next()</code> function described below. | ||
+ | |||
+ | <c>struct Engine { | ||
+ | const char* eng_name; /* a name for the engine */ | ||
+ | EngineInit eng_init; /* initialize engine */ | ||
+ | EngineSignal eng_signal; /* express interest in a signal */ | ||
+ | EngineAdd eng_add; /* express interest in a socket */ | ||
+ | EngineState eng_state; /* mention a change in state to engine */ | ||
+ | EngineEvents eng_events; /* express interest in socket events */ | ||
+ | EngineDelete eng_closing; /* socket is being closed */ | ||
+ | EngineLoop eng_loop; /* actual event loop */ | ||
+ | };</c> | ||
+ | |||
+ | Each engine is described by the <code>struct Engine</code> structure. Each engine must define all of the functions described above except for the <code>EngineSignal</code> function, which is optional. | ||
+ | |||
+ | <c>#define SOCK_EVENT_READABLE 0x0001 /* interested in readable */</c> | ||
+ | |||
+ | The <code>SOCK_EVENT_READABLE</code> flag indicates to the engine that the application is interested in readability on this particular socket. | ||
+ | |||
+ | <c>#define SOCK_EVENT_WRITABLE 0x0002 /* interested in writable */</c> | ||
+ | |||
+ | The <code>SOCK_EVENT_WRITABLE</code> flag indicates to the engine that the application is interested in this socket being writable. | ||
+ | |||
+ | <c>#define SOCK_EVENT_MASK (SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE)</c> | ||
+ | |||
+ | <code>SOCK_EVENT_MASK</code> may be used to extract only the event interest flags from an event interest set. | ||
+ | |||
+ | <c>#define SOCK_ACTION_SET 0x0000 /* set interest set as follows */</c> | ||
+ | |||
+ | When <code>socket_events()</code> is called with a set of event interest flags and <code>SOCK_ACTION_SET</code>, the socket's event interest flags are set to those passed into <code>socket_events()</code>. | ||
+ | |||
+ | <c>#define SOCK_ACTION_ADD 0x1000 /* add to interest set */</c> | ||
+ | |||
+ | When <code>SOCK_ACTION_ADD</code> is used in a call to <code>socket_events()</code>, the event interest flags passed in are added to the existing event interest flags for the socket. | ||
+ | |||
+ | <c>#define SOCK_ACTION_DEL 0x2000 /* remove from interest set */</c> | ||
+ | |||
+ | When <code>SOCK_ACTION_DEL</code> is used in a call to <code>socket_events()</code>, the event interest flags passed in are removed from the existing event interest flags for the socket. | ||
+ | |||
+ | <c>#define SOCK_ACTION_MASK 0x3000 /* mask out the actions */</c> | ||
+ | |||
+ | <code>SOCK_ACTION_MASK</code> is used to isolate the socket action desired. | ||
+ | |||
+ | <c>enum SocketState s_state(struct Socket* sock);</c> | ||
+ | |||
+ | This macro returns the state of the given socket. | ||
+ | |||
+ | <c>unsigned int s_events(struct Socket* sock);</c> | ||
+ | |||
+ | This macro returns the current event interest mask for a given socket. Note that if the socket is in the <code>SS_CONNECTING</code> or <code>SS_LISTENING</code> states, this mask has no meaning. | ||
+ | |||
+ | <c>int s_fd(struct Socket* sock);</c> | ||
+ | |||
+ | This macro simply returns the file descriptor for the given socket. | ||
+ | |||
+ | <c>void* s_data(struct Socket* sock);</c> | ||
+ | |||
+ | When a <code>struct Socket</code> is initialized, data that the call-back function may find useful, such as a pointer to a <code>struct Connection</code>, is stored in the <code>struct Socket</code>. This macro returns that pointer. | ||
+ | |||
+ | <c>int s_ed_int(struct Socket* sock);</c> | ||
+ | |||
+ | Engines may find it convenient to associate an integer with a <code>struct Socket</code>. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>void* s_ed_ptr(struct Socket* sock);</c> | ||
+ | |||
+ | Engines may find it convenient to associate a void* pointer with a <code>struct Socket</code>. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>int s_active(struct Socket* sock);</c> | ||
+ | |||
+ | A socket's active flag is set when initialized by <code>socket_add()</code>, and is cleared immediately prior to generating an event of type <code>ET_DESTROY</code>. This may be used by the application to determine whether or not the socket is still in use by the events subsystem. If it is, <code>s_active()</code> returns a non-zero value; otherwise, its value is 0. | ||
+ | |||
+ | <c>int socket_add(struct Socket* sock, EventCallBack call, void* data, | ||
+ | enum SocketState state, unsigned int events, int fd);</c> | ||
+ | |||
+ | This function is called to add a socket to the list of sockets to be monitored. The ''sock'' parameter is a pointer to a <code>struct Socket</code> that is allocated by the application. The ''call'' parameter is a pointer to a function to process any events on the socket. The ''data'' parameter is for use of the socket call-back and may be zero. The ''state'' parameter must be one of the valid socket states. The ''events'' parameter must be a valid events interest mask--0, or the binary OR of <code>SOCK_EVENT_READABLE</code> or <code>SOCK_EVENT_WRITABLE</code>. Finally, the ''fd'' parameter specifies the socket's file descriptor. This function returns 1 if successful or 0 otherwise. | ||
+ | |||
+ | <c>void socket_del(struct Socket* sock);</c> | ||
+ | |||
+ | When the application is no longer interested in a particular socket, it should call the <code>socket_del()</code> function. This function must be called no later than when the socket has been closed, to avoid attempting to call <code>select()</code> or similar functions on closed sockets. | ||
+ | |||
+ | <c>void socket_state(struct Socket* sock, enum SocketState state);</c> | ||
+ | |||
+ | Occasionally, a socket's state will change. This function is used to inform the events subsystem of that change. Only certain state transitions are valid--a socket in the <code>SS_LISTENING</code> or <code>SS_CONNECTED</code> states cannot change states, nor can an <code>SS_CONNECTING</code> socket change to some state other than <code>SS_CONNECTED</code>. Of course, <code>SS_DATAGRAM</code> sockets may change state only to <code>SS_CONNECTDG</code>, and <code>SS_CONNECTDG</code> sockets may only change states to <code>SS_DATAGRAM</code>. | ||
+ | |||
+ | <c>void socket_events(struct Socket* sock, unsigned int events);</c> | ||
+ | |||
+ | When the application changes the events it is interested in, it uses <code>socket_events()</code> to notify the events subsystem of that change. The ''events'' parameter is the binary OR of one of <code>SOCK_ACTION_SET</code>, <code>SOCK_ACTION_ADD</code>, or <code>SOCK_ACTION_DEL</code> with an events mask. See the documentation for the SOCK_* macros for more information. | ||
+ | |||
+ | <c>const char* state_to_name(enum SocketState state);</c> | ||
+ | |||
+ | This function is defined only when <code>DEBUGMODE</code> is <code>#define</code>'d. It takes the given ''state'' and returns a string giving that state's name. This function may safely be called from <code>Debug()</code> macros. | ||
+ | |||
+ | <c>const char* sock_flags(unsigned int flags);</c> | ||
+ | |||
+ | This function is defined only when <code>DEBUGMODE</code> is <code>#define</code>'d. It takes the given event interest flags and returns a string naming each of those flags. This function may safely be called from <code>Debug()</code> macros, but may only be called once, since it uses function static storage to store the flag strings. | ||
+ | |||
+ | <c>int sig_signal(struct Signal* sig);</c> | ||
+ | |||
+ | This macro returns the signal number for the given <code>struct Signal</code>. | ||
+ | |||
+ | <c>void* sig_data(struct Signal* sig);</c> | ||
+ | |||
+ | When a <code>struct Signal</code> is initialized, data that the call-back function may find useful is stored in the <code>struct Signal</code>. This macro returns that pointer. | ||
+ | |||
+ | <c>int sig_ed_int(struct Signal* sig);</c> | ||
+ | |||
+ | Engines may find it convenient to associate an integer with a <code>struct Signal</code>. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>void* sig_ed_ptr(struct Signal* sig);</c> | ||
+ | |||
+ | Engines may find it convenient to associate a void* pointer with a <code>struct Signal</code>. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>int sig_active(struct Signal* sig);</c> | ||
+ | |||
+ | A signal's active flag is set when initialized by <code>signal_add()</code>. This may be used by the application to determine whether or not the signal has been initialized yet. If it is, <code>sig_active()</code> returns a non-zero value; otherwise, its value is 0. | ||
+ | |||
+ | <c>void signal_add(struct Signal* signal, EventCallBack call, void* data, int sig);</c> | ||
+ | |||
+ | This function is called to add a signal to the list of signals to be monitored. The ''signal'' parameter is a pointer is a pointer to a <code>struct Signal</code> that is allocated by the application. The ''call'' parameter is a pointer to a function to process any signal events. The ''data'' parameter is for use of the signal call-back and may be zero. The ''sig'' parameter is the integer value of the signal to be monitored. | ||
+ | |||
+ | <c>enum TimerType t_type(struct Timer* tim);</c> | ||
+ | |||
+ | This macro returns the type of the given timer. | ||
+ | |||
+ | <c>time_t t_value(struct Timer* tim);</c> | ||
+ | |||
+ | This macro returns the value that was used when the given timer was initialized by the events subsystem. It will contain an absolute time if the timer type is <code>TT_ABSOLUTE</code>, and a relative time otherwise. | ||
+ | |||
+ | <c>time_t t_expire(struct Timer* tim);</c> | ||
+ | |||
+ | This macro returns the absolute time at which the timer will next expire. | ||
+ | |||
+ | <c>void* t_data(struct Timer* tim);</c> | ||
+ | |||
+ | When a <code>struct Timer</code> is initialized, data that the call-back function may find useful is stored in the <code>struct Socket</code>. This macro returns that pointer. | ||
+ | |||
+ | <c>int t_ed_int(struct Timer *tim);</c> | ||
+ | |||
+ | Engines may find it convenient to associate an integer with a <code>struct Timer</code>. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>void* t_ed_ptr(struct Timer *tim);</c> | ||
+ | |||
+ | Engines may find it convenient to associate a void* pointer with a <code>struct Timer</code>. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited. | ||
+ | |||
+ | <c>int t_active(struct Timer *tim);</c> | ||
+ | |||
+ | A timer's active flag is set when initialized by <code>timer_add()</code>, and is cleared immediately prior to generating an event of type <code>ET_DESTROY</code>. This may be used by the application to determine whether or not the timer is still in use by the events subsystem. If it is, <code>s_active()</code> returns a non-zero value; otherwise, its value is 0. | ||
+ | |||
+ | <c>void timer_add(struct Timer* timer, EventCallBack call, void* data, | ||
+ | enum TimerType type, time_t value);</c> | ||
+ | |||
+ | This function is called to initialize and queue a timer. The ''timer'' parameter is a pointer to a <code>struct Timer</code> that is allocated by the application. The ''call'' parameter is a pointer to a function to process the timer's expiration. The ''data'' parameter is for use of the timer call-back and may be zero. The ''type'' parameter must be one of the valid timer types -- <code>TT_ABSOLUTE</code>, <code>TT_RELATIVE</code>, or <code>TT_PERIODIC</code>. Finally, ''value'' is the value for the timer's expiration. | ||
+ | |||
+ | <c>void timer_del(struct Timer* timer);</c> | ||
+ | |||
+ | When the application no longer needs a <code>TT_PERIODIC</code> timer, or when it wishes to stop a <code>TT_ABSOLUTE</code> or <code>TT_RELATIVE</code> timer before its expiration, it should call the <code>timer_del()</code> function. | ||
+ | |||
+ | <c>void timer_chg(struct Timer* timer, enum TimerType type, time_t value);</c> | ||
+ | |||
+ | Occasionally, an application may wish to delay an existing <code>TT_ABSOLUTE</code> or <code>TT_RELATIVE</code> timer; this may be done with the <code>timer_chg()<code> function. The ''type'' parameter must be one of <code>TT_ABSOLUTE</code> or <code>TT_RELATIVE</code> --changing the values of <code>TT_PERIODIC</code> timers is not supported. The ''value'' parameter is the same as would be given to <code>timer_add()</code> for that particular type of timer. | ||
+ | |||
+ | <c>void timer_run(void);</c> | ||
+ | |||
+ | When an engine has finished processing the results of its socket and signal checks -- just before it loops around to test for more events -- it should call the <code>timer_run()</code> function to expire any waiting timers. | ||
+ | |||
+ | <c>time_t timer_next(struct Generators* gen);</c> | ||
+ | |||
+ | Most engines will use a blocking call with a timeout to check for socket activity. To determine when the next timer needs to be run, | ||
+ | and thus to calculate how long the call should block, the engine should call <code>timer_next()</code> with the ''gen'' parameter passed to the <code>EngineLoop</code> function. The <code>timer_next()</code> function returns an absolute time, which may have to be massaged into a relative time before the engine may use it. | ||
+ | |||
+ | <c>const char* timer_to_name(enum TimerType type);</c> | ||
+ | |||
+ | This function is defined only when <code>DEBUGMODE<code> is <code>#define</code>'d. It takes the given ''type'' and returns a string giving that type's name. This function may safely be called from <code>Debug()</code> macros. | ||
+ | |||
+ | <c>enum EventType ev_type(struct Event* ev);</c> | ||
+ | |||
+ | This macro simply returns the type of the event ''ev''. | ||
+ | |||
+ | <c>int ev_data(struct Event* ev);</c> | ||
+ | |||
+ | When an event is generated, a single integer can be passed along as a piece of extra information. This can be used, for instance, to carry an <code>errno</code> value when an <code>ET_ERROR</code> is generated. This macro simply returns that integer. | ||
+ | |||
+ | <c>struct Socket* ev_socket(struct Event* ev);</c> | ||
+ | |||
+ | If the event was generated by a socket, this macro returns a pointer to the <code>struct Socket</code> that generated the event. The results are undefined if the event was not generated by a socket. | ||
+ | |||
+ | <c>struct Signal* ev_signal(struct Event* ev);</c> | ||
+ | |||
+ | If the event was generated by a signal, this macro returns a pointer to the <code>struct Signal</code> that generated the event. The results are undefined if the event was not generated by a signal. | ||
+ | |||
+ | <c>struct Timer* ev_timer(struct Event* ev);</c> | ||
+ | |||
+ | If the event was generated by a timer, this macro returns a pointer to the <code>struct Timer</code> that generated the event. The results are undefined if the event was not generated by a timer. | ||
+ | |||
+ | <c>void event_init(int max_sockets);</c> | ||
+ | |||
+ | Before any of the functions or macros described here can be called, the events subsystem must be initialized by calling <code>event_init()</code>. The ''max_sockets'' parameter specifies to the events subsystem how many sockets it must be able to support; this figure may be used for memory allocation by the engines. | ||
+ | |||
+ | <c>void event_loop(void);</c> | ||
+ | |||
+ | Once the initial sockets are open, signals added, and timers queued, the application must call the <code>event_loop()</code> function in order to actually begin monitoring those sockets, signals, and timers. | ||
+ | |||
+ | <c>void event_generate(enum EventType type, void* arg, int data);</c> | ||
+ | |||
+ | This is the function called by the events subsystem to generate particular events. The ''type'' parameter specifies the type of event to generate, and the ''arg'' parameter must be a pointer to the event's generator. The ''data'' parameter may be used for passing such things as signal numbers or <code>errno</code> values. | ||
+ | |||
+ | <c>const char* event_to_name(enum EventType type);</c> | ||
+ | |||
+ | This function is defined only when <code>DEBUGMODE</code> is <code>#define</code>'d. It takes the given ''type'' and returns a string giving that event type's name. This function may safely be called from <code>Debug()</code> macros. | ||
+ | |||
+ | <c>const char* engine_name(void);</c> | ||
+ | |||
+ | This function is used to retrieve the name of the engine presently being used by the events subsystem. | ||
+ | |||
+ | <c>void gen_ref_inc(void* gen);</c> | ||
+ | |||
+ | This macro increments the reference count of the generator ''gen'', preventing it from simply disappearing without warning. | ||
+ | |||
+ | <c>void gen_ref_dec(void* gen);</c> | ||
+ | |||
+ | This macro decrements the reference count of the generator ''gen'', and releases the memory associated with it by generating at <code>ET_DESTROY</code> event if the reference count falls to zero and the generator is marked for destruction. No references should be made to the generator after calling this macro. |
Current revision as of 15:14, 13 July 2010
In This Guide: |
Overview
The IRC server is built around an event loop. Until the u2.10.11 release (which is what ircd-darenet 1.x is based off of), this event loop has been rather ad-hoc; timed events are hard-coded in, signals are handled inside the signal handler, etc. All of this changed with u2.10.11. A new subsystem, the events subsystem, was introduced; the new subsystem contains a generalization of the concept of an event. An event is a signal, the expiration of a timer, or some form of activity on a network socket. This new subsystem has the potential to vastly simplify the code that is arguably the core of any network program, and makes it much simpler to support more exotic forms of network activity monitoring than the conventional select() and poll() calls.
The primary concepts that the events subsystem works with are the "event," represented by a struct Event
, and the "generator." There are three types of generators: sockets, represented by a struct Socket
; signals, represented by a struct Signal
; and timers, represented by struct Timer
. Each of these generators will be described in turn.
Signals
The signal is perhaps the simplest generator in the entire events subsystem. Basically, instead of setting a signal handler, the function signal_add()
is called, specifying a function to be called when a given signal is detected. Most importantly, that call-back function is called outside the context of a signal handler, permitting the call-back to use more exotic functions that are anathema within a signal handler, such as MyMalloc()
. Once a call-back for a signal has been established, it cannot be deleted; this design decision was driven by the fact that ircd never changes its signal handlers.
Whenever a signal is received, an event of type ET_SIGNAL
is generated, and that event is passed to the event call-back function specified in the signal_add()
call.
Timers
Execution of the call-back functions for a timer occur when that timer expires; when a timer expires depends on the type of timer and the expiration time that was used for that timer. A TT_ABSOLUTE
timer, for instance, expires at exactly the time given as the expiration time. This time is a standard UNIX time_t
value, measuring seconds since the UNIX epoch. The TT_ABSOLUTE
timer type is complemented by the TT_RELATIVE
timer; the time passed as its expiration time is relative to the current time. If a TT_RELATIVE
is given an expiration time of 5, for instance, it will expire 5 seconds after the present time. Internally, TT_RELATIVE
timers are converted into TT_ABSOLUTE
timers, with the expiration time adjusted by addition of the current time.
These two types of timers, TT_ABSOLUTE
and TT_RELATIVE
, are single-shot timers. Once they expire, they are removed from the timer list unless re-added by the event call-back or through some other mechanism. There is another type of timer, however, the TT_PERIODIC
timer, that is not removed from the timer list. TT_PERIODIC
timers are similar to TT_RELATIVE
timers, in that one passes in the expire time as a relative number of seconds, but when they expire, they are re-added to the timer list with the same relative expire time. This means that a TT_PERIODIC
timer with an expire time of 5 seconds that is set at 11:50:00 will have its call-back called at 11:50:05, 11:50:10, 11:50:15, and so on.
Timers have to be run by the event engines explicitly by calling timer_run()
on the generator list passed to the engine event loop. In addition, engines may determine the next (absolute) time that a timer needs to be run by calling the time_next()
macro; this may be used to set a timeout on the engine's network activity monitoring function. Engines are described in detail below.
When a timer expires, an event of ET_EXPIRE
is generated, and the call-back function is called. When a timer is destroyed, either as the result of an expiration or as result of an explicit timer_del()
call, am event of ET_DESTROY
is generated, notifying the call-back that the struct Timer
can be deallocated.
Sockets
Perhaps the most complicated event generator in all of the event subsystem is the socket, as described by struct Socket
. This single classification covers datagram sockets and stream sockets. To differentiate the different kinds of sockets, there is a socket state associated with each socket. The available states are SS_CONNECTING
, which indicates that a particular socket is in the process of completing a non-blocking connect()
; SS_LISTENING
, which indicates that a particular socket is a listening socket; SS_CONNECTED
, which is the state of every other stream socket; SS_DATAGRAM
, which is an ordinary datagram socket, and SS_CONNECTDG
, which describes a connected datagram socket. The SS_NOTSOCK
state is for the internal use of the event subsystem and will not be described here.
In addition to the socket states, there is also an event mask for each socket; this set of flags is used to tell the events subsystem what events the application is interested in for the socket. For SS_CONNECTING
and SS_LISTENING
sockets, this events mask has no meaning, but on the other socket states, the event mask is used to determine if the application is interested in readable (SOCK_EVENT_READABLE
) or writable (SOCK_EVENT_WRITABLE
) indications.
Most of the defined event types have to do with socket generators. When a socket turns up readable, for instance, an event type ET_READ
is generated. Similarly, ET_WRITE
is generated when a socket can be written to. The ET_ACCEPT
event is generated when a listening socket indicates that there is a connection to be accepted; ET_CONNECT
is generated when a non-blocking connect is completed. Finally, if an end-of-file indication is detected, ET_EOF
is generated, whereas if an error has occurred on the socket, ET_ERROR
is generated. Of course, when a socket has been deleted by the socket_del()
function, an event of ET_DESTROY
is generated when it is safe for the memory used by the struct Socket
to be reclaimed.
Events
An event, represented by a struct Event
, describes in detail all of the particulars of an event. Each event has a type, and an optional integer piece of data may be passed with some events -- in particular, ET_SIGNAL
events pass the signal number, and ET_ERROR
events pass the errno
value. The struct Event
also contains a pointer to the structure describing the generated event -- although it should be noted that the only way to disambiguate which type of generator is contained within the struct Event
is by which call-back function has been called.
All generators have a void pointer which can be used to pass important information to the call-back, such as a pointer to a struct Client
. Additionally, generators have a reference count, and a union of a void pointer and an integer that should only be utilized by the event engine. Finally, there is also a field for flags, although the only flag of concern to the application (or the engine) is the active flag, which may be tested using the test macros described below.
Whatever the generator, the call-back function is a function returning nothing (void) and taking as its sole argument a pointer to struct Event
. This call-back function may be implemented as a single switch statement that calls out to appropriate external functions as needed.
Engines
Engines implement the actual socket event loop, and may also have some means of receiving signal events. Each engine has a name, which should describe what its core function is; for instance, the engine based on the standard select()
function is named, simply, "select()
." Each engine must implement several call-backs which are used to initialize the engine, notify the engine of sockets the application is interested in, etc. All of this data is described by a single struct Engine
, which should be the only non-static variable or function in the engine's source file.
The engine's event loop, pointed to by the eng_loop
field of the struct Engine
, must consist of a single while loop predicated on the global variable running
. Additionally, this loop's final statement must be a call to timer_run()
, to execute all timers that have become due. Ideally, this construction should be pulled out of each engine's
eng_loop
and put in the event_loop()
function of the events subsystem.
Reference Counts
As mentioned previously, all generators keep a reference count. Should timer_del()
or socket_del()
be called on a generator with a non-zero reference count, for whatever reason, the actual destruction of the generator will be delayed until the reference count again reaches zero. This is used by the event loop to keep sockets that it is currently referencing from being deallocated before it is done checking all pending events on them. To increment the reference count by one, call gen_ref_inc()
on the generator; the corresponding macro gen_ref_dec()
decrements the reference counts, and will automatically destroy the generator if the appropriate conditions are met.
Debugging Functions
It can be difficult to debug an engine if, say, a socket state can only be expressed as a meaningless number; therefore, when DEBUGMODE
is #define
'd, five number-to-name functions are also defined to make the debugging data more meaningful. These functions must only be called when DEBUGMODE
is #define
'd. Calling them from within Debug()
macro calls is safe; calling them from log_write()
calls is not.
Types, Enumerations, Structures, Macros and Functions
typedef void (*EventCallBack)(struct Event*);
The EventCallBack
type is used to simplify declaration of event call-back functions. It is used in timer_add()
, signal_add()
, and socket_add()
. The event call-back should process the event, taking whatever actions are necessary. The function should be declared as returning void.
typedef int (*EngineInit)(int);
The EngineInit
function takes an integer specifying the maximum number of sockets the event system is expecting to handle. This number may be used by the engine initialization function for memory allocation computations. If initialization succeeds, this function must return 1. If initialization fails, the function should clean up after itself and return 0. The events subsystem has the ability to fall back upon another engine, should an engine initialization fail. Needless to say, the engines based upon poll()
and select()
should never fail in this way.
typedef void (*EngineSignal)(struct Signal*);
If an engine has the capability to directly detect signals, it should set the eng_signal
field of struct Engine
non-zero. When the application indicates interest in a particular signal, the EngineSignal
function will be called with the filled-in struct Signal
, in order to register interest in that signal with the engine.
typedef int (*EngineAdd)(struct Socket*);
All engines must define an EngineAdd
function, which is used to inform the engine of the application's interest in the socket. If the new socket cannot be accommodated by the engine for whatever reason, this function must return 0. Otherwise, the function must return 1, informing the events subsystem that the interest has been noted.
typedef void (*EngineState)(struct Socket*, enum SocketState new_state);
Sockets can change state. SS_CONNECTING
sockets, for instance, can become SS_CONNECTED
. Whenever a socket state changes, the engine is informed, since some states require different notification procedures than others. This is accomplished by calling the EngineState
function with the new state. The struct Socket
passed to the engine will still have the old state, if the engine must reference that.
typedef void (*EngineEvents)(struct Socket*, unsigned int new_events);
Applications may only be interested in given events on a socket for a limited time. When the application's interest shifts, a new events mask is set for the socket. The engine is informed of this change by a call to its EngineEvents
function.
typedef void (*EngineDelete)(struct Socket*);
Eventually, an application will close all the sockets it has opened. When a socket is closed, and the corresponding struct Socket
deleted with a call to socket_del()
, the EngineDelete
function will be called to notify the engine of the change.
typedef void (*EngineLoop)(struct Generators*);
The workhorse of the entire events subsystem is the event loop, implemented by each engine as the EngineLoop
function. This function is called with a single argument that may be passed to timer_next()
to calculate the next time a timer will expire.
enum SocketState { SS_CONNECTING, /* Connection in progress on socket */ SS_LISTENING, /* Socket is a listening socket */ SS_CONNECTED, /* Socket is a connected socket */ SS_DATAGRAM, /* Socket is a datagram socket */ SS_CONNECTDG, /* Socket is a connected datagram socket */ SS_NOTSOCK /* Socket isn't a socket at all */ };
This enumeration contains a list of all possible states a socket can be in. Applications should not use SS_NOTSOCK
; engines should treat it as a special socket state for non-sockets. The only event that should be watched for on a struct Socket
in the SS_NOTSOCK
state is readability. This socket state is used to implement the fall-back signal event generation.
enum TimerType { TT_ABSOLUTE, /* timer that runs at a specific time */ TT_RELATIVE, /* timer that runs so many seconds in the future */ TT_PERIODIC /* timer that runs periodically */ };
The three possible timer types are defined by the TimerType
enumeration. More details can be found in the "Timers" section, above.
enum EventType { ET_READ, /* Readable event detected */ ET_WRITE, /* Writable event detected */ ET_ACCEPT, /* Connection can be accepted */ ET_CONNECT, /* Connection completed */ ET_EOF, /* End-of-file on connection */ ET_ERROR, /* Error condition detected */ ET_SIGNAL, /* A signal was received */ ET_EXPIRE, /* A timer expired */ ET_DESTROY /* The generator is being destroyed */ };
This enumeration contains all the types of events that can be generated by the events subsystem. The first 6 are generated by socket generators, the next by signal generators, and the next by timer generators. ET_DESTROY
is generated by both socket and timer generators when the events subsystem is finished with the memory allocated by both.
struct Socket { struct GenHeader s_header; /* Generator information */ enum SocketState s_state; /* State socket's in */ unsigned int s_events; /* Events socket is interested in */ int s_fd; /* File descriptor for socket */ #ifdef USE_SSL SSL* ssl; /* If not NULL, use SSL routines on socket */ #endif /* USE_SSL */ };
This structure describes everything the events subsystem knows about a given socket. All of its fields may be accessed through the s_* macros described below.
struct Timer { struct GenHeader t_header; /* Generator information */ enum TimerType t_type; /* What type of timer this is */ time_t t_value; /* Value timer was added with */ time_t t_expire; /* Time at which timer expires */ };
The struct Timer
structure describes everything the events subsystem knows about a given timer. Again, all of its fields may be accessed through the t_* macros described below.
struct Signal { struct GenHeader sig_header; /* Generator information */ int sig_signal; /* Signal number */ };
Signal generators are described by a struct Signal
. All of the fields of a struct Signal
may be accessed by the sig_* macros described below.
struct Event { struct Event* ev_next; /* Linked list of events on queue */ struct Event** ev_prev_p; /* Previous pointer to this event */ enum EventType ev_type; /* Event type */ int ev_data; /* Extra data, like errno value */ union { struct GenHeader* gen_header; /* Generator header */ struct Socket* gen_socket; /* Socket generating event */ struct Signal* gen_signal; /* Signal generating event */ struct Timer* gen_timer; /* Timer generating event */ } ev_gen; /* Object generating event */ };
Each event is described by a struct Event
. Its fields may be examined using the ev_* macros described below.
struct Generators { struct GenHeader* g_socket; /* List of socket generators */ struct GenHeader* g_signal; /* List of signal generators */ struct GenHeader* g_timer; /* List of timer generators */ };
Each engine is passed a list of all generators when the engine's EngineLoop
function is called. The only valid way to access this structure is via the timer_next()
function described below.
struct Engine { const char* eng_name; /* a name for the engine */ EngineInit eng_init; /* initialize engine */ EngineSignal eng_signal; /* express interest in a signal */ EngineAdd eng_add; /* express interest in a socket */ EngineState eng_state; /* mention a change in state to engine */ EngineEvents eng_events; /* express interest in socket events */ EngineDelete eng_closing; /* socket is being closed */ EngineLoop eng_loop; /* actual event loop */ };
Each engine is described by the struct Engine
structure. Each engine must define all of the functions described above except for the EngineSignal
function, which is optional.
#define SOCK_EVENT_READABLE 0x0001 /* interested in readable */
The SOCK_EVENT_READABLE
flag indicates to the engine that the application is interested in readability on this particular socket.
#define SOCK_EVENT_WRITABLE 0x0002 /* interested in writable */
The SOCK_EVENT_WRITABLE
flag indicates to the engine that the application is interested in this socket being writable.
#define SOCK_EVENT_MASK (SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE)
SOCK_EVENT_MASK
may be used to extract only the event interest flags from an event interest set.
#define SOCK_ACTION_SET 0x0000 /* set interest set as follows */
When socket_events()
is called with a set of event interest flags and SOCK_ACTION_SET
, the socket's event interest flags are set to those passed into socket_events()
.
#define SOCK_ACTION_ADD 0x1000 /* add to interest set */
When SOCK_ACTION_ADD
is used in a call to socket_events()
, the event interest flags passed in are added to the existing event interest flags for the socket.
#define SOCK_ACTION_DEL 0x2000 /* remove from interest set */
When SOCK_ACTION_DEL
is used in a call to socket_events()
, the event interest flags passed in are removed from the existing event interest flags for the socket.
#define SOCK_ACTION_MASK 0x3000 /* mask out the actions */
SOCK_ACTION_MASK
is used to isolate the socket action desired.
enum SocketState s_state(struct Socket* sock);
This macro returns the state of the given socket.
unsigned int s_events(struct Socket* sock);
This macro returns the current event interest mask for a given socket. Note that if the socket is in the SS_CONNECTING
or SS_LISTENING
states, this mask has no meaning.
int s_fd(struct Socket* sock);
This macro simply returns the file descriptor for the given socket.
void* s_data(struct Socket* sock);
When a struct Socket
is initialized, data that the call-back function may find useful, such as a pointer to a struct Connection
, is stored in the struct Socket
. This macro returns that pointer.
int s_ed_int(struct Socket* sock);
Engines may find it convenient to associate an integer with a struct Socket
. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
void* s_ed_ptr(struct Socket* sock);
Engines may find it convenient to associate a void* pointer with a struct Socket
. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
int s_active(struct Socket* sock);
A socket's active flag is set when initialized by socket_add()
, and is cleared immediately prior to generating an event of type ET_DESTROY
. This may be used by the application to determine whether or not the socket is still in use by the events subsystem. If it is, s_active()
returns a non-zero value; otherwise, its value is 0.
int socket_add(struct Socket* sock, EventCallBack call, void* data, enum SocketState state, unsigned int events, int fd);
This function is called to add a socket to the list of sockets to be monitored. The sock parameter is a pointer to a struct Socket
that is allocated by the application. The call parameter is a pointer to a function to process any events on the socket. The data parameter is for use of the socket call-back and may be zero. The state parameter must be one of the valid socket states. The events parameter must be a valid events interest mask--0, or the binary OR of SOCK_EVENT_READABLE
or SOCK_EVENT_WRITABLE
. Finally, the fd parameter specifies the socket's file descriptor. This function returns 1 if successful or 0 otherwise.
void socket_del(struct Socket* sock);
When the application is no longer interested in a particular socket, it should call the socket_del()
function. This function must be called no later than when the socket has been closed, to avoid attempting to call select()
or similar functions on closed sockets.
void socket_state(struct Socket* sock, enum SocketState state);
Occasionally, a socket's state will change. This function is used to inform the events subsystem of that change. Only certain state transitions are valid--a socket in the SS_LISTENING
or SS_CONNECTED
states cannot change states, nor can an SS_CONNECTING
socket change to some state other than SS_CONNECTED
. Of course, SS_DATAGRAM
sockets may change state only to SS_CONNECTDG
, and SS_CONNECTDG
sockets may only change states to SS_DATAGRAM
.
void socket_events(struct Socket* sock, unsigned int events);
When the application changes the events it is interested in, it uses socket_events()
to notify the events subsystem of that change. The events parameter is the binary OR of one of SOCK_ACTION_SET
, SOCK_ACTION_ADD
, or SOCK_ACTION_DEL
with an events mask. See the documentation for the SOCK_* macros for more information.
const char* state_to_name(enum SocketState state);
This function is defined only when DEBUGMODE
is #define
'd. It takes the given state and returns a string giving that state's name. This function may safely be called from Debug()
macros.
const char* sock_flags(unsigned int flags);
This function is defined only when DEBUGMODE
is #define
'd. It takes the given event interest flags and returns a string naming each of those flags. This function may safely be called from Debug()
macros, but may only be called once, since it uses function static storage to store the flag strings.
int sig_signal(struct Signal* sig);
This macro returns the signal number for the given struct Signal
.
void* sig_data(struct Signal* sig);
When a struct Signal
is initialized, data that the call-back function may find useful is stored in the struct Signal
. This macro returns that pointer.
int sig_ed_int(struct Signal* sig);
Engines may find it convenient to associate an integer with a struct Signal
. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
void* sig_ed_ptr(struct Signal* sig);
Engines may find it convenient to associate a void* pointer with a struct Signal
. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
int sig_active(struct Signal* sig);
A signal's active flag is set when initialized by signal_add()
. This may be used by the application to determine whether or not the signal has been initialized yet. If it is, sig_active()
returns a non-zero value; otherwise, its value is 0.
void signal_add(struct Signal* signal, EventCallBack call, void* data, int sig);
This function is called to add a signal to the list of signals to be monitored. The signal parameter is a pointer is a pointer to a struct Signal
that is allocated by the application. The call parameter is a pointer to a function to process any signal events. The data parameter is for use of the signal call-back and may be zero. The sig parameter is the integer value of the signal to be monitored.
enum TimerType t_type(struct Timer* tim);
This macro returns the type of the given timer.
time_t t_value(struct Timer* tim);
This macro returns the value that was used when the given timer was initialized by the events subsystem. It will contain an absolute time if the timer type is TT_ABSOLUTE
, and a relative time otherwise.
time_t t_expire(struct Timer* tim);
This macro returns the absolute time at which the timer will next expire.
void* t_data(struct Timer* tim);
When a struct Timer
is initialized, data that the call-back function may find useful is stored in the struct Socket
. This macro returns that pointer.
int t_ed_int(struct Timer *tim);
Engines may find it convenient to associate an integer with a struct Timer
. This macro may be used to retrieve that integer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
void* t_ed_ptr(struct Timer *tim);
Engines may find it convenient to associate a void* pointer with a struct Timer
. This macro may be used to retrieve that pointer or, when used as an lvalue, to assign a value to it. Engine data must be either an int or a void*; use of both is prohibited.
int t_active(struct Timer *tim);
A timer's active flag is set when initialized by timer_add()
, and is cleared immediately prior to generating an event of type ET_DESTROY
. This may be used by the application to determine whether or not the timer is still in use by the events subsystem. If it is, s_active()
returns a non-zero value; otherwise, its value is 0.
void timer_add(struct Timer* timer, EventCallBack call, void* data, enum TimerType type, time_t value);
This function is called to initialize and queue a timer. The timer parameter is a pointer to a struct Timer
that is allocated by the application. The call parameter is a pointer to a function to process the timer's expiration. The data parameter is for use of the timer call-back and may be zero. The type parameter must be one of the valid timer types -- TT_ABSOLUTE
, TT_RELATIVE
, or TT_PERIODIC
. Finally, value is the value for the timer's expiration.
void timer_del(struct Timer* timer);
When the application no longer needs a TT_PERIODIC
timer, or when it wishes to stop a TT_ABSOLUTE
or TT_RELATIVE
timer before its expiration, it should call the timer_del()
function.
void timer_chg(struct Timer* timer, enum TimerType type, time_t value);
Occasionally, an application may wish to delay an existing TT_ABSOLUTE
or TT_RELATIVE
timer; this may be done with the timer_chg()<code> function. The type parameter must be one of <code>TT_ABSOLUTE
or TT_RELATIVE
--changing the values of TT_PERIODIC
timers is not supported. The value parameter is the same as would be given to timer_add()
for that particular type of timer.
void timer_run(void);
When an engine has finished processing the results of its socket and signal checks -- just before it loops around to test for more events -- it should call the timer_run()
function to expire any waiting timers.
time_t timer_next(struct Generators* gen);
Most engines will use a blocking call with a timeout to check for socket activity. To determine when the next timer needs to be run,
and thus to calculate how long the call should block, the engine should call timer_next()
with the gen parameter passed to the EngineLoop
function. The timer_next()
function returns an absolute time, which may have to be massaged into a relative time before the engine may use it.
const char* timer_to_name(enum TimerType type);
This function is defined only when DEBUGMODE<code> is <code>#define
'd. It takes the given type and returns a string giving that type's name. This function may safely be called from Debug()
macros.
enum EventType ev_type(struct Event* ev);
This macro simply returns the type of the event ev.
int ev_data(struct Event* ev);
When an event is generated, a single integer can be passed along as a piece of extra information. This can be used, for instance, to carry an errno
value when an ET_ERROR
is generated. This macro simply returns that integer.
struct Socket* ev_socket(struct Event* ev);
If the event was generated by a socket, this macro returns a pointer to the struct Socket
that generated the event. The results are undefined if the event was not generated by a socket.
struct Signal* ev_signal(struct Event* ev);
If the event was generated by a signal, this macro returns a pointer to the struct Signal
that generated the event. The results are undefined if the event was not generated by a signal.
struct Timer* ev_timer(struct Event* ev);
If the event was generated by a timer, this macro returns a pointer to the struct Timer
that generated the event. The results are undefined if the event was not generated by a timer.
void event_init(int max_sockets);
Before any of the functions or macros described here can be called, the events subsystem must be initialized by calling event_init()
. The max_sockets parameter specifies to the events subsystem how many sockets it must be able to support; this figure may be used for memory allocation by the engines.
void event_loop(void);
Once the initial sockets are open, signals added, and timers queued, the application must call the event_loop()
function in order to actually begin monitoring those sockets, signals, and timers.
void event_generate(enum EventType type, void* arg, int data);
This is the function called by the events subsystem to generate particular events. The type parameter specifies the type of event to generate, and the arg parameter must be a pointer to the event's generator. The data parameter may be used for passing such things as signal numbers or errno
values.
const char* event_to_name(enum EventType type);
This function is defined only when DEBUGMODE
is #define
'd. It takes the given type and returns a string giving that event type's name. This function may safely be called from Debug()
macros.
const char* engine_name(void);
This function is used to retrieve the name of the engine presently being used by the events subsystem.
void gen_ref_inc(void* gen);
This macro increments the reference count of the generator gen, preventing it from simply disappearing without warning.
void gen_ref_dec(void* gen);
This macro decrements the reference count of the generator gen, and releases the memory associated with it by generating at ET_DESTROY
event if the reference count falls to zero and the generator is marked for destruction. No references should be made to the generator after calling this macro.