Explorar o código

system: add a BReactor implementation using the GLib event loop

ambrop7 %!s(int64=14) %!d(string=hai) anos
pai
achega
af505a92a2

+ 20 - 0
CMakeLists.txt

@@ -53,12 +53,32 @@ if (BUILD_SERVER OR BUILD_CLIENT OR BUILD_FLOODER)
     find_package(NSS REQUIRED)
     find_package(NSS REQUIRED)
 endif ()
 endif ()
 
 
+# choose reactor
+if (DEFINED BREACTOR_BACKEND)
+    if (NOT (BREACTOR_BACKEND STREQUAL "badvpn" OR BREACTOR_BACKEND STREQUAL "glib"))
+        message(FATAL_ERROR "unknown reactor backend specified")
+    endif ()
+else ()
+    set(BREACTOR_BACKEND "badvpn")
+endif ()
+
+if (BREACTOR_BACKEND STREQUAL "badvpn")
+    add_definitions(-DBADVPN_BREACTOR_BADVPN)
+elseif (BREACTOR_BACKEND STREQUAL "glib")
+    if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux"))
+        message(FATAL_ERROR "GLib reactor backend is only available on Linux")
+    endif ()
+    find_package(GLIB2 REQUIRED)
+    add_definitions(-DBADVPN_BREACTOR_GLIB)
+endif ()
+
 include_directories(
 include_directories(
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}
     ${LIBCRYPTO_INCLUDE_DIRS}
     ${LIBCRYPTO_INCLUDE_DIRS}
     ${NSPR_INCLUDE_DIRS}
     ${NSPR_INCLUDE_DIRS}
     ${NSS_INCLUDE_DIRS}
     ${NSS_INCLUDE_DIRS}
     ${FUSE_INCLUDE_DIRS}
     ${FUSE_INCLUDE_DIRS}
+    ${GLIB2_INCLUDE_DIR}
     lwip/custom
     lwip/custom
     lwip/src/include
     lwip/src/include
     lwip/src/include/ipv4
     lwip/src/include/ipv4

+ 52 - 0
cmake/modules/FindGLIB2.cmake

@@ -0,0 +1,52 @@
+# - Try to find the GLIB2 libraries
+# Once done this will define
+#
+#  GLIB2_FOUND - system has glib2
+#  GLIB2_INCLUDE_DIR - the glib2 include directory
+#  GLIB2_LIBRARIES - glib2 library
+
+# Copyright (c) 2008 Laurent Montel, <montel@kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+if(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
+    # Already in cache, be silent
+    set(GLIB2_FIND_QUIETLY TRUE)
+endif(GLIB2_INCLUDE_DIR AND GLIB2_LIBRARIES)
+
+find_package(PkgConfig)
+pkg_check_modules(PC_LibGLIB2 QUIET glib-2.0)
+
+find_path(GLIB2_MAIN_INCLUDE_DIR
+          NAMES glib.h
+          HINTS ${PC_LibGLIB2_INCLUDEDIR}
+          PATH_SUFFIXES glib-2.0)
+
+find_library(GLIB2_LIBRARY 
+             NAMES glib-2.0 
+             HINTS ${PC_LibGLIB2_LIBDIR}
+)
+
+set(GLIB2_LIBRARIES ${GLIB2_LIBRARY})
+
+# search the glibconfig.h include dir under the same root where the library is found
+get_filename_component(glib2LibDir "${GLIB2_LIBRARIES}" PATH)
+
+find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h
+          PATH_SUFFIXES glib-2.0/include
+          HINTS ${PC_LibGLIB2_INCLUDEDIR} "${glib2LibDir}" ${CMAKE_SYSTEM_LIBRARY_PATH})
+
+set(GLIB2_INCLUDE_DIR "${GLIB2_MAIN_INCLUDE_DIR}")
+
+# not sure if this include dir is optional or required
+# for now it is optional
+if(GLIB2_INTERNAL_INCLUDE_DIR)
+  set(GLIB2_INCLUDE_DIR ${GLIB2_INCLUDE_DIR} "${GLIB2_INTERNAL_INCLUDE_DIR}")
+endif(GLIB2_INTERNAL_INCLUDE_DIR)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GLIB2  DEFAULT_MSG  GLIB2_LIBRARIES GLIB2_MAIN_INCLUDE_DIR)
+
+mark_as_advanced(GLIB2_INCLUDE_DIR GLIB2_LIBRARIES)

+ 6 - 497
system/BReactor.h

