Files
sics/nwatch.c
Koennecke Mark b60ce746d4 Merge commit 'refs/merge-requests/1' of ssh://gitorious.psi.ch/sinqdev/sics into merge-requests/1
First merge with ANSTO which compiles

Conflicts:
	SICSmain.c
	asynnet.c
	confvirtualmot.c
	counter.c
	devexec.c
	drive.c
	exebuf.c
	hipadaba.c
	interface.h
	make_gen
	motor.c
	nserver.c
	nwatch.c
	ofac.c
	protocol.c
	sicshipadaba.c
2015-04-23 17:00:33 +02:00

477 lines
12 KiB
C

/*
* N E T W A T C H E R
*
* This module watches network connections for sockets becoming readable or
* writeable and invokes callbacks. It also provides a timer mechanism.
*
* Douglas Clowes, February 2007
*
*/
#include <stdlib.h>
#include <assert.h>
#ifdef CYGNUS
#include <sys/socket.h>
#else
#include <sys/select.h>
#endif
#include <sys/time.h>
#include "fortify.h"
#include "nwatch.h"
#include "uselect.h"
#include "sics.h"
#define NWMAGIC 51966
/* Net Watcher control structure */
typedef struct __netwatcher_s {
pNWContext cq_head; /* head of socket context queue */
pNWContext cq_tail; /* tail of socket context queue */
int nInvalid; /* number of invalidated entries */
pNWTimer tq_head; /* head of timer context queue */
pNWTimer tq_tail; /* tail of timer context queue */
long lMagic; /* integrity check */
} NetWatch, *pNetWatch;
/* Singleton pattern */
static pNetWatch instance = NULL;
/**
* \brief Initialises the Net Watcher singleton and starts the task
*
* \return 1=success, 0=failure
*/
static pNetWatch NetWatchGetInstance(void)
{
/*
* If the singleton has not yet been created, do so now
*/
if (instance == NULL) {
instance = (pNetWatch) malloc(sizeof(NetWatch));
if (instance == NULL)
return 0;
memset(instance, 0, sizeof(NetWatch));
instance->lMagic = NWMAGIC;
}
return instance;
}
/*
* The timer context object private definition
*/
typedef struct __netwatchtimer {
pNWTimer next; /* chain to next event */
struct timeval tv; /* time when event is due */
pNWCallback func; /* function to call */
void *cntx; /* abstract context to pass to callback */
int msec; /* millisecond delay time */
int tick; /* millisecond repeat rate */
long int vrfy; /* integrity check */
} NWTimer;
static pNWTimer activeTimer = NULL;
/*
* \brief private function to insert an entry into the sorted timer queue.
*
* \param self singleton
* \param handle new timer to insert
*/
static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
{
/* if the queue is empty, just stick new one in */
if (self->tq_head == NULL) {
self->tq_head = self->tq_tail = handle;
handle->next = NULL;
handle->vrfy = NWMAGIC;
return 1;
}
/* if new one is not earlier than latest one, insert after latest */
if (handle->tv.tv_sec > self->tq_tail->tv.tv_sec ||
(handle->tv.tv_sec == self->tq_tail->tv.tv_sec &&
handle->tv.tv_usec >= self->tq_tail->tv.tv_usec)) {
self->tq_tail->next = handle;
self->tq_tail = handle;
handle->next = NULL;
handle->vrfy = NWMAGIC;
return 1;
}
/* if new one is not later than earliest one, insert before earliest */
if (handle->tv.tv_sec < self->tq_head->tv.tv_sec ||
(handle->tv.tv_sec == self->tq_head->tv.tv_sec &&
handle->tv.tv_usec <= self->tq_head->tv.tv_usec)) {
handle->next = self->tq_head;
self->tq_head = handle;
handle->vrfy = NWMAGIC;
return 1;
} else {
/* must be in between two so start at the first entry */
pNWTimer pNxt = self->tq_head;
/* follow chain until the one after this one is greater than new one */
while (pNxt->next &&
(handle->tv.tv_sec > pNxt->next->tv.tv_sec ||
(handle->tv.tv_sec == pNxt->next->tv.tv_sec &&
handle->tv.tv_usec > pNxt->next->tv.tv_usec)))
pNxt = pNxt->next;
/* slip new one in between this one and the next one */
handle->next = pNxt->next;
pNxt->next = handle;
handle->vrfy = NWMAGIC;
return 1;
}
return 0;
}
/*
* \brief private function to remove an entry from the sorted timer queue.
*
* \param self singleton
* \param handle existing timer to remove
*/
static int NetWatchTimerRemQue(pNetWatch self, pNWTimer handle)
{
assert(handle->vrfy == NWMAGIC);
/* handle the case of first and possibly only */
if (handle == self->tq_head) {
self->tq_head = self->tq_head->next; /* may be NULL */
if (handle == self->tq_tail)
self->tq_tail = NULL;
}
/* handle general case */
else {
pNWTimer pNxt = self->tq_head;
while (pNxt) {
if (handle == pNxt->next) {
pNxt->next = pNxt->next->next;
break;
}
pNxt = pNxt->next;
}
/* It it was the last entry, point tail to its predecessor */
if (handle == self->tq_tail)
self->tq_tail = pNxt;
}
handle->vrfy = 0;
return 1;
}
int NetWatchRegisterTimer(pNWTimer * handle, int mSec,
pNWCallback callback, void *context)
{
assert(callback);
pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
pNWTimer pNew = (pNWTimer) malloc(sizeof(NWTimer));
if (pNew == NULL)
return 0;
memset(pNew, 0, sizeof(NWTimer));
gettimeofday(&pNew->tv, NULL);
pNew->tv.tv_sec += mSec / 1000;
pNew->tv.tv_usec += 1000 * (mSec % 1000);
if (pNew->tv.tv_usec > 1000000) {
pNew->tv.tv_sec++;
pNew->tv.tv_usec -= 1000000;
}
pNew->msec = mSec;
pNew->tick = 0;
pNew->func = callback;
pNew->cntx = context;
NetWatchTimerInsQue(self, pNew);
*handle = pNew;
return 1;
}
int NetWatchRegisterTimerPeriodic(pNWTimer * handle, int mSecInitial,
int mSecPeriod, pNWCallback callback,
void *context)
{
assert(callback);
if (NetWatchRegisterTimer(handle, mSecInitial, callback, context)) {
pNWTimer pNew = *handle;
if (pNew == NULL)
return 0;
if (mSecPeriod > 0)
pNew->tick = mSecPeriod;
return 1;
}
return 0;
}
pNWTimer NetWatchGetActiveTimer(void)
{
return activeTimer;
}
int NetWatchGetTimerInitial(pNWTimer handle)
{
if (handle == NULL
|| (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
return 0;
return handle->msec;
}
int NetWatchGetTimerDelay(pNWTimer handle)
{
return NetWatchGetTimerInitial(handle);
}
int NetWatchGetTimerPeriod(pNWTimer handle)
{
if (handle == NULL
|| (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
return 0;
return handle->tick;
}
int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod)
{
if (handle == NULL
|| (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
return 0;
handle->tick = mSecPeriod;
return 1;
}
int NetWatchRemoveTimer(pNWTimer handle)
{
pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
if (handle == NULL || handle->vrfy != NWMAGIC)
return 0;
NetWatchTimerRemQue(self, handle);
handle->vrfy = 0;
free(handle);
return 1;
}
/* private data */
typedef struct __netwatchcontext {
pNWContext next; /* chain pointer */
int sock; /* socket to watch */
int mode; /* read or write */
pNWCallback func; /* user supplied callback function */
void *cntx; /* user supplied callback context */
long vrfy; /* integrity check */
} NWContext;
/**
* \brief private function to insert entry into unsorted queue
*
* \param self singleton
* \param handle entry to insert
*/
static int NetWatchContextInsQue(pNetWatch self, pNWContext handle)
{
if (self->cq_head == NULL) /* empty */
self->cq_head = self->cq_tail = handle;
else {
self->cq_tail->next = handle;
self->cq_tail = handle;
}
handle->vrfy = NWMAGIC;
return 1;
}
/**
* \brief private function to purge invalid entries
*
* \param self singleton
*/
static void NetWatchContextPrgQue(pNetWatch self)
{
pNWContext pNxt = NULL;
/* while the first entry is invalid remove it */
while (self->cq_head && self->cq_head->sock < 0) {
pNWContext tmp = NULL;
tmp = self->cq_head;
self->cq_head = self->cq_head->next;
tmp->vrfy = 0;
free(tmp);
}
pNxt = self->cq_head;
while (pNxt && pNxt->next) {
if (pNxt->next->sock < 0) {
pNWContext tmp = NULL;
tmp = pNxt->next;
pNxt->next = pNxt->next->next;
tmp->vrfy = 0;
free(tmp);
continue;
}
pNxt = pNxt->next;
}
/* if the queue is empty then pNxt is NULL else pNxt points to the tail */
self->cq_tail = pNxt;
self->nInvalid = 0;
return;
}
int NetWatchRegisterCallback(pNWContext * handle, int iSocket,
pNWCallback callback, void *context)
{
assert(callback);
pNWContext pNew = NULL;
pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
if (iSocket < 0 || iSocket > 65535)
return 0;
pNew = (pNWContext) malloc(sizeof(NWContext));
if (pNew == NULL)
return 0;
memset(pNew, 0, sizeof(NWContext));
pNew->sock = iSocket;
pNew->mode = nwatch_read;
pNew->func = callback;
pNew->cntx = context;
*handle = pNew;
NetWatchContextInsQue(self, pNew);
return 1;
}
int NetWatchRemoveCallback(pNWContext handle)
{
pNetWatch self = NetWatchGetInstance();
if (handle == NULL || handle->vrfy != NWMAGIC)
return 0;
if (!self || self->lMagic != NWMAGIC)
return 0;
/* mark as invalid */
handle->sock = -1;
/* increment count of invalid */
self->nInvalid++;
/* leave for garbage collection */
return 1;
}
int NetWatchGetMode(pNWContext handle)
{
if (handle == NULL || handle->vrfy != NWMAGIC)
return 0;
return handle->mode;
}
int NetWatchSetMode(pNWContext handle, int mode)
{
if (handle == NULL || handle->vrfy != NWMAGIC)
return 0;
handle->mode = mode;
return 1;
}
/**
* \brief the task to drive all this
* Should be called periodically
*/
int NetWatchTask(void *pData)
{
pNetWatch self = NULL;
pNWContext pNWC = NULL;
fd_set rMask;
fd_set wMask;
struct timeval tmo = { 0, 0 };
int iRet;
int iCount;
/* Check the singleton */
self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
/* Purge the invalidated */
if (self->nInvalid > 0)
NetWatchContextPrgQue(self);
/* build the select mask */
FD_ZERO(&rMask);
FD_ZERO(&wMask);
pNWC = self->cq_head;
iCount = -1;
while (pNWC) {
if (pNWC->sock >= 0 && pNWC->sock <= 65535) {
if (pNWC->mode & nwatch_read)
FD_SET(pNWC->sock, &rMask);
if (pNWC->mode & nwatch_write)
FD_SET(pNWC->sock, &wMask);
if (pNWC->sock > iCount) {
iCount = pNWC->sock;
}
}
pNWC = pNWC->next;
}
iRet = 0;
if (iCount >= 0)
iRet = uselect(iCount + 1, &rMask, &wMask, NULL, &tmo);
if (iRet > 0) {
/* invoke the active callbacks */
iCount = 0;
pNWC = self->cq_head;
while (pNWC) {
if (pNWC->sock >= 0 && pNWC->sock <= 65535) {
int action_mode = 0;
if ((pNWC->mode & nwatch_read) && FD_ISSET(pNWC->sock, &rMask))
action_mode |= nwatch_read;
if ((pNWC->mode & nwatch_write) && FD_ISSET(pNWC->sock, &wMask))
action_mode |= nwatch_write;
if (action_mode != 0) {
int iStatus;
iStatus = (*pNWC->func) (pNWC->cntx, action_mode);
}
}
pNWC = pNWC->next;
}
}
/* Now do the timers */
if (self->tq_head) {
int iStatus;
struct timeval tv;
gettimeofday(&tv, NULL);
while (self->tq_head) {
pNWTimer pNew = self->tq_head;
if (tv.tv_sec < pNew->tv.tv_sec ||
(tv.tv_sec == pNew->tv.tv_sec &&
tv.tv_usec < pNew->tv.tv_usec)) {
break;
}
NetWatchTimerRemQue(self, pNew);
activeTimer = pNew;
activeTimer->vrfy = ~NWMAGIC;
iStatus = pNew->func(pNew->cntx, 0);
activeTimer->vrfy = 0;
activeTimer = NULL;
/*
* If this is a recurrent timer and the function
* indicates to keep it going, put it back in
*/
if (pNew->tick && iStatus == 1) {
/*
* While the expiration time is in the past, increment
*/
gettimeofday(&tv, NULL);
while (tv.tv_sec > pNew->tv.tv_sec ||
(tv.tv_sec == pNew->tv.tv_sec &&
tv.tv_usec > pNew->tv.tv_usec)) {
pNew->tv.tv_usec += 1000 * pNew->tick;
if (pNew->tv.tv_usec > 1000000) {
pNew->tv.tv_sec += pNew->tv.tv_usec / 1000000;
pNew->tv.tv_usec %= 1000000;
}
}
NetWatchTimerInsQue(self, pNew);
} else {
pNew->vrfy = 0;
free(pNew);
}
}
}
/* done, finally */
return 1;
}