Просмотр исходного кода

BSocket: execute handlers such that after executing a handler, control is returned to the reactor. This makes sure all the handler's work is done before
another handler is called.

ambrop7 15 лет назад
Родитель
Сommit
b9c13e8911
2 измененных файлов с 110 добавлено и 44 удалено
  1. 87 42
      system/BSocket.c
  2. 23 2
      system/BSocket.h

+ 87 - 42
system/BSocket.c

@@ -45,6 +45,14 @@
 #define HANDLER_WRITE 1
 #define HANDLER_ACCEPT 2
 #define HANDLER_CONNECT 3
+#define NUM_EVENTS 4
+
+int handler_events[] = {
+    [HANDLER_READ] = BSOCKET_READ,
+    [HANDLER_WRITE] = BSOCKET_WRITE,
+    [HANDLER_ACCEPT] = BSOCKET_ACCEPT,
+    [HANDLER_CONNECT] = BSOCKET_CONNECT
+};
 
 static void init_handlers (BSocket *bs)
 {
@@ -217,48 +225,70 @@ static int get_event_index (int event)
     }
 }
 
-static void call_handlers (BSocket *bs, int returned_events)
+static void dispatch_event (BSocket *bs)
+{
+    ASSERT(!bs->global_handler)
+    ASSERT(bs->current_event_index >= 0)
+    ASSERT(bs->current_event_index < NUM_EVENTS)
+    ASSERT(((bs->ready_events)&~(bs->waitEvents)) == 0)
+    
+    do {
+        // get event
+        int ev_index = bs->current_event_index;
+        int ev_mask = handler_events[ev_index];
+        int ev_dispatch = (bs->ready_events&ev_mask);
+        
+        // jump to next event, clear this event
+        bs->current_event_index++;
+        bs->ready_events &= ~ev_mask;
+        
+        ASSERT(!(bs->ready_events) || bs->current_event_index < NUM_EVENTS)
+        
+        if (ev_dispatch) {
+            // if there are more events to dispatch, schedule job
+            if (bs->ready_events) {
+                BPending_Set(&bs->job);
+            }
+            
+            // dispatch this event
+            bs->handlers[ev_index](bs->handlers_user[ev_index], ev_mask);
+            return;
+        }
+    } while (bs->current_event_index < NUM_EVENTS);
+    
+    ASSERT(!bs->ready_events)
+}
+
+static void job_handler (BSocket *bs)
+{
+    ASSERT(!bs->global_handler) // BSocket_RemoveGlobalEventHandler unsets the job
+    ASSERT(bs->current_event_index >= 0)
+    ASSERT(bs->current_event_index < NUM_EVENTS)
+    ASSERT(((bs->ready_events)&~(bs->waitEvents)) == 0) // BSocket_DisableEvent clears events from ready_events
+    
+    dispatch_event(bs);
+    return;
+}
+
+static void dispatch_events (BSocket *bs, int events)
 {
+    ASSERT((events&~(bs->waitEvents)) == 0)
+    
     // reset recv number
     bs->recv_num = 0;
     
     if (bs->global_handler) {
-        bs->global_handler(bs->global_handler_user, returned_events);
+        if (events) {
+            bs->global_handler(bs->global_handler_user, events);
+        }
         return;
     }
     
-    if (returned_events&BSOCKET_READ) {
-        ASSERT(bs->handlers[HANDLER_READ])
-        DEAD_ENTER(bs->dead)
-        bs->handlers[HANDLER_READ](bs->handlers_user[HANDLER_READ], BSOCKET_READ);
-        if (DEAD_LEAVE(bs->dead)) {
-            return;
-        }
-    }
-    if (returned_events&BSOCKET_WRITE) {
-        ASSERT(bs->handlers[HANDLER_WRITE])
-        DEAD_ENTER(bs->dead)
-        bs->handlers[HANDLER_WRITE](bs->handlers_user[HANDLER_WRITE], BSOCKET_WRITE);
-        if (DEAD_LEAVE(bs->dead)) {
-            return;
-        }
-    }
-    if (returned_events&BSOCKET_ACCEPT) {
-        ASSERT(bs->handlers[HANDLER_ACCEPT])
-        DEAD_ENTER(bs->dead)
-        bs->handlers[HANDLER_ACCEPT](bs->handlers_user[HANDLER_ACCEPT], BSOCKET_ACCEPT);
-        if (DEAD_LEAVE(bs->dead)) {
-            return;
-        }
-    }
-    if (returned_events&BSOCKET_CONNECT) {
-        ASSERT(bs->handlers[HANDLER_CONNECT])
-        DEAD_ENTER(bs->dead)
-        bs->handlers[HANDLER_CONNECT](bs->handlers_user[HANDLER_CONNECT], BSOCKET_CONNECT);
-        if (DEAD_LEAVE(bs->dead)) {
-            return;
-        }
-    }
+    bs->ready_events = events;
+    bs->current_event_index = 0;
+    
+    dispatch_event(bs);
+    return;
 }
 
 #ifdef BADVPN_USE_WINAPI
@@ -333,7 +363,8 @@ static void handle_handler (BSocket *bs)
         }
     }
     
-    call_handlers(bs, returned_events);
+    dispatch_events(bs, returned_events);
+    return;
 }
 
 #else
@@ -393,7 +424,8 @@ static void file_descriptor_handler (BSocket *bs, int events)
         }
     }
     
-    call_handlers(bs, returned_events);
+    dispatch_events(bs, returned_events);
+    return;
 }
 
 #endif
@@ -523,6 +555,8 @@ fail0:
 
 int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
 {
+    // NOTE: if you change something here, you might also have to change BSocket_Accept
+    
     // translate domain
     int sys_domain;
     switch (domain) {
@@ -578,7 +612,6 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     // setup winsock exts
     setup_winsock_exts(fd, type, bs);
     
-    DEAD_INIT(bs->dead);
     bs->bsys = bsys;
     bs->type = type;
     bs->domain = domain;
@@ -589,6 +622,10 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     bs->connecting_status = 0;
     bs->recv_max = BSOCKET_DEFAULT_RECV_MAX;
     bs->recv_num = 0;
+    bs->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
+    
+    // init job
+    BPending_Init(&bs->job, BReactor_PendingGroup(bsys), (BPending_handler)job_handler, bs);
     
     // initialize event backend
     if (!init_event_backend(bs)) {
@@ -602,6 +639,7 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     return 0;
     
 fail1:
+    BPending_Free(&bs->job);
     close_socket(fd);
 fail0:
     return -1;
@@ -615,12 +653,11 @@ void BSocket_Free (BSocket *bs)
     // free event backend
     free_event_backend(bs);
     
+    // free job
+    BPending_Free(&bs->job);
+    
     // close socket
     close_socket(bs->socket);
-    
-    // if we're being called indirectly from a socket event handler,
-    // allow the function invoking the handler to know that the socket was freed
-    DEAD_KILL(bs->dead);
 }
 
 void BSocket_SetRecvMax (BSocket *bs, int max)
@@ -647,6 +684,9 @@ void BSocket_AddGlobalEventHandler (BSocket *bs, BSocket_handler handler, void *
     
     bs->global_handler = handler;
     bs->global_handler_user = user;
+    
+    // stop event dispatching job
+    BPending_Unset(&bs->job);
 }
 
 void BSocket_RemoveGlobalEventHandler (BSocket *bs)
@@ -753,6 +793,7 @@ void BSocket_DisableEvent (BSocket *bs, uint8_t event)
     
     // update events
     bs->waitEvents &= ~event;
+    bs->ready_events &= ~event;
     
     // give new events to event backend
     update_event_backend(bs);
@@ -904,7 +945,6 @@ int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
         // setup winsock exts
         setup_winsock_exts(fd, bs->type, newsock);
         
-        DEAD_INIT(newsock->dead);
         newsock->bsys = bs->bsys;
         newsock->type = bs->type;
         newsock->domain = bs->domain;
@@ -915,6 +955,10 @@ int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
         newsock->connecting_status = 0;
         newsock->recv_max = BSOCKET_DEFAULT_RECV_MAX;
         newsock->recv_num = 0;
+        newsock->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
+        
+        // init job
+        BPending_Init(&newsock->job, BReactor_PendingGroup(bs->bsys), (BPending_handler)job_handler, newsock);
     
         if (!init_event_backend(newsock)) {
             DEBUG("WARNING: init_event_backend failed");
@@ -934,6 +978,7 @@ int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
     return 0;
     
 fail0:
+    BPending_Free(&newsock->job);
     close_socket(fd);
     bs->error = BSOCKET_ERROR_UNKNOWN;
     return -1;

+ 23 - 2
system/BSocket.h

@@ -33,11 +33,11 @@
 #include <misc/mswsock.h>
 #endif
 
-#include <misc/dead.h>
 #include <misc/debug.h>
 #include <system/BAddr.h>
 #include <system/BReactor.h>
 #include <system/DebugObject.h>
+#include <system/BPending.h>
 
 // errors
 #define BSOCKET_ERROR_NONE 0
@@ -72,6 +72,23 @@
 
 struct BSocket_t;
 
+/**
+ * Handler function called when an event is detected on the socket.
+ * 
+ * If the socket does not have a global event handler registered:
+ * The event parameter is the event that was detected.
+ * It is guaranteed that that the corresponding event handler is registered
+ * and the event is enabled.
+ * 
+ * If the socket has a global event handler registered:
+ * The event parameter is a bitmask of detected events. It is guaranteed
+ * that it is a subset of enabled events, and contains at least one event.
+ * 
+ * It is guaranteed that the handler returns control to the reactor immediately.
+ * 
+ * @param user as in {@link BSocket_AddEventHandler} or {@link BSocket_AddGlobalEventHandler}
+ * @param event events that were detected, see above
+ */
 typedef void (*BSocket_handler) (void *user, int event);
 
 /**
@@ -83,7 +100,6 @@ typedef void (*BSocket_handler) (void *user, int event);
  */
 typedef struct BSocket_t {
     DebugObject d_obj;
-    dead_t dead;
     BReactor *bsys;
     int type;
     int domain;
@@ -99,6 +115,11 @@ typedef struct BSocket_t {
     int recv_max;
     int recv_num;
     
+    // event dispatching
+    int ready_events;
+    int current_event_index;
+    BPending job;
+    
     #ifdef BADVPN_USE_WINAPI
     WSAEVENT event;
     BHandle bhandle;