Files
freeswitch/libs/sipcc/cpr/android/cpr_android_timers_using_select.c
T
2012-12-17 20:15:23 -05:00

1301 lines
40 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* @brief CPR layer for Timers.
*
* This file contains the Cisco Portable Runtime layer for non-blocking
* timers. This implementation is for the Linux operating system using
* select with a timeout.
*
* Timer Service runs in its own thread and blocks on select
* call with a timeout. The value of timeout is set equal to
* the duration of the earliest expiring timer. The timer list
* is kept sorted by earliest to latest expiration times. Therefore,
* timeout value is simply the duration on the head of the list.
*
* For starting or cancelling a timer, the timer library contacts
* the timer service using a local socket based IPC. This is done
* by bringing up a connection between timer service and the client
* during timer init.
* Timer Service thread is the only thread that adds or removes
* timer blocks from the list. When timer library client wants
* to start or cancel a timer, the library sends an IPC message
* to the timer service thread on the socket connection. This
* unblocks the select call. Select also returns the amount of
* time left on the timeout when it gets unblocked. This amount is
* first subtracted from the head timer block duration. Then rest
* of the processing is carried out based on whether the request
* was to add or to remove a timer block and duration
* on all timer blocks is adjusted as required.
* When the select times out it implies the timers at the head
* of the list has expired. The list is scanned for expired timers
* and expiry processing such as posting message to the handler
* etc. is done.
*
* When there are no timers in the list, service blocks on select
* forever waiting for at least one timer to be added.
*
*/
/**
* @defgroup Timers The Timer implementation module
* @ingroup CPR
* @brief The module related to Timer abstraction for the pSIPCC
* @addtogroup TimerAPIs The Timer APIs
* @ingroup Timers
* @brief APIs expected by pSIPCC for using Timers
*
*/
#include "cpr.h"
#include "cpr_socket.h"
#include "cpr_stdlib.h"
#include "cpr_stdio.h"
#include "cpr_threads.h"
#include "cpr_timers.h"
#include "cpr_string.h"
#include "phntask.h"
#include <errno.h>
#include <unistd.h>
#include "cpr_android_timers.h"
#include "platform_api.h"
/*--------------------------------------------------------------------------
* Local definitions
*--------------------------------------------------------------------------
*/
typedef struct timer_ipc_cmd_s
{
cpr_timer_t *timer_ptr;
void *user_data_ptr;
uint32_t duration;
} timer_ipc_cmd_t;
typedef struct timer_ipc_s
{
uint32_t msg_type;
union
{
timer_ipc_cmd_t cmd;
cprRC_t result;
}u;
} timer_ipc_t;
#define TMR_CMD_ADD 1
#define TMR_CMD_REMOVE 2
#define TMR_RESULT 3
#define API_RETURN(_val) \
{ \
pthread_mutex_unlock(&api_mutex); \
return (_val); \
}\
#define API_ENTER() \
{\
pthread_mutex_lock(&api_mutex);\
}\
/* for AF_LOCAL not all implementations return client addr in recvfrom so
* using explicit path for client too.
*/
#define SERVER_PATH "/tmp/CprTmrServer"
#define CLIENT_PATH "/tmp/CprTmrClient"
/*--------------------------------------------------------------------------
* Global data
*--------------------------------------------------------------------------
*/
static timerBlk *timerListHead;
static pthread_t timerThreadId;
static pthread_mutex_t api_mutex;
/* local socket used by timer library */
static int client_sock = INVALID_SOCKET;
/* local socket used by the timer service */
static int serv_sock = INVALID_SOCKET;
static struct sockaddr_un tmr_serv_addr;
static struct sockaddr_un tmr_client_addr;
static fd_set socks; /* descriptor set */
/*--------------------------------------------------------------------------
* External data references
*--------------------------------------------------------------------------
*/
/*--------------------------------------------------------------------------
* External function prototypes
*--------------------------------------------------------------------------
*/
/*
* Internal CPR function to fill in data in the sysheader.
* This is to prevent knowledge of the evil syshdr structure
* from spreading to cpr_linux_timers.c This thing is
* like kudzu...
*/
extern void fillInSysHeader(void *buffer, uint16_t cmd, uint16_t len,
void *timerMsg);
/*--------------------------------------------------------------------------
* Local scope function prototypes
*--------------------------------------------------------------------------
*/
static void *timerThread(void *data);
static cprRC_t start_timer_service_loop();
static void process_expired_timers();
static void send_api_result(cprRC_t result, struct sockaddr_un *addr, socklen_t len);
/**
* @addtogroup TimerAPIs The Timer APIs
* @ingroup Timers
* @{
*/
/**
* cprSleep
*
* @brief Suspend the calling thread
* The cprSleep function blocks the calling thread for the indicated number of
* milliseconds.
*
* @param[in] duration - Number of milliseconds the thread should sleep
*
* @return - none
*/
void
cprSleep (uint32_t duration)
{
/*
* usleep() can only support up to one second, so split
* between sleep and usleep if one second or more
*/
if (duration >= 1000) {
(void) sleep(duration / 1000);
(void) usleep((duration % 1000) * 1000);
} else {
(void) usleep(duration * 1000);
}
}
/**
* @}
*/
/**
* @defgroup TimerInternal The Timer internal functions
* @ingroup Timers
* @{
*/
/**
* addTimerToList
* Send message to timer service to add the timer pointed by cprTimerPtr
* to the list. This routine is just sending IPC message to timer service
* but the actual addition is done by timer service using the addTimer function.
* This function is only called by CPR functions and is not visible to external
* applications.
* @param[in] cprTimerPtr - timer pointer
* @param[in] duration - timer duration in msec.
* @param[in] data - opaque data
* @return - CPR_SUCCESS or CPR_FAILURE
*/
static cprRC_t addTimerToList (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
{
static const char fname[] = "addTimerToList";
timer_ipc_t tmr_cmd = {0};
timer_ipc_t tmr_rsp={0};
API_ENTER();
//CPR_INFO("%s: cprTimerptr=0x%x dur=%d user_data=%x\n",
// fname, cprTimerPtr, duration, data);
tmr_cmd.msg_type = TMR_CMD_ADD;
tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
tmr_cmd.u.cmd.user_data_ptr = data;
tmr_cmd.u.cmd.duration = duration;
//CPR_INFO("%s:sending messge of type=%d\n", fname, tmr_cmd.msg_type);
/* simply post a request here to the timer service.*/
if (client_sock != -1) {
if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
(struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
CPR_ERROR("Failed to tx IPC msg to timer service, errno = %s %s\n",
strerror(errno), fname);
API_RETURN(CPR_FAILURE);
}
} else {
CPR_ERROR("can not make IPC connection, client_sock is invalid %s\n", fname);
API_RETURN(CPR_FAILURE);
}
/*
* wait for the timer service to excute the request
* so that we get result of operation
*/
if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
//CPR_INFO("error in recving the result error=%s\n", strerror(errno));
API_RETURN(CPR_FAILURE);
} else {
//CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
API_RETURN(tmr_rsp.u.result);
}
}
/**
* addTimer
*
* Add a timer to the timer linked list.
* This function is only called by CPR functions and is not visible to external
* applications.
*
* @param[in] cprTimerPtr - pointer to the CPR timer structure
* @param[in] duration - how long before timer expires in milliseconds
* @param[in] data - information to be passed to callback function
*
* @return - CPR_SUCCESS or CPR_FAILURE
*/
static cprRC_t addTimer (cpr_timer_t *cprTimerPtr, uint32_t duration, void *data)
{
static const char fname[] = "addTimer";
timerBlk *timerList;
timerBlk *newTimerPtr;
CPR_INFO("%s:adding timer=0x%x timerblk=%x\n", fname,
cprTimerPtr, cprTimerPtr->u.handlePtr);
/* Verify the timer has been initialized */
newTimerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
if (newTimerPtr == NULL) {
CPR_ERROR("%s - Timer %s has not been initialized.\n",
fname, cprTimerPtr->name);
errno = EINVAL;
return(CPR_FAILURE);
}
/* Ensure this timer is not already running */
if (newTimerPtr->timerActive) {
CPR_ERROR("%s - Timer %s is already active.\n", fname, cprTimerPtr->name);
errno = EAGAIN;
return(CPR_FAILURE);
}
/* Sanity tests passed, store the data the application passed in */
newTimerPtr->duration = duration;
cprTimerPtr->data = data;
/*
* Insert timer into the linked list. The timer code only
* decrements the first timer in the list to be efficient.
* Therefore, the timer at the top of the list is the timer
* that will expire first. Timers are added to the list
* in ascending order of time left before expiration and
* ticksLeft is calculated to be the difference between
* when the timer before them expires and when the newly
* inserted timer expires.
*/
/* Check for insertion into an empty list */
if (timerListHead == NULL) {
//CPR_INFO("no timer in the list case..\n");
timerListHead = newTimerPtr;
} else {
/* Insert timer into list */
timerList = timerListHead;
while (timerList != NULL) {
/*
* If the duration on this new timer are less than the
* timer in the list, insert this new timer before
* it in the list as it will expire first. In doing so
* the code must subtract the new timer's duration from
* the duration of the timer in the in the list to keep the deltas correct.
*/
if (newTimerPtr->duration < timerList->duration) {
//CPR_INFO("less than case..\n");
timerList->duration -= newTimerPtr->duration;
newTimerPtr->next = timerList;
newTimerPtr->previous = timerList->previous;
if (newTimerPtr->previous) {
newTimerPtr->previous->next = newTimerPtr;
}
timerList->previous = newTimerPtr;
/* Check for insertion at the head of list */
if (timerListHead == timerList) {
//CPR_INFO("insert at the head case..\n");
timerListHead = newTimerPtr;
}
break;
} else {
/*
* Else this new timer expires after the timer in
* the list. Therefore subtract the timer's duration
* from the new timer's duration. Since only the
* first timer is decremented all other timers added
* expiration must be calculated as a delta from the
* timer in front of them in the list.
*/
//CPR_INFO("greater than case..\n");
newTimerPtr->duration -= timerList->duration;
/* Check for insertion at the end of list */
if (timerList->next == NULL) {
//CPR_INFO("insert at the end sub case..\n");
newTimerPtr->previous = timerList;
timerList->next = newTimerPtr;
newTimerPtr->next = NULL;
break;
}
timerList = timerList->next;
}
}
}
newTimerPtr->timerActive = TRUE;
return(CPR_SUCCESS);
}
/**
* removeTimerFromList
* Send message to timer service to remove the timer pointed by cprTimerPtr
* from the list. This routine is just sending IPC message to timer service
* and the actual removal is done by timer service using the removeTimer function..
* This function is only called by CPR functions and is not visible to external
* applications.
*
* @param[in] cprTimerPtr - pointer to the timer to be removed from the list
* @return - CPR_SUCCESS or CPR_FAILURE
*
*/
static cprRC_t
removeTimerFromList (cpr_timer_t *cprTimerPtr)
{
static const char fname[] = "removeTimerFromList";
timer_ipc_t tmr_cmd = {0};
timer_ipc_t tmr_rsp = {0};
API_ENTER();
//CPR_INFO("%s:remove timer from list=0x%x\n",fname, cprTimerPtr);
tmr_cmd.msg_type = TMR_CMD_REMOVE;
tmr_cmd.u.cmd.timer_ptr = cprTimerPtr;
//CPR_INFO("sending messge of type=%d\n", tmr_cmd.msg_type);
/* simply post a request here to the timer service.. */
if (client_sock != -1) {
if (sendto(client_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
(struct sockaddr *)&tmr_serv_addr, sizeof(tmr_serv_addr)) < 0) {
CPR_ERROR("%s:failed to tx IPC Msg to timer service, errno = %s\n",
fname, strerror(errno));
API_RETURN(CPR_FAILURE);
}
} else {
CPR_ERROR("%s:client_sock invalid, no IPC connection \n", fname);
API_RETURN(CPR_FAILURE);
}
/*
* wait for the timer service to excute the request
* so that we get result of operation
*/
if (recvfrom(client_sock, &tmr_rsp, sizeof(timer_ipc_t),0, NULL, NULL) < 0) {
//CPR_INFO("error in recving the result error=%s\n", strerror(errno));
API_RETURN(CPR_FAILURE);
} else {
//CPR_INFO("received response from the timer result=%d\n", tmr_rsp.u.result);
API_RETURN(tmr_rsp.u.result);
}
}
/**
* removeTimer
*
* Remove a timer from the timer linked list. This function is only
* called by CPR functions and is not visible to applications.
*
* @param[in] cprTimerPtr - which timer to cancel
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
static cprRC_t
removeTimer (cpr_timer_t *cprTimerPtr)
{
static const char fname[] = "removeTimer";
timerBlk *timerList;
timerBlk *previousTimer;
timerBlk *nextTimer;
timerBlk *timerPtr;
//CPR_INFO("removing timer..0x%x\n", cprTimerPtr);
/*
* No need to sanitize the cprTimerPtr data as only
* internal CPR functions call us and they have already
* sanitized that data. In addition those functions
* have already grabbed the timer list mutex so no need
* to do that here.
*/
timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
//CPR_INFO("%s: timer ptr=%x\n", fname, timerPtr);
if (timerPtr != NULL) {
/* Walk the list looking for this timer to cancel. */
timerList = timerListHead;
while (timerList != NULL) {
if (timerList->cprTimerPtr->cprTimerId == cprTimerPtr->cprTimerId) {
/* Removing only element in the list */
if ((timerList->previous == NULL) &&
(timerList->next == NULL)) {
timerListHead = NULL;
/* Removing head of the list */
} else if (timerList->previous == NULL) {
nextTimer = timerList->next;
nextTimer->previous = NULL;
timerListHead = nextTimer;
/* Removing tail of the list */
} else if (timerList->next == NULL) {
previousTimer = timerList->previous;
previousTimer->next = NULL;
/* Removing from middle of the list */
} else {
nextTimer = timerList->next;
previousTimer = timerList->previous;
previousTimer->next = nextTimer;
nextTimer->previous = previousTimer;
}
/* Add time back to next timer in the list */
if (timerList->next) {
timerList->next->duration += timerList->duration;
}
/*
* Reset timer values
*/
timerList->next = NULL;
timerList->previous = NULL;
timerList->duration = -1;
timerList->timerActive = FALSE;
cprTimerPtr->data = NULL;
return(CPR_SUCCESS);
}
/* Walk the list */
timerList = timerList->next;
}
/*
* Either the timer was not active or it was marked active, but not
* found on the timer list. If the timer was inactive then that is OK,
* but let user know about an active timer not found in the timer list.
*/
if ((timerPtr->next != NULL) || (timerPtr->previous != NULL)) {
CPR_ERROR("%s - Timer %s marked as active, "
"but was not found on the timer list.\n",
fname, cprTimerPtr->name);
timerPtr->next = NULL;
timerPtr->previous = NULL;
}
timerPtr->duration = -1;
timerPtr->cprTimerPtr->data = NULL;
timerPtr->timerActive = FALSE;
return(CPR_SUCCESS);
}
/* Bad application! */
CPR_ERROR("%s - Timer not initialized.\n", fname);
errno = EINVAL;
return(CPR_FAILURE);
}
/**
* @}
* @addtogroup TimerAPIs The Timer APIs
* @ingroup Timers
* @{
*/
/**
* cprCreateTimer
*
* @brief Initialize a timer
*
* The cprCreateTimer function is called to allow the OS to perform whatever
* work is needed to create a timer. The input name parameter is optional. If present, CPR assigns
* this name to the timer to assist in debugging. The callbackMsgQueue is the
* address of a message queue created with cprCreateMsgQueue. This is the
* queue where the timer expire message will be sent.
* So, when this timer expires a msg of type "applicationMsgId" will be sent to the msg queue
* "callbackMsgQueue" indicating that timer applicationTimerId has expired.
*
* @param[in] name - name of the timer
* @param[in] applicationTimerId - ID for this timer from the application's
* perspective
* @param[in] applicationMsgId - ID for syshdr->cmd when timer expire msg
* is sent
* @param[in] callBackMsgQueue - where to send a msg when this timer expires
*
* @return Timer handle or NULL if creation failed.
*/
cprTimer_t
cprCreateTimer (const char *name,
uint16_t applicationTimerId,
uint16_t applicationMsgId,
cprMsgQueue_t callBackMsgQueue)
{
static const char fname[] = "cprCreateTimer";
static uint32_t cprTimerId = 0;
cpr_timer_t *cprTimerPtr;
timerBlk *timerPtr;
/*
* Malloc memory for a new timer. Need to
* malloc memory for the generic CPR view and
* one for the CNU specific version.
*/
cprTimerPtr = (cpr_timer_t *) cpr_malloc(sizeof(cpr_timer_t));
timerPtr = (timerBlk *) cpr_malloc(sizeof(timerBlk));
if ((cprTimerPtr != NULL) && (timerPtr != NULL)) {
/* Assign name (Optional) */
cprTimerPtr->name = name;
/* Set timer ids, msg id and callback msg queue (Mandatory) */
cprTimerPtr->applicationTimerId = applicationTimerId;
cprTimerPtr->applicationMsgId = applicationMsgId;
cprTimerPtr->cprTimerId = cprTimerId++;
if (callBackMsgQueue == NULL) {
CPR_ERROR("%s - Callback msg queue for timer %s is NULL.\n",
fname, name);
cpr_free(timerPtr);
cpr_free(cprTimerPtr);
return NULL;
}
cprTimerPtr->callBackMsgQueue = callBackMsgQueue;
/*
* Set remaining values in both structures to defaults
*/
timerPtr->next = NULL;
timerPtr->previous = NULL;
timerPtr->duration = -1;
timerPtr->timerActive = FALSE;
cprTimerPtr->data = NULL;
/*
* TODO - It would be nice for CPR to keep a linked
* list of active timers for debugging purposes
* such as a show command or walking the list to ensure
* that an application does not attempt to create
* the same timer twice.
*
* TODO - It would be nice to initialize of pool of
* timers at init time and have this function just
* return a timer from the pool. Then when the
* timer expired or cancel the code would not free
* it, but just return it to the pool.
*/
timerPtr->cprTimerPtr = cprTimerPtr;
cprTimerPtr->u.handlePtr = timerPtr;
//CPR_INFO("cprTimerCreate: timer_t=%x blk=%x\n",cprTimerPtr, timerPtr);
return cprTimerPtr;
}
/*
* If we get here there has been a malloc failure.
*/
if (timerPtr) {
cpr_free(timerPtr);
}
if (cprTimerPtr) {
cpr_free(cprTimerPtr);
}
/* Malloc failed */
CPR_ERROR("%s - Malloc for timer %s failed.\n", fname, name);
errno = ENOMEM;
return NULL;
}
/**
* cprStartTimer
*
* @brief Start a system timer
*
* The cprStartTimer function starts a previously created timer referenced by
* the parameter timer. CPR timer granularity is 10ms. The "timer" input
* parameter is the handle returned from a previous successful call to
* cprCreateTimer.
*
* @param[in] timer - which timer to start
* @param[in] duration - how long before timer expires in milliseconds
* @param[in] data - information to be passed to callback function
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t
cprStartTimer (cprTimer_t timer,
uint32_t duration,
void *data)
{
static const char fname[] = "cprStartTimer";
cpr_timer_t *cprTimerPtr;
cprTimerPtr = (cpr_timer_t *) timer;
if (cprTimerPtr != NULL) {
/* add timer to the list */
return addTimerToList(cprTimerPtr, duration, data);
}
/* Bad application! */
CPR_ERROR("%s - NULL pointer passed in.\n", fname);
errno = EINVAL;
return CPR_FAILURE;
}
/**
* cprIsTimerRunning
*
* @brief Determine if a timer is active
*
* This function determines whether the passed in timer is currently active. The
* "timer" parameter is the handle returned from a previous successful call to
* cprCreateTimer.
*
* @param[in] timer - which timer to check
*
* @return True is timer is active, False otherwise
*/
boolean
cprIsTimerRunning (cprTimer_t timer)
{
static const char fname[] = "cprIsTimerRunning";
cpr_timer_t *cprTimerPtr;
timerBlk *timerPtr;
//CPR_INFO("istimerrunning(): timer=0x%x\n", timer);
cprTimerPtr = (cpr_timer_t *) timer;
if (cprTimerPtr != NULL) {
timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
if (timerPtr == NULL) {
CPR_ERROR("%s - Timer %s has not been initialized.\n",
fname, cprTimerPtr->name);
errno = EINVAL;
return FALSE;
}
if (timerPtr->timerActive) {
return TRUE;
}
} else {
/* Bad application! */
CPR_ERROR("%s - NULL pointer passed in.\n", fname);
errno = EINVAL;
}
return FALSE;
}
/**
* cprCancelTimer
*
* @brief Cancels a running timer
*
* The cprCancelTimer function cancels a previously started timer referenced by
* the parameter timer.
*
* @param[in] timer - which timer to cancel
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t
cprCancelTimer (cprTimer_t timer)
{
static const char fname[] = "cprCancelTimer";
timerBlk *timerPtr;
cpr_timer_t *cprTimerPtr;
cprRC_t rc = CPR_SUCCESS;
//CPR_INFO("cprCancelTimer: timer ptr=%x\n", timer);
cprTimerPtr = (cpr_timer_t *) timer;
if (cprTimerPtr != NULL) {
timerPtr = (timerBlk *) cprTimerPtr->u.handlePtr;
if (timerPtr == NULL) {
CPR_ERROR("%s - Timer %s has not been initialized.\n",
fname, cprTimerPtr->name);
errno = EINVAL;
return CPR_FAILURE;
}
/*
* Ensure timer is active before trying to remove it.
* If already inactive then just return SUCCESS.
*/
if (timerPtr->timerActive) {
//CPR_INFO("removing timer from the list=%x\n", timerPtr);
rc = removeTimerFromList(timer);
}
return rc;
}
/* Bad application! */
CPR_ERROR("%s - NULL pointer passed in.\n", fname);
errno = EINVAL;
return CPR_FAILURE;
}
/**
* cprUpdateTimer
*
* @brief Updates the expiration time for a running timer
*
* The cprUpdateTimer function cancels a previously started timer referenced by
* the parameter timer and then restarts the same timer with the duration passed
* in.
*
* @param[in] timer - which timer to update
* @param[in] duration - how long before timer expires in milliseconds
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t
cprUpdateTimer (cprTimer_t timer, uint32_t duration)
{
static const char fname[] = "cprUpdateTimer";
cpr_timer_t *cprTimerPtr;
void *timerData;
cprTimerPtr = (cpr_timer_t *) timer;
if (cprTimerPtr != NULL) {
/* Grab data before cancelling timer */
timerData = cprTimerPtr->data;
} else {
CPR_ERROR("%s - NULL pointer passed in.\n", fname);
errno = EINVAL;
return CPR_FAILURE;
}
if (cprCancelTimer(timer) == CPR_SUCCESS) {
if (cprStartTimer(timer, duration, timerData) == CPR_SUCCESS) {
return CPR_SUCCESS;
} else {
CPR_ERROR("%s - Failed to start timer %s\n",
fname, cprTimerPtr->name);
return CPR_FAILURE;
}
}
CPR_ERROR("%s - Failed to cancel timer %s\n", fname, cprTimerPtr->name);
return CPR_FAILURE;
}
/**
* cprDestroyTimer
*
* @brief Destroys a timer.
*
* This function will cancel the timer and then destroy it. It sets
* all links to NULL and then frees the timer block.
*
* @param[in] timer - which timer to destroy
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t
cprDestroyTimer (cprTimer_t timer)
{
static const char fname[] = "cprDestroyTimer";
cpr_timer_t *cprTimerPtr;
cprRC_t rc;
//CPR_INFO("cprDestroyTimer:destroying timer=%x\n", timer);
cprTimerPtr = (cpr_timer_t *) timer;
if (cprTimerPtr != NULL) {
rc = cprCancelTimer(timer);
if (rc == CPR_SUCCESS) {
cprTimerPtr->cprTimerId = 0;
cpr_free(cprTimerPtr->u.handlePtr);
cpr_free(cprTimerPtr);
return CPR_SUCCESS;
} else {
CPR_ERROR("%s - Cancel of Timer %s failed.\n",
fname, cprTimerPtr->name);
return CPR_FAILURE;
}
}
/* Bad application! */
CPR_ERROR("%s - NULL pointer passed in.\n", fname);
errno = EINVAL;
return CPR_FAILURE;
}
/**
* @}
* @addtogroup TimerInternal The Timer internal functions
* @ingroup Timers
* @{
*/
/**
* cpr_timer_pre_init
*
* @brief Initalize timer service and client IPC
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t cpr_timer_pre_init (void)
{
static const char fname[] = "cpr_timer_pre_init";
int32_t returnCode;
/* start the timer service first */
returnCode = (int32_t)pthread_create(&timerThreadId, NULL, timerThread, NULL);
if (returnCode == -1) {
CPR_ERROR("%s: Failed to create Timer Thread : %s\n", fname, strerror(errno));
return CPR_FAILURE;
}
/*
* wait some time so that timer service thread is up
* TBD:we should really implement wait on timerthread using condvar.
*/
cprSleep(1000);
return CPR_SUCCESS;
}
/**
* cpr_timer_de_init
*
* @brief De-Initalize timer service and client IPC
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t cpr_timer_de_init(void)
{
// close all sockets..
close(client_sock);
close(serv_sock);
// destroy api mutex
pthread_mutex_destroy(&api_mutex);
return CPR_SUCCESS;
}
/**
* Timer service thread
*
*/
/**
* timerThread
*
* @brief Timer service thread
*
* This is the start function for the timer server thread.
*
* @param[in] data - The data passed in (UNUSED)
*
* @return This function eventually starts an infinite loop on a "select".
*/
void *timerThread (void *data)
{
static const char fname[] = "timerThread";
//CPR_INFO("timerThread:started..\n");
#ifndef HOST
#ifndef PTHREAD_SET_NAME
#define PTHREAD_SET_NAME(s) do { } while (0)
#endif
PTHREAD_SET_NAME("CPR Timertask");
#endif
/*
* Increase the timer thread priority from default priority.
* This is required to make sure timers fire with reasonable precision.
*
* NOTE: always make sure the priority is higher than sip/gsm threads;
* otherwise, we must use mutex around the following while loop.
*/
(void) cprAdjustRelativeThreadPriority(TIMER_THREAD_RELATIVE_PRIORITY);
/* get ready to listen for timer commands and service them */
if (start_timer_service_loop() == CPR_FAILURE) {
CPR_ERROR("%s: timer service loop failed\n", fname);
}
return NULL;
}
/**
* local_bind
* Function used to do a bind on the local socket.
*
* @param[in] sock - socket descriptor to bind
* @param[in] name - name to use for binding local socket
* @return 0 if success, -1 on error (errno will be set)
*
*/
static int local_bind (int sock, char *name)
{
struct sockaddr_un addr;
/* construct the address structure */
addr.sun_family = AF_LOCAL;
sstrncpy(addr.sun_path, name, sizeof(addr.sun_path));
/* make sure file doesn't already exist */
unlink(addr.sun_path);
return bind(sock, (struct sockaddr *) &addr, sizeof(addr));
}
/**
* select_sockets
*
* Set the socket descriptors to be used for reading.
* Only server side uses select.
*
* @return The server socket number
*/
static int select_sockets (void)
{
FD_ZERO(&socks);
FD_SET(serv_sock, &socks);
return (serv_sock);
}
/**
* read_timer_cmd
* read message received on the IPC from the client
* the only messages are timer commands {add, remove}
*
* @return CPR_SUCCESS or CPR_FAILURE
*/
static cprRC_t read_timer_cmd ()
{
static const char fname[] = "read_timer_cmd";
int rcvlen;
timer_ipc_t tmr_cmd ={0};
cprRC_t ret = CPR_FAILURE;
rcvlen =recvfrom(serv_sock, &tmr_cmd, sizeof(timer_ipc_t), 0,
NULL, NULL);
if (rcvlen > 0) {
//CPR_INFO("got message type=%d\n", tmr_cmd.msg_type);
switch(tmr_cmd.msg_type) {
case TMR_CMD_ADD:
//CPR_INFO("request to add timer ptr=%x duration=%d datptr=%x\n",
// tmr_cmd.u.cmd.timer_ptr, tmr_cmd.u.cmd.duration, tmr_cmd.u.cmd.user_data_ptr);
ret = addTimer(tmr_cmd.u.cmd.timer_ptr,tmr_cmd.u.cmd.duration,
(void *)tmr_cmd.u.cmd.user_data_ptr);
break;
case TMR_CMD_REMOVE:
//CPR_INFO("request to remove timer ptr=%x\n", tmr_cmd.u.cmd.timer_ptr);
ret = removeTimer(tmr_cmd.u.cmd.timer_ptr);
break;
default:
CPR_ERROR("%s:invalid ipc command = %d\n", tmr_cmd.msg_type);
ret = CPR_FAILURE;
break;
}
} else {
CPR_ERROR("%s:while reading serv_sock err =%s: Closing Socket..Timers not operational !!! \n",
fname, strerror(errno));
(void) close(serv_sock);
serv_sock = INVALID_SOCKET;
ret = CPR_FAILURE;
}
/* send the result back */
send_api_result(ret, &tmr_client_addr, sizeof(tmr_client_addr));
return (ret);
}
/**
* send_api_result back to client via a socket sendto operation
* @param[in] retVal - value of result
* @param[in] addr - address to send the result to
* @param[in] len - length of addr
*/
void send_api_result(cprRC_t retVal, struct sockaddr_un *addr, socklen_t len)
{
static const char fname[] = "send_api_result";
timer_ipc_t tmr_rsp = {0};
tmr_rsp.msg_type = TMR_RESULT;
tmr_rsp.u.result = retVal;
if (sendto(serv_sock, &tmr_rsp, sizeof(timer_ipc_t),0, (struct sockaddr *)addr, len) < 0) {
CPR_ERROR("%s: error in sending on serv_sock err=%s\n", fname, strerror(errno));
}
}
/**
* Start the timer service loop.
* Service loop waits for timer commands from timer clients processes them.
* Waiting is done by issuing a select call with a timeout. This is the main
* function that implements the timer functionality using the select call.
* timeout value = duration on the head of the timer list.
* @return CPR_SUCCESS or CPR_FAILURE
*/
cprRC_t start_timer_service_loop (void)
{
static const char fname[] = "start_timer_service_loop";
int lsock = -1;
struct timeval tv;
int ret;
boolean use_timeout;
/* initialize server and client addresses used for sending.*/
cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_serv_addr, SERVER_PATH, getpid());
cpr_set_sockun_addr((cpr_sockaddr_un_t *) &tmr_client_addr, CLIENT_PATH, getpid());
/*
* init mutex and cond var.
* these are used for making API synchronous etc..
*/
if (pthread_mutex_init(&api_mutex, NULL) != 0) {
CPR_ERROR("%s: failed to initialize api_mutex err=%s\n", fname,
strerror(errno));
return CPR_FAILURE;
}
/* open a unix datagram socket for client library */
client_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
if (client_sock == INVALID_SOCKET) {
CPR_ERROR("%s:could not create client socket error=%s\n", fname, strerror(errno));
return CPR_FAILURE;
}
/* bind service name to the socket */
if (local_bind(client_sock,tmr_client_addr.sun_path) < 0) {
CPR_ERROR("%s:could not bind local socket:error=%s\n", fname, strerror(errno));
(void) close(client_sock);
client_sock = INVALID_SOCKET;
return CPR_FAILURE;
}
/* open another unix datagram socket for timer service */
serv_sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
if (serv_sock == INVALID_SOCKET) {
CPR_ERROR("%s:could not create server socket error=%s\n", fname, strerror(errno));
serv_sock = INVALID_SOCKET;
close(client_sock);
client_sock = INVALID_SOCKET;
return CPR_FAILURE;
}
if (local_bind(serv_sock, tmr_serv_addr.sun_path) < 0) {
CPR_ERROR("%s:could not bind serv socket:error=%s\n", fname, strerror(errno));
(void) close(serv_sock);
(void) close(client_sock);
client_sock = serv_sock = INVALID_SOCKET;
return CPR_FAILURE;
}
while (1) {
lsock = select_sockets();
/* set the timer equal to duration on head */
if (timerListHead != NULL) {
tv.tv_sec = (timerListHead->duration)/1000;
tv.tv_usec = (timerListHead->duration%1000)*1000;
//CPR_INFO("%s:time duration on head =%d sec:%d usec (or %d msec)\n",
// fname, tv.tv_sec, tv.tv_usec,
// timerListHead->duration);
use_timeout = TRUE;
} else {
//CPR_INFO("%s:no timer in the list.. will block until there is one\n",
// fname);
use_timeout = FALSE;
}
ret = select(lsock + 1, &socks, NULL, NULL, (use_timeout == TRUE) ? &tv:NULL);
if (ret == -1) {
CPR_ERROR("%s:error in select err=%s\n", fname,
strerror(errno));
return(CPR_FAILURE);
} else if (ret == 0) {
/*
* this means the head timer has expired..there could be others
*/
timerListHead->duration = 0;
process_expired_timers();
} else {
if (FD_ISSET(serv_sock, &socks)) {
//CPR_INFO("Got something on serv_sock..\n");
/* first reduce the duration of the head by current run time */
if (timerListHead != NULL) {
//CPR_INFO("set head duration to =%d prev was= %d\n",
// tv.tv_sec * 1000 + (tv.tv_usec/1000),
// timerListHead->duration);
/* set the head with the remaining duration(tv) as indicated by select */
timerListHead->duration = tv.tv_sec * 1000 + (tv.tv_usec/1000);
}
/* read the ipc message to remove or add a timer */
(void) read_timer_cmd();
}
}
}
}
/**
*
* Process the timers expired. Generally this is called when head timer
* has expired.
* @note we need to process the list as there could be
* other timers too in the list which have expired.
*
*/
void process_expired_timers() {
static const char fname[] = "process_expired_timer";
cprCallBackTimerMsg_t *timerMsg;
void *syshdr;
boolean processingTimers;
/* nothing to do if no timers running */
if (timerListHead == NULL) {
return;
}
/* nothing to do if head has not expired */
if (timerListHead->duration > 0) {
return;
}
/* There are one or more expired timers on the list */
processingTimers = TRUE;
while (processingTimers) {
if (timerListHead != NULL) {
/*
* Send msg to queue to indicate this timer has expired
*/
if (timerListHead->duration <= 0) {
timerMsg = (cprCallBackTimerMsg_t *)
cpr_malloc(sizeof(cprCallBackTimerMsg_t));
if (timerMsg) {
timerMsg->expiredTimerName =
timerListHead->cprTimerPtr->name;
//CPR_INFO("%s: timer %s expired..\n",fname,
// timerMsg->expiredTimerName);
timerMsg->expiredTimerId =
timerListHead->cprTimerPtr->applicationTimerId;
timerMsg->usrData =
timerListHead->cprTimerPtr->data;
syshdr = cprGetSysHeader(timerMsg);
if (syshdr) {
fillInSysHeader(syshdr,
timerListHead->cprTimerPtr->applicationMsgId,
sizeof(cprCallBackTimerMsg_t), timerMsg);
if (cprSendMessage(timerListHead->cprTimerPtr->callBackMsgQueue,
timerMsg, (void **) &syshdr) == CPR_FAILURE) {
cprReleaseSysHeader(syshdr);
cpr_free(timerMsg);
CPR_ERROR("%s - Call to cprSendMessage failed\n", fname);
CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
fname, timerListHead->cprTimerPtr->name);
}
} else {
cpr_free(timerMsg);
CPR_ERROR("%s - Call to cprGetSysHeader failed\n", fname);
CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
fname, timerListHead->cprTimerPtr->name);
}
} else {
CPR_ERROR("%s - Call to cpr_malloc failed\n", fname);
CPR_ERROR("%s - Unable to send timer %s expiration msg\n",
fname, timerListHead->cprTimerPtr->name);
}
(void) removeTimer(timerListHead->cprTimerPtr);
} else {
/* The rest of the timers on the list have not yet expired */
processingTimers = FALSE;
}
} else {
/* The timer list is now empty */
processingTimers = FALSE;
}
} /* still more to process */
}
/**
* @}
*/