blob: e2e8b0778e8db01f52e6e0faf04fc0cfd572334d [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version
* 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* etch_simpletimer.c
* thread-per-use one-shot relative timer
* this should be replaced with a timer pool implementation when development resources permit
*/
#include "etch_simpletimer.h"
#include "etch_mutex.h"
#include "etch_exception.h"
#include "etch_log.h"
#include "etch_mem.h"
typedef struct etch_timerdata
{
void* userdata;
int durationms;
int is_started;
etch_timer_callback usercallback;
} etch_timerdata;
void etch_timerproc(void* data);
etch_timerdata* new_timerdata(const int, void*, etch_timer_callback);
/**
* new_timer()
* etch_timer constructor
* @param durationms timer duration in milliseconds.
* @param passthrudata user data to be returned in expiration callback.
* caller retains ownership of this memory.
* @param callback expiration callback void (*callback) (void*, int);
* in which creator's passthrudata is passed as the first parameter, and the timeout
* reason passed in parameter 2 (reason value 0 is signaled, 1 is timeout, -1 error).
* @return the timer object. caller owns this object but must not destroy it until
* after the expiration callback has completed and returned.
*/
etch_timer* new_timer(const int durationms, void* passthrudata, etch_timer_callback callback)
{
etch_thread* timer_thread = NULL;
etch_timerdata* timerdata = NULL;
if (NULL == callback || durationms <= 0) return NULL;
timerdata = new_timerdata(durationms, passthrudata, callback);
/* create thread in wait state */
if (NULL == (timer_thread = new_thread(etch_timerproc, timerdata)))
etch_free(timerdata);
else /* timerdata wrapper freed in etch_timerproc */
{ timer_thread->params.is_own_data = FALSE;
/* semikludge to complete thread start and thus avoid crash in the
* event timer is destroyed without having been started.
* surely there is a cleaner way to do this. */
etch_sleep(1);
}
return timer_thread;
}
/**
* etch_timer_start()
* start the specified timer. a timer must always be explicitly started.
*/
int etch_timer_start(etch_timer* timer_thread)
{
int result = -1;
etch_thread_params* params = timer_thread? &timer_thread->params: NULL;
etch_timerdata* timerdata = params? (etch_timerdata*) params->data: NULL;
if (timerdata)
if (0 == (result = timer_thread->start(timer_thread)))
timerdata->is_started = TRUE;
return result;
}
/**
* etch_timer_stop()
* signal specified timer to stop waiting and exit its thread.
*/
int etch_timer_stop(etch_timer* timer_thread)
{
int result = -1;
etch_wait_t* waiter;
if (NULL == timer_thread) return -1;
waiter = timer_thread->params.waitobj;
if (waiter && timer_thread->params.threadstate == ETCH_THREADSTATE_STARTED)
{
etch_wait_set(waiter, 1);
etch_join(timer_thread); //wait until timer thread finished
}
return result;
}
/* - - - - - - -
* private
* - - - - - - -
*/
/**
* new_timerdata()
* create timer thread data
*/
struct etch_timerdata* new_timerdata(const int durationms, void* passthrudata,
etch_timer_callback callback)
{
etch_timerdata* timerdata = etch_malloc(sizeof(etch_timerdata), 0);
timerdata->userdata = passthrudata;
timerdata->durationms = durationms;
timerdata->usercallback = callback;
timerdata->is_started = FALSE;
return timerdata;
}
/**
* etch_timerproc()
* timer thread procedure
*/
void etch_timerproc(void* data)
{
etch_status_t status = ETCH_SUCCESS;
etch_thread_params* params = (etch_thread_params*) data;
etch_timerdata* timerdata = (etch_timerdata*) params->data;
etch_timer_callback usercallback = timerdata->usercallback;
etch_thread* threadx = params->etchobj;
const int durationms = timerdata->durationms;
void* userdata = timerdata->userdata;
int result = 0;
ETCH_ASSERT(usercallback);
if (NULL == threadx->params.waitobj) {
// TODO: pool
status = etch_wait_create(&threadx->params.waitobj, NULL);
ETCH_ASSERT(status == ETCH_SUCCESS);
}
/* start timer and wait for expiration or signal */
status = etch_wait_timedwait(threadx->params.waitobj, 1, durationms);
switch(status) {
// TODO refactor this stuff and remove ETCH_TIMEOUT ..
case ETCH_SUCCESS:
result = ETCH_INTERRUPTED;
break;
case ETCH_ETIMEOUT:
result = ETCH_TIMEOUT;
break;
default:
result = -1;
}
//ETCHOBJ_FREE(); /* etch_timerdata wrapper */
if (params->data)
etch_free(params->data);
params->data = NULL;
etch_wait_destroy(threadx->params.waitobj);
threadx->params.waitobj = NULL;
/* pass ownership of userdata back to user */
usercallback(userdata, result);
}