badvpn/system/BReactor_badvpn.h
Ambroz Bizjak fac69ce0c6 Fix assertion failures on FreeBSD due to kevent() returning multiple entries for the same file event source.
Maintain linked lists of all returned events for each event source, instead of supporting just a single event per event source.
2016-01-10 17:33:23 +01:00

574 lines
18 KiB
C

/**
* @file BReactor_badvpn.h
* @author Ambroz Bizjak <ambrop7@gmail.com>
*
* @section LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @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 <windows.h>
#endif
#ifdef BADVPN_USE_EPOLL
#include <sys/epoll.h>
#endif
#ifdef BADVPN_USE_KEVENT
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#endif
#ifdef BADVPN_USE_POLL
#include <poll.h>
#endif
#include <stdint.h>
#include <misc/debug.h>
#include <misc/debugcounter.h>
#include <base/DebugObject.h>
#include <structure/LinkedList1.h>
#include <structure/CAvl.h>
#include <system/BTime.h>
#include <base/BPending.h>
struct BSmallTimer_t;
typedef struct BSmallTimer_t *BReactor_timerstree_link;
#include "BReactor_badvpn_timerstree.h"
#include <structure/CAvl_decl.h>
#define BTIMER_SET_ABSOLUTE 1
#define BTIMER_SET_RELATIVE 2
/**
* 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 timer pointer to the timer. Use the {@link UPPER_OBJECT} macro
* to obtain the pointer to the containing structure.
*/
typedef void (*BSmallTimer_handler) (struct BSmallTimer_t *timer);
/**
* 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 BSmallTimer_t {
union {
BSmallTimer_handler smalll; // MSVC doesn't like "small"
BTimer_handler heavy;
} handler;
union {
LinkedList1Node list_node;
struct BSmallTimer_t *tree_child[2];
} u;
struct BSmallTimer_t *tree_parent;
btime_t absTime;
int8_t tree_balance;
uint8_t state;
uint8_t is_small;
} BSmallTimer;
/**
* Initializes the timer object.
* The timer object is initialized in not running state.
*
* @param bt the object
* @param handler handler function invoked when the timer expires
*/
void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler);
/**
* Checks if the timer is running.
*
* @param bt the object
* @return 1 if running, 0 if not running
*/
int BSmallTimer_IsRunning (BSmallTimer *bt);
/**
* Timer object used with {@link BReactor}. This is a legacy wrapper
* around {@link BSmallTimer} with an extra field for the default time.
*/
typedef struct {
BSmallTimer base;
void *user;
btime_t msTime;
} 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)
#define BREACTOR_HUP (1 << 3)
/**
* 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 and the hang-up event BREACTOR_HUP.
* 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_last_event;
#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
BReactor__TimersTree timers_tree;
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_prev_event[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} or {@link BSmallPending} 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 at the specified time.
* The timer must have been initialized with {@link BSmallTimer_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 mode interpretation of time (BTIMER_SET_ABSOLUTE or BTIMER_SET_RELATIVE)
* @param time absolute or relative expiration time
*/
void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, 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_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt);
/**
* 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} and {@link BSmallPending} 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, BSmallPending *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_last_event;
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