568 lines
17 KiB
C
568 lines
17 KiB
C
/*
|
|
******************************************************************************
|
|
* 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 <lob@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 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
|
|
****************************************************************************
|
|
*/
|
|
|
|
|