From 3dd80622f4f69a611e819075e723dbed5927b008 Mon Sep 17 00:00:00 2001 From: madsenco Date: Thu, 8 Mar 2012 12:33:28 +0100 Subject: [PATCH] libuevent now compiles --- libuevent/Makefile | 42 +++++ libuevent/libuevent.c | 425 ++++++++++++++++++++++++++++++++++++++++++ libuevent/libuevent.h | 74 ++++++++ libuevent/llist.h | 150 +++++++++++++++ 4 files changed, 691 insertions(+) create mode 100644 libuevent/Makefile create mode 100644 libuevent/libuevent.c create mode 100644 libuevent/libuevent.h create mode 100644 libuevent/llist.h diff --git a/libuevent/Makefile b/libuevent/Makefile new file mode 100644 index 0000000..99c9afd --- /dev/null +++ b/libuevent/Makefile @@ -0,0 +1,42 @@ +## +# @file Makefile +# Make the micro eventlib, lua bindings and tests +# +# $Id$ +# +# (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h +## + +CROSS_COMPILE= +#CROSS_COMPILE=arm-linux-uclibc- +CC=$(CROSS_COMPILE)gcc +AR=$(CROSS_COMPILE)ar + +LDFLAGS=-s +CFLAGS=-fPIC -g -Os + +MAJOR = 1 + +all: libuevent.a libuevent.so.$(MAJOR) +lua: luauevent.so + +libuevent.a: libuevent.o + $(AR) r $@ $^ + +libuevent.so.$(MAJOR): libuevent.o + $(CC) $(LDFLAGS) -shared -o $@ $^ -Wl,-soname=libuevent + +luauevent.so: luauevent.o all + $(CC) $(LDFLAGS) -shared -o $@ $^ -L . -l uevent -Wl,-soname=uevent + +test: all + $(CC) -o test test.c && test + +luatest: lua + lua test.lua + +clean: + rm -f libuevent.o luauevent.o libuevent.so.$(MAJOR) luauevent.so test + + +.PHONY: all test clean luatest lua diff --git a/libuevent/libuevent.c b/libuevent/libuevent.c new file mode 100644 index 0000000..c2b1ff4 --- /dev/null +++ b/libuevent/libuevent.c @@ -0,0 +1,425 @@ +/** + * @file + * Micro event library libuevent. + * Asynchronous event / mainloop library. + * Not thread safe! + * The ctxt structure needs mutex protection in case of multithreaded access + * $Id:$ + * + * (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "llist.h" +#include "libuevent.h" + +static clock_t clock_tick = 0; + +/// Enumerate file handle direction +enum LUEFhDirectionT {LUE_FHIN = 0, LUE_FHOUT = 1}; + +/// File event handle +struct LUEFileEventH +{ + LListField(struct LUEFileEventH); ///< Elements for linked list + + int fd; ///< File descriptor + enum LUEFhDirectionT direction; ///< Direction: in or out + int pollIx; ///< This file handle index into list of file handles to poll + LUEFileHandler handler; ///< File handler callback + void *data; ///< File handler callback parameter +}; + +/// Timer context object +struct LUETimerH +{ + LListField(struct LUETimerH); ///< Linked list elements + + int dueTime; ///< Epoch timestamp + LUETimerHandler handler; ///< Timer callback + void *data; ///< Timer callback parameter +}; + +/** + * @cond doxygen does not like this construction + */ +typedef LList(struct LUETimerH) LUETimerHandlerDL; +typedef LList(struct LUEFileEventH) LUEFileEventDL; +/** + * @endcond + */ + +/// Main lue context object +struct LUECtxt +{ + LUEFileEventDL fileHandlers; ///< File handlers + LUETimerHandlerDL timeoutHandlers; ///< Timer handlers + + LUEFileEventDL fileRem; ///< List of file handles to be garbage collected + LUETimerHandlerDL timeRem; ///< List of timer handles to be garbage collected + + clock_t baseTime; ///< Time at last timer tick + clock_t setTime; ///< Next timer tick epoch + int timeSet; ///< True if next timer epoch is set + int exiting; ///< True if the application is terminating +}; + +static +struct LUEFileEventH *lueAddIO(struct LUECtxt * ctxt, + int fd, LUEFileHandler handler, void *data, + enum LUEFhDirectionT direction) +{ + struct LUEFileEventH *ent; + + ent = (struct LUEFileEventH *) malloc(sizeof(*ent)); + assert(ent); + ent->fd = fd; + ent->direction = direction; + ent->handler = handler; + ent->data = data; + ent->pollIx = -1; + lListAppend(&ctxt->fileHandlers, ent); + + return ent; +} + +/** + * Add an input file handler + */ +struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt, + int fd, LUEFileHandler handler, void *data) +{ + return lueAddIO(ctxt, fd, handler, data, LUE_FHIN); +} + +/** + * Add an output file handler + */ +struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt, + int fd, LUEFileHandler handler, void *data) +{ + return lueAddIO(ctxt, fd, handler, data, LUE_FHOUT); +} + +static +int lueRemIO(struct LUECtxt *ctxt, struct LUEFileEventH *hdl) +{ + struct LUEFileEventH *ent; + + lListForeachIn (ent, &ctxt->fileRem) + if (ent == hdl) + return -1; // Already removed + + hdl->handler = NULL; + lListAppend(&ctxt->fileRem, hdl); + return 0; +} + +/** + * Remove all handlers for an fd + */ +int lueRemFd(struct LUECtxt *ctxt, int fd) +{ + struct LUEFileEventH *ent, *rem; + + lListForeachIn(ent, &ctxt->fileHandlers) + { + if (ent->fd != fd) + continue; + lListForeachIn(rem, &ctxt->fileRem) if (rem == ent) break; // Already removed + if (rem) { + ent->handler = NULL; + lListAppend(&ctxt->fileRem, ent); + } + } + return 0; +} + +/** + * Remove an input file handler + */ +int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl) +{ + return lueRemIO(ctxt, hdl); +} + +/** + * Remove an output file handler + */ +int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl) +{ + return lueRemIO(ctxt, hdl); +} + + +/** + * Remove a timer event + */ +int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl) +{ + if (hdl) + { + struct LUETimerH *ent; + + lListForeachIn (ent, &ctxt->timeRem) + if (ent == hdl) + return -1; // Already removed + + lListRemove(&ctxt->timeoutHandlers, hdl); + lListAppend(&ctxt->timeRem, hdl); // Remember to free him (at a safe time) + } + return 0; +} + +/** + * Add a timer event + */ +struct LUETimerH *lueAddTimer(struct LUECtxt * ctxt, int periodMs, + LUETimerHandler handler, void *data) +{ + int timeDiff; + clock_t curTime; + struct LUETimerH *nent, + *ret, + *ent; + + nent = (struct LUETimerH *) malloc(sizeof(*ent)); + assert(nent); + nent->dueTime = periodMs; + nent->handler = handler; + nent->data = data; + + if (0 == periodMs) + { + // Zero delay is a special case: A oneshot workproc: Must go first + lListInsertFirst(&ctxt->timeoutHandlers, nent); + return nent; + + } + + // Adjust timers. + // Jiffie wraparound is OK since we only care about diff time + curTime = ctxt->timeSet ? ctxt->setTime : times(NULL); + timeDiff = (curTime - ctxt->baseTime) / clock_tick * 1000 + + ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick; + + ret = nent; + lListForeachIn(ent, &ctxt->timeoutHandlers) + { + if (ent->dueTime > timeDiff) + ent->dueTime -= timeDiff; + else + ent->dueTime = 0; + if (nent && ent->dueTime > periodMs) + { + lListInsert(&ctxt->timeoutHandlers, nent, ent); + nent = NULL; + } + } + if (nent) + lListAppend(&ctxt->timeoutHandlers, nent); + ctxt->baseTime = curTime; + + return ret; +} + +/** + * Create an application context + */ +struct LUECtxt *lueCtxtCreate() +{ + struct LUECtxt *lue; + + lue = (struct LUECtxt *) malloc(sizeof(*lue)); + assert(lue); + lListInit(&lue->fileHandlers); + lListInit(&lue->timeoutHandlers); + lListInit(&lue->fileRem); + lListInit(&lue->timeRem); + + lue->baseTime = times(NULL); + lue->setTime = 0; + lue->timeSet = false; + lue->exiting = false; + + if (0 == clock_tick) + clock_tick = sysconf(_SC_CLK_TCK); + + return lue; +} + +/** + * Destroy an application context + */ +void lueCtxtDestroy(struct LUECtxt *lue) +{ + lListPurge(&lue->fileHandlers); + lListPurge(&lue->timeoutHandlers); + free(lue); +} + +/** + * Drive the timeouts ourself (from now on until an increment of -1) + * This is intended for use in testing frameworks + * Caveat emptor: Currently, interactions with the scheduler (agent) API is undefined. + * Not for use in lues that use the agent scheduler + * @param ctxt LUElication context object + * @param mSecs Ms to advance. Use -1 to return to realtime + */ +void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs) +{ + // Obviously we are executing a callback right now (arent we always) + // The time leap is taken into account before falling back into poll() below + if (mSecs >= 0) + { + if (!ctxt->timeSet) + { + ctxt->timeSet = true; + ctxt->setTime = times(NULL) + mSecs; + } + else + { + ctxt->setTime += mSecs; + } + } + else if (mSecs == -1) + { + // Go back to realtime + ctxt->timeSet = false; + ctxt->baseTime += times(NULL) - ctxt->setTime; + ctxt->setTime = 0; + } +} + +/** + * Run the application + */ +void lueRun(struct LUECtxt *ctxt) +{ + struct LUETimerH *timer; + int pollRet; + + do + { + clock_t curTime; + int curPeriod = 0; + + // Do due timers + if (lListHead(&ctxt->timeoutHandlers)) + { + curTime = ctxt->timeSet ? ctxt->setTime : times(NULL); + curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000 + + ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick; + while((timer = lListHead(&ctxt->timeoutHandlers)) && + timer->dueTime <= curPeriod) + { + timer->handler(ctxt, timer, timer->data); + lueRemTimer(ctxt, timer); + + // baseTime changes for every lueAddTime + curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000 + + ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick; + } + lListPurge(&ctxt->timeRem); + } + + // Calculate waiting time + if (ctxt->exiting) + curPeriod = 0; + else if ((timer = lListHead(&ctxt->timeoutHandlers)) && + timer->dueTime > curPeriod) + { + curPeriod = timer->dueTime - curPeriod; + } + else if (NULL != timer) + curPeriod = 0; + else + curPeriod = -1; + + // Do pipe handlers + pollRet = 0; + if (lListHead(&ctxt->fileHandlers)) + { + struct pollfd polls[lListLength(&ctxt->fileHandlers)]; + struct LUEFileEventH *ent; + + int ix = 0; + lListForeachIn(ent, &ctxt->fileHandlers) + { + ent->pollIx = ix; + polls[ix].fd = ent->fd; + polls[ix].events = LUE_FHIN == ent->direction ? POLLIN : POLLOUT; + polls[ix].revents = 0; + ix++; + } + pollRet = poll(polls, ix, curPeriod); + if (pollRet < 0) + { + if (EINTR == errno) + continue; + else if (EBADF == errno) + { + lListForeachIn(ent, &ctxt->fileHandlers) + { + if (fcntl(ent->fd, F_GETFD) == -1 && EBADF == errno) + { + ent->handler(ctxt, ent, ent->fd, ent->data); + } + } + } + else + { + // Error in poll. Cannot continue + assert(false); + return; + } + } + else if (pollRet > 0) + { + lListForeachIn(ent, &ctxt->fileHandlers) + { + if (ent->handler && ent->pollIx >= 0 && polls[ent->pollIx].revents) + { + ent->handler(ctxt, ent, ent->fd, ent->data); + } + } + } + if (lListHead(&ctxt->fileRem)) + { + struct LUEFileEventH *ent; + + while (NULL != (ent = lListHead(&ctxt->fileRem))) + { + lListRemove(&ctxt->fileHandlers, ent); + free(ent); + } + } + } + else if (curPeriod > 0) + poll(NULL, 0, curPeriod); + else + break; // LUEarently were out of work! + } while (!ctxt->exiting || + // Wont exit while there is due timers or files + ((timer = lListHead(&ctxt->timeoutHandlers)) && + timer->dueTime <= 0) || pollRet > 0); + //logdebug("LUE exiting - bye"); +} + +/** + * Terminate the application + */ +void lueExit(struct LUECtxt *ctxt) +{ + ctxt->exiting = true; +} + +// vim: set ts=8 sw=3 sts=3 et: diff --git a/libuevent/libuevent.h b/libuevent/libuevent.h new file mode 100644 index 0000000..15969ed --- /dev/null +++ b/libuevent/libuevent.h @@ -0,0 +1,74 @@ +/** + * @file + * Micro event library libuevent. + * Asynchronous event / mainloop library. + * Not thread safe! + * The ctxt structure needs mutex protection in case of multithreaded access + * $Id:$ + * + * (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file + */ +#ifndef LIBUEVENT_H +#define LIBUEVENT_H + +struct LUETimerH; +struct LUEFileEventH; +struct LUECtxt; + +/** + * Typedef file handler callbacks + */ +typedef int (*LUEFileHandler)(struct LUECtxt *ctxt, + struct LUEFileEventH *handle, + int fd, + void *data); +/** + * Typedef timer handler callbacks + */ +typedef int (*LUETimerHandler)(struct LUECtxt *ctxt, + struct LUETimerH *handle, + void *data); + +struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt, + int fd, LUEFileHandler handler, void *data); +int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl); +struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt, + int fd, LUEFileHandler handler, void *data); +int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl); +int lueRemFd(struct LUECtxt *ctxt, int fd); + +struct LUETimerH *lueAddTimer(struct LUECtxt *ctxt, int periodMs, + LUETimerHandler handler, void *data); +int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl); + +struct LUECtxt *lueCtxtCreate(); +void lueCtxtDestroy(struct LUECtxt *lue); +void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs); +void lueRun(struct LUECtxt *ctxt); +void lueExit(struct LUECtxt *ctxt); + +#endif + +/****************************************************************************** +* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + diff --git a/libuevent/llist.h b/libuevent/llist.h new file mode 100644 index 0000000..e657d72 --- /dev/null +++ b/libuevent/llist.h @@ -0,0 +1,150 @@ +/** + * @file llist.h + * (Doubly) Linked lists anchored at both ends + * + * $Id$ + * + * (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file + */ + +/// Struct mix-in. Use this inside your struct declaration +#define LListField(tn) tn * _llNext; tn * _llPrev + +/// Anchor Declaration. Use like: LList(typename) varname; +#define LList(tn) struct { tn *head; tn *tail; int numElm; } + +/// List init +#define lListInit(a) ((a)->head = (a)->tail = NULL, (a)->numElm = 0, (a)) + +/// List empty +#define lListEmpty(a) ((a)->head == NULL) + +/// List head +#define lListHead(a) ((a)->head) + +/// List tail +#define lListTail(a) ((a)->tail) + +/// List length +#define lListLength(a) ((a)->numElm) + +/// List element next +#define lListNext(e) ((e)->_llNext) + +/// List element prev +#define lListPrev(e) ((e)->_llPrev) + +/// Append element e to list e +#define lListAppend(a, e) \ +{ \ + if (lListEmpty(a)) (a)->head = (e); \ + else (a)->tail->_llNext = (e); \ + (e)->_llPrev = (a)->tail; \ + (a)->tail = (e); \ + (e)->_llNext = NULL; \ + (a)->numElm++; \ +} + +/// Insert element n before element e in list a +#define lListInsertFirst(a, n) lListInsert(a, n, (a)->head) + +/// Insert element n before element e in list a +#define lListInsert(a, n, e) \ +{ \ + if (!(e) || (a)->head == (e)) { \ + n->_llNext = (a)->head; \ + n->_llPrev = NULL; \ + (a)->head = (e); \ + } else { \ + n->_llNext = (e); \ + n->_llPrev = (e)->_llPrev; \ + (e)->_llPrev->_llNext = n; \ + } \ + if (n->_llNext) n->_llNext->_llPrev = n; \ + (a)->numElm++; \ +} + +/// Remove element e from list a. Caller must free element. +#define lListRemove(a, e) \ +{ \ + if ((a)->head) { \ + if ((e) == (a)->head) (a)->head = (e)->_llNext; \ + else (e)->_llPrev->_llNext = (e)->_llNext; \ + if ((e) == (a)->tail) (a)->tail = (e)->_llPrev; \ + else (e)->_llNext->_llPrev = (e)->_llPrev; \ + (a)->numElm--; \ + } \ +} + +/// Purge list a elements with free() +#define lListPurgeT(a, T) \ +{ \ + T p1, p2; \ + for (p1 = (a)->head; p1; ) { \ + p2 = p1; p1 = p1->_llNext; free(p2); \ + } \ + lListInit(a); \ +} + +/// Remove last element from list a +#define lListRemoveTailT(a, T) \ +{ \ + T p; \ + if ((a)->tail) { \ + p = (a)->tail; \ + (a)->tail = (a)->tail->_llPrev; \ + if ((a)->tail) (a)->tail->_llNext = NULL; \ + else (a)->head = NULL; \ + free(p); \ + (a)->numElm--; \ + } \ +} + +/// Remove first element from list a +#define lListRemoveHeadT(a, T) \ +{ \ + T p; \ + if ((a)->head) { \ + p = (a)->head; \ + (a)->head = (a)->head->_llNext; \ + if ((a)->head) (a)->head->_llPrev = NULL; \ + else (a)->tail = NULL; \ + free(p); \ + (a)->numElm--; \ + } \ +} + +// Convenience +// Only use if you have typeof() eg. gcc +#define lListPurge(a) lListPurgeT(a, typeof((a)->head)) +#define lListRemoveTail(a) lListRemoveTailT(a, typeof((a)->head)) +#define lListRemoveHead(a) lListRemoveHeadT(a, typeof((a)->head)) + +/// Traverse the list foreach e in a +#define lListForeachIn(e, a) for (e = (a)->head; e; e = e->_llNext) + + +/****************************************************************************** +* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +// vim: set sw=3 sts=3 et: