source: lib/events_libevent.c @ e046390

Last change on this file since e046390 was e046390, checked in by Wilmer van der Gaast <wilmer@…>, at 2009-10-10T23:25:54Z

Make purple use BitlBee's event handling API. Since the APIs never really
diverged too much this is fairly transparent. I did rename and redefine
GAIM_INPUT_* variables to really make it work without adding another stupid
layer in between.

One problem left, the new libpurple input API doesn't care about return
values. Fixing that in the next CL.

  • Property mode set to 100644
File size: 7.7 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8 * Event handling (using libevent)
9 */
10
11/*
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  GNU General Public License for more details.
21
22  You should have received a copy of the GNU General Public License with
23  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
24  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
25  Suite 330, Boston, MA  02111-1307  USA
26*/
27
28#define BITLBEE_CORE
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#include <sys/types.h>
34#include <sys/time.h>
35#include <event.h>
36#include "proxy.h"
37
38static void b_main_restart();
39static guint id_next = 1; /* Next ID to be allocated to an event handler. */
40static guint id_cur = 0; /* Event ID that we're currently handling. */
41static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */
42static GHashTable *id_hash;
43static int quitting = 0; /* Prepare to quit, stop handling events. */
44
45/* Since libevent doesn't handle two event handlers for one fd-condition
46   very well (which happens sometimes when BitlBee changes event handlers
47   for a combination), let's buid some indexes so we can delete them here
48   already, just in time. */
49static GHashTable *read_hash;
50static GHashTable *write_hash;
51
52struct event_base *leh;
53struct event_base *old_leh;
54
55struct b_event_data
56{
57        guint id;
58        struct event evinfo;
59        gint timeout;
60        b_event_handler function;
61        void *data;
62};
63
64void b_main_init()
65{
66        if( leh != NULL )
67        {
68                /* Clean up the hash tables? */
69               
70                b_main_restart();
71                old_leh = leh;
72        }
73       
74        leh = event_init();
75       
76        id_hash = g_hash_table_new( g_int_hash, g_int_equal );
77        read_hash = g_hash_table_new( g_int_hash, g_int_equal );
78        write_hash = g_hash_table_new( g_int_hash, g_int_equal );
79}
80
81void b_main_run()
82{
83        /* This while loop is necessary to exit the event loop and start a
84           different one (necessary for ForkDaemon mode). */
85        while( event_base_dispatch( leh ) == 0 && !quitting )
86        {
87                if( old_leh != NULL )
88                {
89                        /* For some reason this just isn't allowed...
90                           Possibly a bug in older versions, will see later.
91                        event_base_free( old_leh ); */
92                        old_leh = NULL;
93                }
94               
95                event_debug( "New event loop.\n" );
96        }
97}
98
99static void b_main_restart()
100{
101        struct timeval tv;
102       
103        memset( &tv, 0, sizeof( struct timeval ) );
104        event_base_loopexit( leh, &tv );
105       
106        event_debug( "b_main_restart()\n" );
107}
108
109void b_main_quit()
110{
111        /* Tell b_main_run() that it shouldn't restart the loop. Also,
112           libevent sometimes generates events before really quitting,
113           we want to stop them. */
114        quitting = 1;
115       
116        b_main_restart();
117}
118
119static void b_event_passthrough( int fd, short event, void *data )
120{
121        struct b_event_data *b_ev = data;
122        b_input_condition cond = 0;
123        gboolean st;
124       
125        if( fd >= 0 )
126        {
127                if( event & EV_READ )
128                        cond |= B_EV_IO_READ;
129                if( event & EV_WRITE )
130                        cond |= B_EV_IO_WRITE;
131        }
132       
133        event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id );
134       
135        /* Since the called function might cancel this handler already
136           (which free()s b_ev), we have to remember the ID here. */
137        id_cur = b_ev->id;
138        id_dead = 0;
139       
140        if( quitting )
141        {
142                b_event_remove( id_cur );
143                return;
144        }
145       
146        st = b_ev->function( b_ev->data, fd, cond );
147        if( id_dead )
148        {
149                /* This event was killed already, don't touch it! */
150                return;
151        }
152        else if( !st )
153        {
154                event_debug( "Handler returned FALSE: " );
155                b_event_remove( id_cur );
156        }
157        else if( fd == -1 )
158        {
159                /* fd == -1 means it was a timer. These can't be auto-repeated
160                   so it has to be recreated every time. */
161                struct timeval tv;
162               
163                tv.tv_sec = b_ev->timeout / 1000;
164                tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000;
165               
166                evtimer_add( &b_ev->evinfo, &tv );
167        }
168}
169
170gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data )
171{
172        struct b_event_data *b_ev;
173       
174        event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
175       
176        if( ( condition & B_EV_IO_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
177            ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
178        {
179                /* We'll stick with this libevent entry, but give it a new BitlBee id. */
180                g_hash_table_remove( id_hash, &b_ev->id );
181               
182                event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next );
183               
184                b_ev->id = id_next++;
185                b_ev->function = function;
186                b_ev->data = data;
187        }
188        else
189        {
190                GIOCondition out_cond;
191               
192                event_debug( "(new) = %d\n", id_next );
193               
194                b_ev = g_new0( struct b_event_data, 1 );
195                b_ev->id = id_next++;
196                b_ev->function = function;
197                b_ev->data = data;
198               
199                out_cond = EV_PERSIST;
200                if( condition & B_EV_IO_READ )
201                        out_cond |= EV_READ;
202                if( condition & B_EV_IO_WRITE )
203                        out_cond |= EV_WRITE;
204               
205                event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev );
206                event_add( &b_ev->evinfo, NULL );
207               
208                if( out_cond & EV_READ )
209                        g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev );
210                if( out_cond & EV_WRITE )
211                        g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev );
212        }
213       
214        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
215        return b_ev->id;
216}
217
218/* TODO: Persistence for timers! */
219gint b_timeout_add( gint timeout, b_event_handler function, gpointer data )
220{
221        struct b_event_data *b_ev = g_new0( struct b_event_data, 1 );
222        struct timeval tv;
223       
224        b_ev->id = id_next++;
225        b_ev->timeout = timeout;
226        b_ev->function = function;
227        b_ev->data = data;
228       
229        tv.tv_sec = timeout / 1000;
230        tv.tv_usec = ( timeout % 1000 ) * 1000;
231       
232        evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev );
233        evtimer_add( &b_ev->evinfo, &tv );
234       
235        event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id );
236       
237        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
238       
239        return b_ev->id;
240}
241
242void b_event_remove( gint id )
243{
244        struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id );
245       
246        event_debug( "b_event_remove( %d )\n", id );
247        if( b_ev )
248        {
249                if( id == id_cur )
250                        id_dead = TRUE;
251               
252                g_hash_table_remove( id_hash, &b_ev->id );
253                if( b_ev->evinfo.ev_fd >= 0 )
254                {
255                        if( b_ev->evinfo.ev_events & EV_READ )
256                                g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd );
257                        if( b_ev->evinfo.ev_events & EV_WRITE )
258                                g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd );
259                }
260               
261                event_del( &b_ev->evinfo );
262                g_free( b_ev );
263        }
264        else
265        {
266                event_debug( "Already removed?\n" );
267        }
268}
269
270void closesocket( int fd )
271{
272        struct b_event_data *b_ev;
273       
274        /* Since epoll() (the main reason we use libevent) automatically removes sockets from
275           the epoll() list when a socket gets closed and some modules have a habit of
276           closing sockets before removing event handlers, our and libevent's administration
277           get a little bit messed up. So this little function will remove the handlers
278           properly before closing a socket. */
279       
280        if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) )
281        {
282                event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd );
283                b_event_remove( b_ev->id );
284        }
285        if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) )
286        {
287                event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd );
288                b_event_remove( b_ev->id );
289        }
290       
291        close( fd );
292}
Note: See TracBrowser for help on using the repository browser.