aboutsummaryrefslogtreecommitdiffstats
path: root/lib/events_libevent.c
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2008-12-14 10:19:41 +0000
committerWilmer van der Gaast <wilmer@gaast.net>2008-12-14 10:19:41 +0000
commit939370c674071a198e3115f9fa511fa51e8bea9e (patch)
tree4fb4438ec7d016182c0376bed055925e3f6cf3ba /lib/events_libevent.c
parente1720ce8958ae014ce38d03b7b9ab129d947921b (diff)
events_libevent now detects when the event currently being handled is
removed. This could otherwise cause unpredictable behaviour, especially for timers.
Diffstat (limited to 'lib/events_libevent.c')
-rw-r--r--lib/events_libevent.c28
1 files changed, 21 insertions, 7 deletions
diff --git a/lib/events_libevent.c b/lib/events_libevent.c
index d3403152..cf616576 100644
--- a/lib/events_libevent.c
+++ b/lib/events_libevent.c
@@ -36,9 +36,11 @@
#include "proxy.h"
static void b_main_restart();
-static guint id_next = 1;
+static guint id_next = 1; /* Next ID to be allocated to an event handler. */
+static guint id_cur = 0; /* Event ID that we're currently handling. */
+static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */
static GHashTable *id_hash;
-static int quitting = 0;
+static int quitting = 0; /* Prepare to quit, stop handling events. */
/* Since libevent doesn't handle two event handlers for one fd-condition
very well (which happens sometimes when BitlBee changes event handlers
@@ -118,7 +120,7 @@ static void b_event_passthrough( int fd, short event, void *data )
{
struct b_event_data *b_ev = data;
b_input_condition cond = 0;
- int id;
+ gboolean st;
if( fd >= 0 )
{
@@ -132,21 +134,30 @@ static void b_event_passthrough( int fd, short event, void *data )
/* Since the called function might cancel this handler already
(which free()s b_ev), we have to remember the ID here. */
- id = b_ev->id;
+ id_cur = b_ev->id;
+ id_dead = 0;
if( quitting )
{
- b_event_remove( id );
+ b_event_remove( id_cur );
return;
}
- if( !b_ev->function( b_ev->data, fd, cond ) )
+ st = b_ev->function( b_ev->data, fd, cond );
+ if( id_dead )
+ {
+ /* This event was killed already, don't touch it! */
+ return;
+ }
+ else if( !st )
{
event_debug( "Handler returned FALSE: " );
- b_event_remove( id );
+ b_event_remove( id_cur );
}
else if( fd == -1 )
{
+ /* fd == -1 means it was a timer. These can't be auto-repeated
+ so it has to be recreated every time. */
struct timeval tv;
tv.tv_sec = b_ev->timeout / 1000;
@@ -235,6 +246,9 @@ void b_event_remove( gint id )
event_debug( "b_event_remove( %d )\n", id );
if( b_ev )
{
+ if( id == id_cur )
+ id_dead = TRUE;
+
g_hash_table_remove( id_hash, &b_ev->id );
if( b_ev->evinfo.ev_fd >= 0 )
{