/* ****************************************************************************** * SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipcom_tmo.c,v $ $Revision: 1.23.126.8 $ * $Source: /home/interpeak/CVSRoot/ipcom/src/ipcom_tmo.c,v $ * $Author: csun $ $Date: 2017-08-23 02:00:04 $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Lennart Bang ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** * Interpeak timeout server. */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipcom_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #define IPCOM_USE_CLIB_PROTO #include "ipcom_tmo.h" #include "ipcom_err.h" #include "ipcom_list.h" #include "ipcom_clib.h" #include "ipcom_os.h" #include "ipcom_syslog.h" /* **************************************************************************** * 4 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 5 DEFINES **************************************************************************** */ #define IPCOM_SYSLOG_PRIORITY IPCOM_SYSLOG_IPCOM_PRIORITY #define IPCOM_SYSLOG_FACILITY IPCOM_LOG_IPCOM /* Protect timeout server API functions from race conditions. */ #define IPCOM_USE_TMOMUTEX #ifndef IPCOM_USE_TMOMUTEX #define ipcom_mutex_create(x) IP_NOOP #define ipcom_mutex_delete(x) IP_NOOP #define ipcom_mutex_lock(x) IP_NOOP #define ipcom_mutex_unlock(x) IP_NOOP #endif #ifdef IP_DEBUG #define IPCOM_TMO_POST #endif /* **************************************************************************** * 6 TYPES **************************************************************************** */ /* *=========================================================================== * Global ipcom_tmo data *=========================================================================== * Internal global Ipcom_tmo data. */ typedef struct Ipcom_tmo_data_struct { #ifdef IPCOM_USE_TMOMUTEX Ipcom_mutex mutex; #endif /* * The tick daemon will sleep on this semaphore with a timeout * until the text timer expires */ Ipcom_block sem; /* Collection of active timers */ Ipcom_pqueue *head; } Ipcom_tmo_data; /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ IP_STATIC Ip_err ipcom_tmo_init(void *ununsed); IP_STATIC IPCOM_PROCESS(ipcom_tickd); /* **************************************************************************** * 8 DATA **************************************************************************** */ /* *=========================================================================== * Global ipcom_tmo data *=========================================================================== */ IP_STATIC Ipcom_tmo_data ipcom_tmo; IP_STATIC Ipcom_once_t ipcom_tmo_once = IPCOM_ONCE_INIT; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ #ifdef IPCOM_TMO_POST enum Ipcom_tmo_post_state { /* Timer is pending */ IPCOM_TMO_PENDING, /* Timer has been run */ IPCOM_TMO_RUN, /* Timer is pending and will be reset when run */ IPCOM_TMO_PENDING_RESET, /* Timer as been run and reset to pending again */ IPCOM_TMO_RUN_RESET }; struct Ipcom_tmo_post { Ipcom_tmo tmo; /* State of this POST timer */ enum Ipcom_tmo_post_state state; /* Protects 'state' */ Ipcom_mutex mutex; /* Will be posted each time the timeout handler is run */ Ipcom_block sem; }; IP_STATIC void ipcom_tmo_post_assert(Ip_bool pred) { if (!pred) { ipcom_debug_printf("IPCOM tmo POST failed!"); IP_PANIC(); } } IP_STATIC Ip_s32 ipcom_tmo_post_cb(Ipcom_tmo *tmo_arg, void *cookie) { Ip_s32 restart = 0; IPCOM_UNUSED_ARG(tmo_arg); if (cookie) { struct Ipcom_tmo_post *tmo = cookie; switch (tmo->state) { case IPCOM_TMO_PENDING: case IPCOM_TMO_RUN_RESET: tmo->state = IPCOM_TMO_RUN; break; case IPCOM_TMO_PENDING_RESET: tmo->state = IPCOM_TMO_RUN_RESET; restart = 1; break; default: ipcom_tmo_post_assert(IP_FALSE); break; } ipcom_block_post(tmo->sem); } return restart; } IP_STATIC void ipcom_tmo_post_init(struct Ipcom_tmo_post *tmo) { IPCOM_TMO_INIT(&tmo->tmo); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo->tmo) == IP_FALSE); ipcom_tmo_post_assert(ipcom_block_create(&tmo->sem) == IPCOM_SUCCESS); } IP_STATIC void ipcom_tmo_post_finish(struct Ipcom_tmo_post *tmo) { ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo->tmo) == IP_FALSE); ipcom_tmo_post_assert(ipcom_block_delete(&tmo->sem) == IPCOM_SUCCESS); } IP_STATIC void ipcom_tmo_wait(struct Ipcom_tmo_post *tmo) { ipcom_tmo_post_assert(ipcom_block_wait(tmo->sem, (Ip_s32)ipcom_tmo_get(&tmo->tmo) * 100) == IPCOM_SUCCESS); } /* *=========================================================================== * ipcom_tmo_post *=========================================================================== * Description: Runs self-test on the timeout handler. * Parameters: * Returns: * */ IP_STATIC IPCOM_PROCESS(ipcom_tmo_post) { struct Ipcom_tmo_post tmo1; struct Ipcom_tmo_post tmo2; const Ip_u32 msec = 200; ipcom_proc_init(); ipcom_tmo_post_init(&tmo1); ipcom_tmo_post_init(&tmo2); /* * Verify that a timer handler is run */ tmo1.state = IPCOM_TMO_PENDING; ipcom_tmo_post_assert(ipcom_tmo_request(&tmo1.tmo, ipcom_tmo_post_cb, &tmo1, msec) == IPCOM_SUCCESS); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo1.tmo)); ipcom_tmo_wait(&tmo1); ipcom_tmo_post_assert(tmo1.state == IPCOM_TMO_RUN); /* * Verify that it is possible to cancel a timer and that the timer * handler is not run in that case. */ tmo1.state = tmo2.state = IPCOM_TMO_PENDING; ipcom_tmo_post_assert(ipcom_tmo_request(&tmo1.tmo, ipcom_tmo_post_cb, &tmo1, msec) == IPCOM_SUCCESS); ipcom_tmo_post_assert(ipcom_tmo_request(&tmo2.tmo, ipcom_tmo_post_cb, &tmo2, msec) == IPCOM_SUCCESS); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo1.tmo)); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo2.tmo)); ipcom_tmo_post_assert(ipcom_tmo_cancel(&tmo1.tmo) == IPCOM_SUCCESS); ipcom_tmo_post_assert(!IPCOM_TMO_PENDING(&tmo1.tmo)); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo2.tmo)); ipcom_tmo_post_assert(ipcom_tmo_cancel(&tmo1.tmo) == IPCOM_SUCCESS); ipcom_tmo_wait(&tmo2); ipcom_tmo_post_assert(tmo1.state == IPCOM_TMO_PENDING); ipcom_tmo_post_assert(tmo2.state == IPCOM_TMO_RUN); /* * Verify that the timer will reschedule itself if the handler * returns 1. */ tmo1.state = IPCOM_TMO_PENDING_RESET; ipcom_tmo_post_assert(ipcom_tmo_request(&tmo1.tmo, ipcom_tmo_post_cb, &tmo1, msec) == IPCOM_SUCCESS); ipcom_tmo_wait(&tmo1); ipcom_tmo_post_assert(tmo1.state == IPCOM_TMO_RUN_RESET); /* * Note, on an SMP system, the timer might not have been put * back onto the queue yet by ipcom_tickd(). So, wait a short while * (msec/4) using the second timer to give a chance for the first * timer to be requeued, before checking that tmo1 is pending. */ tmo2.state = IPCOM_TMO_PENDING; ipcom_tmo_post_assert(ipcom_tmo_request(&tmo2.tmo, ipcom_tmo_post_cb, &tmo2, msec/4) == IPCOM_SUCCESS); ipcom_tmo_wait(&tmo2); ipcom_tmo_post_assert(IPCOM_TMO_PENDING(&tmo1.tmo)); ipcom_tmo_wait(&tmo1); ipcom_tmo_post_assert(tmo1.state == IPCOM_TMO_RUN); ipcom_tmo_post_assert(!IPCOM_TMO_PENDING(&tmo1.tmo)); ipcom_tmo_post_finish(&tmo1); ipcom_tmo_post_finish(&tmo2); IPCOM_LOG0(INFO, "ipcom_tmo :: POST successful"); ipcom_proc_exit(); } #endif /* *=========================================================================== * ipcom_tickd *=========================================================================== * Description: * */ IP_STATIC IPCOM_PROCESS(ipcom_tickd) { ipcom_proc_init(); IPCOM_LOG0(DEBUG, "ipcom_tickd :: starting tickd"); /*********** tick loop ***********/ for (;;) { Ipcom_tmo2 *tmo; Ip_s32 msec; ipcom_mutex_lock(ipcom_tmo.mutex); while (IP_NULL != (tmo = ipcom_pqueue_get_next(ipcom_tmo.head)) && 0 == (msec = (Ip_s32)ipcom_tmo2_get(tmo))) { int cb_ret; tmo = ipcom_pqueue_remove_next(ipcom_tmo.head); ipcom_mutex_unlock(ipcom_tmo.mutex); ip_assert(tmo != IP_NULL); cb_ret = ((Ipcom_tmo_func)tmo->func)((Ipcom_tmo*)tmo, tmo->cookie); ipcom_mutex_lock(ipcom_tmo.mutex); if (cb_ret) (void)ipcom_tmo2_request_timeval(tmo->head, tmo, tmo->func, tmo->cookie, &tmo->duration); } if (tmo == IP_NULL) msec = IPC_TIMEOUT_INFINITE; ipcom_mutex_unlock(ipcom_tmo.mutex); (void)ipcom_block_wait(ipcom_tmo.sem, msec); } /*NOT REACHED: ipcom_proc_exit();*/ } /* *=========================================================================== * ipcom_tmo_init *=========================================================================== * Description: Initialize and create timeout server. * Parameters: . * Returns: . * */ IP_STATIC Ip_err ipcom_tmo_init(void *unused) { Ip_err err; Ipcom_proc_attr attr; IPCOM_UNUSED_ARG(unused); err = ipcom_block_create(&ipcom_tmo.sem); ip_assert(err == IPCOM_SUCCESS); if (err != IPCOM_SUCCESS) IP_PANIC(); err = ipcom_mutex_create(&ipcom_tmo.mutex); ip_assert(err == IPCOM_SUCCESS); if (err != IPCOM_SUCCESS) IP_PANIC(); ipcom_tmo.head = ipcom_tmo2_new(); ip_assert(ipcom_tmo.head != IP_NULL); if (ipcom_tmo.head == IP_NULL) IP_PANIC(); ipcom_proc_attr_init(&attr); attr.priority = IPCOM_PRIORITY_MAX; err = ipcom_proc_acreate("ipcom_tickd", ipcom_tickd, &attr, IP_NULL); ip_assert(err == IPCOM_SUCCESS); #ifdef IPCOM_TMO_POST --attr.priority; err = ipcom_proc_acreate("ipcom_tmo_post", ipcom_tmo_post, &attr, IP_NULL); ip_assert(err == IPCOM_SUCCESS); #endif return err; } /* **************************************************************************** * 10 PUBLIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipcom_tmo_request *=========================================================================== * Description: Request a timeout in 'msec' milliseconds. If the timeout * triggers the function 'func' is called with the timeout * 'tmo'. * Parameters: tmo A pointer to a Ipcom_tmo structure which stores * information about the timeout and serves as a handle * to cancel the timeout. * func A user callback function that is called if/when the * timeout triggers. * cookie A user 32-bit value that is passed with the timeout * callback function 'func'. * msec Number of timeout milliseconds. * Returns: IPCOM_SUCCESS - Success. * */ IP_PUBLIC Ip_err ipcom_tmo_request(Ipcom_tmo *tmo, Ipcom_tmo_func func, void *cookie, Ip_u32 msec) { Ip_err err; err = ipcom_once(&ipcom_tmo_once, ipcom_tmo_init, IP_NULL); if (err != IPCOM_SUCCESS) return err; ipcom_mutex_lock(ipcom_tmo.mutex); err = ipcom_tmo2_request(ipcom_tmo.head, &tmo->tmo, (Ipcom_tmo2_func) func, cookie, msec); ipcom_mutex_unlock(ipcom_tmo.mutex); /* * Make sure that 'ipcom_tickd' recalculates the time to the * expiration of the next timer. */ ipcom_block_post(ipcom_tmo.sem); return err; } /* *=========================================================================== * ipcom_tmo_cancel *=========================================================================== * Description: Cancel a pending timeout. There is no danger in calling this * function on a timeout that is not pending. * Parameters: tmo - The same Ipcom_tmo timeout handle that was given * upon requesting the timeout. * Returns: IPCOM_SUCCESS - Success. * */ IP_PUBLIC Ip_err ipcom_tmo_cancel(Ipcom_tmo *tmo) { Ip_err err; err = ipcom_once(&ipcom_tmo_once, ipcom_tmo_init, IP_NULL); if (err != IPCOM_SUCCESS) return err; ipcom_mutex_lock(ipcom_tmo.mutex); err = ipcom_tmo2_cancel(&tmo->tmo); ipcom_mutex_unlock(ipcom_tmo.mutex); return (err == IPCOM_ERR_NOT_STARTED ? IPCOM_SUCCESS : err); } /* *=========================================================================== * ipcom_tmo_reset *=========================================================================== * Description: Reset a timeout. The timeout is started over with its * original time. * Parameters: tmo - The same Ipcom_tmo timeout handle that was given * upon requesting the timeout. * Returns: IPCOM_SUCCESS - Success. * */ IP_PUBLIC Ip_err ipcom_tmo_reset(Ipcom_tmo *tmo) { Ip_err err; err = ipcom_once(&ipcom_tmo_once, ipcom_tmo_init, IP_NULL); if (err != IPCOM_SUCCESS) return err; ipcom_mutex_lock(ipcom_tmo.mutex); err = ipcom_tmo2_reset(&tmo->tmo); ipcom_mutex_unlock(ipcom_tmo.mutex); return err; } /* *=========================================================================== * ipcom_tmo_get *=========================================================================== * Description: Get milliseconds left to timeout. * Parameters: tmo - The same Ipcom_tmo timeout handle that was given * upon requesting the timeout. * Returns: Number of milliseconds before timeout. * */ IP_PUBLIC Ip_u32 ipcom_tmo_get(Ipcom_tmo *tmo) { Ip_u32 msec; if (ipcom_once(&ipcom_tmo_once, ipcom_tmo_init, IP_NULL) != IPCOM_SUCCESS) return 0; ipcom_mutex_lock(ipcom_tmo.mutex); msec = ipcom_tmo2_get(&tmo->tmo); ipcom_mutex_unlock(ipcom_tmo.mutex); return msec; } /* **************************************************************************** * END OF FILE **************************************************************************** */