Rebuild GEvent.
It should be faster, have less contention problems, use less memory and allow reentrancy from a callback handler.
This commit is contained in:
parent
949290c01d
commit
1d4d9b4c94
2 changed files with 82 additions and 37 deletions
|
@ -22,26 +22,40 @@
|
||||||
#define GEVENT_ASSERT(x)
|
#define GEVENT_ASSERT(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Flags in the listener structure */
|
||||||
|
#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy
|
||||||
|
#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal
|
||||||
|
#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event
|
||||||
|
|
||||||
/* This mutex protects access to our tables */
|
/* This mutex protects access to our tables */
|
||||||
static gfxMutex geventMutex;
|
static gfxMutex geventMutex;
|
||||||
|
|
||||||
/* Our table of listener/source pairs */
|
/* Our table of listener/source pairs */
|
||||||
static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS];
|
static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS];
|
||||||
|
|
||||||
|
/* Send an exit event if possible. */
|
||||||
|
/* We already have the geventMutex */
|
||||||
|
static void doExitEvent(GListener *pl) {
|
||||||
|
// Don't do the exit if someone else currently has the event lock
|
||||||
|
if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) {
|
||||||
|
pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use
|
||||||
|
pl->event.type = GEVENT_EXIT; // Set up the EXIT event
|
||||||
|
pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data)
|
||||||
|
gfxSemSignal(&pl->waitqueue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Loop through the assignment table deleting this listener/source pair. */
|
/* Loop through the assignment table deleting this listener/source pair. */
|
||||||
/* Null is treated as a wildcard. */
|
/* Null is treated as a wildcard. */
|
||||||
|
/* We already have the geventMutex */
|
||||||
static void deleteAssignments(GListener *pl, GSourceHandle gsh) {
|
static void deleteAssignments(GListener *pl, GSourceHandle gsh) {
|
||||||
GSourceListener *psl;
|
GSourceListener *psl;
|
||||||
|
|
||||||
for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
||||||
if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) {
|
if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) {
|
||||||
if (gfxSemCounter(&psl->pListener->waitqueue) < 0) {
|
doExitEvent(psl->pListener);
|
||||||
gfxSemWait(&psl->pListener->eventlock, TIME_INFINITE); // Obtain the buffer lock
|
|
||||||
psl->pListener->event.type = GEVENT_EXIT; // Set up the EXIT event
|
|
||||||
gfxSemSignal(&psl->pListener->waitqueue); // Wake up the listener
|
|
||||||
gfxSemSignal(&psl->pListener->eventlock); // Release the buffer lock
|
|
||||||
}
|
|
||||||
psl->pListener = 0;
|
psl->pListener = 0;
|
||||||
|
psl->pSource = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,9 +72,9 @@ void _geventDeinit(void)
|
||||||
|
|
||||||
void geventListenerInit(GListener *pl) {
|
void geventListenerInit(GListener *pl) {
|
||||||
gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block
|
gfxSemInit(&pl->waitqueue, 0, MAX_SEMAPHORE_COUNT); // Next wait'er will block
|
||||||
gfxSemInit(&pl->eventlock, 1, 1); // Only one thread at a time looking at the event buffer
|
|
||||||
pl->callback = 0; // No callback active
|
pl->callback = 0; // No callback active
|
||||||
pl->event.type = GEVENT_NULL; // Always safety
|
pl->event.type = GEVENT_NULL; // Always safety
|
||||||
|
pl->flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) {
|
bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) {
|
||||||
|
@ -80,9 +94,7 @@ bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) {
|
||||||
|
|
||||||
if (pl == psl->pListener && gsh == psl->pSource) {
|
if (pl == psl->pListener && gsh == psl->pSource) {
|
||||||
// Just update the flags
|
// Just update the flags
|
||||||
gfxSemWait(&pl->eventlock, TIME_INFINITE); // Safety first - just in case a source is using it
|
|
||||||
psl->listenflags = flags;
|
psl->listenflags = flags;
|
||||||
gfxSemSignal(&pl->eventlock); // Release this lock
|
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -106,33 +118,37 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh) {
|
||||||
if (pl) {
|
if (pl) {
|
||||||
gfxMutexEnter(&geventMutex);
|
gfxMutexEnter(&geventMutex);
|
||||||
deleteAssignments(pl, gsh);
|
deleteAssignments(pl, gsh);
|
||||||
if (!gsh && gfxSemCounter(&pl->waitqueue) < 0) {
|
if (!gsh)
|
||||||
gfxSemWait(&pl->eventlock, TIME_INFINITE); // Obtain the buffer lock
|
doExitEvent(pl);
|
||||||
pl->event.type = GEVENT_EXIT; // Set up the EXIT event
|
|
||||||
gfxSemSignal(&pl->waitqueue); // Wake up the listener
|
|
||||||
gfxSemSignal(&pl->eventlock); // Release the buffer lock
|
|
||||||
}
|
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GEvent *geventEventWait(GListener *pl, delaytime_t timeout) {
|
GEvent *geventEventWait(GListener *pl, delaytime_t timeout) {
|
||||||
if (pl->callback || gfxSemCounter(&pl->waitqueue) < 0)
|
gfxMutexEnter(&geventMutex);
|
||||||
|
// Don't allow waiting if we are on callbacks or if there is already a thread waiting
|
||||||
|
if (pl->callback || (pl->flags & GLISTENER_WAITING)) {
|
||||||
|
gfxMutexExit(&geventMutex);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy
|
||||||
|
pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread
|
||||||
|
gfxMutexExit(&geventMutex);
|
||||||
return gfxSemWait(&pl->waitqueue, timeout) ? &pl->event : 0;
|
return gfxSemWait(&pl->waitqueue, timeout) ? &pl->event : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void geventEventComplete(GListener *pl) {
|
||||||
|
pl->flags &= ~GLISTENER_EVENTBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
|
void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
|
||||||
if (pl) {
|
if (pl) {
|
||||||
gfxMutexEnter(&geventMutex);
|
gfxMutexEnter(&geventMutex);
|
||||||
gfxSemWait(&pl->eventlock, TIME_INFINITE); // Obtain the buffer lock
|
doExitEvent(pl);
|
||||||
pl->param = param; // Set the param
|
pl->param = param; // Set the param
|
||||||
pl->callback = fn; // Set the callback function
|
pl->callback = fn; // Set the callback function
|
||||||
if (gfxSemCounter(&pl->waitqueue) < 0) {
|
if (fn)
|
||||||
pl->event.type = GEVENT_EXIT; // Set up the EXIT event
|
pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available
|
||||||
gfxSemSignal(&pl->waitqueue); // Wake up the listener
|
|
||||||
}
|
|
||||||
gfxSemSignal(&pl->eventlock); // Release the buffer lock
|
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,14 +162,13 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las
|
||||||
|
|
||||||
gfxMutexEnter(&geventMutex);
|
gfxMutexEnter(&geventMutex);
|
||||||
|
|
||||||
// Unlock the last listener event buffer
|
// Unlock the last listener event buffer if it wasn't used.
|
||||||
if (lastlr)
|
if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE))
|
||||||
gfxSemSignal(&lastlr->pListener->eventlock);
|
lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY);
|
||||||
|
|
||||||
// Loop through the table looking for attachments to this source
|
// Loop through the table looking for attachments to this source
|
||||||
for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
|
||||||
if (gsh == psl->pSource) {
|
if (gsh == psl->pSource) {
|
||||||
gfxSemWait(&psl->pListener->eventlock, TIME_INFINITE); // Obtain a lock on the listener event buffer
|
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
return psl;
|
return psl;
|
||||||
}
|
}
|
||||||
|
@ -163,21 +178,38 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las
|
||||||
}
|
}
|
||||||
|
|
||||||
GEvent *geventGetEventBuffer(GSourceListener *psl) {
|
GEvent *geventGetEventBuffer(GSourceListener *psl) {
|
||||||
// We already know we have the event lock
|
gfxMutexEnter(&geventMutex);
|
||||||
return &psl->pListener->callback || gfxSemCounter(&psl->pListener->waitqueue) < 0 ? &psl->pListener->event : 0;
|
if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) {
|
||||||
|
// Oops - event buffer is still in use
|
||||||
|
gfxMutexExit(&geventMutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the event buffer
|
||||||
|
psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY);
|
||||||
|
gfxMutexExit(&geventMutex);
|
||||||
|
return &psl->pListener->event;
|
||||||
}
|
}
|
||||||
|
|
||||||
void geventSendEvent(GSourceListener *psl) {
|
void geventSendEvent(GSourceListener *psl) {
|
||||||
gfxMutexEnter(&geventMutex);
|
gfxMutexEnter(&geventMutex);
|
||||||
if (psl->pListener->callback) { // This test needs to be taken inside the mutex
|
if (psl->pListener->callback) {
|
||||||
|
|
||||||
|
// Mark it back as free and as sent. This is early to be marking as free but it protects
|
||||||
|
// if the callback alters the listener in any way
|
||||||
|
psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY);
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
// We already know we have the event lock
|
|
||||||
|
// Do the callback
|
||||||
psl->pListener->callback(psl->pListener->param, &psl->pListener->event);
|
psl->pListener->callback(psl->pListener->param, &psl->pListener->event);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Wake up the listener
|
// Wake up the listener
|
||||||
if (gfxSemCounter(&psl->pListener->waitqueue) <= 0)
|
if ((psl->pListener->flags & GLISTENER_WAITING)) {
|
||||||
|
psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_WAITING);
|
||||||
gfxSemSignal(&psl->pListener->waitqueue);
|
gfxSemSignal(&psl->pListener->waitqueue);
|
||||||
|
// The listener thread will free the event buffer when ready
|
||||||
|
}
|
||||||
gfxMutexExit(&geventMutex);
|
gfxMutexExit(&geventMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ typedef void (*GEventCallbackFn)(void *param, GEvent *pe);
|
||||||
// The Listener Object
|
// The Listener Object
|
||||||
typedef struct GListener {
|
typedef struct GListener {
|
||||||
gfxSem waitqueue; // Private: Semaphore for the listener to wait on.
|
gfxSem waitqueue; // Private: Semaphore for the listener to wait on.
|
||||||
gfxSem eventlock; // Private: Protect against more than one sources trying to use this event lock at the same time
|
uint16_t flags; // Private: Flags for operation
|
||||||
GEventCallbackFn callback; // Private: Call back Function
|
GEventCallbackFn callback; // Private: Call back Function
|
||||||
void *param; // Private: Parameter for the callback function.
|
void *param; // Private: Parameter for the callback function.
|
||||||
GEvent event; // Public: The event object into which the event information is stored.
|
GEvent event; // Public: The event object into which the event information is stored.
|
||||||
|
@ -149,9 +149,11 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh);
|
||||||
* timeout specifies the time to wait in system ticks.
|
* timeout specifies the time to wait in system ticks.
|
||||||
* TIME_INFINITE means no timeout - wait forever for an event.
|
* TIME_INFINITE means no timeout - wait forever for an event.
|
||||||
* TIME_IMMEDIATE means return immediately
|
* TIME_IMMEDIATE means return immediately
|
||||||
* @note The GEvent buffer is staticly allocated within the GListener so the event does not
|
* @note The returned GEvent is released when this routine is called again
|
||||||
* need to be dynamicly freed however it will get overwritten by the next call to
|
* or when optionally @p geventEventComplete() is called. Calling @p geventEventComplete()
|
||||||
* this routine.
|
* allows the GEvent object to be reused earlier which can reduce missed events. The GEvent
|
||||||
|
* object MUST NOT be used after this function is called (and is blocked waiting for the next
|
||||||
|
* event) or after geventEventComplete() is called.
|
||||||
*
|
*
|
||||||
* @param[in] pl The listener
|
* @param[in] pl The listener
|
||||||
* @param[in] timeout The timeout
|
* @param[in] timeout The timeout
|
||||||
|
@ -160,6 +162,17 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh);
|
||||||
*/
|
*/
|
||||||
GEvent *geventEventWait(GListener *pl, delaytime_t timeout);
|
GEvent *geventEventWait(GListener *pl, delaytime_t timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release the GEvent buffer associated with a listener.
|
||||||
|
* @details The GEvent returned by @p geventEventWait() is released.
|
||||||
|
* @note The GEvent pointer returned by @p geventEventWait() is released when @p geventEventWait()
|
||||||
|
* is called again or when this function is called. The GEvent
|
||||||
|
* object MUST NOT be used after this function is called.
|
||||||
|
*
|
||||||
|
* @param[in] pl The listener
|
||||||
|
*/
|
||||||
|
void geventEventComplete(GListener *pl);
|
||||||
|
|
||||||
/* @brief Register a callback for an event on a listener from an assigned source.
|
/* @brief Register a callback for an event on a listener from an assigned source.
|
||||||
* @details The type of the event should be checked (pevent->type) and then pevent should be typecast to the
|
* @details The type of the event should be checked (pevent->type) and then pevent should be typecast to the
|
||||||
* actual event type if it needs to be processed.
|
* actual event type if it needs to be processed.
|
||||||
|
|
Loading…
Add table
Reference in a new issue