/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Interpeak timeout server. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* **************************************************************************** * 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 **************************************************************************** */