@@ -1,500 +1,9 @@
-/**
- * @file BReactor.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/BHeap.h>
-#include <system/BTime.h>
-#include <base/BPending.h>
-
-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);
-
+#if defined(BADVPN_BREACTOR_BADVPN) + defined(BADVPN_BREACTOR_GLIB) != 1
+#error No reactor backend or too many reactor backens
 #endif
 #endif
 
 
+#if defined(BADVPN_BREACTOR_BADVPN)
+#include "BReactor_badvpn.h"
+#elif defined(BADVPN_BREACTOR_GLIB)
+#include "BReactor_glib.h"
 #endif
 #endif

+ 1 - 1
system/BReactor.c → system/BReactor_badvpn.c

@@ -1,5 +1,5 @@
 /**
 /**
- * @file BReactor.c
+ * @file BReactor_badvpn.c
  * @author Ambroz Bizjak <ambrop7@gmail.com>
  * @author Ambroz Bizjak <ambrop7@gmail.com>
  * 
  * 
  * @section LICENSE
  * @section LICENSE

+ 500 - 0
system/BReactor_badvpn.h

@@ -0,0 +1,500 @@
+/**
+ * @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/BHeap.h>
+#include <system/BTime.h>
+#include <base/BPending.h>
+
+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

+ 477 - 0
system/BReactor_glib.c

@@ -0,0 +1,477 @@
+/**
+ * @file BReactor_glib.c
+ * @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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <misc/offset.h>
+#include <base/BLog.h>
+
+#include "BReactor_glib.h"
+
+#include <generated/blog_channel_BReactor.h>
+
+struct fd_source {
+    GSource source;
+    BFileDescriptor *bfd;
+};
+
+static void dispatch_pending (BReactor *o)
+{
+    while (!o->exiting && BPendingGroup_HasJobs(&o->pending_jobs)) {
+        BPendingGroup_ExecuteJob(&o->pending_jobs);
+    }
+}
+
+static void reset_limits (BReactor *o)
+{
+    LinkedList1Node *list_node;
+    while (list_node = LinkedList1_GetFirst(&o->active_limits_list)) {
+        BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node);
+        ASSERT(limit->count > 0)
+        limit->count = 0;
+        LinkedList1_Remove(&o->active_limits_list, &limit->active_limits_list_node);
+    }
+}
+
+static gushort get_glib_wait_events (int ev)
+{
+    gushort gev = G_IO_ERR | G_IO_HUP;
+    
+    if (ev & BREACTOR_READ) {
+        gev |= G_IO_IN;
+    }
+    
+    if (ev & BREACTOR_WRITE) {
+        gev |= G_IO_OUT;
+    }
+    
+    return gev;
+}
+
+static int get_fd_dispatchable_events (BFileDescriptor *bfd)
+{
+    ASSERT(bfd->active)
+    
+    int ev = 0;
+    
+    if ((bfd->waitEvents & BREACTOR_READ) && (bfd->pollfd.revents & G_IO_IN)) {
+        ev |= BREACTOR_READ;
+    }
+    
+    if ((bfd->waitEvents & BREACTOR_WRITE) && (bfd->pollfd.revents & G_IO_OUT)) {
+        ev |= BREACTOR_WRITE;
+    }
+    
+    if ((bfd->pollfd.revents & G_IO_ERR) || (bfd->pollfd.revents & G_IO_HUP)) {
+        ev |= BREACTOR_ERROR;
+    }
+    
+    return ev;
+}
+
+static gboolean timer_source_handler (gpointer data)
+{
+    BTimer *bt = (void *)data;
+    BReactor *reactor = bt->reactor;
+    ASSERT(bt->active)
+    
+    if (reactor->exiting) {
+        return FALSE;
+    }
+    
+    g_source_destroy(bt->source);
+    g_source_unref(bt->source);
+    bt->active = 0;
+    DebugCounter_Decrement(&reactor->d_timers_ctr);
+    
+    bt->handler(bt->handler_pointer);
+    dispatch_pending(reactor);
+    reset_limits(reactor);
+    
+    return FALSE;
+}
+
+static gboolean fd_source_func_prepare (GSource *source, gint *timeout)
+{
+    BFileDescriptor *bfd = ((struct fd_source *)source)->bfd;
+    ASSERT(bfd->active)
+    ASSERT(bfd->source == source)
+    
+    *timeout = -1;
+    return FALSE;
+}
+
+static gboolean fd_source_func_check (GSource *source)
+{
+    BFileDescriptor *bfd = ((struct fd_source *)source)->bfd;
+    ASSERT(bfd->active)
+    ASSERT(bfd->source == source)
+    
+    return (get_fd_dispatchable_events(bfd) ? TRUE : FALSE);
+}
+
+static gboolean fd_source_func_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+    BFileDescriptor *bfd = ((struct fd_source *)source)->bfd;
+    BReactor *reactor = bfd->reactor;
+    ASSERT(bfd->active)
+    ASSERT(bfd->source == source)
+    
+    if (reactor->exiting) {
+        return TRUE;
+    }
+    
+    int events = get_fd_dispatchable_events(bfd);
+    if (!events) {
+        return TRUE;
+    }
+    
+    bfd->handler(bfd->user, events);
+    dispatch_pending(reactor);
+    reset_limits(reactor);
+    
+    return TRUE;
+}
+
+void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user)
+{
+    bt->msTime = msTime;
+    bt->handler = handler;
+    bt->handler_pointer = user;
+    bt->active = 0;
+}
+
+int BTimer_IsRunning (BTimer *bt)
+{
+    ASSERT(bt->active == 0 || bt->active == 1)
+    
+    return bt->active;
+}
+
+void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user)
+{
+    bs->fd = fd;
+    bs->handler = handler;
+    bs->user = user;
+    bs->active = 0;
+}
+
+int BReactor_Init (BReactor *bsys)
+{
+    return BReactor_InitFromExistingGMainLoop(bsys, g_main_loop_new(NULL, FALSE), 1);
+}
+
+void BReactor_Free (BReactor *bsys)
+{
+    DebugObject_Free(&bsys->d_obj);
+    DebugCounter_Free(&bsys->d_timers_ctr);
+    DebugCounter_Free(&bsys->d_limits_ctr);
+    DebugCounter_Free(&bsys->d_fds_counter);
+    ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
+    ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list))
+    
+    // free job queue
+    BPendingGroup_Free(&bsys->pending_jobs);
+    
+    // unref main loop if needed
+    if (bsys->unref_gloop_on_free) {
+        g_main_loop_unref(bsys->gloop);
+    }
+}
+
+int BReactor_Exec (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    // dispatch pending jobs (until exiting) and reset limits
+    dispatch_pending(bsys);
+    reset_limits(bsys);
+    
+    // if exiting, do not enter glib loop
+    if (bsys->exiting) {
+        return bsys->exit_code;
+    }
+    
+    // enter glib loop
+    g_main_loop_run(bsys->gloop);
+    
+    ASSERT(bsys->exiting)
+    
+    return bsys->exit_code;
+}
+
+void BReactor_Quit (BReactor *bsys, int code)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    // remember exiting
+    bsys->exiting = 1;
+    bsys->exit_code = code;
+    
+    // request termination of glib loop
+    g_main_loop_quit(bsys->gloop);
+}
+
+void BReactor_SetTimer (BReactor *bsys, BTimer *bt)
+{
+    BReactor_SetTimerAfter(bsys, bt, bt->msTime);
+}
+
+void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after)
+{
+    BReactor_SetTimerAbsolute(bsys, bt, btime_add(btime_gettime(), after));
+}
+
+void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    // remove timer if it's already set
+    BReactor_RemoveTimer(bsys, bt);
+    
+    // set active and reactor
+    bt->active = 1;
+    bt->reactor = bsys;
+    
+    // calculate relative time
+    btime_t now = btime_gettime();
+    btime_t relTime = (time < now ? 0 : time - now);
+    
+    // init source
+    bt->source = g_timeout_source_new(relTime);
+    g_source_set_callback(bt->source, timer_source_handler, bt, NULL);
+    g_source_attach(bt->source, g_main_loop_get_context(bsys->gloop));
+    
+    DebugCounter_Increment(&bsys->d_timers_ctr);
+}
+
+void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    // do nothing if timer is not active
+    if (!bt->active) {
+        return;
+    }
+    
+    // free source
+    g_source_destroy(bt->source);
+    g_source_unref(bt->source);
+    
+    // set not active
+    bt->active = 0;
+    
+    DebugCounter_Decrement(&bsys->d_timers_ctr);
+}
+
+BPendingGroup * BReactor_PendingGroup (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    return &bsys->pending_jobs;
+}
+
+int BReactor_Synchronize (BReactor *bsys, BPending *ref)
+{
+    DebugObject_Access(&bsys->d_obj);
+    ASSERT(ref)
+    
+    while (!bsys->exiting) {
+        ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs))
+        
+        if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) {
+            return 1;
+        }
+        
+        BPendingGroup_ExecuteJob(&bsys->pending_jobs);
+    }
+    
+    return 0;
+}
+
+int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
+{
+    DebugObject_Access(&bsys->d_obj);
+    ASSERT(!bs->active)
+    
+    // set active, no wait events, and set reactor
+    bs->active = 1;
+    bs->waitEvents = 0;
+    bs->reactor = bsys;
+    
+    // create source
+    bs->source = g_source_new(&bsys->fd_source_funcs, sizeof(struct fd_source));
+    ((struct fd_source *)bs->source)->bfd = bs;
+    
+    // init pollfd
+    bs->pollfd.fd = bs->fd;
+    bs->pollfd.events = get_glib_wait_events(bs->waitEvents);
+    bs->pollfd.revents = 0;
+    
+    // start source
+    g_source_add_poll(bs->source, &bs->pollfd);
+    g_source_attach(bs->source, g_main_loop_get_context(bsys->gloop));
+    
+    DebugCounter_Increment(&bsys->d_fds_counter);
+    return 1;
+}
+
+void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
+{
+    DebugObject_Access(&bsys->d_obj);
+    DebugCounter_Decrement(&bsys->d_fds_counter);
+    ASSERT(bs->active)
+    
+    // free source
+    g_source_destroy(bs->source);
+    g_source_unref(bs->source);
+    
+    // set not active
+    bs->active = 0;
+}
+
+void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events)
+{
+    DebugObject_Access(&bsys->d_obj);
+    ASSERT(bs->active)
+    ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE)))
+    
+    // set new wait events
+    bs->waitEvents = events;
+    
+    // update pollfd wait events
+    bs->pollfd.events = get_glib_wait_events(bs->waitEvents);
+}
+
+int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free)
+{
+    ASSERT(gloop)
+    ASSERT(unref_gloop_on_free == !!unref_gloop_on_free)
+    
+    // set not exiting
+    bsys->exiting = 0;
+    
+    // set gloop and unref on free flag
+    bsys->gloop = gloop;
+    bsys->unref_gloop_on_free = unref_gloop_on_free;
+    
+    // init fd source functions table
+    memset(&bsys->fd_source_funcs, 0, sizeof(bsys->fd_source_funcs));
+    bsys->fd_source_funcs.prepare = fd_source_func_prepare;
+    bsys->fd_source_funcs.check = fd_source_func_check;
+    bsys->fd_source_funcs.dispatch = fd_source_func_dispatch;
+    bsys->fd_source_funcs.finalize = NULL;
+    
+    // init job queue
+    BPendingGroup_Init(&bsys->pending_jobs);
+    
+    // init active limits list
+    LinkedList1_Init(&bsys->active_limits_list);
+    
+    DebugCounter_Init(&bsys->d_fds_counter);
+    DebugCounter_Init(&bsys->d_limits_ctr);
+    DebugCounter_Init(&bsys->d_timers_ctr);
+    DebugObject_Init(&bsys->d_obj);
+    return 1;
+}
+
+GMainLoop * BReactor_GetGMainLoop (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    return bsys->gloop;
+}
+
+int BReactor_SynchronizeAll (BReactor *bsys)
+{
+    DebugObject_Access(&bsys->d_obj);
+    
+    dispatch_pending(bsys);
+    
+    return !bsys->exiting;
+}
+
+void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit)
+{
+    DebugObject_Access(&reactor->d_obj);
+    ASSERT(limit > 0)
+    
+    // init arguments
+    o->reactor = reactor;
+    o->limit = limit;
+    
+    // set count zero
+    o->count = 0;
+    
+    DebugCounter_Increment(&reactor->d_limits_ctr);
+    DebugObject_Init(&o->d_obj);
+}
+
+void BReactorLimit_Free (BReactorLimit *o)
+{
+    BReactor *reactor = o->reactor;
+    DebugObject_Free(&o->d_obj);
+    DebugCounter_Decrement(&reactor->d_limits_ctr);
+    
+    // remove from active limits list
+    if (o->count > 0) {
+        LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node);
+    }
+}
+
+int BReactorLimit_Increment (BReactorLimit *o)
+{
+    BReactor *reactor = o->reactor;
+    DebugObject_Access(&o->d_obj);
+    
+    // check count against limit
+    if (o->count >= o->limit) {
+        return 0;
+    }
+    
+    // increment count
+    o->count++;
+    
+    // if limit was zero, add to active limits list
+    if (o->count == 1) {
+        LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node);
+    }
+    
+    return 1;
+}
+
+void BReactorLimit_SetLimit (BReactorLimit *o, int limit)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(limit > 0)
+    
+    // set limit
+    o->limit = limit;
+}

+ 127 - 0
system/BReactor_glib.h

@@ -0,0 +1,127 @@
+/**
+ * @file BReactor_glib.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.
+ */
+
+#ifndef BADVPN_SYSTEM_BREACTOR_H
+#define BADVPN_SYSTEM_BREACTOR_H
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <misc/debug.h>
+#include <misc/debugcounter.h>
+#include <structure/LinkedList1.h>
+#include <base/DebugObject.h>
+#include <base/BPending.h>
+#include <system/BTime.h>
+
+typedef struct BReactor_s BReactor;
+
+typedef void (*BTimer_handler) (void *user);
+
+typedef struct BTimer_t {
+    btime_t msTime;
+    BTimer_handler handler;
+    void *handler_pointer;
+    uint8_t active;
+    BReactor *reactor;
+    GSource *source;
+} BTimer;
+
+void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user);
+int BTimer_IsRunning (BTimer *bt);
+
+struct BFileDescriptor_t;
+
+#define BREACTOR_READ (1 << 0)
+#define BREACTOR_WRITE (1 << 1)
+#define BREACTOR_ERROR (1 << 2)
+
+typedef void (*BFileDescriptor_handler) (void *user, int events);
+
+typedef struct BFileDescriptor_t {
+    int fd;
+    BFileDescriptor_handler handler;
+    void *user;
+    int active;
+    int waitEvents;
+    BReactor *reactor;
+    GSource *source;
+    GPollFD pollfd;
+} BFileDescriptor;
+
+void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user);
+
+struct BReactor_s {
+    int exiting;
+    int exit_code;
+    GMainLoop *gloop;
+    int unref_gloop_on_free;
+    GSourceFuncs fd_source_funcs;
+    BPendingGroup pending_jobs;
+    LinkedList1 active_limits_list;
+    
+    DebugCounter d_fds_counter;
+    DebugCounter d_limits_ctr;
+    DebugCounter d_timers_ctr;
+    DebugObject d_obj;
+};
+
+int BReactor_Init (BReactor *bsys) WARN_UNUSED;
+void BReactor_Free (BReactor *bsys);
+int BReactor_Exec (BReactor *bsys);
+void BReactor_Quit (BReactor *bsys, int code);
+void BReactor_SetTimer (BReactor *bsys, BTimer *bt);
+void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after);
+void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time);
+void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt);
+BPendingGroup * BReactor_PendingGroup (BReactor *bsys);
+int BReactor_Synchronize (BReactor *bsys, BPending *ref);
+int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs) WARN_UNUSED;
+void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs);
+void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events);
+
+int BReactor_InitFromExistingGMainLoop (BReactor *bsys, GMainLoop *gloop, int unref_gloop_on_free);
+GMainLoop * BReactor_GetGMainLoop (BReactor *bsys);
+int BReactor_SynchronizeAll (BReactor *bsys);
+
+typedef struct {
+    BReactor *reactor;
+    int limit;
+    int count;
+    LinkedList1Node active_limits_list_node;
+    DebugObject d_obj;
+} BReactorLimit;
+
+void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit);
+void BReactorLimit_Free (BReactorLimit *o);
+int BReactorLimit_Increment (BReactorLimit *o);
+void BReactorLimit_SetLimit (BReactorLimit *o, int limit);
+
+#endif

+ 7 - 1
system/CMakeLists.txt

@@ -19,8 +19,14 @@ if (NOT WIN32)
     )
     )
 endif ()
 endif ()
 
 
+if (BREACTOR_BACKEND STREQUAL "badvpn")
+    list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_badvpn.c)
+elseif (BREACTOR_BACKEND STREQUAL "glib")
+    list(APPEND BSYSTEM_ADDITIONAL_SOURCES BReactor_glib.c)
+    list(APPEND BSYSTEM_ADDITIONAL_LIBS ${GLIB2_LIBRARIES})
+endif ()
+
 add_library(system
 add_library(system
-    BReactor.c
     BSignal.c
     BSignal.c
     BTime.c
     BTime.c
     BNetwork.c
     BNetwork.c