Files
m241Neelima/components/ip_net2-6.9/ipmip/src/ipmip_timer.c
T
2026-07-30 11:19:44 +05:30

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
****************************************************************************
*/