Files
libupnp/threadutil/src/TimerThread.c
Marcelo Roberto Jimenez 5c008e3634 Doxygen.
git-svn-id: https://pupnp.svn.sourceforge.net/svnroot/pupnp/trunk@432 119443c7-1b9e-41f8-b6fc-b9c35fce742c
2008-06-10 04:31:44 +00:00

427 lines
10 KiB
C

/*******************************************************************************
*
* Copyright (c) 2000-2003 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************/
/*!
* \file
*/
#include "TimerThread.h"
#include <assert.h>
/*!
* \brief Deallocates a dynamically allocated TimerEvent.
*/
static void FreeTimerEvent(
/*! [in] Valid timer thread pointer. */
TimerThread *timer,
/*! [in] Must be allocated with CreateTimerEvent*/
TimerEvent *event)
{
assert(timer != NULL);
FreeListFree(&timer->freeEvents, event);
}
/*!
* \brief Implements timer thread.
*
* Waits for next event to occur and schedules associated job into threadpool.
*/
static void *TimerThreadWorker(
/*! [in] arg is cast to (TimerThread *). */
void *arg)
{
TimerThread *timer = ( TimerThread * ) arg;
ListNode *head = NULL;
TimerEvent *nextEvent = NULL;
time_t currentTime = 0;
time_t nextEventTime = 0;
struct timespec timeToWait;
int tempId;
assert( timer != NULL );
ithread_mutex_lock( &timer->mutex );
while( 1 )
{
//mutex should always be locked at top of loop
//Check for shutdown
if( timer->shutdown )
{
timer->shutdown = 0;
ithread_cond_signal( &timer->condition );
ithread_mutex_unlock( &timer->mutex );
return NULL;
}
nextEvent = NULL;
//Get the next event if possible
if( timer->eventQ.size > 0 )
{
head = ListHead( &timer->eventQ );
nextEvent = ( TimerEvent * ) head->item;
nextEventTime = nextEvent->eventTime;
}
currentTime = time( NULL );
//If time has elapsed, schedule job
if( ( nextEvent != NULL ) && ( currentTime >= nextEventTime ) )
{
if( nextEvent->persistent ) {
ThreadPoolAddPersistent( timer->tp, &nextEvent->job,
&tempId );
} else {
ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId );
}
ListDelNode( &timer->eventQ, head, 0 );
FreeTimerEvent( timer, nextEvent );
continue;
}
if( nextEvent != NULL ) {
timeToWait.tv_nsec = 0;
timeToWait.tv_sec = nextEvent->eventTime;
ithread_cond_timedwait( &timer->condition, &timer->mutex,
&timeToWait );
} else {
ithread_cond_wait( &timer->condition, &timer->mutex );
}
}
}
/*!
* \brief Calculates the appropriate timeout in absolute seconds
* since Jan 1, 1970.
*
* \return
*/
static int CalculateEventTime(
/*! [in] Timeout. */
time_t *timeout,
/*! [in] Timeout type. */
TimeoutType type)
{
time_t now;
assert( timeout != NULL );
if( type == ABS_SEC )
return 0;
else if( type == REL_SEC ) {
time( &now );
( *timeout ) += now;
return 0;
}
return -1;
}
/*!
* \brief Creates a Timer Event. (Dynamically allocated).
*
* \return (TimerEvent *) on success, NULL on failure.
*/
static TimerEvent *CreateTimerEvent(
/*! [in] Valid timer thread pointer. */
TimerThread *timer,
/*! [in] . */
ThreadPoolJob *job,
/*! [in] . */
Duration persistent,
/*! [in] The absoule time of the event in seconds from Jan, 1970. */
time_t eventTime,
/*! [in] Id of job. */
int id)
{
TimerEvent *temp = NULL;
assert( timer != NULL );
assert( job != NULL );
temp = ( TimerEvent * ) FreeListAlloc( &timer->freeEvents );
if( temp == NULL )
return temp;
temp->job = ( *job );
temp->persistent = persistent;
temp->eventTime = eventTime;
temp->id = id;
return temp;
}
int TimerThreadInit(TimerThread *timer, ThreadPool *tp)
{
int rc = 0;
ThreadPoolJob timerThreadWorker;
assert( timer != NULL );
assert( tp != NULL );
if( ( timer == NULL ) || ( tp == NULL ) ) {
return EINVAL;
}
rc += ithread_mutex_init( &timer->mutex, NULL );
assert( rc == 0 );
rc += ithread_mutex_lock( &timer->mutex );
assert( rc == 0 );
rc += ithread_cond_init( &timer->condition, NULL );
assert( rc == 0 );
rc += FreeListInit( &timer->freeEvents, sizeof( TimerEvent ), 100 );
assert( rc == 0 );
timer->shutdown = 0;
timer->tp = tp;
timer->lastEventId = 0;
rc += ListInit( &timer->eventQ, NULL, NULL );
assert( rc == 0 );
if( rc != 0 ) {
rc = EAGAIN;
} else {
TPJobInit( &timerThreadWorker, TimerThreadWorker, timer );
TPJobSetPriority( &timerThreadWorker, HIGH_PRIORITY );
rc = ThreadPoolAddPersistent( tp, &timerThreadWorker, NULL );
}
ithread_mutex_unlock( &timer->mutex );
if( rc != 0 ) {
ithread_cond_destroy( &timer->condition );
ithread_mutex_destroy( &timer->mutex );
FreeListDestroy( &timer->freeEvents );
ListDestroy( &timer->eventQ, 0 );
}
return rc;
}
int TimerThreadSchedule(
TimerThread *timer,
time_t timeout,
TimeoutType type,
ThreadPoolJob *job,
Duration duration,
int *id)
{
int rc = EOUTOFMEM;
int found = 0;
int tempId = 0;
ListNode *tempNode = NULL;
TimerEvent *temp = NULL;
TimerEvent *newEvent = NULL;
assert( timer != NULL );
assert( job != NULL );
if( ( timer == NULL ) || ( job == NULL ) ) {
return EINVAL;
}
CalculateEventTime( &timeout, type );
ithread_mutex_lock( &timer->mutex );
if( id == NULL )
id = &tempId;
( *id ) = INVALID_EVENT_ID;
newEvent = CreateTimerEvent( timer, job, duration, timeout,
timer->lastEventId );
if( newEvent == NULL ) {
ithread_mutex_unlock( &timer->mutex );
return rc;
}
tempNode = ListHead( &timer->eventQ );
//add job to Q
//Q is ordered by eventTime
//with the head of the Q being the next event
while( tempNode != NULL ) {
temp = ( TimerEvent * ) tempNode->item;
if( temp->eventTime >= timeout )
{
if( ListAddBefore( &timer->eventQ, newEvent, tempNode ) !=
NULL )
rc = 0;
found = 1;
break;
}
tempNode = ListNext( &timer->eventQ, tempNode );
}
//add to the end of Q
if( !found ) {
if( ListAddTail( &timer->eventQ, newEvent ) != NULL )
rc = 0;
}
//signal change in Q
if( rc == 0 ) {
ithread_cond_signal( &timer->condition );
} else {
FreeTimerEvent( timer, newEvent );
}
( *id ) = timer->lastEventId++;
ithread_mutex_unlock( &timer->mutex );
return rc;
}
int TimerThreadRemove(
TimerThread *timer,
int id,
ThreadPoolJob *out)
{
int rc = INVALID_EVENT_ID;
ListNode *tempNode = NULL;
TimerEvent *temp = NULL;
assert( timer != NULL );
if( timer == NULL ) {
return EINVAL;
}
ithread_mutex_lock( &timer->mutex );
tempNode = ListHead( &timer->eventQ );
while( tempNode != NULL ) {
temp = ( TimerEvent * ) tempNode->item;
if( temp->id == id )
{
ListDelNode( &timer->eventQ, tempNode, 0 );
if( out != NULL )
( *out ) = temp->job;
FreeTimerEvent( timer, temp );
rc = 0;
break;
}
tempNode = ListNext( &timer->eventQ, tempNode );
}
ithread_mutex_unlock( &timer->mutex );
return rc;
}
int TimerThreadShutdown(TimerThread *timer)
{
ListNode *tempNode2 = NULL;
ListNode *tempNode = NULL;
assert( timer != NULL );
if( timer == NULL ) {
return EINVAL;
}
ithread_mutex_lock( &timer->mutex );
timer->shutdown = 1;
tempNode = ListHead( &timer->eventQ );
//Delete nodes in Q
//call registered free function
//on argument
while( tempNode != NULL ) {
TimerEvent *temp = ( TimerEvent * ) tempNode->item;
tempNode2 = ListNext( &timer->eventQ, tempNode );
ListDelNode( &timer->eventQ, tempNode, 0 );
if( temp->job.free_func ) {
temp->job.free_func( temp->job.arg );
}
FreeTimerEvent( timer, temp );
tempNode = tempNode2;
}
ListDestroy( &timer->eventQ, 0 );
FreeListDestroy( &timer->freeEvents );
ithread_cond_broadcast( &timer->condition );
while( timer->shutdown ) //wait for timer thread to shutdown
{
ithread_cond_wait( &timer->condition, &timer->mutex );
}
ithread_mutex_unlock( &timer->mutex );
//destroy condition
while( ithread_cond_destroy( &timer->condition ) != 0 ) {
}
//destroy mutex
while( ithread_mutex_destroy( &timer->mutex ) != 0 ) {
}
return 0;
}