source: trunk/libtransmission/trevent.c @ 3319

Last change on this file since 3319 was 3319, checked in by charles, 15 years ago

fix minor shutdown bug

  • Property svn:keywords set to Date Rev Author Id
File size: 10.2 KB
Line 
1/*
2 * This file Copyright (C) 2007 Charles Kerr <charles@rebelbase.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: trevent.c 3319 2007-10-08 00:56:12Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h>
15#include <stdlib.h>
16#include <string.h>
17#include <stdio.h>
18
19#include <signal.h>
20#include <sys/queue.h> /* for evhttp */
21#include <sys/types.h> /* for evhttp */
22
23#include <event.h>
24#include <evdns.h>
25#include <evhttp.h>
26
27#include "transmission.h"
28#include "list.h"
29#include "platform.h"
30#include "trevent.h"
31#include "utils.h"
32
33/* #define DEBUG */
34#ifdef DEBUG
35#include <stdio.h>
36#undef tr_dbg
37#define tr_dbg( a, b... ) fprintf(stderr, a "\n", ##b )
38#endif
39
40/***
41****
42***/
43
44typedef struct tr_event_handle
45{
46    tr_lock * lock;
47    tr_handle * h;
48    tr_thread * thread;
49    tr_list * commands;
50    struct event_base * base;
51    struct event pulse;
52    struct timeval pulseInterval;
53    uint8_t die;
54
55    int timerCount; 
56}
57tr_event_handle;
58
59#ifdef DEBUG
60static int reads = 0;
61static int writes = 0;
62#endif
63
64enum mode
65{
66    TR_EV_EVHTTP_MAKE_REQUEST,
67    TR_EV_BUFFEREVENT_SET,
68    TR_EV_BUFFEREVENT_WRITE,
69    TR_EV_TIMER_ADD,
70    TR_EV_TIMER_DEL,
71    TR_EV_EXEC
72};
73
74typedef int timer_func(void*);
75
76struct tr_timer
77{
78    struct event event;
79    struct timeval tv;
80    timer_func * func;
81    void * user_data;
82    struct tr_event_handle * eh;
83    uint8_t inCallback;
84};
85
86struct tr_event_command
87{
88    int mode;
89
90    struct tr_timer * timer;
91
92    struct evhttp_connection * evcon;
93    struct evhttp_request * req;
94    enum evhttp_cmd_type evtype;
95    char * uri;
96
97    struct bufferevent * bufev;
98    short enable;
99    short disable;
100    char * buf;
101    size_t buflen;
102
103    void (*func)( void* );
104    void * user_data;
105};
106
107static void
108pumpList( int i UNUSED, short s UNUSED, void * veh )
109{
110    tr_event_handle * eh = veh;
111    int doDie;
112
113    for( ;; )
114    {
115        struct tr_event_command * cmd;
116
117        doDie = eh->die && !eh->timerCount;
118        if( doDie )
119            break;
120
121        /* get the next command */
122        tr_lockLock( eh->lock );
123        cmd = tr_list_pop_front( &eh->commands );
124        tr_lockUnlock( eh->lock );
125        if( cmd == NULL )
126            break;
127
128        /* process the command */
129        switch( cmd->mode )
130        {
131            case TR_EV_TIMER_ADD:
132                timeout_add( &cmd->timer->event, &cmd->timer->tv );
133                ++eh->timerCount;
134                break;
135
136            case TR_EV_TIMER_DEL:
137                event_del( &cmd->timer->event );
138                tr_free( cmd->timer );
139                --eh->timerCount;
140                break;
141
142            case TR_EV_EVHTTP_MAKE_REQUEST:
143                evhttp_make_request( cmd->evcon, cmd->req, cmd->evtype, cmd->uri );
144                tr_free( cmd->uri );
145                break;
146
147           case TR_EV_BUFFEREVENT_SET:
148                bufferevent_enable( cmd->bufev, cmd->enable );
149                bufferevent_disable( cmd->bufev, cmd->disable );
150                break;
151
152            case TR_EV_BUFFEREVENT_WRITE:
153                bufferevent_write( cmd->bufev, cmd->buf, cmd->buflen );
154                tr_free( cmd->buf );
155                break;
156
157            case TR_EV_EXEC:
158                (cmd->func)( cmd->user_data );
159                break;
160
161            default:
162                assert( 0 && "unhandled command type!" );
163        }
164
165        /* cleanup */
166        tr_free( cmd );
167    }
168
169    if( !doDie )
170        timeout_add( &eh->pulse, &eh->pulseInterval );
171    else {
172        assert( eh->timerCount ==  0 );
173        evdns_shutdown( FALSE );
174        event_del( &eh->pulse );
175    }
176}
177
178static void
179logFunc( int severity, const char * message )
180{
181    switch( severity )
182    {
183        case _EVENT_LOG_DEBUG: 
184            tr_dbg( "%s", message );
185            break;
186        case _EVENT_LOG_ERR:
187            tr_err( "%s", message );
188            break;
189        default:
190            tr_inf( "%s", message );
191            break;
192    }
193}
194
195static void
196libeventThreadFunc( void * veh )
197{
198    tr_event_handle * eh = (tr_event_handle *) veh;
199    tr_dbg( "Starting libevent thread" );
200
201#ifndef WIN32
202    /* Don't exit when writing on a broken socket */
203    signal( SIGPIPE, SIG_IGN );
204#endif
205
206    eh->base = event_init( );
207    //event_set_log_callback( logFunc );
208    evdns_init( );
209    timeout_set( &eh->pulse, pumpList, veh );
210    timeout_add( &eh->pulse, &eh->pulseInterval );
211    eh->h->events = eh;
212
213    event_dispatch( );
214
215    tr_lockFree( eh->lock );
216    event_base_free( eh->base );
217
218    eh->h->events = NULL;
219
220    tr_free( eh );
221    tr_dbg( "Closing libevent thread" );
222}
223
224void
225tr_eventInit( tr_handle * handle )
226{
227    tr_event_handle * eh;
228
229    eh = tr_new0( tr_event_handle, 1 );
230    eh->lock = tr_lockNew( );
231    eh->h = handle;
232    eh->pulseInterval = timevalMsec( 50 );
233    eh->thread = tr_threadNew( libeventThreadFunc, eh, "libeventThreadFunc" );
234}
235
236void
237tr_eventClose( tr_handle * handle )
238{
239    tr_event_handle * eh = handle->events;
240
241    tr_lockLock( eh->lock );
242    tr_list_free( &eh->commands, tr_free );
243    eh->die = TRUE;
244    tr_lockUnlock( eh->lock );
245
246    //event_base_loopexit( eh->base, NULL );
247}
248
249/**
250***
251**/
252
253static void
254pushList( struct tr_event_handle * eh, struct tr_event_command * command )
255{
256    tr_lockLock( eh->lock );
257    tr_list_append( &eh->commands, command );
258    tr_lockUnlock( eh->lock );
259}
260
261void
262tr_evhttp_make_request (tr_handle                 * handle,
263                        struct evhttp_connection  * evcon,
264                        struct evhttp_request     * req,
265                        enum   evhttp_cmd_type      type,
266                        char                      * uri)
267{
268    if( tr_amInThread( handle->events->thread ) ) {
269        evhttp_make_request( evcon, req, type, uri );
270        tr_free( uri );
271    } else {
272        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
273        cmd->mode = TR_EV_EVHTTP_MAKE_REQUEST;
274        cmd->evcon = evcon;
275        cmd->req = req;
276        cmd->evtype = type;
277        cmd->uri = uri;
278        pushList( handle->events, cmd );
279    }
280}
281
282void
283tr_bufferevent_write( tr_handle             * handle,
284                      struct bufferevent    * bufev,
285                      const void            * buf,
286                      size_t                  buflen )
287{
288    if( tr_amInThread( handle->events->thread ) )
289        bufferevent_write( bufev, (void*)buf, buflen );
290    else {
291        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
292        cmd->mode = TR_EV_BUFFEREVENT_WRITE;
293        cmd->bufev = bufev;
294        cmd->buf = tr_strndup( buf, buflen );
295        cmd->buflen = buflen;
296        pushList( handle->events, cmd );
297    }
298}
299
300void
301tr_setBufferEventMode( struct tr_handle   * handle,
302                       struct bufferevent * bufev,
303                       short                mode_enable,
304                       short                mode_disable )
305{
306    if( tr_amInThread( handle->events->thread ) ) {
307        bufferevent_enable( bufev, mode_enable );
308        bufferevent_disable( bufev, mode_disable );
309    } else {
310        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
311        cmd->mode = TR_EV_BUFFEREVENT_SET;
312        cmd->bufev = bufev;
313        cmd->enable = mode_enable;
314        cmd->disable = mode_disable;
315        pushList( handle->events, cmd );
316    }
317}
318
319/**
320***
321**/
322
323static int
324timerCompareFunc( const void * va, const void * vb )
325{
326    const struct tr_event_command * a = va;
327    const struct tr_timer * b = vb;
328    return a->timer == b ? 0 : 1;
329}
330
331static void
332timerCallback( int fd UNUSED, short event UNUSED, void * vtimer )
333{
334    int more;
335    struct tr_timer * timer = vtimer;
336    void * del;
337
338    del = tr_list_remove( &timer->eh->commands, timer, timerCompareFunc );
339
340    if( del != NULL ) /* there's a TIMER_DEL command queued for this timer... */
341        more = FALSE;
342    else {
343        timer->inCallback = 1;
344        more = (*timer->func)( timer->user_data );
345        timer->inCallback = 0;
346    }
347
348    if( more )
349        timeout_add( &timer->event, &timer->tv );
350    else
351        tr_timerFree( &timer );
352
353    tr_free( del );
354}
355
356void
357tr_timerFree( tr_timer ** ptimer )
358{
359    tr_timer * timer;
360
361    /* zero out the argument passed in */
362    assert( ptimer );
363    timer = *ptimer;
364    *ptimer = NULL;
365
366    /* destroy the timer directly or via the command queue */
367    if( timer!=NULL && !timer->inCallback ) {
368        if( tr_amInThread( timer->eh->thread ) ) {
369            void * del = tr_list_remove( &timer->eh->commands, timer, timerCompareFunc );
370            --timer->eh->timerCount;
371            event_del( &timer->event );
372            tr_free( timer );
373            tr_free( del );
374        } else {
375            struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
376            cmd->mode = TR_EV_TIMER_DEL;
377            cmd->timer = timer;
378            pushList( timer->eh, cmd );
379        }
380    }
381}
382
383tr_timer*
384tr_timerNew( struct tr_handle * handle,
385             timer_func         func,
386             void             * user_data,
387             int                timeout_milliseconds )
388{
389    tr_timer * timer = tr_new0( tr_timer, 1 );
390    timer->tv = timevalMsec( timeout_milliseconds );
391    timer->func = func;
392    timer->user_data = user_data;
393    timer->eh = handle->events;
394    timeout_set( &timer->event, timerCallback, timer );
395
396    if( tr_amInThread( handle->events->thread ) ) {
397        timeout_add( &timer->event,  &timer->tv );
398        ++handle->events->timerCount;
399    } else {
400        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
401        cmd->mode = TR_EV_TIMER_ADD;
402        cmd->timer = timer;
403        pushList( handle->events, cmd );
404    }
405
406    return timer;
407}
408
409void
410tr_runInEventThread( struct tr_handle * handle,
411                     void               func( void* ),
412                     void             * user_data )
413{
414    if( tr_amInThread( handle->events->thread ) )
415        (func)( user_data );
416    else {
417        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
418        cmd->mode = TR_EV_EXEC;
419        cmd->func = func;
420        cmd->user_data = user_data;
421        pushList( handle->events, cmd );
422    }
423}
Note: See TracBrowser for help on using the repository browser.