pupnp (libupnp) snapshot from SourceForge: git clone git://pupnp.git.sourceforge...
[igd2-for-linux:pandonghui1211s-igd2-for-linux.git] / pupnp_branch-1.6.x / threadutil / src / TimerThread.c
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2000-2003 Intel Corporation 
4 // All rights reserved. 
5 //
6 // Redistribution and use in source and binary forms, with or without 
7 // modification, are permitted provided that the following conditions are met: 
8 //
9 // * Redistributions of source code must retain the above copyright notice, 
10 // this list of conditions and the following disclaimer. 
11 // * Redistributions in binary form must reproduce the above copyright notice, 
12 // this list of conditions and the following disclaimer in the documentation 
13 // and/or other materials provided with the distribution. 
14 // * Neither name of Intel Corporation nor the names of its contributors 
15 // may be used to endorse or promote products derived from this software 
16 // without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
22 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
23 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
25 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
26 // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 ///////////////////////////////////////////////////////////////////////////
31
32 #include "TimerThread.h"
33 #include <assert.h>
34
35 /****************************************************************************
36  * Function: FreeTimerEvent
37  *
38  *  Description:
39  *      Deallocates a dynamically allocated TimerEvent.
40  *  Parameters:
41  *      TimerEvent *event - must be allocated with CreateTimerEvent
42  *****************************************************************************/
43 static void
44 FreeTimerEvent( TimerThread * timer,
45                 TimerEvent * event )
46 {
47
48     assert( timer != NULL );
49
50     FreeListFree( &timer->freeEvents, event );
51 }
52
53 /****************************************************************************
54  * Function: TimerThreadWorker
55  *
56  *  Description:
57  *      Implements timer thread.
58  *      Waits for next event to occur and schedules
59  *      associated job into threadpool.
60  *      Internal Only.
61  *  Parameters:
62  *      void * arg -> is cast to TimerThread *
63  *****************************************************************************/
64 static void *
65 TimerThreadWorker( void *arg )
66 {
67     TimerThread *timer = ( TimerThread * ) arg;
68     ListNode *head = NULL;
69
70     TimerEvent *nextEvent = NULL;
71
72     time_t currentTime = 0;
73     time_t nextEventTime = 0;
74     struct timespec timeToWait;
75
76     int tempId;
77
78     assert( timer != NULL );
79
80     ithread_mutex_lock( &timer->mutex );
81
82     while( 1 )
83     {
84
85         //mutex should always be locked at top of loop
86
87         //Check for shutdown
88
89         if( timer->shutdown )
90         {
91
92             timer->shutdown = 0;
93             ithread_cond_signal( &timer->condition );
94             ithread_mutex_unlock( &timer->mutex );
95             return NULL;
96
97         }
98
99         nextEvent = NULL;
100
101         //Get the next event if possible
102         if( timer->eventQ.size > 0 )
103         {
104             head = ListHead( &timer->eventQ );
105
106             nextEvent = ( TimerEvent * ) head->item;
107             nextEventTime = nextEvent->eventTime;
108         }
109
110         currentTime = time( NULL );
111
112         //If time has elapsed, schedule job
113
114         if( ( nextEvent != NULL ) && ( currentTime >= nextEventTime ) )
115         {
116
117             if( nextEvent->persistent ) {
118
119                 ThreadPoolAddPersistent( timer->tp, &nextEvent->job,
120                                          &tempId );
121             } else {
122
123                 ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId );
124             }
125
126             ListDelNode( &timer->eventQ, head, 0 );
127             FreeTimerEvent( timer, nextEvent );
128
129             continue;
130
131         }
132
133         if( nextEvent != NULL ) {
134             timeToWait.tv_nsec = 0;
135             timeToWait.tv_sec = nextEvent->eventTime;
136
137             ithread_cond_timedwait( &timer->condition, &timer->mutex,
138                                     &timeToWait );
139
140         } else {
141             ithread_cond_wait( &timer->condition, &timer->mutex );
142         }
143
144     }
145 }
146
147 /****************************************************************************
148  * Function: CalculateEventTime
149  *
150  *  Description:
151  *      Calculates the appropriate timeout in absolute seconds since
152  *      Jan 1, 1970
153  *      Internal Only.
154  *  Parameters:
155  *      time_t *timeout - timeout
156  *      
157  *****************************************************************************/
158 static int
159 CalculateEventTime( time_t * timeout,
160                     TimeoutType type )
161 {
162     time_t now;
163
164     assert( timeout != NULL );
165
166     if( type == ABS_SEC )
167         return 0;
168     else if( type == REL_SEC ) {
169         time( &now );
170         ( *timeout ) += now;
171         return 0;
172     }
173
174     return -1;
175
176 }
177
178 /****************************************************************************
179  * Function: CreateTimerEvent
180  *
181  *  Description:
182  *      Creates a Timer Event. (Dynamically allocated)
183  *      Internal to timer thread.
184  *  Parameters:
185  *      func - thread function to run.
186  *      arg - argument to function.
187  *      priority - priority of job.
188  *      eventTime - the absoule time of the event
189  *                  in seconds from Jan, 1970
190  *      id - id of job
191  *      
192  *  Returns:
193  *      TimerEvent * on success, NULL on failure.
194  ****************************************************************************/
195 static TimerEvent *
196 CreateTimerEvent( TimerThread * timer,
197                   ThreadPoolJob * job,
198                   Duration persistent,
199                   time_t eventTime,
200                   int id )
201 {
202     TimerEvent *temp = NULL;
203
204     assert( timer != NULL );
205     assert( job != NULL );
206
207     temp = ( TimerEvent * ) FreeListAlloc( &timer->freeEvents );
208     if( temp == NULL )
209         return temp;
210     temp->job = ( *job );
211     temp->persistent = persistent;
212     temp->eventTime = eventTime;
213     temp->id = id;
214
215     return temp;
216 }
217
218 /************************************************************************
219  * Function: TimerThreadInit
220  * 
221  *  Description:
222  *     Initializes and starts timer thread.
223  *
224  *  Parameters:
225  *             timer - valid timer thread pointer.
226  *             tp  - valid thread pool to use. Must be
227  *                   started. Must be valid for lifetime
228  *                   of timer.  Timer must be shutdown
229  *                   BEFORE thread pool.
230  *  Return:
231  *            0 on success, nonzero on failure
232  *            Returns error from ThreadPoolAddPersistent if failure.
233  ************************************************************************/
234 int
235 TimerThreadInit( TimerThread * timer,
236                  ThreadPool * tp )
237 {
238
239     int rc = 0;
240
241     ThreadPoolJob timerThreadWorker;
242
243     assert( timer != NULL );
244     assert( tp != NULL );
245
246     if( ( timer == NULL ) || ( tp == NULL ) ) {
247         return EINVAL;
248     }
249
250     rc += ithread_mutex_init( &timer->mutex, NULL );
251
252     assert( rc == 0 );
253
254     rc += ithread_mutex_lock( &timer->mutex );
255     assert( rc == 0 );
256
257     rc += ithread_cond_init( &timer->condition, NULL );
258     assert( rc == 0 );
259
260     rc += FreeListInit( &timer->freeEvents, sizeof( TimerEvent ), 100 );
261     assert( rc == 0 );
262
263     timer->shutdown = 0;
264     timer->tp = tp;
265     timer->lastEventId = 0;
266     rc += ListInit( &timer->eventQ, NULL, NULL );
267
268     assert( rc == 0 );
269
270     if( rc != 0 ) {
271         rc = EAGAIN;
272     } else {
273
274         TPJobInit( &timerThreadWorker, TimerThreadWorker, timer );
275         TPJobSetPriority( &timerThreadWorker, HIGH_PRIORITY );
276
277         rc = ThreadPoolAddPersistent( tp, &timerThreadWorker, NULL );
278     }
279
280     ithread_mutex_unlock( &timer->mutex );
281
282     if( rc != 0 ) {
283         ithread_cond_destroy( &timer->condition );
284         ithread_mutex_destroy( &timer->mutex );
285         FreeListDestroy( &timer->freeEvents );
286         ListDestroy( &timer->eventQ, 0 );
287     }
288
289     return rc;
290
291 }
292
293 /************************************************************************
294  * Function: TimerThreadSchedule
295  * 
296  *  Description:
297  *     Schedules an event to run at a specified time.
298  *
299  *  Parameters:
300  *             timer - valid timer thread pointer.
301  *             time_t - time of event.
302  *                      either in absolute seconds,
303  *                      or relative seconds in the future.
304  *             timeoutType - either ABS_SEC, or REL_SEC.
305  *                           if REL_SEC, then the event
306  *                           will be scheduled at the
307  *                           current time + REL_SEC.
308  *             
309  *             func - function to schedule
310  *             arg - argument to function
311  *             priority - priority of job.
312  *             id - id of timer event. (out)
313  *  Return:
314  *            0 on success, nonzero on failure
315  *                        EOUTOFMEM if not enough memory to schedule job
316  ************************************************************************/
317 int
318 TimerThreadSchedule( TimerThread * timer,
319                      time_t timeout,
320                      TimeoutType type,
321                      ThreadPoolJob * job,
322                      Duration duration,
323                      int *id )
324 {
325
326     int rc = EOUTOFMEM;
327     int found = 0;
328     int tempId = 0;
329
330     ListNode *tempNode = NULL;
331     TimerEvent *temp = NULL;
332     TimerEvent *newEvent = NULL;
333
334     assert( timer != NULL );
335     assert( job != NULL );
336
337     if( ( timer == NULL ) || ( job == NULL ) ) {
338         return EINVAL;
339     }
340
341     CalculateEventTime( &timeout, type );
342     ithread_mutex_lock( &timer->mutex );
343
344     if( id == NULL )
345         id = &tempId;
346
347     ( *id ) = INVALID_EVENT_ID;
348
349     newEvent = CreateTimerEvent( timer, job, duration, timeout,
350                                  timer->lastEventId );
351
352     if( newEvent == NULL ) {
353         ithread_mutex_unlock( &timer->mutex );
354         return rc;
355     }
356
357     tempNode = ListHead( &timer->eventQ );
358     //add job to Q
359     //Q is ordered by eventTime
360     //with the head of the Q being the next event
361
362     while( tempNode != NULL ) {
363         temp = ( TimerEvent * ) tempNode->item;
364         if( temp->eventTime >= timeout )
365         {
366
367             if( ListAddBefore( &timer->eventQ, newEvent, tempNode ) !=
368                 NULL )
369                 rc = 0;
370             found = 1;
371             break;
372
373         }
374         tempNode = ListNext( &timer->eventQ, tempNode );
375     }
376
377     //add to the end of Q
378     if( !found ) {
379
380         if( ListAddTail( &timer->eventQ, newEvent ) != NULL )
381             rc = 0;
382
383     }
384     //signal change in Q
385     if( rc == 0 ) {
386
387         ithread_cond_signal( &timer->condition );
388     } else {
389         FreeTimerEvent( timer, newEvent );
390     }
391     ( *id ) = timer->lastEventId++;
392     ithread_mutex_unlock( &timer->mutex );
393
394     return rc;
395 }
396
397 /************************************************************************
398  * Function: TimerThreadRemove
399  * 
400  *  Description:
401  *     Removes an event from the timer Q.
402  *     Events can only be removed 
403  *     before they have been placed in the
404  *     thread pool.
405  *
406  *  Parameters:
407  *             timer - valid timer thread pointer.
408  *             id - id of event to remove.
409  *             out - space for returned job (Can be NULL)
410  *  Return:
411  *            0 on success.
412  *            INVALID_EVENT_ID on error.
413  *
414  ************************************************************************/
415 int
416 TimerThreadRemove( TimerThread * timer,
417                    int id,
418                    ThreadPoolJob * out )
419 {
420     int rc = INVALID_EVENT_ID;
421     ListNode *tempNode = NULL;
422     TimerEvent *temp = NULL;
423
424     assert( timer != NULL );
425
426     if( timer == NULL ) {
427         return EINVAL;
428     }
429
430     ithread_mutex_lock( &timer->mutex );
431
432     tempNode = ListHead( &timer->eventQ );
433
434     while( tempNode != NULL ) {
435         temp = ( TimerEvent * ) tempNode->item;
436         if( temp->id == id )
437         {
438
439             ListDelNode( &timer->eventQ, tempNode, 0 );
440             if( out != NULL )
441                 ( *out ) = temp->job;
442             FreeTimerEvent( timer, temp );
443             rc = 0;
444             break;
445         }
446         tempNode = ListNext( &timer->eventQ, tempNode );
447     }
448
449     ithread_mutex_unlock( &timer->mutex );
450     return rc;
451 }
452
453 /************************************************************************
454  * Function: TimerThreadShutdown
455  * 
456  *  Description:
457  *    Shutdown the timer thread
458  *    Events scheduled in the future will NOT be run.
459  *    Timer thread should be shutdown BEFORE it's associated
460  *    thread pool.
461  *  Returns:
462  *    returns 0 if succesfull,
463  *            nonzero otherwise.
464  *            Always returns 0.
465  ***********************************************************************/
466 int
467 TimerThreadShutdown( TimerThread * timer )
468 {
469     ListNode *tempNode2 = NULL;
470     ListNode *tempNode = NULL;
471
472     assert( timer != NULL );
473
474     if( timer == NULL ) {
475         return EINVAL;
476     }
477
478     ithread_mutex_lock( &timer->mutex );
479
480     timer->shutdown = 1;
481     tempNode = ListHead( &timer->eventQ );
482
483     //Delete nodes in Q
484     //call registered free function 
485     //on argument
486     while( tempNode != NULL ) {
487         TimerEvent *temp = ( TimerEvent * ) tempNode->item;
488
489         tempNode2 = ListNext( &timer->eventQ, tempNode );
490         ListDelNode( &timer->eventQ, tempNode, 0 );
491         if( temp->job.free_func ) {
492             temp->job.free_func( temp->job.arg );
493         }
494         FreeTimerEvent( timer, temp );
495         tempNode = tempNode2;
496     }
497
498     ListDestroy( &timer->eventQ, 0 );
499     FreeListDestroy( &timer->freeEvents );
500
501     ithread_cond_broadcast( &timer->condition );
502
503     while( timer->shutdown )    //wait for timer thread to shutdown
504     {
505         ithread_cond_wait( &timer->condition, &timer->mutex );
506     }
507
508     ithread_mutex_unlock( &timer->mutex );
509
510     //destroy condition
511     while( ithread_cond_destroy( &timer->condition ) != 0 ) {
512     }
513
514     //destroy mutex
515     while( ithread_mutex_destroy( &timer->mutex ) != 0 ) {
516     }
517
518     return 0;
519 }