source: trunk/libtransmission/trevent.c @ 3247

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

fix some memory issues.

  • Property svn:keywords set to Date Rev Author Id
File size: 9.9 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 3247 2007-10-01 04:12:24Z 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
112    while( !eh->die )
113    {
114        struct tr_event_command * cmd;
115
116        /* get the next command */
117        tr_lockLock( eh->lock );
118        cmd = tr_list_pop_front( &eh->commands );
119        tr_lockUnlock( eh->lock );
120        if( cmd == NULL )
121            break;
122
123        /* process the command */
124        switch( cmd->mode )
125        {
126            case TR_EV_TIMER_ADD:
127                timeout_add( &cmd->timer->event, &cmd->timer->tv );
128                ++eh->timerCount;
129                break;
130
131            case TR_EV_TIMER_DEL:
132                event_del( &cmd->timer->event );
133                tr_free( cmd->timer );
134                --eh->timerCount;
135                break;
136
137            case TR_EV_EVHTTP_MAKE_REQUEST:
138                evhttp_make_request( cmd->evcon, cmd->req, cmd->evtype, cmd->uri );
139                tr_free( cmd->uri );
140                break;
141
142           case TR_EV_BUFFEREVENT_SET:
143                bufferevent_enable( cmd->bufev, cmd->enable );
144                bufferevent_disable( cmd->bufev, cmd->disable );
145                break;
146
147            case TR_EV_BUFFEREVENT_WRITE:
148                bufferevent_write( cmd->bufev, cmd->buf, cmd->buflen );
149                tr_free( cmd->buf );
150                break;
151
152            case TR_EV_EXEC:
153                (cmd->func)( cmd->user_data );
154                break;
155
156            default:
157                assert( 0 && "unhandled command type!" );
158        }
159
160        /* cleanup */
161        tr_free( cmd );
162    }
163
164    if( !eh->die )
165        timeout_add( &eh->pulse, &eh->pulseInterval );
166    else {
167        assert( eh->timerCount ==  0 );
168        evdns_shutdown( FALSE );
169        event_del( &eh->pulse );
170    }
171}
172
173static void
174logFunc( int severity, const char * message )
175{
176    switch( severity )
177    {
178        case _EVENT_LOG_DEBUG: 
179            tr_dbg( "%s", message );
180            break;
181        case _EVENT_LOG_ERR:
182            tr_err( "%s", message );
183            break;
184        default:
185            tr_inf( "%s", message );
186            break;
187    }
188}
189
190static void
191libeventThreadFunc( void * veh )
192{
193    tr_event_handle * eh = (tr_event_handle *) veh;
194    tr_dbg( "Starting libevent thread" );
195
196#ifndef WIN32
197    /* Don't exit when writing on a broken socket */
198    signal( SIGPIPE, SIG_IGN );
199#endif
200
201    eh->base = event_init( );
202    //event_set_log_callback( logFunc );
203    evdns_init( );
204    timeout_set( &eh->pulse, pumpList, veh );
205    timeout_add( &eh->pulse, &eh->pulseInterval );
206    eh->h->events = eh;
207
208    event_dispatch( );
209
210    tr_lockFree( eh->lock );
211    event_base_free( eh->base );
212
213    eh->h->events = NULL;
214
215    tr_free( eh );
216    tr_dbg( "Closing libevent thread" );
217}
218
219void
220tr_eventInit( tr_handle * handle )
221{
222    tr_event_handle * eh;
223
224    eh = tr_new0( tr_event_handle, 1 );
225    eh->lock = tr_lockNew( );
226    eh->h = handle;
227    eh->pulseInterval = timevalMsec( 50 );
228    eh->thread = tr_threadNew( libeventThreadFunc, eh, "libeventThreadFunc" );
229}
230
231void
232tr_eventClose( tr_handle * handle )
233{
234    tr_event_handle * eh = handle->events;
235
236    tr_lockLock( eh->lock );
237    tr_list_free( &eh->commands, tr_free );
238    eh->die = TRUE;
239    tr_lockUnlock( eh->lock );
240
241    //event_base_loopexit( eh->base, NULL );
242}
243
244/**
245***
246**/
247
248static void
249pushList( struct tr_event_handle * eh, struct tr_event_command * command )
250{
251    tr_lockLock( eh->lock );
252    tr_list_append( &eh->commands, command );
253    tr_lockUnlock( eh->lock );
254}
255
256void
257tr_evhttp_make_request (tr_handle                 * handle,
258                        struct evhttp_connection  * evcon,
259                        struct evhttp_request     * req,
260                        enum   evhttp_cmd_type      type,
261                        char                      * uri)
262{
263    if( tr_amInThread( handle->events->thread ) ) {
264        evhttp_make_request( evcon, req, type, uri );
265        tr_free( uri );
266    } else {
267        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
268        cmd->mode = TR_EV_EVHTTP_MAKE_REQUEST;
269        cmd->evcon = evcon;
270        cmd->req = req;
271        cmd->evtype = type;
272        cmd->uri = uri;
273        pushList( handle->events, cmd );
274    }
275}
276
277void
278tr_bufferevent_write( tr_handle             * handle,
279                      struct bufferevent    * bufev,
280                      const void            * buf,
281                      size_t                  buflen )
282{
283    if( tr_amInThread( handle->events->thread ) )
284        bufferevent_write( bufev, (void*)buf, buflen );
285    else {
286        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
287        cmd->mode = TR_EV_BUFFEREVENT_WRITE;
288        cmd->bufev = bufev;
289        cmd->buf = tr_strndup( buf, buflen );
290        cmd->buflen = buflen;
291        pushList( handle->events, cmd );
292    }
293}
294
295void
296tr_setBufferEventMode( struct tr_handle   * handle,
297                       struct bufferevent * bufev,
298                       short                mode_enable,
299                       short                mode_disable )
300{
301    if( tr_amInThread( handle->events->thread ) ) {
302        bufferevent_enable( bufev, mode_enable );
303        bufferevent_disable( bufev, mode_disable );
304    } else {
305        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
306        cmd->mode = TR_EV_BUFFEREVENT_SET;
307        cmd->bufev = bufev;
308        cmd->enable = mode_enable;
309        cmd->disable = mode_disable;
310        pushList( handle->events, cmd );
311    }
312}
313
314/**
315***
316**/
317
318static int
319timerCompareFunc( const void * va, const void * vb )
320{
321    const struct tr_event_command * a = va;
322    const struct tr_timer * b = vb;
323    return a->timer == b ? 0 : 1;
324}
325
326static void
327timerCallback( int fd UNUSED, short event UNUSED, void * vtimer )
328{
329    int more;
330    struct tr_timer * timer = vtimer;
331    void * del;
332
333    del = tr_list_remove( &timer->eh->commands, timer, timerCompareFunc );
334
335    if( del != NULL ) /* there's a TIMER_DEL command queued for this timer... */
336        more = FALSE;
337    else {
338        timer->inCallback = 1;
339        more = (*timer->func)( timer->user_data );
340        timer->inCallback = 0;
341    }
342
343    if( more )
344        timeout_add( &timer->event, &timer->tv );
345    else
346        tr_timerFree( &timer );
347}
348
349void
350tr_timerFree( tr_timer ** ptimer )
351{
352    tr_timer * timer;
353
354    /* zero out the argument passed in */
355    assert( ptimer );
356    timer = *ptimer;
357    *ptimer = NULL;
358
359    /* destroy the timer directly or via the command queue */
360    if( timer!=NULL && !timer->inCallback ) {
361        if( tr_amInThread( timer->eh->thread ) ) {
362            --timer->eh->timerCount;
363            event_del( &timer->event );
364            tr_free( timer );
365        } else {
366            struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
367            cmd->mode = TR_EV_TIMER_DEL;
368            cmd->timer = timer;
369            pushList( timer->eh, cmd );
370        }
371    }
372}
373
374tr_timer*
375tr_timerNew( struct tr_handle * handle,
376             timer_func         func,
377             void             * user_data,
378             int                timeout_milliseconds )
379{
380    tr_timer * timer = tr_new0( tr_timer, 1 );
381    timer->tv = timevalMsec( timeout_milliseconds );
382    timer->func = func;
383    timer->user_data = user_data;
384    timer->eh = handle->events;
385    timeout_set( &timer->event, timerCallback, timer );
386
387    if( tr_amInThread( handle->events->thread ) ) {
388        timeout_add( &timer->event,  &timer->tv );
389        ++handle->events->timerCount;
390    } else {
391        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
392        cmd->mode = TR_EV_TIMER_ADD;
393        cmd->timer = timer;
394        pushList( handle->events, cmd );
395    }
396
397    return timer;
398}
399
400void
401tr_runInEventThread( struct tr_handle * handle,
402                     void               func( void* ),
403                     void             * user_data )
404{
405    if( tr_amInThread( handle->events->thread ) )
406        (func)( user_data );
407    else {
408        struct tr_event_command * cmd = tr_new0( struct tr_event_command, 1 );
409        cmd->mode = TR_EV_EXEC;
410        cmd->func = func;
411        cmd->user_data = user_data;
412        pushList( handle->events, cmd );
413    }
414}
Note: See TracBrowser for help on using the repository browser.