/** * @file BReactor.h * @author Ambroz Bizjak * * @section LICENSE * * This file is part of BadVPN. * * BadVPN is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * BadVPN 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * @section DESCRIPTION * * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events * and timers. */ #ifndef BADVPN_SYSTEM_BREACTOR_H #define BADVPN_SYSTEM_BREACTOR_H #if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_USE_EPOLL) + defined(BADVPN_USE_KEVENT) + defined(BADVPN_USE_POLL)) != 1 #error Unknown event backend or too many event backends #endif #ifdef BADVPN_USE_WINAPI #include #endif #ifdef BADVPN_USE_EPOLL #include #endif #ifdef BADVPN_USE_KEVENT #include #include #include #endif #ifdef BADVPN_USE_POLL #include #endif #include #include #include #include #include #include #include #include struct BTimer_t; /** * Handler function invoked when the timer expires. * The timer was in running state. * The timer enters not running state before this function is invoked. * This function is being called from within the timer's previosly * associated reactor. * * @param user value passed to {@link BTimer_Init} */ typedef void (*BTimer_handler) (void *user); /** * Timer object used with {@link BReactor}. */ typedef struct BTimer_t { btime_t msTime; BTimer_handler handler; void *handler_pointer; uint8_t active; uint8_t expired; btime_t absTime; union { BHeapNode heap_node; LinkedList1Node list_node; }; } BTimer; /** * Initializes the timer object. * The timer object is initialized in not running state. * * @param bt the object * @param msTime default timeout in milliseconds * @param handler handler function invoked when the timer expires * @param user value to pass to the handler function */ void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user); /** * Checks if the timer is running. * * @param bt the object * @return 1 if running, 0 if not running */ int BTimer_IsRunning (BTimer *bt); #ifndef BADVPN_USE_WINAPI struct BFileDescriptor_t; #define BREACTOR_READ (1 << 0) #define BREACTOR_WRITE (1 << 1) #define BREACTOR_ERROR (1 << 2) /** * Handler function invoked by the reactor when one or more events are detected. * The events argument will contain a subset of the monitored events (BREACTOR_READ, BREACTOR_WRITE), * plus possibly the error event (BREACTOR_ERROR). * The file descriptor object is in active state, being called from within * the associated reactor. * * @param user value passed to {@link BFileDescriptor_Init} * @param events bitmask composed of a subset of monitored events (BREACTOR_READ, BREACTOR_WRITE), * and possibly the error event (BREACTOR_ERROR). * Will be nonzero. */ typedef void (*BFileDescriptor_handler) (void *user, int events); /** * File descriptor object used with {@link BReactor}. */ typedef struct BFileDescriptor_t { int fd; BFileDescriptor_handler handler; void *user; int active; int waitEvents; #ifdef BADVPN_USE_EPOLL struct BFileDescriptor_t **epoll_returned_ptr; #endif #ifdef BADVPN_USE_KEVENT int kevent_tag; int **kevent_returned_ptr; #endif #ifdef BADVPN_USE_POLL LinkedList1Node poll_enabled_fds_list_node; int poll_returned_index; #endif } BFileDescriptor; /** * Intializes the file descriptor object. * The object is initialized in not active state. * * @param bs file descriptor object to initialize * @param fb file descriptor to represent * @param handler handler function invoked by the reactor when a monitored event is detected * @param user value passed to the handler functuon */ void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user); #endif // BReactor #define BSYSTEM_MAX_RESULTS 64 #define BSYSTEM_MAX_HANDLES 64 #define BSYSTEM_MAX_POLL_FDS 4096 /** * Event loop that supports file desciptor (Linux) or HANDLE (Windows) events * and timers. */ typedef struct { int exiting; int exit_code; // jobs BPendingGroup pending_jobs; // timers BHeap timers_heap; LinkedList1 timers_expired_list; // limits LinkedList1 active_limits_list; #ifdef BADVPN_USE_WINAPI LinkedList1 iocp_list; HANDLE iocp_handle; LinkedList1 iocp_ready_list; #endif #ifdef BADVPN_USE_EPOLL int efd; // epoll fd struct epoll_event epoll_results[BSYSTEM_MAX_RESULTS]; // epoll returned events buffer int epoll_results_num; // number of events in the array int epoll_results_pos; // number of events processed so far #endif #ifdef BADVPN_USE_KEVENT int kqueue_fd; struct kevent kevent_results[BSYSTEM_MAX_RESULTS]; int kevent_results_num; int kevent_results_pos; #endif #ifdef BADVPN_USE_POLL LinkedList1 poll_enabled_fds_list; int poll_num_enabled_fds; int poll_results_num; int poll_results_pos; struct pollfd *poll_results_pollfds; BFileDescriptor **poll_results_bfds; #endif DebugObject d_obj; #ifndef BADVPN_USE_WINAPI DebugCounter d_fds_counter; #endif #ifdef BADVPN_USE_KEVENT DebugCounter d_kevent_ctr; #endif DebugCounter d_limits_ctr; } BReactor; /** * Initializes the reactor. * {@link BLog_Init} must have been done. * {@link BTime_Init} must have been done. * * @param bsys the object * @return 1 on success, 0 on failure */ int BReactor_Init (BReactor *bsys) WARN_UNUSED; /** * Frees the reactor. * Must not be called from within the event loop ({@link BReactor_Exec}). * There must be no {@link BPending} objects using the pending group * returned by {@link BReactor_PendingGroup}. * There must be no running timers in this reactor. * There must be no limit objects in this reactor. * There must be no file descriptors or handles registered * with this reactor. * There must be no {@link BReactorKEvent} objects in this reactor. * * @param bsys the object */ void BReactor_Free (BReactor *bsys); /** * Runs the event loop. * * @param bsys the object * @return value passed to {@link BReactor_Quit} */ int BReactor_Exec (BReactor *bsys); /** * Causes the event loop ({@link BReactor_Exec}) to cease * dispatching events and return. * Any further calls of {@link BReactor_Exec} will return immediately. * * @param bsys the object * @param code value {@link BReactor_Exec} should return. If this is * called more than once, it will return the last code. */ void BReactor_Quit (BReactor *bsys, int code); /** * Starts a timer to expire after its default time. * The timer must have been initialized with {@link BTimer_Init}. * If the timer is in running state, it must be associated with this reactor. * The timer enters running state, associated with this reactor. * * @param bsys the object * @param bt timer to start */ void BReactor_SetTimer (BReactor *bsys, BTimer *bt); /** * Starts a timer to expire after a given time. * The timer must have been initialized with {@link BTimer_Init}. * If the timer is in running state, it must be associated with this reactor. * The timer enters running state, associated with this reactor. * * @param bsys the object * @param bt timer to start * @param after relative expiration time */ void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after); /** * Starts a timer to expire at the specified time. * The timer must have been initialized with {@link BTimer_Init}. * If the timer is in running state, it must be associated with this reactor. * The timer enters running state, associated with this reactor. * The timer's expiration time is set to the time argument. * * @param bsys the object * @param bt timer to start * @param time absolute expiration time (according to {@link btime_gettime}) */ void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time); /** * Stops a timer. * If the timer is in running state, it must be associated with this reactor. * The timer enters not running state. * * @param bsys the object * @param bt timer to stop */ void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt); /** * Returns a {@link BPendingGroup} object that can be used to schedule jobs for * the reactor to execute. These jobs have complete priority over other events * (timers, file descriptors and Windows handles). * The returned pending group may only be used as an argument to {@link BPending_Init}, * and must not be accessed by other means. * All {@link BPending} objects using this group must be freed before freeing * the reactor. * * @param bsys the object * @return pending group for scheduling jobs for the reactor to execute */ BPendingGroup * BReactor_PendingGroup (BReactor *bsys); /** * Executes pending jobs until either: * - the reference job is reached, or * - {@link BReactor_Quit} is called. * The reference job must be reached before the job list empties. * The reference job will not be executed. * * WARNING: Use with care. This should only be used to to work around third-party software * that does not integrade into the jobs system. In particular, you should think about: * - the effects the jobs to be executed may have, and * - the environment those jobs expect to be executed in. * * @param bsys the object * @param ref reference job. It is not accessed in any way, only its address is compared to * pending jobs before they are executed. * @return 1 if the reference job was reached, * 0 if {@link BReactor_Quit} was called (either while executing a job, or before) */ int BReactor_Synchronize (BReactor *bsys, BPending *ref); #ifndef BADVPN_USE_WINAPI /** * Starts monitoring a file descriptor. * * @param bsys the object * @param bs file descriptor object. Must have been initialized with * {@link BFileDescriptor_Init} Must be in not active state. * On success, the file descriptor object enters active state, * associated with this reactor. * @return 1 on success, 0 on failure */ int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED; /** * Stops monitoring a file descriptor. * * @param bsys the object * @param bs {@link BFileDescriptor} object. Must be in active state, * associated with this reactor. The file descriptor object * enters not active state. */ void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs); /** * Sets monitored file descriptor events. * * @param bsys the object * @param bs {@link BFileDescriptor} object. Must be in active state, * associated with this reactor. * @param events events to watch for. Must not have any bits other than * BREACTOR_READ and BREACTOR_WRITE. * This overrides previosly monitored events. */ void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events); #endif typedef struct { BReactor *reactor; int limit; int count; LinkedList1Node active_limits_list_node; DebugObject d_obj; } BReactorLimit; /** * Initializes a limit object. * A limit object consists of a counter integer, which is initialized to * zero, is incremented by {@link BReactorLimit_Increment} up to \a limit, * and is reset to zero every time the event loop performs a wait. * If the event loop has processed all detected events, and before performing * a wait, it determines that timers have expired, the counter will not be reset. * * @param o the object * @param reactor reactor the object is tied to * @param limit maximum counter value. Must be >0. */ void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit); /** * Frees a limit object. * * @param o the object */ void BReactorLimit_Free (BReactorLimit *o); /** * Attempts to increment the counter of a limit object. * * @param o the object * @return 1 if the counter was lesser than the limit and was incremented, * 0 if the counter was greater or equal to the limit and could not be * incremented */ int BReactorLimit_Increment (BReactorLimit *o); /** * Sets the limit of a limit object. * * @param o the object * @param limit new limit. Must be >0. */ void BReactorLimit_SetLimit (BReactorLimit *o, int limit); #ifdef BADVPN_USE_KEVENT typedef void (*BReactorKEvent_handler) (void *user, u_int fflags, intptr_t data); typedef struct { BReactor *reactor; BReactorKEvent_handler handler; void *user; uintptr_t ident; short filter; int kevent_tag; int **kevent_returned_ptr; DebugObject d_obj; } BReactorKEvent; int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data); void BReactorKEvent_Free (BReactorKEvent *o); #endif #ifdef BADVPN_USE_WINAPI #define BREACTOR_IOCP_EVENT_SUCCEEDED 1 #define BREACTOR_IOCP_EVENT_FAILED 2 #define BREACTOR_IOCP_EVENT_EXITING 3 typedef void (*BReactorIOCPOverlapped_handler) (void *user, int event, DWORD bytes); typedef struct { OVERLAPPED olap; BReactor *reactor; void *user; BReactorIOCPOverlapped_handler handler; LinkedList1Node iocp_list_node; int is_ready; LinkedList1Node ready_list_node; int ready_succeeded; DWORD ready_bytes; DebugObject d_obj; } BReactorIOCPOverlapped; HANDLE BReactor_GetIOCPHandle (BReactor *reactor); void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler); void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o); void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes); #endif #endif