blob: e3a7b6145f70b097f1f716f150d734029ef1083c [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2008 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/*****************************************************************************
* Utility Library
*
* DESCRIPTION
* An application's main event-dispatch loop. Dispatches file-descriptor,
* idle, and signal events. All dispatched functions execute outside of
* the context of raw UNIX/Linux signal handlers.
*
* Signal handling is done via pipes. The use of a pipe solves the problem
* of re-entrency. Signals are asynchronous and could thus occur at any
* time. If application signal handlers were called directly from the signal
* handler, those handlers could not call functions that are not re-entrent
* (i.e. `malloc()'). This is a serious restriction. Careful signal blocking
* and unblocking could work around this but is extremely error prone. A pipe
* allows an asynchronous signal to be turned into the synchronous arrival
* of a character on a file descriptor. Now application signal handlers can
* call functions that are not re-entrant with no problems. But this does
* mean that a `read()' or `select()' must be hanging on the pipe to catch
* signal characters, and that when such a character arrives that the signal
* handler function is invoked.
*
* FUTURES
* Q: How should one mix signals and threads in programs?
* A: The less you mix them, the better. Notice that all pthread_* functions are
* not async-signal safe, meaning that you should not call them from signal
* handlers. This recommendation is not to be taken lightly: your program
* can deadlock if you call a pthread_* function from a signal handler!
*
* The only sensible things you can do from a signal handler is set a global
* flag, or call sem_post on a semaphore, to record the delivery of the signal.
* The remainder of the program can then either poll the global flag, or use
* sem_wait() and sem_trywait() on the semaphore.
*
* Another option is to do nothing in the signal handler, and dedicate one
* thread (preferably the initial thread) to wait synchronously for signals,
* using sigwait(), and send messages to the other threads accordingly.
*
* Semaphores were added to POSIX not as part of the thread package but as a
* separate set of interfaces. The interface allows easy use of semaphores
* in different processes by creating named semaphore objects. At the same
* time anonymous semaphores are available for the use in multi-threaded
* applications. Affected interfaces:
* sem_close()
* sem_destroy()
* sem_getvalue()
* sem_init()
* sem_open()
* sem_post()
* sem_trywait()
* sem_unlink()
* sem_wait()
*
* Q: When one thread is blocked in sigwait(), other threads no longer receive
* the signals sigwait() is waiting for! What happens?
* A: It's an unfortunate consequence of how LinuxThreads implements sigwait().
* Basically, it installs signal handlers on all signals waited for, in
* order to record which signal was received. Since signal handlers are
* shared with the other threads, this temporarily deactivates any signal
* handlers you might have previously installed on these signals.
* Though surprising, this behavior actually seems to conform to the POSIX
* standard. According to POSIX, sigwait() is guaranteed to work as
* expected only if all other threads in the program block the signals
* waited for (otherwise, the signals could be delivered to other threads
* than the one doing sigwait(), which would make sigwait() useless). In
* this particular case, the problem described in this question does not
* appear. One day, sigwait() will be implemented in the kernel, along
* with other POSIX 1003.1b extensions, and sigwait() will have a more
* natural behavior (as well as better performance).
*****************************************************************************/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef utlTEST
# include <math.h>
#endif
#include "utlTypes.h"
#include "utlError.h"
#include "utlLinkedList.h"
#include "utlMalloc.h"
#include "utlTime.h"
#include "utlTimer.h"
#include "utlSemaphore.h"
#include "utlTrace.h"
#include "utlEventHandler.h"
/*--- Configuration ---------------------------------------------------------*/
/*--- for tracing event handling ---*/
#define utlTRACE_EVENT_HANDLER "events"
/*--- Data Types ------------------------------------------------------------*/
typedef struct utlEventHandlerNode_S *utlEventHandlerNode_P;
typedef struct utlEventHandlerNode_S {
utlEventHandlerNode_P next_p;
utlEventHandlerType_T type; /* event handler type */
utlEventHandlerPriority_T priority; /* event handler priority */
bool enable;
utlEventHandlerId_T handler_id; /* event handler ID */
union {
utlRelativeTime_T period; /* only valid when type == utlEVENT_HANDLER_TYPE_IDLE */
int fd; /* only valid when type == utlEVENT_HANDLER_TYPE_READ
or utlEVENT_HANDLER_TYPE_WRITE
or utlEVENT_HANDLER_TYPE_READ_WRITE
or utlEVENT_HANDLER_TYPE_EXCEPTION
or utlEVENT_HANDLER_TYPE_EXCEPTION_READ
or utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE
or utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE */
int signal; /* only valid when type == utlEVENT_HANDLER_TYPE_SIG* */
int exit; /* only valid when type == utlEVENT_HANDLER_TYPE_EXIT */
} u;
struct {
utlEventHandlerFunction_P function_p;
void *arg_p;
} call_back;
} utlEventHandlerNode_T;
typedef const utlEventHandlerNode_T *utlEventHandlerNode_P2c;
/*--- Local Data ------------------------------------------------------------*/
static utlSemaphore_T events_semaphore = utlNULL_SEMAPHORE; /* protection for static event-handling data */
/*--- signal-event pipe ---*/
static struct {
bool initialized;
int read_fd;
int write_fd;
} plumbing = { false, 0, 0 };
static utlLinkedList_T fd_handlers = utlEMPTY_LINKED_LIST;
static utlLinkedList_T idle_handlers = utlEMPTY_LINKED_LIST;
static utlLinkedList_T signal_handlers = utlEMPTY_LINKED_LIST;
static utlLinkedList_T exit_handlers = utlEMPTY_LINKED_LIST;
static enum {
utlDELETED_NONE = 0x00,
utlDELETED_FD_HANDLER = 0x01,
utlDELETED_SIGNAL_HANDLER = 0x02,
utlDELETED_IDLE_HANDLER = 0x04,
utlDELETED_EXIT_HANDLER = 0x08
} deleted_pending = utlDELETED_NONE;
static bool running = false;
static utlEventHandlerId_T next_possible_handler_id = 1; /* next unused handler ID */
/*--- signal-number & name ---*/
typedef struct {
int signum;
const char *signame;
} utlSigNumName;
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlFindUnusedHandlerId()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* An event handler ID for success, utlFAILED for failure
* DESCRIPTION
* Finds the next unused event handler ID.
*---------------------------------------------------------------------------*/
static utlEventHandlerId_T _utlFindUnusedHandlerId(void)
{
for (;; )
{
utlEventHandlerNode_P curr_p;
for (curr_p = (utlEventHandlerNode_P)fd_handlers.head_p; curr_p != NULL; curr_p = curr_p->next_p)
if (curr_p->handler_id == next_possible_handler_id)
break;
if (curr_p == NULL)
{
for (curr_p = (utlEventHandlerNode_P)idle_handlers.head_p; curr_p != NULL; curr_p = curr_p->next_p)
if (curr_p->handler_id == next_possible_handler_id)
break;
if (curr_p == NULL)
{
for (curr_p = (utlEventHandlerNode_P)signal_handlers.head_p; curr_p != NULL; curr_p = curr_p->next_p)
if (curr_p->handler_id == next_possible_handler_id)
break;
if (curr_p == NULL)
{
for (curr_p = (utlEventHandlerNode_P)exit_handlers.head_p; curr_p != NULL; curr_p = curr_p->next_p)
if (curr_p->handler_id == next_possible_handler_id)
break;
if ((curr_p == NULL) && (next_possible_handler_id != (utlTimerId_T)0) &&
(next_possible_handler_id != (utlTimerId_T)utlFAILED))
return next_possible_handler_id;
}
}
}
next_possible_handler_id++;
}
utlError(_utlFindUnusedHandlerId, "No more free event-handler IDs");
return utlFAILED;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlSignalToString(signal)
* INPUT
* signal == a signal
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Converts a signal into a character string.
*---------------------------------------------------------------------------*/
static const char *_utlSignalToString(const int signal)
{
const utlSigNumName sigNumName[] =
{
{SIGALRM, "SIGALRM"},
{SIGHUP, "SIGHUP"},
{SIGINT, "SIGINT"},
{SIGQUIT, "SIGQUIT"},
{SIGILL, "SIGILL"},
{SIGTRAP, "SIGTRAP"},
{SIGABRT, "SIGABRT"},
{SIGFPE, "SIGFPE"},
{SIGKILL, "SIGKILL"},
{SIGBUS, "SIGBUS"},
{SIGSEGV, "SIGSEGV"},
{SIGSYS, "SIGSYS"},
{SIGPIPE, "SIGPIPE"},
{SIGTERM, "SIGTERM"},
{SIGURG, "SIGURG"},
{SIGSTOP, "SIGSTOP"},
{SIGTSTP, "SIGTSTP"},
{SIGCONT, "SIGCONT"},
{SIGCHLD, "SIGCHLD"},
{SIGTTIN, "SIGTTIN"},
{SIGTTOU, "SIGTTOU"},
{SIGIO, "SIGIO"},
{SIGXCPU, "SIGXCPU"},
{SIGXFSZ, "SIGXFSZ"},
{SIGVTALRM, "SIGVTALRM"},
{SIGPROF, "SIGPROF"},
{SIGWINCH, "SIGWINCH"},
{SIGUSR1, "SIGUSR1"},
{SIGUSR2, "SIGUSR2"},
};
static char buf[20];
int index, len;
for(index = 0, len = sizeof(sigNumName) / sizeof(sigNumName[0]); index < len; index++)
{
if(signal == sigNumName[index].signum)
return sigNumName[index].signame;
}
(void)sprintf(buf, "<%d>", signal);
return buf;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlEventTypeToString(event_type)
* INPUT
* event_type == an event type
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Converts an event type into a character string.
*---------------------------------------------------------------------------*/
static char *_utlEventTypeToString(const utlEventHandlerType_T event_type)
{
static char buf[20];
switch (event_type)
{
case utlEVENT_HANDLER_TYPE_UNKNOWN: return "unknown";
case utlEVENT_HANDLER_TYPE_SIGHUP: return "SIGHUP";
case utlEVENT_HANDLER_TYPE_SIGINT: return "SIGINT";
case utlEVENT_HANDLER_TYPE_SIGQUIT: return "SIGQUIT";
case utlEVENT_HANDLER_TYPE_SIGILL: return "SIGILL";
case utlEVENT_HANDLER_TYPE_SIGTRAP: return "SIGTRAP";
case utlEVENT_HANDLER_TYPE_SIGABRT: return "SIGABRT";
case utlEVENT_HANDLER_TYPE_SIGFPE: return "SIGFPE";
case utlEVENT_HANDLER_TYPE_SIGKILL: return "SIGKILL";
case utlEVENT_HANDLER_TYPE_SIGBUS: return "SIGBUS";
case utlEVENT_HANDLER_TYPE_SIGSEGV: return "SIGSEGV";
case utlEVENT_HANDLER_TYPE_SIGSYS: return "SIGSYS";
case utlEVENT_HANDLER_TYPE_SIGPIPE: return "SIGPIPE";
case utlEVENT_HANDLER_TYPE_SIGALRM: return "SIGALRM";
case utlEVENT_HANDLER_TYPE_SIGTERM: return "SIGTERM";
case utlEVENT_HANDLER_TYPE_SIGURG: return "SIGURG";
case utlEVENT_HANDLER_TYPE_SIGSTOP: return "SIGSTOP";
case utlEVENT_HANDLER_TYPE_SIGTSTP: return "SIGTSTP";
case utlEVENT_HANDLER_TYPE_SIGCONT: return "SIGCONT";
case utlEVENT_HANDLER_TYPE_SIGCHLD: return "SIGCHLD";
case utlEVENT_HANDLER_TYPE_SIGTTIN: return "SIGTTIN";
case utlEVENT_HANDLER_TYPE_SIGTTOU: return "SIGTTOU";
case utlEVENT_HANDLER_TYPE_SIGIO: return "SIGIO";
case utlEVENT_HANDLER_TYPE_SIGXCPU: return "SIGXCPU";
case utlEVENT_HANDLER_TYPE_SIGXFSZ: return "SIGXFSZ";
case utlEVENT_HANDLER_TYPE_SIGVTALRM: return "SIGVTALRM";
case utlEVENT_HANDLER_TYPE_SIGPROF: return "SIGPROF";
case utlEVENT_HANDLER_TYPE_SIGWINCH: return "SIGWINCH";
case utlEVENT_HANDLER_TYPE_SIGUSR1: return "SIGUSR1";
case utlEVENT_HANDLER_TYPE_SIGUSR2: return "SIGUSR2";
/* case utlEVENT_HANDLER_TYPE_SIGPOLL: return "SIGPOLL"; */
/* case utlEVENT_HANDLER_TYPE_SIGIOT: return "SIGIOT"; */
/* case utlEVENT_HANDLER_TYPE_SIGSTKFLT: return "SIGSTKFLT"; */
/* case utlEVENT_HANDLER_TYPE_SIGCLD: return "SIGCLD"; */
/* case utlEVENT_HANDLER_TYPE_SIGPWR: return "SIGPWR"; */
/* case utlEVENT_HANDLER_TYPE_SIGUNUSED: return "SIGUNUSED"; */
case utlEVENT_HANDLER_TYPE_WRITE: return "write";
case utlEVENT_HANDLER_TYPE_READ: return "read";
case utlEVENT_HANDLER_TYPE_READ_WRITE: return "read-write";
case utlEVENT_HANDLER_TYPE_EXCEPTION: return "exception";
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: return "exception-write";
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: return "exception-read";
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: return "exception-read-write";
case utlEVENT_HANDLER_TYPE_IDLE: return "idle";
case utlEVENT_HANDLER_TYPE_EXIT: return "exit";
case utlEVENT_HANDLER_TYPE_DIRECT_FUNC_CALL: return "direct";
case utlEVENT_HANDLER_TYPE_ZOMBIE: return "zombie";
default:
(void)sprintf(buf, "<%u>", event_type);
return buf;
}
/*NOTREACHED*/
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlEventHandlerSignal(signal)
* INPUT
* signal == which signal occurred
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Handles signal events: Sends one signal ID byte down the signal-event
* pipe. This function is only invoked by the operating system upon
* receipt of a signal.
*---------------------------------------------------------------------------*/
static void _utlEventHandlerSignal(int signal)
{
char buf[1];
int n;
/*--- write signal ID byte to signal-event pipe ---*/
buf[0] = signal;
for (n = 100; n > 0; n--)
{
ssize_t rv;
/*--- if write succeeds, we're done ---*/
if ((rv = write(plumbing.write_fd, buf, (size_t)1)) == (ssize_t)1)
return;
if (rv == (ssize_t)0)
continue;
/*--- if write didn't fail due to an interrupt, pause before trying again ---*/
if (errno != EINTR)
{
int select_rv;
for (;; )
{
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 100000;
if ((select_rv = select(0, NULL, NULL, NULL, &timeout)) >= 0)
break;
if (errno != EINTR)
{
utlSigError(_utlEventHandlerSignal, "Call to %s() failed, %s", "select", strerror(errno));
break;
}
}
}
}
/*--- we're inside a signal handler, so we must use utlSigError() for error reporting ---*/
utlSigError(_utlEventHandlerSignal1, "write() to signal-event pipe failed after 100 attempts");
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlEventHandlerExit()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Handles exit events: invokes all set exit-handler functions.
*---------------------------------------------------------------------------*/
static void _utlEventHandlerExit(void)
{
utlEventHandlerPriority_T priority;
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(_utlEventHandlerExit, "exit event\n");
);
/*--- acquire exclusive-access. We're going to use an exclusive-lock even
though a shared-lock would do so that add/remove event handlers,
and enable/disable FD event handlers routines can be called without
resulting in deadlock (all these routines need to engage an
exclusive-lock) */
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return;
/*--- for each possible priority level... ---*/
for (priority = utlEVENT_HANDLER_PRIORITY_INTERNAL; priority >= utlEVENT_HANDLER_PRIORITY_LOW; priority--)
{
utlEventHandlerNode_P node_p;
/*--- for each exit handler... ---*/
for (node_p = (utlEventHandlerNode_P)exit_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
/*--- wrong priority? ---*/
if (node_p->priority != priority)
continue;
/*--- perform exit-handler action ---*/
if (node_p->call_back.function_p != NULL)
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(_utlEventHandlerExit1, "exit event for 0x%x\n", (unsigned int)node_p->call_back.function_p);
);
if ((*(node_p->call_back.function_p))(node_p->type, utlEVENT_HANDLER_TYPE_EXIT, -1, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(_utlEventHandlerExit2, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return;
}
}
}
}
/*--- release exclusive-access ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlEventHandlerSignalHandler(handler_type, event_type, fd, period, arg)
* INPUT
* handler_type == handler type
* event_type == event type
* fd == which file descriptor
* period_p == idle period
* arg_p == pointer to user-defined data
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCRIPTION
* Handles one or more signal events. This function is only invoked by
* receipt of a signal ID byte on the signal-event pipe.
*---------------------------------------------------------------------------*/
static utlReturnCode_T _utlEventHandlerSignalHandler(const utlEventHandlerType_T handler_type,
const utlEventHandlerType_T event_type,
const int fd,
const utlRelativeTime_P2c period_p,
void *arg_p)
{
UNUSEDPARAM(period_p)
utlAssert(handler_type == utlEVENT_HANDLER_TYPE_READ);
utlAssert(event_type == utlEVENT_HANDLER_TYPE_READ);
utlAssert(fd == plumbing.read_fd);
utlAssert(arg_p == NULL);
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(_utlEventHandlerSignalHandler, "%s event\n", _utlEventTypeToString(event_type));
);
/*--- acquire exclusive-access. We're going to use an exclusive-lock even
though a shared-lock would do so that add/remove event handlers,
and enable/disable FD event handlers routines can be called without
resulting in deadlock (all these routines need to engage an
exclusive-lock) */
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- read all signal ID bytes queued in signal-event pipe ---*/
{
char buf[128];
for (;; )
{
int n;
int i;
for (;; )
{
if ((n = read(plumbing.read_fd, buf, sizeof(buf))) >= 0)
break;
/*--- nothing to read? ---*/
if (errno == EAGAIN)
{
n = 0;
break;
}
if (errno != EINTR)
{
utlError(_utlEventHandlerSignalHandler1, "Call to %s() failed, %s", "read", strerror(errno));
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
}
/*--- for each signal ID byte... ---*/
for (i = 0; i < n; i++)
{
utlEventHandlerPriority_T priority;
/*--- for each possible priority level... ---*/
for (priority = utlEVENT_HANDLER_PRIORITY_INTERNAL; priority >= utlEVENT_HANDLER_PRIORITY_LOW; priority--)
{
utlEventHandlerNode_P node_p;
/*--- for each signal handler... ---*/
for (node_p = (utlEventHandlerNode_P)signal_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
/*--- wrong priority? ---*/
if (node_p->priority != priority)
continue;
/*--- found a(nother) handler for this signal? ---*/
if (node_p->u.signal == buf[i])
{
/*--- perform signal-handler action ---*/
{
if (node_p->call_back.function_p != NULL)
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(_utlEventHandlerSignalHandler2, "signal %s event for 0x%x\n",
_utlSignalToString(node_p->u.signal),
(unsigned int)node_p->call_back.function_p);
);
if ((*(node_p->call_back.function_p))(node_p->type, (utlEventHandlerType_T)node_p->u.signal, -1, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(_utlEventHandlerSignalHandler3, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
}
}
}
}
}
}
/*--- if we've read everything ---*/
if ((unsigned int)n < sizeof(buf))
break;
}
}
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlInitSignalPipe()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Initializes the signal-event pipe.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlInitSignalPipe(void)
{
utlEventHandlerId_T handler_id;
utlEventHandlerNode_P node_p;
/*--- if signal handling is already initialized, bail out ---*/
if (plumbing.initialized != false)
return utlSUCCESS;
/*--- create signal-event pipe ---*/
{
int pipe_fds[2];
if (pipe(pipe_fds) < 0)
{
utlError(utlInitSignalPipe, "Call to %s() failed, %s", "pipe", strerror(errno));
return utlFAILED;
}
plumbing.read_fd = pipe_fds[0];
plumbing.write_fd = pipe_fds[1];
}
if ((handler_id = _utlFindUnusedHandlerId()) == (utlEventHandlerId_T)utlFAILED)
{
/*--- clean ---*/
(void)close(plumbing.write_fd);
(void)close(plumbing.read_fd);
return utlFAILED;
}
if ((node_p = (utlEventHandlerNode_P)utlMalloc(sizeof(utlEventHandlerNode_T))) == NULL)
{
/*--- clean ---*/
(void)close(plumbing.write_fd);
(void)close(plumbing.read_fd);
return utlFAILED;
}
/*--- initialize the new event handler node ---*/
node_p->next_p = NULL;
node_p->type = utlEVENT_HANDLER_TYPE_READ;
node_p->priority = utlEVENT_HANDLER_PRIORITY_INTERNAL;
node_p->enable = true;
node_p->handler_id = handler_id;
node_p->u.fd = plumbing.read_fd;
node_p->call_back.function_p = _utlEventHandlerSignalHandler;
node_p->call_back.arg_p = NULL;
/*--- add to list of known fd-event handlers ---*/
(void)utlPutTailNode(&fd_handlers, utlEventHandlerNode_T, node_p);
plumbing.initialized = true;
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlDeleteEventHandler(handler_id)
* INPUT
* handler_id == which event handler to delete
* OUTPUT
* none
* RETURNS
* utlSUCCESS success, utlFAILED for failure
* DESCRIPTION
* Deletes the specified event handler. This function actually only
* marks handlers for deletion. The actual deletion occurs the next
* time the event handlers list is traversed by the main event loop.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlDeleteEventHandler(const utlEventHandlerId_T handler_id)
{
utlEventHandlerNode_P node_p;
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- mark fd-event handler we wish to delete... ---*/
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
if (node_p->handler_id == handler_id)
{
utlTrace(utlTRACE_EVENT_HANDLER,
char *what;
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_READ: what = "read"; break;
case utlEVENT_HANDLER_TYPE_WRITE: what = "write"; break;
case utlEVENT_HANDLER_TYPE_READ_WRITE: what = "read/write"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION: what = "exception"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: what = "exception/read"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: what = "exception/write"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: what = "exception/read/write"; break;
default: what = "<unknown>"; break;
}
utlPrintTrace(utlDeleteEventHandler, "deleted `%s' event handler (id=%u) for fd %d\n",
what, (unsigned int)handler_id, node_p->u.fd);
);
node_p->type = utlEVENT_HANDLER_TYPE_ZOMBIE;
deleted_pending |= utlDELETED_FD_HANDLER;
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*--- mark idle event handler we wish to delete... ---*/
for (node_p = (utlEventHandlerNode_P)idle_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
if (node_p->handler_id == handler_id)
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlDeleteEventHandler1, "deleted `idle' event handler (HID=%u), called at %d.%09d secs\n",
(unsigned int)handler_id,
node_p->u.period.seconds,
node_p->u.period.nanoseconds);
);
node_p->type = utlEVENT_HANDLER_TYPE_ZOMBIE;
deleted_pending |= utlDELETED_IDLE_HANDLER;
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*--- mark signal event handler we wish to delete... ---*/
for (node_p = (utlEventHandlerNode_P)signal_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
if (node_p->handler_id == handler_id)
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlDeleteEventHandler2, "deleted `signal' event handler (HID=%u)\n",
(unsigned int)handler_id);
);
node_p->type = utlEVENT_HANDLER_TYPE_ZOMBIE;
deleted_pending |= utlDELETED_SIGNAL_HANDLER;
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*--- mark exit event handler we wish to delete... ---*/
for (node_p = (utlEventHandlerNode_P)exit_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
if (node_p->handler_id == handler_id)
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlDeleteEventHandler3, "deleted `exit' event handler (HID=%u)\n",
(unsigned int)handler_id);
);
node_p->type = utlEVENT_HANDLER_TYPE_ZOMBIE;
deleted_pending |= utlDELETED_EXIT_HANDLER;
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
}
utlError(utlDeleteEventHandler4, "Event handler %d not found", handler_id);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetFdEventHandler(type, priority, fd, function, arg)
* INPUT
* type == the type of event to report:
* utlEVENT_HANDLER_TYPE_READ
* utlEVENT_HANDLER_TYPE_WRITE
* utlEVENT_HANDLER_TYPE_READ_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ
* utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE
* priority == the handler priority
* fd == the file descriptor to watch
* function_p == pointer to the event call-back function
* arg_p == user-defined function argument
* OUTPUT
* none
* RETURNS
* An event handler ID for success, utlFAILED for failure
* DESCRIPTION
* Arranges for `function_p' to be called when there is `type' activity
* on `fd'.
*
* Note that there can be zero or more call-back functions defined for
* the same "type" and "fd".
*---------------------------------------------------------------------------*/
utlEventHandlerId_T utlSetFdEventHandler(const utlEventHandlerType_T type,
const utlEventHandlerPriority_T priority,
const int fd,
const utlEventHandlerFunction_P function_p,
void *arg_p)
{
utlEventHandlerId_T handler_id;
utlEventHandlerNode_P node_p;
switch (type)
{
case utlEVENT_HANDLER_TYPE_READ:
case utlEVENT_HANDLER_TYPE_WRITE:
case utlEVENT_HANDLER_TYPE_READ_WRITE:
case utlEVENT_HANDLER_TYPE_EXCEPTION:
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ:
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE:
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: break;
default:
return utlFAILED;
}
switch (priority)
{
case utlEVENT_HANDLER_PRIORITY_CRITICAL:
case utlEVENT_HANDLER_PRIORITY_HIGH:
case utlEVENT_HANDLER_PRIORITY_MEDIUM:
case utlEVENT_HANDLER_PRIORITY_LOW: break;
default:
return utlFAILED;
}
utlAssert(function_p != NULL);
/*--- make all reads non-blocking for this file descriptor... ---*/
{
bool fd_valid;
int oflags;
fd_valid = false;
/*--- fetch current flag settings ---*/
for (;; )
{
if ((oflags = fcntl(fd, F_GETFL)) != -1)
{
fd_valid = true;
break;
}
if (errno == EBADF)
break;
if (errno == EINTR)
{
utlError(utlSetFdEventHandler, "Call to %s() failed, %s", "fcntl", strerror(errno));
return utlFAILED;
}
}
/*--- if file descriptor is valid ---*/
if (fd_valid == true)
{
oflags |= O_NONBLOCK;
/*--- apply new flag settings ---*/
for (;; )
{
if (fcntl(fd, F_SETFL, oflags) == 0)
break;
if (errno == EINTR)
{
utlError(utlSetFdEventHandler1, "Call to %s() failed, %s", "fcntl", strerror(errno));
return utlFAILED;
}
}
}
}
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
if ((handler_id = _utlFindUnusedHandlerId()) == (utlEventHandlerId_T)utlFAILED)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
if ((node_p = (utlEventHandlerNode_P)utlMalloc(sizeof(utlEventHandlerNode_T))) == NULL)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- initialize the new event handler node ---*/
node_p->next_p = NULL;
node_p->type = type;
node_p->priority = priority;
node_p->enable = true;
node_p->handler_id = handler_id;
node_p->u.fd = fd;
node_p->call_back.function_p = function_p;
node_p->call_back.arg_p = arg_p;
/*--- add to list of known fd-event handlers ---*/
(void)utlPutTailNode(&fd_handlers, utlEventHandlerNode_T, node_p);
utlTrace(utlTRACE_EVENT_HANDLER,
char *what;
switch (type)
{
case utlEVENT_HANDLER_TYPE_READ: what = "read"; break;
case utlEVENT_HANDLER_TYPE_WRITE: what = "write"; break;
case utlEVENT_HANDLER_TYPE_READ_WRITE: what = "read/write"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION: what = "exception"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: what = "exception/read"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: what = "exception/write"; break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: what = "exception/read/write"; break;
default: what = "<unknown>"; break;
}
utlPrintTrace(utlSetFdEventHandler2, "added `%s' event handler (HID=%u) for fd %d\n",
what, (unsigned int)node_p->handler_id, node_p->u.fd);
);
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return handler_id;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlSetFdEventDetection(type, fd)
* INPUT
* type == the type of event to enable:
* utlEVENT_HANDLER_TYPE_READ
* utlEVENT_HANDLER_TYPE_WRITE
* utlEVENT_HANDLER_TYPE_READ_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ
* utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE
* fd == the file descriptor being watched
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Sets detection of events for the specified file descriptor.
*---------------------------------------------------------------------------*/
static utlReturnCode_T _utlSetFdEventDetection(const utlEventHandlerType_T type,
const int fd,
const bool value)
{
utlEventHandlerNode_P node_p;
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- find fd-event handlers we wish to toggle... ---*/
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
if ((node_p->type == type) && (node_p->u.fd == fd))
{
node_p->enable = value;
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
utlError(_utlSetFdEventDetection, "fd %d not found", fd);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlEnableFdEventHandler(type, fd)
* INPUT
* type == the type of event to enable:
* utlEVENT_HANDLER_TYPE_READ
* utlEVENT_HANDLER_TYPE_WRITE
* utlEVENT_HANDLER_TYPE_READ_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ
* utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE
* fd == the file descriptor being watched
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Enables detection of events for the specified file descriptor.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlEnableFdEventHandler(const utlEventHandlerType_T type,
const int fd)
{
return _utlSetFdEventDetection(type, fd, true);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlDisableFdEventHandler(type, fd)
* INPUT
* type == the type of event to disable:
* utlEVENT_HANDLER_TYPE_READ
* utlEVENT_HANDLER_TYPE_WRITE
* utlEVENT_HANDLER_TYPE_READ_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ
* utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE
* utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE
* fd == the file descriptor being watched
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Disables detection of events for the specified file descriptor.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlDisableFdEventHandler(const utlEventHandlerType_T type,
const int fd)
{
return _utlSetFdEventDetection(type, fd, false);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetIdleEventHandler(period, priority, function, arg)
* INPUT
* period_p == pointer to a filled utlRelativeTime_T structure
* priority == the handler priority
* function_p == pointer to the event call-back function
* arg_p == user-defined function argument
* OUTPUT
* none
* RETURNS
* An event handler ID for success, utlFAILED for failure
* DESCRIPTION
* Arranges for `function_p' to be called when there has been no event
* activity for more than the time specified by `period_p'. If `function_p'
* is NULL, deletes all previously set call-back functions having the
* same time-out period. The actual deletion is deferred until the event
* handler loop processes the next round of events.
*
* Note that there can be zero or more idle call-back functions defined.
*---------------------------------------------------------------------------*/
utlEventHandlerId_T utlSetIdleEventHandler(const utlRelativeTime_P2c period_p,
const utlEventHandlerPriority_T priority,
const utlEventHandlerFunction_P function_p,
void *arg_p)
{
utlEventHandlerId_T handler_id;
utlEventHandlerNode_P node_p;
utlAssert(period_p != NULL);
switch (priority)
{
case utlEVENT_HANDLER_PRIORITY_CRITICAL:
case utlEVENT_HANDLER_PRIORITY_HIGH:
case utlEVENT_HANDLER_PRIORITY_MEDIUM:
case utlEVENT_HANDLER_PRIORITY_LOW: break;
default:
return utlFAILED;
}
utlAssert(function_p != NULL);
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
if ((handler_id = _utlFindUnusedHandlerId()) == (utlEventHandlerId_T)utlFAILED)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
if ((node_p = (utlEventHandlerNode_P)utlMalloc(sizeof(utlEventHandlerNode_T))) == NULL)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- initialize the new event handler node ---*/
node_p->next_p = NULL;
node_p->type = utlEVENT_HANDLER_TYPE_IDLE;
node_p->priority = priority;
node_p->enable = true;
node_p->handler_id = handler_id;
node_p->u.period = *period_p;
node_p->call_back.function_p = function_p;
node_p->call_back.arg_p = arg_p;
/*--- add to list of known idle handlers (insertion-sort) ---*/
{
utlEventHandlerNode_P curr_p;
utlEventHandlerNode_P prev_p;
/*--- find where to insert 'node' into the idle-handlers list (order from smallest-to-largest
timeout period, and from highest-to-lowest priority for entries having the same timeout
period) */
prev_p = NULL;
curr_p = (utlEventHandlerNode_P)idle_handlers.head_p;
while (curr_p != NULL)
{
if ( (curr_p->u.period.seconds < node_p->u.period.seconds) ||
((curr_p->u.period.seconds == node_p->u.period.seconds) && ((curr_p->u.period.nanoseconds < node_p->u.period.nanoseconds) ||
((curr_p->u.period.nanoseconds == node_p->u.period.nanoseconds) && (curr_p->priority >= node_p->priority)))))
{
prev_p = curr_p;
curr_p = curr_p->next_p;
}
else
break;
}
/*--- link node into idle-handlers list ---*/
(void)utlPutNode(&idle_handlers, utlEventHandlerNode_T, prev_p, node_p);
}
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlSetIdleEventHandler, "added `idle' event handler (HID=%u), called after %d.%09d secs\n",
(unsigned int)node_p->handler_id,
node_p->u.period.seconds,
node_p->u.period.nanoseconds);
);
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return handler_id;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* _utlEstablishSignaling(signal)
* INPUT
* signal == the signal to establish a handler for.
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Sets up signal handling for the specified signal (if not already done).
*---------------------------------------------------------------------------*/
static utlReturnCode_T _utlEstablishSignaling(int signal)
{
if (plumbing.initialized == false)
if (utlInitSignalPipe() != utlSUCCESS)
return utlFAILED;
/*--- Check if signal is already established or is used for something else ---*/
{
struct sigaction curr_action;
if (sigemptyset(&(curr_action.sa_mask)) != 0)
{
utlError(_utlEstablishSignaling, "Call to %s() failed, %s", "sigemptyset", strerror(errno));
return utlFAILED;
}
if (sigaction(signal, NULL, &curr_action) != 0)
{
utlError(_utlEstablishSignaling1, "Call to %s() failed, %s", "sigaction", strerror(errno));
return utlFAILED;
}
/*--- if already established ---*/
if (curr_action.sa_handler == _utlEventHandlerSignal)
return utlSUCCESS;
/*--- if used for something else ---*/
if ((curr_action.sa_handler != SIG_IGN) &&
(curr_action.sa_handler != SIG_DFL))
{
utlError(_utlEstablishSignaling2, "Signal resource %s already in use", _utlSignalToString(signal));
/*--- clean ---*/
(void)close(plumbing.write_fd);
(void)close(plumbing.read_fd);
return utlFAILED;
}
}
/*--- Establish the signal handler ---*/
{
struct sigaction action;
(void)memset(&action, 0, sizeof(action));
action.sa_handler = _utlEventHandlerSignal;
action.sa_flags = SA_RESTART;
if (sigemptyset(&(action.sa_mask)) != 0)
{
utlError(_utlEstablishSignaling3, "Call to %s() failed, %s", "sigemptyset", strerror(errno));
return utlFAILED;
}
if (sigaction(signal, &action, NULL) != 0)
{
utlError(_utlEstablishSignaling4, "Call to %s() failed, %s", "sigaction", strerror(errno));
return utlFAILED;
}
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetSignalEventHandler(signal, priority, function, arg)
* INPUT
* signal == the UNIX/Linux signal of interest
* priority == the handler priority
* function_p == pointer to the event call-back function
* arg_p == user-defined function argument
* OUTPUT
* none
* RETURNS
* An event handler ID for success, utlFAILED for failure
* DESCRIPTION
* Arranges for `function_p' to be called whenever the specified `signal'
* occurs. If `function_p' is NULL, deletes all previously set call-back
* functions for `signal'. The actual deletion is deferred until the
* event handler loop processes the next round of events.
*
* Note that there can be zero or more signal-event call-back functions
* defined.
*---------------------------------------------------------------------------*/
utlEventHandlerId_T utlSetSignalEventHandler(const int signal,
const utlEventHandlerPriority_T priority,
const utlEventHandlerFunction_P function_p,
void *arg_p)
{
utlEventHandlerId_T handler_id;
utlEventHandlerNode_P node_p;
utlEventHandlerType_T type;
/*--- check parameters (and convert signal to a type) ---*/
switch (signal)
{
case SIGHUP: type = utlEVENT_HANDLER_TYPE_SIGHUP; break;
case SIGINT: type = utlEVENT_HANDLER_TYPE_SIGINT; break;
case SIGQUIT: type = utlEVENT_HANDLER_TYPE_SIGQUIT; break;
case SIGILL: type = utlEVENT_HANDLER_TYPE_SIGILL; break;
case SIGTRAP: type = utlEVENT_HANDLER_TYPE_SIGTRAP; break;
case SIGABRT: type = utlEVENT_HANDLER_TYPE_SIGABRT; break;
case SIGFPE: type = utlEVENT_HANDLER_TYPE_SIGFPE; break;
case SIGKILL: type = utlEVENT_HANDLER_TYPE_SIGKILL; break;
case SIGBUS: type = utlEVENT_HANDLER_TYPE_SIGBUS; break;
case SIGSEGV: type = utlEVENT_HANDLER_TYPE_SIGSEGV; break;
case SIGSYS: type = utlEVENT_HANDLER_TYPE_SIGSYS; break;
case SIGPIPE: type = utlEVENT_HANDLER_TYPE_SIGPIPE; break;
case SIGALRM: type = utlEVENT_HANDLER_TYPE_SIGALRM; break;
case SIGTERM: type = utlEVENT_HANDLER_TYPE_SIGTERM; break;
case SIGURG: type = utlEVENT_HANDLER_TYPE_SIGURG; break;
case SIGSTOP: type = utlEVENT_HANDLER_TYPE_SIGSTOP; break;
case SIGTSTP: type = utlEVENT_HANDLER_TYPE_SIGTSTP; break;
case SIGCONT: type = utlEVENT_HANDLER_TYPE_SIGCONT; break;
case SIGCHLD: type = utlEVENT_HANDLER_TYPE_SIGCHLD; break;
case SIGTTIN: type = utlEVENT_HANDLER_TYPE_SIGTTIN; break;
case SIGTTOU: type = utlEVENT_HANDLER_TYPE_SIGTTOU; break;
case SIGIO: type = utlEVENT_HANDLER_TYPE_SIGIO; break;
case SIGXCPU: type = utlEVENT_HANDLER_TYPE_SIGXCPU; break;
case SIGXFSZ: type = utlEVENT_HANDLER_TYPE_SIGXFSZ; break;
case SIGVTALRM: type = utlEVENT_HANDLER_TYPE_SIGVTALRM; break;
case SIGPROF: type = utlEVENT_HANDLER_TYPE_SIGPROF; break;
case SIGWINCH: type = utlEVENT_HANDLER_TYPE_SIGWINCH; break;
case SIGUSR1: type = utlEVENT_HANDLER_TYPE_SIGUSR1; break;
case SIGUSR2: type = utlEVENT_HANDLER_TYPE_SIGUSR2; break;
/* case SIGPOLL: type = utlEVENT_HANDLER_TYPE_SIGPOLL; break; */
/* case SIGIOT: type = utlEVENT_HANDLER_TYPE_SIGIOT; break; */
/* case SIGSTKFLT: type = utlEVENT_HANDLER_TYPE_SIGSTKFLT; break; */
/* case SIGCLD: type = utlEVENT_HANDLER_TYPE_SIGCLD; break; */
/* case SIGPWR: type = utlEVENT_HANDLER_TYPE_SIGPWR; break; */
/* case SIGUNUSED: type = utlEVENT_HANDLER_TYPE_SIGUNUSED; break; */
default:
return utlFAILED;
}
switch (priority)
{
case utlEVENT_HANDLER_PRIORITY_CRITICAL:
case utlEVENT_HANDLER_PRIORITY_HIGH:
case utlEVENT_HANDLER_PRIORITY_MEDIUM:
case utlEVENT_HANDLER_PRIORITY_LOW: break;
default:
return utlFAILED;
}
utlAssert(function_p != NULL);
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- setup signaling ---*/
if (_utlEstablishSignaling(signal) != utlSUCCESS)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
if ((handler_id = _utlFindUnusedHandlerId()) == (utlEventHandlerId_T)utlFAILED)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
if ((node_p = (utlEventHandlerNode_P)utlMalloc(sizeof(utlEventHandlerNode_T))) == NULL)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- initialize the new event handler node ---*/
node_p->next_p = NULL;
node_p->type = type;
node_p->priority = priority;
node_p->enable = true;
node_p->handler_id = handler_id;
node_p->u.signal = signal;
node_p->call_back.function_p = function_p;
node_p->call_back.arg_p = arg_p;
/*--- add to list of known signal handlers ---*/
(void)utlPutTailNode(&signal_handlers, utlEventHandlerNode_T, node_p);
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlSetSignalEventHandler, "added `signal' event handler (HID=%u), for signal %s\n",
(unsigned int)node_p->handler_id,
_utlSignalToString(signal));
);
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return handler_id;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetExitEventHandler(priority, function, arg)
* INPUT
* priority == the handler priority
* function_p == pointer to the event call-back function
* arg_p == user-defined function argument
* OUTPUT
* none
* RETURNS
* An event handler ID for success, utlFAILED for failure
* DESCRIPTION
* Arranges for `function_p' to be called whenever the application exits.
* If `function_p' is NULL, deletes all previously set call-back functions.
* The actual deletion is deferred until the event handler loop processes
* the next round of events.
*
* Note that there can be zero or more exit-event call-back functions
* defined.
*---------------------------------------------------------------------------*/
utlEventHandlerId_T utlSetExitEventHandler(const utlEventHandlerPriority_T priority,
const utlEventHandlerFunction_P function_p,
void *arg_p)
{
static bool at_exit_initialized = false;
utlEventHandlerId_T handler_id;
utlEventHandlerNode_P node_p;
switch (priority)
{
case utlEVENT_HANDLER_PRIORITY_CRITICAL:
case utlEVENT_HANDLER_PRIORITY_HIGH:
case utlEVENT_HANDLER_PRIORITY_MEDIUM:
case utlEVENT_HANDLER_PRIORITY_LOW: break;
default:
return utlFAILED;
}
utlAssert(function_p != NULL);
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- setup exit handling (if not yet setup) ---*/
if (at_exit_initialized != true)
{
if (atexit(_utlEventHandlerExit) != 0)
{
utlError(utlSetExitEventHandler, "Call to %s() failed, %s", "atexit", strerror(errno));
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
at_exit_initialized = true;
}
if ((handler_id = _utlFindUnusedHandlerId()) == (utlEventHandlerId_T)utlFAILED)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
if ((node_p = (utlEventHandlerNode_P)utlMalloc(sizeof(utlEventHandlerNode_T))) == NULL)
{
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- initialize the new event handler node ---*/
node_p->next_p = NULL;
node_p->type = utlEVENT_HANDLER_TYPE_EXIT;
node_p->priority = priority;
node_p->enable = true;
node_p->handler_id = handler_id;
node_p->u.exit = 0;
node_p->call_back.function_p = function_p;
node_p->call_back.arg_p = arg_p;
/*--- add to list of known exit handlers ---*/
(void)utlPutTailNode(&exit_handlers, utlEventHandlerNode_T, node_p);
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlSetExitEventHandler1, "added `exit' event handler (HID=%u)\n",
(unsigned int)node_p->handler_id);
);
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return handler_id;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlExitEventLoop()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Arranges for the event handler loop to exit the next time it attempts
* to process events.
*---------------------------------------------------------------------------*/
void utlExitEventLoop(void)
{
running = false;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlEventLoop(block)
* INPUT
* block == block, handling events? (vs only polling once, then returning)
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* The main event handler loop.
*
* Note that if an event handler function returns utlFAILED, this function
* will exit and return utlFAILED.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlEventLoop(const bool block)
{
running = true;
/*--- mutex initialization ---*/
if (!utlIsSemaphoreInitialized(&events_semaphore))
{
if (utlInitSemaphore(&events_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS)
return utlFAILED;
}
for (;; )
{
fd_set read_fds;
fd_set write_fds;
fd_set except_fds;
int highest_fd;
utlEventHandlerNode_P node_p;
int select_rv;
utlEventHandlerType_T event_type;
utlEventHandlerPriority_T priority;
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*-- were any handlers deleted? ---*/
if (deleted_pending != utlDELETED_NONE)
{
utlEventHandlerNode_P prev_p;
/*--- clean-out deleted fd handlers ---*/
prev_p = NULL;
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; )
{
/*--- delete node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
{
utlEventHandlerNode_P deleted_p;
if ((deleted_p = utlGetNode(&fd_handlers, utlEventHandlerNode_T, prev_p, node_p)) != NULL)
{
utlFree(deleted_p);
}
/*--- advance to node following deleted node ---*/
if (prev_p == NULL) node_p = (utlEventHandlerNode_P)fd_handlers.head_p;
else node_p = prev_p->next_p;
continue;
}
/*--- advance to next node ---*/
prev_p = node_p;
node_p = node_p->next_p;
}
/*--- clean-out deleted idle handlers ---*/
prev_p = NULL;
for (node_p = (utlEventHandlerNode_P)idle_handlers.head_p; node_p != NULL; )
{
/*--- delete node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
{
utlEventHandlerNode_P deleted_p;
if ((deleted_p = utlGetNode(&idle_handlers, utlEventHandlerNode_T, prev_p, node_p)) != NULL)
{
utlFree(deleted_p);
}
/*--- advance to node following deleted node ---*/
if (prev_p == NULL) node_p = (utlEventHandlerNode_P)idle_handlers.head_p;
else node_p = prev_p->next_p;
continue;
}
/*--- advance to next node ---*/
prev_p = node_p;
node_p = node_p->next_p;
}
/*--- clean-out deleted signal handlers ---*/
prev_p = NULL;
for (node_p = (utlEventHandlerNode_P)signal_handlers.head_p; node_p != NULL; )
{
/*--- delete node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
{
utlEventHandlerNode_P deleted_p;
if ((deleted_p = utlGetNode(&signal_handlers, utlEventHandlerNode_T, prev_p, node_p)) != NULL)
{
utlFree(deleted_p);
}
/*--- advance to node following deleted node ---*/
if (prev_p == NULL) node_p = (utlEventHandlerNode_P)signal_handlers.head_p;
else node_p = prev_p->next_p;
continue;
}
/*--- advance to next node ---*/
prev_p = node_p;
node_p = node_p->next_p;
}
/*--- clean-out deleted exit handlers ---*/
prev_p = NULL;
for (node_p = (utlEventHandlerNode_P)exit_handlers.head_p; node_p != NULL; )
{
/*--- delete node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
{
utlEventHandlerNode_P deleted_p;
if ((deleted_p = utlGetNode(&exit_handlers, utlEventHandlerNode_T, prev_p, node_p)) != NULL)
{
utlFree(deleted_p);
}
/*--- advance to node following deleted node ---*/
if (prev_p == NULL) node_p = (utlEventHandlerNode_P)exit_handlers.head_p;
else node_p = prev_p->next_p;
continue;
}
/*--- advance to next node ---*/
prev_p = node_p;
node_p = node_p->next_p;
}
deleted_pending = 0;
}
/*--- initialize fd sets ---*/
highest_fd = 0;
FD_ZERO( &read_fds);
FD_ZERO( &write_fds);
FD_ZERO(&except_fds);
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
if (node_p->enable != true)
continue;
if (highest_fd < node_p->u.fd)
highest_fd = node_p->u.fd;
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_READ: FD_SET(node_p->u.fd, &read_fds); break;
case utlEVENT_HANDLER_TYPE_WRITE: FD_SET(node_p->u.fd, &write_fds); break;
case utlEVENT_HANDLER_TYPE_READ_WRITE: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &write_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION: FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: FD_SET(node_p->u.fd, &write_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &write_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: break;
default:
utlError(utlEventLoop, "Default case reached with %d", node_p->type);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
}
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- should we just poll for any events... ---*/
if (block == false)
{
/*--- unblocked check for fd events ---*/
for (;; )
{
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if ((select_rv = select(highest_fd + 1, &read_fds, &write_fds, &except_fds, &timeout)) >= 0)
break;
if (errno != EINTR)
{
utlError(utlEventLoop1, "Call to %s() failed, %s", "select", strerror(errno));
return utlFAILED;
}
}
/*--- else handle only fd events (since no idle-time-out event handlers are defined)... ---*/
}
else if (utlIsListEmpty(idle_handlers))
{
/*--- block waiting for fd events ---*/
for (;; )
{
if ((select_rv = select(highest_fd + 1, &read_fds, &write_fds, &except_fds, NULL)) >= 0)
break;
if (errno != EINTR)
{
utlError(utlEventLoop2, "Call to %s() failed, %s", "select", strerror(errno));
return utlFAILED;
}
}
/*--- else handle both fd and idle-time-out events... ---*/
}
else
{
struct timespec elapsed;
elapsed.tv_sec = 0;
elapsed.tv_nsec = 0;
select_rv = 0;
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- for each idle handler... ---*/
for (node_p = (utlEventHandlerNode_P)idle_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
struct timespec period;
struct timeval timeout;
/*--- skip node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
continue;
utlAssert(node_p->type == utlEVENT_HANDLER_TYPE_IDLE);
/*--- convert idle period to a timeval struct ---*/
period.tv_sec = node_p->u.period.seconds;
period.tv_nsec = node_p->u.period.nanoseconds;
/*--- compute next expiry time ---*/
timeout.tv_sec = period.tv_sec - elapsed.tv_sec;
if (period.tv_nsec < elapsed.tv_nsec)
{
timeout.tv_sec -= 1;
timeout.tv_usec = (period.tv_nsec + 1000000000 - elapsed.tv_nsec)/1000;
}
else
timeout.tv_usec = (period.tv_nsec - elapsed.tv_nsec)/1000;
/*--- advance elapsed idle-time ---*/
elapsed = period;
/*--- release exclusive-access, there is the possibility that new
idle handlers will get added to the list while we're blocked
on the following select(), but we'll just ignore them if
their timeoout is smaller than the current timeout, and
process them if the timeout is greater */
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- wait for either fd or idle-time-out events ---*/
for (;; )
{
if ((select_rv = select(highest_fd + 1, &read_fds, &write_fds, &except_fds, &timeout)) >= 0)
break;
if (errno != EINTR)
{
utlError(utlEventLoop3, "Call to %s() failed, %s", "select", strerror(errno));
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
}
/*--- re-acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- if an fd-event occurred, exit this idle-time-out loop ---*/
if (select_rv > 0)
break;
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop4, "`idle' event after %d.%ld secs for handler 0x%x\n",
node_p->u.period.seconds,
node_p->u.period.nanoseconds,
(unsigned int)node_p->call_back.function_p);
);
/*--- if we get to here, an idle-time-out event occurred, so invoke the idle handler ---*/
if ((*(node_p->call_back.function_p))(node_p->type, utlEVENT_HANDLER_TYPE_IDLE, -1, &(node_p->u.period), node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop5, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- if we were asked to exit the event loop ---*/
if (running == false)
{
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*--- re-initialize fd sets ---*/
{
utlEventHandlerNode_P node_p;
highest_fd = 0;
FD_ZERO( &read_fds);
FD_ZERO( &write_fds);
FD_ZERO(&except_fds);
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
if (node_p->enable != true)
continue;
if (highest_fd < node_p->u.fd)
highest_fd = node_p->u.fd;
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_READ: FD_SET(node_p->u.fd, &read_fds); break;
case utlEVENT_HANDLER_TYPE_WRITE: FD_SET(node_p->u.fd, &write_fds); break;
case utlEVENT_HANDLER_TYPE_READ_WRITE: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &write_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION: FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: FD_SET(node_p->u.fd, &write_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: FD_SET(node_p->u.fd, &read_fds);
FD_SET(node_p->u.fd, &write_fds);
FD_SET(node_p->u.fd, &except_fds); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: break;
default:
utlError(utlEventLoop6, "Default case reached with %d", node_p->type);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
}
}
}
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- no more idle handlers left to process? ---*/
if (select_rv == 0)
{
/*--- wait only for fd events ---*/
for (;; )
{
if ((select_rv = select(highest_fd + 1, &read_fds, &write_fds, &except_fds, NULL)) >= 0)
break;
if (errno != EINTR)
{
utlError(utlEventLoop7, "Call to %s() failed, %s", "select", strerror(errno));
return utlFAILED;
}
}
}
}
/*--- acquire exclusive-access ---*/
if (utlAcquireExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- implement round-robin fd event handling ---*/
{
utlEventHandlerNode_P node_p;
if ((node_p = utlGetHeadNode(&fd_handlers, utlEventHandlerNode_T)) != NULL)
(void)utlPutTailNode(&fd_handlers, utlEventHandlerNode_T, node_p);
}
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop8, "fd event on %d fd(s)\n", select_rv);
);
/*--- for each possible priority level... ---*/
for (priority = utlEVENT_HANDLER_PRIORITY_INTERNAL; priority >= utlEVENT_HANDLER_PRIORITY_LOW; priority--)
{
/*--- for each fd handler... ---*/
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
/*--- wrong priority? ---*/
if (node_p->priority != priority)
continue;
/*--- skip node if it is a zombie ---*/
if (node_p->type == utlEVENT_HANDLER_TYPE_ZOMBIE)
continue;
/*--- invoke call-back function if select knew about this 'fd' and there was activity on it... */
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_READ:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &read_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop9, "`read' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
event_type = utlEVENT_HANDLER_TYPE_READ;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop10, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_WRITE:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &write_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop11, "`write' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
event_type = utlEVENT_HANDLER_TYPE_WRITE;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop12, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_READ_WRITE:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &read_fds) ||
FD_ISSET(node_p->u.fd, &write_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop13, "`read/write' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
if (FD_ISSET(node_p->u.fd, &read_fds)) event_type = utlEVENT_HANDLER_TYPE_READ;
else event_type = utlEVENT_HANDLER_TYPE_WRITE;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop14, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_EXCEPTION:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &except_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop15, "`exception' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
event_type = utlEVENT_HANDLER_TYPE_EXCEPTION;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop16, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &read_fds) ||
FD_ISSET(node_p->u.fd, &except_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop17, "`exception/read' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
if (FD_ISSET(node_p->u.fd, &read_fds)) event_type = utlEVENT_HANDLER_TYPE_READ;
else event_type = utlEVENT_HANDLER_TYPE_EXCEPTION;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop18, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &write_fds) ||
FD_ISSET(node_p->u.fd, &except_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop19, "`exception/write' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
if (FD_ISSET(node_p->u.fd, &write_fds)) event_type = utlEVENT_HANDLER_TYPE_WRITE;
else event_type = utlEVENT_HANDLER_TYPE_EXCEPTION;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop20, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE:
if ((node_p->u.fd <= highest_fd) && (FD_ISSET(node_p->u.fd, &read_fds) ||
FD_ISSET(node_p->u.fd, &write_fds) ||
FD_ISSET(node_p->u.fd, &except_fds)))
{
utlTrace(utlTRACE_EVENT_HANDLER,
utlPrintTrace(utlEventLoop21, "`exception/read/write' event on fd %d for handler 0x%x\n",
node_p->u.fd,
(unsigned int)node_p->call_back.function_p);
);
if (FD_ISSET(node_p->u.fd, &read_fds)) event_type = utlEVENT_HANDLER_TYPE_READ;
else if (FD_ISSET(node_p->u.fd, &write_fds)) event_type = utlEVENT_HANDLER_TYPE_WRITE;
else event_type = utlEVENT_HANDLER_TYPE_EXCEPTION;
if ((*(node_p->call_back.function_p))(node_p->type, event_type, node_p->u.fd, NULL, node_p->call_back.arg_p) != utlSUCCESS)
{
utlError(utlEventLoop22, "Callback function 0x%lx failed", node_p->call_back.function_p);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
select_rv--;
}
break;
default:
utlError(utlEventLoop23, "Default case reached with %d", node_p->type);
/*--- clean ---*/
(void)utlReleaseExclusiveAccess(&events_semaphore);
return utlFAILED;
}
/*--- have all current 'select()' events been handled? ---*/
if (select_rv == 0)
break;
}
}
/*--- release exclusive-access ---*/
if (utlReleaseExclusiveAccess(&events_semaphore) != utlSUCCESS)
return utlFAILED;
/*--- if we were asked to exit the event loop ---*/
if (running == false)
return utlSUCCESS;
/*--- if we were not asked to block, we're done! ---*/
if (block == false)
return utlSUCCESS;
}
/*NOTREACHED*/
utlError(utlEventLoop24, "Not reached error");
return utlFAILED;
}
#if defined(utlDEBUG) || defined(utlTEST)
/*---------------------------------------------------------------------------*
* FUNCTION
* utlVStringDumpEventHandlers(v_string_p, prefix_p)
* INPUT
* v_string_p == pointer to an initialized utlVString_T structure
* prefix_p == pointer to a prefix string
* OUTPUT
* *v_string_p == the updated utlVString_T structure
* RETURNS
* nothing
* DESCRIPTION
* Dumps the event handlers to `v_string_p'.
*----------------------------------------------------------------------*/
void utlVStringDumpEventHandlers(const utlVString_P v_string_p,
const char *prefix_p)
{
utlEventHandlerNode_P node_p;
utlAssert(v_string_p != NULL);
utlAssert(prefix_p != NULL);
(void)utlVStringPrintF(v_string_p, "%s: Registered Event Handlers:\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: | | | | Call Back Function\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Type | fd | Period (seconds) | Event HID | Function | Argument\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: -----------+------+-------------------+-----------+------------+------------\n", prefix_p);
for (node_p = (utlEventHandlerNode_P)fd_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_READ: (void)utlVStringPrintF(v_string_p, "%s: Read |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_WRITE: (void)utlVStringPrintF(v_string_p, "%s: Write |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_READ_WRITE: (void)utlVStringPrintF(v_string_p, "%s: Read/Write |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION: (void)utlVStringPrintF(v_string_p, "%s: Execption |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ: (void)utlVStringPrintF(v_string_p, "%s: Excep/Read |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_WRITE: (void)utlVStringPrintF(v_string_p, "%s: Excep/Write|", prefix_p); break;
case utlEVENT_HANDLER_TYPE_EXCEPTION_READ_WRITE: (void)utlVStringPrintF(v_string_p, "%s: Ex/Rea/Writ|", prefix_p); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: (void)utlVStringPrintF(v_string_p, "%s: Zombie |", prefix_p); break;
default: (void)utlVStringPrintF(v_string_p, "%s: %8d |", prefix_p, node_p->type); break;
}
(void)utlVStringPrintF(v_string_p, " %4d |", node_p->u.fd);
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, " %9d |", node_p->handler_id);
if (node_p->call_back.function_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x |", node_p->call_back.function_p);
else (void)utlVStringPrintF(v_string_p, " <NULL> |");
if (node_p->call_back.arg_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x\n", node_p->call_back.arg_p);
else (void)utlVStringPrintF(v_string_p, " <NULL>\n");
}
for (node_p = (utlEventHandlerNode_P)idle_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_IDLE: (void)utlVStringPrintF(v_string_p, "%s: Idle |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: (void)utlVStringPrintF(v_string_p, "%s: Zombie |", prefix_p); break;
default: (void)utlVStringPrintF(v_string_p, "%s: %4d |", prefix_p, node_p->type); break;
}
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, "%8ld.%09d |", node_p->u.period.seconds, node_p->u.period.nanoseconds);
(void)utlVStringPrintF(v_string_p, " %9d |", node_p->handler_id);
if (node_p->call_back.function_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x |", node_p->call_back.function_p);
else (void)utlVStringPrintF(v_string_p, " <NULL> |");
if (node_p->call_back.arg_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x\n", node_p->call_back.arg_p);
else (void)utlVStringPrintF(v_string_p, " <NULL>\n");
}
for (node_p = (utlEventHandlerNode_P)signal_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_SIGHUP: (void)utlVStringPrintF(v_string_p, "%s: SIGHUP |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGINT: (void)utlVStringPrintF(v_string_p, "%s: SIGINT |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGQUIT: (void)utlVStringPrintF(v_string_p, "%s: SIGQUIT |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGILL: (void)utlVStringPrintF(v_string_p, "%s: SIGILL |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGTRAP: (void)utlVStringPrintF(v_string_p, "%s: SIGTRAP |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGABRT: (void)utlVStringPrintF(v_string_p, "%s: SIGABRT |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGFPE: (void)utlVStringPrintF(v_string_p, "%s: SIGFPE |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGKILL: (void)utlVStringPrintF(v_string_p, "%s: SIGKILL |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGBUS: (void)utlVStringPrintF(v_string_p, "%s: SIGBUS |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGSEGV: (void)utlVStringPrintF(v_string_p, "%s: SIGSEGV |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGSYS: (void)utlVStringPrintF(v_string_p, "%s: SIGSYS |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGPIPE: (void)utlVStringPrintF(v_string_p, "%s: SIGPIPE |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGALRM: (void)utlVStringPrintF(v_string_p, "%s: SIGALRM |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGTERM: (void)utlVStringPrintF(v_string_p, "%s: SIGTERM |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGURG: (void)utlVStringPrintF(v_string_p, "%s: SIGURG |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGSTOP: (void)utlVStringPrintF(v_string_p, "%s: SIGSTOP |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGTSTP: (void)utlVStringPrintF(v_string_p, "%s: SIGTSTP |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGCONT: (void)utlVStringPrintF(v_string_p, "%s: SIGCONT |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGCHLD: (void)utlVStringPrintF(v_string_p, "%s: SIGCHLD |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGTTIN: (void)utlVStringPrintF(v_string_p, "%s: SIGTTIN |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGTTOU: (void)utlVStringPrintF(v_string_p, "%s: SIGTTOU |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGIO: (void)utlVStringPrintF(v_string_p, "%s: SIGIO |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGXCPU: (void)utlVStringPrintF(v_string_p, "%s: SIGXCPU |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGXFSZ: (void)utlVStringPrintF(v_string_p, "%s: SIGXFSZ |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGVTALRM: (void)utlVStringPrintF(v_string_p, "%s: SIGVTALRM |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGPROF: (void)utlVStringPrintF(v_string_p, "%s: SIGPROF |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGWINCH: (void)utlVStringPrintF(v_string_p, "%s: SIGWINCH |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGUSR1: (void)utlVStringPrintF(v_string_p, "%s: SIGUSR1 |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_SIGUSR2: (void)utlVStringPrintF(v_string_p, "%s: SIGUSR2 |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: (void)utlVStringPrintF(v_string_p, "%s: Zombie |", prefix_p); break;
/* case utlEVENT_HANDLER_TYPE_SIGPOLL: (void) utlVStringPrintF(v_string_p, "%s: SIGPOLL |", prefix_p); break;*/
/* case utlEVENT_HANDLER_TYPE_SIGIOT: (void) utlVStringPrintF(v_string_p, "%s: SIGIOT |", prefix_p); break;*/
/* case utlEVENT_HANDLER_TYPE_SIGSTKFLT: (void) utlVStringPrintF(v_string_p, "%s: SIGSTKFLT |", prefix_p); break;*/
/* case utlEVENT_HANDLER_TYPE_SIGCLD: (void) utlVStringPrintF(v_string_p, "%s: SIGCLD |", prefix_p); break;*/
/* case utlEVENT_HANDLER_TYPE_SIGPWR: (void) utlVStringPrintF(v_string_p, "%s: SIGPWR |", prefix_p); break;*/
/* case utlEVENT_HANDLER_TYPE_SIGUNUSED: (void) utlVStringPrintF(v_string_p, "%s: SIGUNUSED |", prefix_p); break;*/
default: (void)utlVStringPrintF(v_string_p, "%s: %4d |", prefix_p, node_p->type); break;
}
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, " %9d |", node_p->handler_id);
if (node_p->call_back.function_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x |", node_p->call_back.function_p);
else (void)utlVStringPrintF(v_string_p, " <NULL> |");
if (node_p->call_back.arg_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x\n", node_p->call_back.arg_p);
else (void)utlVStringPrintF(v_string_p, " <NULL>\n");
}
for (node_p = (utlEventHandlerNode_P)exit_handlers.head_p; node_p != NULL; node_p = node_p->next_p)
{
switch (node_p->type)
{
case utlEVENT_HANDLER_TYPE_EXIT: (void)utlVStringPrintF(v_string_p, "%s: Exit |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_DIRECT_FUNC_CALL: (void)utlVStringPrintF(v_string_p, "%s: Func Call |", prefix_p); break;
case utlEVENT_HANDLER_TYPE_ZOMBIE: (void)utlVStringPrintF(v_string_p, "%s: Zombie |", prefix_p); break;
default: (void)utlVStringPrintF(v_string_p, "%s: %4d |", prefix_p, node_p->type); break;
}
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, " |");
(void)utlVStringPrintF(v_string_p, " %9d |", node_p->handler_id);
if (node_p->call_back.function_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x |", node_p->call_back.function_p);
else (void)utlVStringPrintF(v_string_p, " <NULL>\n");
if (node_p->call_back.arg_p != NULL) (void)utlVStringPrintF(v_string_p, " 0x%08x |", node_p->call_back.arg_p);
else (void)utlVStringPrintF(v_string_p, " <NULL>\n");
}
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlDumpEventHandlers(file_p)
* INPUT
* file_p == pointer to an open file
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Dumps the event handlers to `file_p'.
*----------------------------------------------------------------------*/
void utlDumpEventHandlers(FILE *file_p)
{
utlVString_T v_string;
utlAssert(file_p != NULL);
utlInitVString(&v_string);
utlVStringDumpEventHandlers(&v_string, "EventHandlers");
utlVStringPuts(&v_string, file_p);
utlVStringFree(&v_string);
}
#endif /* defined(utlDEBUG) || defined(utlTEST) */
#ifdef utlTEST
/*----------------------------------------------------------------------*/
typedef struct {
enum {
utlMODE_NULL,
utlMODE_EXIT_EVENT_HANDLER,
} mode;
size_t num_cycles;
size_t curr_cycle;
double last;
double period;
} testInfo_T;
/*----------------------------------------------------------------------*
* FUNCTION
* testFunc(id, timeout_count, arg, curr_time)
* INPUT
* id == timer ID
* timeout_count == the time-out count
* arg_p == pointer to user-defined data
* curr_time_p == current time since the epoch
* OUTPUT
* modifies the data pointed to by `arg_p'
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCIPTION
* Handles timer time outs.
*----------------------------------------------------------------------*/
static utlReturnCode_T testFunc(const utlTimerId_T id,
const unsigned long timeout_count,
void *arg_p,
const utlAbsoluteTime_P2c curr_time_p)
{
testInfo_T *test_info_p;
double delta;
utlAssert(arg_p != NULL);
utlAssert(curr_time_p != NULL);
test_info_p = (testInfo_T *)arg_p;
test_info_p->curr_cycle++;
delta = curr_time_p->seconds + (curr_time_p->nanoseconds / 1000000000.0) - test_info_p->last;
test_info_p->last = curr_time_p->seconds + (curr_time_p->nanoseconds / 1000000000.0);
/*--- time-out time OK? ---*/
if ((test_info_p->curr_cycle > (size_t)1) && (fabs(test_info_p->period - delta) > 0.15))
fprintf(stderr, "eventHandlerTest: warning, timing error = %f\n", fabs(test_info_p->period - delta));
if (test_info_p->curr_cycle == test_info_p->num_cycles)
{
switch (test_info_p->mode)
{
case utlMODE_NULL:
break;
case utlMODE_EXIT_EVENT_HANDLER:
utlExitEventLoop();
break;
}
}
return utlSUCCESS;
}
/*----------------------------------------------------------------------*
* FUNCTION
* idleFunc(handler_type, event_type, fd, period, arg)
* INPUT
* handler_type == handler type
* event_type == event type
* fd == which file descriptor
* period_p == idle period
* arg_p == pointer to user-defined data
* OUTPUT
* modifies the data pointed to by `arg_p'
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCIPTION
* Handles idle-time-out events.
*----------------------------------------------------------------------*/
static utlReturnCode_T idleFunc(const utlEventHandlerType_T handler_type,
const utlEventHandlerType_T event_type,
const int fd,
const utlRelativeTime_P2c period_p,
void *arg_p)
{
testInfo_T *test_info_p;
double n;
utlAssert(arg_p != NULL);
utlAssert(fd == -1);
utlAssert(period_p != NULL);
test_info_p = (testInfo_T *)arg_p;
test_info_p->curr_cycle++;
n = period_p->seconds +
(period_p->nanoseconds / 1000000000.0);
/*--- passed-in period OK? ---*/
if (fabs(test_info_p->period - n) > 0.0001)
return utlFAILED;
return utlSUCCESS;
}
/*----------------------------------------------------------------------*
* FUNCTION
* fdFunc(handler_type, event_type, fd, period, arg)
* INPUT
* handler_type == handler type
* event_type == event type
* fd == which file descriptor
* period_p == idle period
* arg_p == pointer to user-defined data
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCIPTION
* Currently does nothing.
*----------------------------------------------------------------------*/
static utlReturnCode_T fdFunc(const utlEventHandlerType_T handler_type,
const utlEventHandlerType_T event_type,
const int fd,
const utlRelativeTime_P2c period_p,
void *arg_p)
{
return utlSUCCESS;
}
/*----------------------------------------------------------------------*
* FUNCTION
* signalFunc(handler_type, event_type, fd, period_p, arg_p)
* INPUT
* handler_type == handler type
* event_type == event type
* fd == which file descriptor
* period_p == idle period
* arg_p == pointer to user-defined data
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCIPTION
* Prints a message.
*----------------------------------------------------------------------*/
static utlReturnCode_T signalFunc(const utlEventHandlerType_T handler_type,
const utlEventHandlerType_T event_type,
const int fd,
const utlRelativeTime_P2c period_p,
void *arg_p)
{
(void)fprintf(stderr, "EventHandlerTest: signal %s event\n", _utlSignalToString(event_type));
return utlSUCCESS;
}
/*----------------------------------------------------------------------*
* FUNCTION
* eventHandlerTest()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* "true" for pass, "false" for failure
*----------------------------------------------------------------------*/
utlReturnCode_T eventHandlerTest()
{
utlRelativeTime_T period;
/*--- Hook up some signal handlers ---*/
utlSetSignalEventHandler(SIGHUP, utlEVENT_HANDLER_PRIORITY_MEDIUM, signalFunc, NULL);
utlSetSignalEventHandler(SIGQUIT, utlEVENT_HANDLER_PRIORITY_MEDIUM, signalFunc, NULL);
/*--- an idle event loop ---*/
{
testInfo_T exit_test_info;
utlTimerId_T exit_timer_id;
/* exit timer */
period.seconds = 7;
period.nanoseconds = 0;
exit_test_info.mode = utlMODE_EXIT_EVENT_HANDLER;
exit_test_info.num_cycles = 1;
exit_test_info.curr_cycle = 0;
exit_test_info.last = 0.0;
exit_test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((exit_timer_id = utlStartTimer(&period, exit_test_info.num_cycles, &testFunc, &exit_test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(1.0)\n");
return false;
}
if (utlEventLoop(true) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(1.3)\n");
return false;
}
}
/*--- test "read" events using timers ---*/
{
testInfo_T exit_test_info;
utlTimerId_T exit_timer_id;
utlTimerId_T timer_id;
testInfo_T test_info;
/* exit timer */
period.seconds = 7;
period.nanoseconds = 0;
exit_test_info.mode = utlMODE_EXIT_EVENT_HANDLER;
exit_test_info.num_cycles = 1;
exit_test_info.curr_cycle = 0;
exit_test_info.last = 0.0;
exit_test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((exit_timer_id = utlStartTimer(&period, exit_test_info.num_cycles, &testFunc, &exit_test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(2.0)\n");
return false;
}
/* timer to generate read events */
period.seconds = 1;
period.nanoseconds = 0;
test_info.mode = utlMODE_NULL;
test_info.num_cycles = 3;
test_info.curr_cycle = 0;
test_info.last = 0.0;
test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((timer_id = utlStartTimer(&period, test_info.num_cycles, &testFunc, &test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(2.2)\n");
return false;
}
if (utlEventLoop(true) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(2.3)\n");
return false;
}
if ((test_info.num_cycles != 3) ||
(test_info.curr_cycle != 3))
{
fprintf(stderr, "eventHandlerTest: invalid timer data(2.4)\n");
return false;
}
}
/*--- test a single idle event ---*/
{
testInfo_T exit_test_info;
utlTimerId_T exit_timer_id;
utlEventHandlerId_T handler_id;
testInfo_T test_info;
/* exit timer */
period.seconds = 7;
period.nanoseconds = 0;
exit_test_info.mode = utlMODE_EXIT_EVENT_HANDLER;
exit_test_info.num_cycles = 1;
exit_test_info.curr_cycle = 0;
exit_test_info.last = 0.0;
exit_test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((exit_timer_id = utlStartTimer(&period, exit_test_info.num_cycles, &testFunc, &exit_test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(3.0)\n");
return false;
}
/* idle handler */
period.seconds = 1;
period.nanoseconds = 0;
test_info.mode = utlMODE_NULL;
test_info.num_cycles = 0;
test_info.curr_cycle = 0;
test_info.last = 0.0;
test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((handler_id = utlSetIdleEventHandler(&period, utlEVENT_HANDLER_PRIORITY_MEDIUM, idleFunc, &test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(3.1)\n");
return false;
}
if (utlEventLoop(true) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(3.2)\n");
return false;
}
if ((test_info.num_cycles != 0) ||
(test_info.curr_cycle != 1))
{
fprintf(stderr, "eventHandlerTest: invalid timer data(3.3)\n");
return false;
}
/* delete idle event handler */
if (utlDeleteEventHandler(handler_id) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(3.5)\n");
return false;
}
}
/*--- test multiple idle events ---*/
{
testInfo_T exit_test_info;
utlTimerId_T exit_timer_id;
utlEventHandlerId_T handler_id[10];
size_t i;
testInfo_T test_info[10];
/* exit timer */
period.seconds = 7;
period.nanoseconds = 0;
exit_test_info.mode = utlMODE_EXIT_EVENT_HANDLER;
exit_test_info.num_cycles = 1;
exit_test_info.curr_cycle = 0;
exit_test_info.last = 0.0;
exit_test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((exit_timer_id = utlStartTimer(&period, exit_test_info.num_cycles, &testFunc, &exit_test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(4.0)\n");
return false;
}
for (i = 0; i < 10; i++)
{
/* idle handlers */
switch (i)
{
case 0: period.seconds = 0; period.nanoseconds = 500000000; break;
case 1: period.seconds = 1; period.nanoseconds = 0; break;
case 2: period.seconds = 1; period.nanoseconds = 500000000; break;
case 3: period.seconds = 2; period.nanoseconds = 0; break;
case 4: period.seconds = 2; period.nanoseconds = 500000000; break;
case 5: period.seconds = 3; period.nanoseconds = 0; break;
case 6: period.seconds = 3; period.nanoseconds = 500000000; break;
case 7: period.seconds = 4; period.nanoseconds = 0; break;
case 8: period.seconds = 4; period.nanoseconds = 500000000; break;
case 9: period.seconds = 5; period.nanoseconds = 0; break;
}
test_info[i].mode = utlMODE_NULL;
test_info[i].num_cycles = 0;
test_info[i].curr_cycle = 0;
test_info[i].last = 0.0;
test_info[i].period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((handler_id[i] = utlSetIdleEventHandler(&period, utlEVENT_HANDLER_PRIORITY_MEDIUM, idleFunc, test_info + i)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(4.1)\n");
return false;
}
}
if (utlEventLoop(true) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(4.2)\n");
return false;
}
for (i = 0; i < 10; i++)
{
if ((test_info[i].num_cycles != 0) ||
(test_info[i].curr_cycle != 1))
{
fprintf(stderr, "eventHandlerTest: invalid timer data(4.3)\n");
return false;
}
}
/* delete idle event handlers */
for (i = 0; i < 10; i++)
{
/* idle handlers */
switch (i)
{
case 0: period.seconds = 0; period.nanoseconds = 500000000; break;
case 1: period.seconds = 1; period.nanoseconds = 0; break;
case 2: period.seconds = 1; period.nanoseconds = 500000000; break;
case 3: period.seconds = 2; period.nanoseconds = 0; break;
case 4: period.seconds = 2; period.nanoseconds = 500000000; break;
case 5: period.seconds = 3; period.nanoseconds = 0; break;
case 6: period.seconds = 3; period.nanoseconds = 500000000; break;
case 7: period.seconds = 4; period.nanoseconds = 0; break;
case 8: period.seconds = 4; period.nanoseconds = 500000000; break;
case 9: period.seconds = 5; period.nanoseconds = 0; break;
}
if (utlDeleteEventHandler(handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(4.4)\n");
return false;
}
}
}
/*--- test multiple mixed idle and read events ---*/
{
testInfo_T exit_test_info;
utlTimerId_T exit_timer_id;
utlEventHandlerId_T handler_id[10];
size_t i;
testInfo_T test_info[10];
utlTimerId_T timer_id;
/* exit timer */
period.seconds = 10;
period.nanoseconds = 0;
exit_test_info.mode = utlMODE_EXIT_EVENT_HANDLER;
exit_test_info.num_cycles = 1;
exit_test_info.curr_cycle = 0;
exit_test_info.last = 0.0;
exit_test_info.period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((exit_timer_id = utlStartTimer(&period, exit_test_info.num_cycles, &testFunc, &exit_test_info)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(5.0)\n");
return false;
}
/* timer to generate read events */
period.seconds = 2;
period.nanoseconds = 0;
test_info[0].mode = utlMODE_NULL;
test_info[0].num_cycles = 3;
test_info[0].curr_cycle = 0;
test_info[0].last = 0.0;
test_info[0].period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((timer_id = utlStartTimer(&period, test_info[0].num_cycles, &testFunc, test_info + 0)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlStartTimer(5.1)\n");
return false;
}
for (i = 1; i < 4; i++)
{
/* idle handlers */
switch (i)
{
case 1: period.seconds = 0; period.nanoseconds = 500000000; break;
case 2: period.seconds = 1; period.nanoseconds = 0; break;
case 3: period.seconds = 1; period.nanoseconds = 500000000; break;
}
test_info[i].mode = utlMODE_NULL;
test_info[i].num_cycles = 0;
test_info[i].curr_cycle = 0;
test_info[i].last = 0.0;
test_info[i].period = period.seconds +
(period.nanoseconds / 1000000000.0);
if ((handler_id[i] = utlSetIdleEventHandler(&period, utlEVENT_HANDLER_PRIORITY_MEDIUM, idleFunc, test_info + i)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(5.2)\n");
return false;
}
}
utlDumpEventHandlers(stdout);
if (utlEventLoop(true) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(5.3)\n");
return false;
}
for (i = 1; i < 4; i++)
{
if ((test_info[i].num_cycles != 0) ||
(test_info[i].curr_cycle != 4))
{
fprintf(stderr, "eventHandlerTest: invalid timer data(5.4)\n");
return false;
}
}
/* delete idle event handlers */
for (i = 1; i < 4; i++)
{
/* idle handlers */
switch (i)
{
case 1: period.seconds = 0; period.nanoseconds = 500000000; break;
case 2: period.seconds = 1; period.nanoseconds = 0; break;
case 3: period.seconds = 1; period.nanoseconds = 500000000; break;
}
if (utlDeleteEventHandler(handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(5.5)\n");
return false;
}
}
}
/*--- test setting up and deleting multiple event handlers ---*/
{
size_t i;
utlEventHandlerId_T idle_handler_id[10];
utlEventHandlerId_T read_handler_id[10];
utlEventHandlerId_T write_handler_id[10];
utlEventHandlerId_T exception_handler_id[10];
/* setup handlers */
for (i = 0; i < 10; i++)
{
/* idle handler */
switch (i)
{
case 0: period.seconds = 0; period.nanoseconds = 500000000; break;
case 1: period.seconds = 1; period.nanoseconds = 0; break;
case 2: period.seconds = 1; period.nanoseconds = 500000000; break;
case 3: period.seconds = 2; period.nanoseconds = 0; break;
case 4: period.seconds = 2; period.nanoseconds = 500000000; break;
case 5: period.seconds = 3; period.nanoseconds = 0; break;
case 6: period.seconds = 3; period.nanoseconds = 500000000; break;
case 7: period.seconds = 4; period.nanoseconds = 0; break;
case 8: period.seconds = 4; period.nanoseconds = 500000000; break;
case 9: period.seconds = 5; period.nanoseconds = 0; break;
}
if ((idle_handler_id[i] = utlSetIdleEventHandler(&period, utlEVENT_HANDLER_PRIORITY_MEDIUM, idleFunc, NULL)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(6.1)\n");
return false;
}
/* read handler */
if ((read_handler_id[i] = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_MEDIUM, i + 10, fdFunc, NULL)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.3)\n");
return false;
}
/* write handler */
if ((write_handler_id[i] = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_WRITE, utlEVENT_HANDLER_PRIORITY_MEDIUM, i + 10, fdFunc, NULL)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.4)\n");
return false;
}
/* exception handler */
if ((exception_handler_id[i] = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_EXCEPTION, utlEVENT_HANDLER_PRIORITY_MEDIUM, i + 10, fdFunc, NULL)) == utlFAILED)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.5)\n");
return false;
}
}
/* delete handlers */
for (i = 0; i < 10; i++)
{
/* idle handler */
switch (i)
{
case 0: period.seconds = 0; period.nanoseconds = 500000000; break;
case 1: period.seconds = 1; period.nanoseconds = 0; break;
case 2: period.seconds = 1; period.nanoseconds = 500000000; break;
case 3: period.seconds = 2; period.nanoseconds = 0; break;
case 4: period.seconds = 2; period.nanoseconds = 500000000; break;
case 5: period.seconds = 3; period.nanoseconds = 0; break;
case 6: period.seconds = 3; period.nanoseconds = 500000000; break;
case 7: period.seconds = 4; period.nanoseconds = 0; break;
case 8: period.seconds = 4; period.nanoseconds = 500000000; break;
case 9: period.seconds = 5; period.nanoseconds = 0; break;
}
if (utlDeleteEventHandler(idle_handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetIdleEventHandler(6.6)\n");
return false;
}
/* read handler */
if (utlDeleteEventHandler(read_handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.7)\n");
return false;
}
/* write handler */
if (utlDeleteEventHandler(write_handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.8)\n");
return false;
}
/* exception handler */
if (utlDeleteEventHandler(exception_handler_id[i]) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlSetFdEventHandler(6.9)\n");
return false;
}
}
if (utlEventLoop(false) != utlSUCCESS)
{
fprintf(stderr, "eventHandlerTest: utlEventLoop(6.10)\n");
return false;
}
}
return true;
}
#endif /* utlTEST */