
Hi, I had found some bugs about memory leak on libupnp-1.6.18. It may lead to memory leak when calling ThreadPoolAdd() or ThreadPoolAddPersistent() which does not return 0. See the attachment for patch.
402 lines
10 KiB
C
402 lines
10 KiB
C
/*******************************************************************************
|
|
*
|
|
* Copyright (c) 2000-2003 Intel Corporation
|
|
* All rights reserved.
|
|
* Copyright (c) 2012 France Telecom 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 );
|
|
if (head == NULL) {
|
|
ithread_mutex_unlock( &timer->mutex );
|
|
return NULL;
|
|
}
|
|
nextEvent = ( TimerEvent * ) head->item;
|
|
nextEventTime = nextEvent->eventTime;
|
|
}
|
|
currentTime = time(NULL);
|
|
/* If time has elapsed, schedule job. */
|
|
if (nextEvent && currentTime >= nextEventTime) {
|
|
if( nextEvent->persistent ) {
|
|
if (ThreadPoolAddPersistent( timer->tp, &nextEvent->job, &tempId ) != 0) {
|
|
if (nextEvent->job.arg != NULL && nextEvent->job.free_func != NULL) {
|
|
nextEvent->job.free_func(nextEvent->job.arg);
|
|
}
|
|
}
|
|
} else {
|
|
if (ThreadPoolAdd( timer->tp, &nextEvent->job, &tempId ) != 0) {
|
|
if (nextEvent->job.arg != NULL && nextEvent->job.free_func != NULL) {
|
|
nextEvent->job.free_func(nextEvent->job.arg);
|
|
}
|
|
}
|
|
}
|
|
ListDelNode( &timer->eventQ, head, 0 );
|
|
FreeTimerEvent( timer, nextEvent );
|
|
continue;
|
|
}
|
|
if (nextEvent) {
|
|
timeToWait.tv_nsec = 0;
|
|
timeToWait.tv_sec = (long)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 );
|
|
|
|
switch (type) {
|
|
case ABS_SEC:
|
|
return 0;
|
|
default: /* 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))
|
|
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;
|
|
}
|
|
|