source: trunk/libtransmission/trevent.c @ 6253

Last change on this file since 6253 was 6253, checked in by charles, 13 years ago

(rpc) fix crash when quickly toggling rpc on/off reported by John_Clay

  • Property svn:keywords set to Date Rev Author Id
File size: 6.4 KB
Line 
1/*
2 * This file Copyright (C) 2007-2008 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 6253 2008-06-25 11:34:35Z 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
21#ifdef WIN32
22  #include <fcntl.h>
23  #define pipe(f) _pipe(f, 1000, _O_BINARY)
24#else
25  #include <unistd.h>
26#endif
27
28#include <event.h>
29
30#include "transmission.h"
31#include "platform.h"
32#include "trevent.h"
33#include "utils.h"
34
35/***
36****
37***/
38
39typedef struct tr_event_handle
40{
41    uint8_t die;
42    int fds[2];
43    tr_lock * lock;
44    tr_handle * h;
45    tr_thread * thread;
46    struct event_base * base;
47    struct event pipeEvent;
48}
49tr_event_handle;
50
51typedef int timer_func(void*);
52
53struct tr_timer
54{
55    struct event event;
56    struct timeval tv;
57    timer_func * func;
58    void * user_data;
59    struct tr_event_handle * eh;
60    uint8_t inCallback;
61};
62
63struct tr_run_data
64{
65    void (*func)( void * );
66    void * user_data;
67};
68
69#define dbgmsg(fmt...) tr_deepLog( __FILE__, __LINE__, "event", ##fmt )
70
71static void
72readFromPipe( int fd, short eventType, void * veh )
73{
74    char ch;
75    int ret;
76    tr_event_handle * eh = veh;
77    dbgmsg( "readFromPipe: eventType is %hd", eventType );
78
79    /* read the command type */
80    ch = '\0';
81    do {
82        ret = read( fd, &ch, 1 );
83    } while( !eh->die && ret<0 && errno==EAGAIN );
84    dbgmsg( "command is [%c], ret is %d, errno is %d", ch, ret, (int)errno );
85
86    switch( ch )
87    {
88        case 'r': /* run in libevent thread */
89        {
90            struct tr_run_data data;
91            const size_t nwant = sizeof( data );
92            const ssize_t ngot = read( fd, &data, nwant );
93            if( !eh->die && ( ngot == (ssize_t)nwant ) ) {
94                dbgmsg( "invoking function in libevent thread" );
95                (data.func)( data.user_data );
96            }
97            break;
98        }
99        case 't': /* create timer */
100        {
101            tr_timer * timer;
102            const size_t nwant = sizeof( timer );
103            const ssize_t ngot = read( fd, &timer, nwant );
104            if( !eh->die && ( ngot == (ssize_t)nwant ) ) {
105                dbgmsg( "adding timer in libevent thread" );
106                evtimer_add( &timer->event, &timer->tv );
107            }
108            break;
109        }
110        case '\0': /* eof */
111        {
112            dbgmsg( "pipe eof reached... removing event listener" );
113            event_del( &eh->pipeEvent );
114            break;
115        }
116        default:
117        {
118            assert( 0 && "unhandled command type!" );
119            break;
120        }
121    }
122}
123
124static void
125logFunc( int severity, const char * message )
126{
127    if( severity >= _EVENT_LOG_ERR )
128        tr_nerr( "%s", message );
129    else
130        tr_ndbg( "%s", message );
131}
132
133static void
134libeventThreadFunc( void * veh )
135{
136    tr_event_handle * eh = (tr_event_handle *) veh;
137    tr_dbg( "Starting libevent thread" );
138
139#ifndef WIN32
140    /* Don't exit when writing on a broken socket */
141    signal( SIGPIPE, SIG_IGN );
142#endif
143
144    eh->base = event_init( );
145    eh->h->events = eh;
146    event_set_log_callback( logFunc );
147
148    /* listen to the pipe's read fd */
149    event_set( &eh->pipeEvent, eh->fds[0], EV_READ|EV_PERSIST, readFromPipe, veh );
150    event_add( &eh->pipeEvent, NULL );
151
152    event_dispatch( );
153
154    tr_lockFree( eh->lock );
155    event_base_free( eh->base );
156    eh->h->events = NULL;
157    tr_free( eh );
158    tr_dbg( "Closing libevent thread" );
159}
160
161void
162tr_eventInit( tr_handle * handle )
163{
164    tr_event_handle * eh;
165
166    eh = tr_new0( tr_event_handle, 1 );
167    eh->lock = tr_lockNew( );
168    pipe( eh->fds );
169    eh->h = handle;
170    eh->thread = tr_threadNew( libeventThreadFunc, eh, "libeventThreadFunc" );
171}
172
173void
174tr_eventClose( tr_handle * handle )
175{
176    handle->events->die = TRUE;
177    tr_deepLog( __FILE__, __LINE__, NULL, "closing trevent pipe" );
178    close( handle->events->fds[1] );
179}
180
181/**
182***
183**/
184
185int
186tr_amInEventThread( struct tr_handle * handle )
187{
188    return tr_amInThread( handle->events->thread );
189}
190
191/**
192***
193**/
194
195static void
196timerCallback( int fd UNUSED, short event UNUSED, void * vtimer )
197{
198    int more;
199    struct tr_timer * timer = vtimer;
200
201    timer->inCallback = 1;
202    more = (*timer->func)( timer->user_data );
203    timer->inCallback = 0;
204
205    if( more )
206        evtimer_add( &timer->event, &timer->tv );
207    else
208        tr_timerFree( &timer );
209}
210
211void
212tr_timerFree( tr_timer ** ptimer )
213{
214    tr_timer * timer;
215
216    /* zero out the argument passed in */
217    assert( ptimer );
218    timer = *ptimer;
219    *ptimer = NULL;
220
221    /* destroy the timer directly or via the command queue */
222    if( timer && !timer->inCallback )
223    {
224        assert( tr_amInEventThread( timer->eh->h ) );
225        event_del( &timer->event );
226        tr_free( timer );
227    }
228}
229
230tr_timer*
231tr_timerNew( struct tr_handle * handle,
232             timer_func         func,
233             void             * user_data,
234             uint64_t           interval_milliseconds )
235{
236    tr_timer * timer = tr_new0( tr_timer, 1 );
237    timer->tv = tr_timevalMsec( interval_milliseconds );
238    timer->func = func;
239    timer->user_data = user_data;
240    timer->eh = handle->events;
241    evtimer_set( &timer->event, timerCallback, timer );
242
243    if( tr_amInThread( handle->events->thread ) )
244    {
245        evtimer_add( &timer->event,  &timer->tv );
246    }
247    else
248    {
249        const char ch = 't';
250        int fd = handle->events->fds[1];
251        tr_lock * lock = handle->events->lock;
252
253        tr_lockLock( lock );
254        write( fd, &ch, 1 );
255        write( fd, &timer, sizeof(timer) );
256        tr_lockUnlock( lock );
257    }
258
259    return timer;
260}
261
262void
263tr_runInEventThread( struct tr_handle * handle,
264                     void               func( void* ),
265                     void             * user_data )
266{
267    if( tr_amInThread( handle->events->thread ) )
268    {
269        (func)( user_data );
270    }
271    else
272    {
273        const char ch = 'r';
274        int fd = handle->events->fds[1];
275        tr_lock * lock = handle->events->lock;
276        struct tr_run_data data;
277
278        tr_lockLock( lock );
279        write( fd, &ch, 1 );
280        data.func = func;
281        data.user_data = user_data;
282        write( fd, &data, sizeof(data) );
283        tr_lockUnlock( lock );
284    }
285}
Note: See TracBrowser for help on using the repository browser.