1282 lines
39 KiB
C
1282 lines
39 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipmip_timer.c,v $ $Revision: 1.14.10.1 $
|
|
* $Source: /home/interpeak/CVSRoot/ipmip/src/ipmip_timer.c,v $
|
|
* $Author: jsheng $ $Date: 2013-07-31 09:04:30 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Lennart Bang <lob@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* Interpeak timeout server.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include <ipmip_config.h>
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_err.h>
|
|
#include <ipcom_hash.h>
|
|
#include <ipcom_inet.h>
|
|
#include <ipcom_list.h>
|
|
#include <ipcom_os.h>
|
|
#include <ipcom_sock.h>
|
|
#include <ipcom_sock2.h>
|
|
#include <ipcom_sysctl.h>
|
|
#include <ipcom_syslog.h>
|
|
#include <ipcom_sysvar.h>
|
|
|
|
#include <ipmip.h>
|
|
#include <private/ipmip_timer.h>
|
|
#include <private/ipmip_util.h>
|
|
#include <private/ipmip_context.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
/* Use this macro to check if a timeout is pending. */
|
|
#define IPMIP_TIMER_PENDING(xxtmo) ((xxtmo)->pqindex != 0 || (xxtmo)->next.head != IP_NULL)
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IP_TIMER_DEBUG
|
|
|
|
#define IP_TIMERS_NUM 256
|
|
|
|
typedef struct Ip_timer_debug
|
|
{
|
|
void *ptr;
|
|
const char *func;
|
|
const char *file;
|
|
int line;
|
|
} Ip_timer_debug_t;
|
|
|
|
Ip_timer_debug_t rr_timers[IP_TIMERS_NUM];
|
|
int rr_timers_cnt = 0;
|
|
#endif
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_signal
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_signal(Ipmip_timer_context_t *tctx)
|
|
{
|
|
/* Signal it */
|
|
ipcom_block_post(tctx->queue_signal);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_unlink
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_unlink(Ipmip_context_t *ctx,
|
|
Ipmip_timer_t *timer)
|
|
{
|
|
(void)ipcom_pqueue_remove(ctx->timer->queue, timer->pqindex);
|
|
ipcom_list_remove(&timer->next);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_pqueue_cmp_func
|
|
*===========================================================================
|
|
* Description: Compares two timeouts entries.
|
|
* Parameters: tmo1 -
|
|
* tmo2 -
|
|
* Returns: 1 if tmo1 timeouts before tmo2
|
|
* 0 if tmo1 and tmo2 times out at the same time
|
|
* -1 if tmo1 timeouts after tmo2
|
|
*
|
|
*/
|
|
IP_STATIC int
|
|
ipmip_timer_pqueue_cmp_func(IP_CONST Ipmip_timer_t *tmo1,
|
|
IP_CONST Ipmip_timer_t *tmo2)
|
|
{
|
|
if (ip_timeval_cmp(&tmo1->abs, &tmo2->abs, <))
|
|
return 1;
|
|
if (ip_timeval_cmp(&tmo1->abs, &tmo2->abs, >))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_pqueue_store_index
|
|
*===========================================================================
|
|
* Description: Stores the index the passed object has in the priority queue.
|
|
* Parameters: tmo - the timeout object that has been stored in the queue.
|
|
* indx - the index the timeout object has in the queue.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_pqueue_store_index(Ipmip_timer_t *tmo, int indx)
|
|
{
|
|
tmo->pqindex = indx;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_request_timeval
|
|
*===========================================================================
|
|
* Description: Request a new timeout; internal function with locks taken.
|
|
* Parameters: context - The context
|
|
* timer - The timer structure
|
|
* Returns: IPCOM_SUCCESS on success, error code otherwise.
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipmip_timer_request_timeval_locked(Ipmip_context_t *context,
|
|
Ipmip_timer_t *timer)
|
|
{
|
|
Ip_err err;
|
|
|
|
/* Pin down the queue */
|
|
err = ipcom_pqueue_insert(context->timer->queue, timer);
|
|
if (err == IPCOM_SUCCESS)
|
|
{
|
|
/* Insert it */
|
|
ipcom_list_insert_first(&timer->lock->timers, &timer->next);
|
|
/* New first index? */
|
|
if (timer->pqindex == 1)
|
|
ipmip_timer_signal(context->timer);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_loop
|
|
*===========================================================================
|
|
* Description:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_loop(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_context_t *tctx)
|
|
{
|
|
Ipmip_timer_t *timer;
|
|
|
|
/*********** tick loop ***********/
|
|
for (; proc->quit != IP_TRUE;)
|
|
{
|
|
struct Ip_timeval tv;
|
|
Ip_u32 msec = (unsigned)-1;
|
|
|
|
/* Grab the process lock; must be held first */
|
|
ipcom_microtime(&tv);
|
|
|
|
/* Grab the QUEUE lock */
|
|
ipcom_mutex_lock(tctx->queue_lock);
|
|
while (IP_NULL != (timer = ipcom_pqueue_get_next(tctx->queue))
|
|
&& ip_timeval_cmp(&tv, &timer->abs, >))
|
|
{
|
|
Ipmip_timer_lock_t *lock;
|
|
#ifdef IP_TIMER_DEBUG
|
|
int cnt;
|
|
#endif
|
|
|
|
timer = ipcom_pqueue_remove_next(tctx->queue);
|
|
if(IP_NULL == timer)
|
|
return;
|
|
|
|
#ifdef IP_TIMER_DEBUG
|
|
for (cnt = rr_timers_cnt - 1; cnt != rr_timers_cnt; cnt--)
|
|
{
|
|
if (cnt < 0)
|
|
cnt = IP_TIMERS_NUM;
|
|
else
|
|
{
|
|
if (rr_timers[cnt].ptr == timer)
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
ipcom_list_remove(&timer->next);
|
|
|
|
/* Try to acquire the timeout */
|
|
lock = timer->lock;
|
|
if (lock->lock == timer || ipcom_block_wait(lock->block, 0) == IPCOM_SUCCESS)
|
|
{
|
|
/* */
|
|
lock->lock = IP_NULL;
|
|
|
|
/* Unlock the queue lock */
|
|
ipcom_mutex_unlock(tctx->queue_lock);
|
|
|
|
/* Lock function? */
|
|
if (lock->func)
|
|
(*lock->func) (IPMIP_TIMER_LOCK_EVENT_LOCK, ctx, proc, lock->obj);
|
|
|
|
/**/
|
|
switch (timer->func(IPMIP_TIMER_EVENT_RUN, ctx, proc, timer->cookie1, timer->cookie2))
|
|
{
|
|
case IPMIP_TIMER_RET_PENDING:
|
|
break;
|
|
case IPMIP_TIMER_RET_REINSERT:
|
|
/* Get current time */
|
|
ipcom_microtime(&timer->abs);
|
|
|
|
/* Readd it; with the same delay */
|
|
ip_timeval_add(&timer->abs, &timer->duration, &timer->abs);
|
|
|
|
/* Relock the mutex */
|
|
ipcom_mutex_lock(tctx->queue_lock);
|
|
/* Reinsert */
|
|
ipmip_timer_request_timeval_locked(ctx, timer);
|
|
/* Unlock the mutex */
|
|
ipcom_mutex_unlock(tctx->queue_lock);
|
|
/* FALL THROUGH */
|
|
case IPMIP_TIMER_RET_DONE:
|
|
default:
|
|
/* Post it back */
|
|
ipmip_timer_lock_release(ctx, proc, lock);
|
|
break;
|
|
}
|
|
|
|
/* Relock the mutex */
|
|
ipcom_mutex_lock(tctx->queue_lock);
|
|
}
|
|
else
|
|
{
|
|
/* Add it to the pending queue */
|
|
ipcom_list_insert_last(&timer->lock->pending, &timer->next);
|
|
}
|
|
}
|
|
|
|
if (timer != IP_NULL)
|
|
{
|
|
ip_timeval_sub(&timer->abs, &tv, &tv);
|
|
|
|
/* Milliseconds */
|
|
msec = tv.tv_usec / 1000;
|
|
msec += tv.tv_sec * 1000;
|
|
}
|
|
|
|
/* Unlock the queue lock */
|
|
ipcom_mutex_unlock(tctx->queue_lock);
|
|
|
|
/* If we overflow; set it to 60 secs and let it reparse */
|
|
if ((Ip_s32)msec < 0)
|
|
msec = 60 * 1000;
|
|
|
|
/* Wait until we're signalled or times out */
|
|
(void)ipcom_block_wait(tctx->queue_signal, msec);
|
|
}
|
|
|
|
/* Abort all timers currently scheduled */
|
|
while (IP_NULL != (timer = ipcom_pqueue_remove_next(tctx->queue)))
|
|
{
|
|
ipcom_list_remove(&timer->next);
|
|
|
|
/* Abort the timer */
|
|
(*timer->func) (IPMIP_TIMER_EVENT_ABORTED,
|
|
ctx,
|
|
proc,
|
|
timer->cookie1,
|
|
timer->cookie2);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_loop_wakeup
|
|
*===========================================================================
|
|
* Description: Signal the timer.
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_loop_wakeup(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_context_t *tctx)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(ctx);
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipmip_timer_signal(tctx);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_release
|
|
*===========================================================================
|
|
* Description: Release a timer lock
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* lock - The lock
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipmip_timer_lock_release_locked(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_t *lock)
|
|
{
|
|
Ipmip_timer_t *timer;
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
while ((timer = IPCOM_LIST_FIRST(&lock->pending)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&timer->next);
|
|
/* ipcom_microtime(&timer->abs); */
|
|
ipmip_timer_request_timeval_locked(ctx, timer);
|
|
}
|
|
ipcom_block_post(lock->block);
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_call
|
|
*===========================================================================
|
|
* Description: Do call processing for a timer.
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* timer_lock - Must be held.
|
|
* func - The function callback
|
|
* cookie1 - Opaque auxilliary value
|
|
* cookie2 - Opaque auxilliary value
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_call(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *cookie1,
|
|
void *cookie2)
|
|
{
|
|
if (func)
|
|
{
|
|
switch (func(IPMIP_TIMER_EVENT_RUN, ctx, proc, cookie1, cookie2))
|
|
{
|
|
case IPMIP_TIMER_RET_DONE:
|
|
case IPMIP_TIMER_RET_REINSERT:
|
|
/* Post it back */
|
|
if (timer_lock != IP_NULL)
|
|
ipmip_timer_lock_release(ctx, proc, timer_lock);
|
|
break;
|
|
case IPMIP_TIMER_RET_PENDING:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Post it back */
|
|
if (timer_lock != IP_NULL)
|
|
ipmip_timer_lock_release(ctx, proc, timer_lock);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_context_create
|
|
*===========================================================================
|
|
* Description: Creates a new timeout process.
|
|
* Parameters: context - Owning context
|
|
* proc - The process
|
|
* Returns: IP_NULL if out of memory or a pointer to the new timeout proc.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_context_create(struct Ipmip_context *context,
|
|
struct Ipmip_proc *proc)
|
|
{
|
|
Ipmip_timer_context_t *tctx;
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
tctx = ipcom_calloc(1, sizeof(*tctx));
|
|
if (tctx)
|
|
{
|
|
if (ipcom_mutex_create(&tctx->queue_lock) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_free(tctx);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
if (ipcom_block_create(&tctx->queue_signal) != IPCOM_SUCCESS)
|
|
{
|
|
ipcom_mutex_delete(&tctx->queue_lock);
|
|
ipcom_free(tctx);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
tctx->queue = ipcom_pqueue_new((Ipcom_pqueue_cmp_func) ipmip_timer_pqueue_cmp_func,
|
|
(Ipcom_pqueue_store_index) ipmip_timer_pqueue_store_index);
|
|
|
|
if (tctx->queue == IP_NULL)
|
|
{
|
|
ipcom_mutex_delete(&tctx->queue_lock);
|
|
ipcom_block_delete(&tctx->queue_signal);
|
|
ipcom_free(tctx);
|
|
return IP_FALSE;
|
|
}
|
|
|
|
context->timer = tctx;
|
|
|
|
/**/
|
|
return IP_TRUE;
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_context_start
|
|
*===========================================================================
|
|
* Description: Starts the timeout process
|
|
* Parameters: ctx - Owning context
|
|
* proc - Current process
|
|
* Returns: IP_TRUE on success.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_context_start(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc)
|
|
{
|
|
/* Lock the global mutex */
|
|
if (ctx->timer != IP_NULL && ctx->timer->proc == IP_NULL)
|
|
{
|
|
ctx->timer->proc = ipmip_proc_start(ctx,
|
|
proc,
|
|
"timer",
|
|
(Ipmip_proc_func_t) ipmip_timer_loop,
|
|
ctx->timer);
|
|
}
|
|
|
|
return ctx->timer != IP_NULL && ctx->timer->proc != IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_context_stop
|
|
*===========================================================================
|
|
* Description: Stops the timer thread.
|
|
* Parameters: The context
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_context_stop(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
/* Mark it as 'quit' */
|
|
if (ctx->timer && ctx->timer->proc)
|
|
{
|
|
ipmip_proc_stop(ctx,
|
|
proc,
|
|
ctx->timer->proc,
|
|
(Ipmip_proc_func_t) ipmip_timer_loop_wakeup,
|
|
ctx->timer);
|
|
ctx->timer->proc = IP_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_context_destroy
|
|
*===========================================================================
|
|
* Description: Frees all resources helds by a timeout process and terminates
|
|
* the process. The thread must be stopped first.
|
|
* Parameters: ctx - Context
|
|
* proc - The process
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_context_destroy(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
/* Free the memory */
|
|
if (ctx->timer)
|
|
{
|
|
ipcom_block_delete(&ctx->timer->queue_signal);
|
|
ipcom_mutex_delete(&ctx->timer->queue_lock);
|
|
ipcom_pqueue_delete(ctx->timer->queue);
|
|
ipcom_free(ctx->timer);
|
|
ctx->timer = IP_NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_request_timeval
|
|
*===========================================================================
|
|
* Description: Request a new timeout
|
|
* Parameters: ctx - The context
|
|
* timer - The timer structure
|
|
* timer_lock - The timer lock
|
|
* func - The function callback
|
|
* cookie1 - Opaque auxilliary value
|
|
* cookie2 - Opaque auxilliary value
|
|
* duration - Timeout.
|
|
* Returns: IPCOM_SUCCESS on success, error code otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_request_timeval(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_t *timer,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *cookie1,
|
|
void *cookie2,
|
|
IP_CONST struct Ip_timeval *duration)
|
|
{
|
|
Ip_err err;
|
|
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
ip_assert(timer_lock->destroyed == IP_FALSE);
|
|
|
|
if (IPMIP_TIMER_PENDING(timer))
|
|
{
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
return IPCOM_ERR_ALREADY_STARTED;
|
|
}
|
|
|
|
timer->cookie1 = cookie1;
|
|
timer->cookie2 = cookie2;
|
|
timer->duration = *duration;
|
|
timer->func = func;
|
|
timer->lock = timer_lock;
|
|
|
|
ipcom_microtime(&timer->abs);
|
|
ip_timeval_add(&timer->abs, duration, &timer->abs);
|
|
|
|
err = ipmip_timer_request_timeval_locked(ctx, timer);
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_request
|
|
*===========================================================================
|
|
* Description: Request a new timeout
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* timer - The timer structure
|
|
* timer_lock - Lock to be acquired prior to invocation.
|
|
* func - The function callback
|
|
* cookie1 - Opaque auxilliary value
|
|
* cookie2 - Opaque auxilliary value
|
|
* msec - Timeout.
|
|
* Returns: IPCOM_SUCCESS on success, error code otherwise.
|
|
*
|
|
*/
|
|
#ifdef IP_TIMER_DEBUG
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_request_debug(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_t *timer,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *cookie1,
|
|
void *cookie2,
|
|
Ip_u32 msec,
|
|
const char *s_file,
|
|
const char *s_func,
|
|
int line)
|
|
#else
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_request(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_t *timer,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *cookie1,
|
|
void *cookie2,
|
|
Ip_u32 msec)
|
|
#endif
|
|
{
|
|
struct Ip_timeval duration;
|
|
|
|
#ifdef IP_TIMER_DEBUG
|
|
rr_timers[rr_timers_cnt].func = s_func;
|
|
rr_timers[rr_timers_cnt].file = s_file;
|
|
rr_timers[rr_timers_cnt].line = line;
|
|
rr_timers[rr_timers_cnt].ptr = timer;
|
|
rr_timers_cnt = (rr_timers_cnt + 1) % IP_TIMERS_NUM;
|
|
#endif
|
|
|
|
duration.tv_sec = msec / 1000;
|
|
duration.tv_usec = (msec % 1000) * 1000;
|
|
return ipmip_timer_request_timeval(ctx, proc, timer, timer_lock, func, cookie1, cookie2, &duration);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_cancel
|
|
*===========================================================================
|
|
* Description: Cancel a timeout
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* timer - The timer.
|
|
* Returns: IPCOM_SUCCESS if successful, error code otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_cancel(Ipmip_context_t *context,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_t *timer)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipcom_mutex_lock(context->timer->queue_lock);
|
|
|
|
/* Verify changes in between the lock */
|
|
if (timer->pqindex != 0)
|
|
{
|
|
/* Unlink */
|
|
ipmip_timer_unlink(context, timer);
|
|
|
|
/* Cancel the timer */
|
|
(*timer->func) (IPMIP_TIMER_EVENT_CANCEL,
|
|
context,
|
|
proc,
|
|
timer->cookie1,
|
|
timer->cookie2);
|
|
}
|
|
else if (timer->next.head != IP_NULL)
|
|
{
|
|
/* We're pending */
|
|
ipcom_list_remove(&timer->next);
|
|
|
|
/* Cancel the timer */
|
|
(*timer->func) (IPMIP_TIMER_EVENT_CANCEL,
|
|
context,
|
|
proc,
|
|
timer->cookie1,
|
|
timer->cookie2);
|
|
}
|
|
else
|
|
{
|
|
ipcom_mutex_unlock(context->timer->queue_lock);
|
|
return IPCOM_ERR_NOT_STARTED;
|
|
}
|
|
|
|
ipcom_mutex_unlock(context->timer->queue_lock);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_alloc
|
|
*===========================================================================
|
|
* Description: Initialize a timer lock.
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* func - Callback event function
|
|
* obj - Object
|
|
* Returns: The timer lock
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipmip_timer_lock_t *
|
|
ipmip_timer_lock_alloc(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_event_func_t func,
|
|
void *obj)
|
|
{
|
|
Ipmip_timer_lock_t *lock = ipcom_calloc(1, sizeof(*lock));
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
if (!lock)
|
|
return IP_NULL;
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
(void)ipcom_block_create(&lock->block);
|
|
ipcom_list_init(&lock->timers);
|
|
ipcom_list_init(&lock->pending);
|
|
|
|
/* Start with it available */
|
|
ipcom_block_post(lock->block);
|
|
|
|
lock->func = func;
|
|
lock->obj = obj;
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
return lock;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_destroy
|
|
*===========================================================================
|
|
* Description: Destroy a timer lock.
|
|
* Parameters: ctx - The context
|
|
* lock - The lock
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_lock_destroy(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_lock_t *lock)
|
|
{
|
|
Ipmip_timer_t *timer;
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
lock->destroyed = IP_TRUE;
|
|
|
|
while ((timer = IPCOM_LIST_FIRST(&lock->timers)) != IP_NULL)
|
|
{
|
|
ipmip_timer_unlink(ctx, timer);
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
/* Cancel the timer */
|
|
(*timer->func) (IPMIP_TIMER_EVENT_ABORTED,
|
|
ctx,
|
|
proc,
|
|
timer->cookie1,
|
|
timer->cookie2);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
}
|
|
|
|
while ((timer = IPCOM_LIST_FIRST(&lock->pending)) != IP_NULL)
|
|
{
|
|
ipcom_list_remove(&timer->next);
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
/* Cancel the timer */
|
|
(*timer->func) (IPMIP_TIMER_EVENT_ABORTED,
|
|
ctx,
|
|
proc,
|
|
timer->cookie1,
|
|
timer->cookie2);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
}
|
|
|
|
if (ipcom_block_wait(lock->block, 0) == IPCOM_SUCCESS)
|
|
{
|
|
/* Got it; destroy it */
|
|
ipcom_block_delete(&lock->block);
|
|
ipcom_free(lock);
|
|
}
|
|
else
|
|
{
|
|
/* Someone's holding it */
|
|
lock->destroyed = IP_TRUE;
|
|
lock->func = IP_NULL;
|
|
}
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_acquire
|
|
*===========================================================================
|
|
* Description: Acquire a timer lock
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* lock - The lock
|
|
* Returns: The lock if successful.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipmip_timer_lock_t *
|
|
ipmip_timer_lock_acquire(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_t *lock)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(ctx);
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
(void)ipcom_block_wait(lock->block, -1);
|
|
|
|
/* Invoke the lock function */
|
|
if (lock->func)
|
|
(*lock->func) (IPMIP_TIMER_LOCK_EVENT_LOCK, ctx, proc, lock->obj);
|
|
|
|
return lock;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_try
|
|
*===========================================================================
|
|
* Description: Try to acquire a timer lock
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* lock - The lock
|
|
* Returns: IP_TRUE on success, IP_FALSE otherwise
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipmip_timer_lock_t *
|
|
ipmip_timer_lock_try(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_t *lock)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(ctx);
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
if (ipcom_block_wait(lock->block, 0) == IPCOM_SUCCESS)
|
|
{
|
|
/* Invoke the lock function */
|
|
if (lock->func)
|
|
(*lock->func) (IPMIP_TIMER_LOCK_EVENT_LOCK, ctx, proc, lock->obj);
|
|
return lock;
|
|
}
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_lock_release
|
|
*===========================================================================
|
|
* Description: Release a timer lock
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* lock - The lock
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_lock_release(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_lock_t *lock)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(ctx);
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
/* Invoke the lock function */
|
|
if (lock->func && !lock->destroyed)
|
|
(*lock->func) (IPMIP_TIMER_LOCK_EVENT_UNLOCK, ctx, proc, lock->obj);
|
|
|
|
/* Move pending timers to timeout queue */
|
|
/* Get current time */
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
if (lock->destroyed)
|
|
{
|
|
/* This lock is destroyed; clean up */
|
|
ipcom_block_delete(&lock->block);
|
|
ipcom_free(lock);
|
|
}
|
|
else
|
|
{
|
|
/* Not destroyed; unlock as usual */
|
|
ipmip_timer_lock_release_locked(ctx, proc, lock);
|
|
}
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_prepare
|
|
*===========================================================================
|
|
* Description: Prepare a timer block
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* timer - Timer
|
|
* timer_lock - The timer lock
|
|
* func - The function callback
|
|
* cookie1 - Opaque auxilliary value
|
|
* cookie2 - Opaque auxilliary value
|
|
* Returns: IPCOM_SUCCESS on success
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_prepare(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_t *timer,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *cookie1,
|
|
void *cookie2)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
ip_assert(timer_lock->lock == IP_NULL);
|
|
|
|
if (IPMIP_TIMER_PENDING(timer))
|
|
{
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
return IPCOM_ERR_ALREADY_STARTED;
|
|
}
|
|
|
|
timer->cookie1 = cookie1;
|
|
timer->cookie2 = cookie2;
|
|
timer->func = func;
|
|
timer->lock = timer_lock;
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_execute
|
|
*===========================================================================
|
|
* Description: Unblock if the timer's pending
|
|
* Parameters: ctx - The context
|
|
* proc - The process
|
|
* timer - Timer
|
|
* Returns: IP_TRUE if unlocked
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_execute(struct Ipmip_context *ctx,
|
|
Ipmip_timer_t *timer)
|
|
{
|
|
Ip_bool success = IP_TRUE;
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
/* Unlock this particular lock; if the timer's pending */
|
|
timer->lock->lock = timer;
|
|
ipcom_microtime(&timer->abs);
|
|
ipmip_timer_request_timeval_locked(ctx, timer);
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
return success;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_pending
|
|
*===========================================================================
|
|
* Description: Check whether or not a timer is pending timeout.
|
|
* Parameters: ctx - Context
|
|
* proc - The process
|
|
* timer - The timer.
|
|
* Returns: IP_TRUE if pending
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_pending(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_t *timer)
|
|
{
|
|
Ip_bool pending;
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
pending = IPMIP_TIMER_PENDING(timer);
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
return pending;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_remaining
|
|
*===========================================================================
|
|
* Description: The remaining timeout
|
|
* Parameters: ctx - Context
|
|
* proc - The process
|
|
* timer - The timer.
|
|
* remaining - Remaining
|
|
* Returns: IP_TRUE if successful
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_remaining(struct Ipmip_context *ctx,
|
|
struct Ipmip_proc *proc,
|
|
Ipmip_timer_t *timer,
|
|
struct Ip_timeval *remaining)
|
|
{
|
|
Ip_bool pending;
|
|
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
ipcom_mutex_lock(ctx->timer->queue_lock);
|
|
|
|
if ((pending = IPMIP_TIMER_PENDING(timer)))
|
|
{
|
|
if (timer->pqindex == 0)
|
|
ip_timeval_clear(remaining);
|
|
else
|
|
{
|
|
ipcom_microtime(remaining);
|
|
if (ip_timeval_cmp(&timer->abs, remaining, >))
|
|
ip_timeval_sub(&timer->abs, remaining, remaining);
|
|
else
|
|
ip_timeval_clear(remaining);
|
|
}
|
|
}
|
|
|
|
ipcom_mutex_unlock(ctx->timer->queue_lock);
|
|
|
|
return pending;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_retransmit_init
|
|
*===========================================================================
|
|
* Description: Initializes a retransmit structure.
|
|
* Parameters: r - The retransmit context
|
|
* limit - Rate limit
|
|
* original - Original retransmit value
|
|
* initial - Initial retransmit value
|
|
* expire - Expire value.
|
|
* Returns: IP_TRUE if we've hit the retransmit rate ceiling, IP_FALSE
|
|
* otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_retransmit_init(Ipmip_timer_retransmit_t *r,
|
|
Ip_u32 limit,
|
|
Ip_u32 original,
|
|
Ip_u32 initial,
|
|
Ip_u32 expire)
|
|
{
|
|
ipcom_memset(r, 0, sizeof(*r));
|
|
|
|
IPMIP_TIMER_INIT(&r->timer);
|
|
|
|
r->rate_limit = limit;
|
|
r->original = original;
|
|
r->retransmit = initial;
|
|
r->expire = expire;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_retransmit_ratelimit
|
|
*===========================================================================
|
|
* Description: Decides whether or not a send is legal based on timeouts,
|
|
* previously sent packets and maximum rate values.
|
|
* Parameters: r - The retransmit value.
|
|
* Returns: IP_TRUE if it's OK, IP_FALSE otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_retransmit_ratelimit(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_retransmit_t *r)
|
|
{
|
|
Ip_time_t now;
|
|
|
|
now = ipmip_util_time();
|
|
|
|
/* Reset counter in case we've passed into a new second */
|
|
if (r->last != now)
|
|
r->count = 0;
|
|
|
|
/* Current time value */
|
|
r->last = now;
|
|
|
|
/* We've passed the rate limit? */
|
|
if (++r->count > r->rate_limit)
|
|
{
|
|
/* Only disallow if there's an outstanding retransmit timer */
|
|
if (ipmip_timer_pending(ctx, proc, &r->timer))
|
|
return IP_FALSE;
|
|
}
|
|
|
|
/* It's OK to send this packet */
|
|
return IP_TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_retransmit_reset
|
|
*===========================================================================
|
|
* Description: Resets a retransmit counter, and removes any outstanding
|
|
* timeouts.
|
|
* Parameters: r - The retransmit value.
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipmip_timer_retransmit_reset(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_retransmit_t *r)
|
|
{
|
|
/* Reset to original */
|
|
r->retransmit = r->original;
|
|
r->retransmits = 0;
|
|
|
|
ipmip_timer_cancel(ctx, proc, &r->timer);
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_retransmit_expire
|
|
*===========================================================================
|
|
* Description: Expires a retransmit value. It will handle exponential backoffs
|
|
* and abide by max values.
|
|
* Parameters: r - The retransmit value.
|
|
* Returns: IP_TRUE if we've hit the rate max ceiling, IP_FALSE otherwise.
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_bool
|
|
ipmip_timer_retransmit_expire(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_retransmit_t *r)
|
|
{
|
|
IPMIP_UNUSED_PARAMETER(ctx);
|
|
IPMIP_UNUSED_PARAMETER(proc);
|
|
|
|
/* Multiply by two */
|
|
r->retransmit <<= 1;
|
|
|
|
/* Number of retransmits */
|
|
r->retransmits++;
|
|
|
|
/* Don't pass by the maximum timeout */
|
|
if (r->retransmit > r->expire)
|
|
{
|
|
r->retransmit = r->expire;
|
|
return IP_TRUE;
|
|
}
|
|
|
|
return IP_FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipmip_timer_retransmit_request
|
|
*===========================================================================
|
|
* Description: Request a retransmit timeout.
|
|
* Parameters: context - The context
|
|
* r - Retransmit structure.
|
|
* func - Callback
|
|
* cookie - Cookie
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipmip_timer_retransmit_request(Ipmip_context_t *ctx,
|
|
Ipmip_proc_t *proc,
|
|
Ipmip_timer_retransmit_t *r,
|
|
Ipmip_timer_lock_t *timer_lock,
|
|
Ipmip_timer_func_t func,
|
|
void *arg1,
|
|
void *arg2)
|
|
{
|
|
return ipmip_timer_request(ctx, proc, &r->timer, timer_lock, func, arg1, arg2, r->retransmit);
|
|
}
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|
|
|