Fixes to GEVENT

This commit is contained in:
inmarket 2016-11-19 19:39:04 +10:00
parent da2735ab67
commit e0eacb846c
3 changed files with 42 additions and 52 deletions

View File

@ -23,6 +23,7 @@ FEATURE: Added high-level functions to modify image color palettes
FIX: Improving gdispDrawThickLine() FIX: Improving gdispDrawThickLine()
FEATURE: Added gdispAddFont() for adding a dynamic font to the permanent font list FEATURE: Added gdispAddFont() for adding a dynamic font to the permanent font list
FEATURE: Added gmiscHittestPoly() for checking whether a point is inside of a polygon FEATURE: Added gmiscHittestPoly() for checking whether a point is inside of a polygon
FIX: Fixed strange multi-thread issues in GEVENT
*** Release 2.6 *** *** Release 2.6 ***

View File

@ -16,10 +16,8 @@
#endif #endif
/* Flags in the listener structure */ /* Flags in the listener structure */
#define GLISTENER_EVENTBUSY 0x0001 // The event buffer is busy #define GLISTENER_WITHLISTENER 0x0001 // The listener is current using the buffer
#define GLISTENER_WAITING 0x0002 // The listener is waiting for a signal #define GLISTENER_WITHSOURCE 0x0002 // The source is currently using the buffer
#define GLISTENER_WITHSOURCE 0x0004 // The listener is being looked at by a source for a possible event
#define GLISTENER_PENDING 0x0008 // There is an event waiting ready to go without a current listener
/* This mutex protects access to our tables */ /* This mutex protects access to our tables */
static gfxMutex geventMutex; static gfxMutex geventMutex;
@ -30,11 +28,10 @@ static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS];
/* Send an exit event if possible. */ /* Send an exit event if possible. */
/* We already have the geventMutex */ /* We already have the geventMutex */
static void doExitEvent(GListener *pl) { static void doExitEvent(GListener *pl) {
// Don't do the exit if someone else currently has the event lock // Don't do the exit if someone else currently is using the buffer
if ((pl->flags & (GLISTENER_WAITING|GLISTENER_EVENTBUSY)) == GLISTENER_WAITING) { if (!(pl->flags & GLISTENER_WITHLISTENER)) {
pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is in use
pl->event.type = GEVENT_EXIT; // Set up the EXIT event pl->event.type = GEVENT_EXIT; // Set up the EXIT event
pl->flags &= ~GLISTENER_WAITING; // Wake up the listener (with data) pl->flags = GLISTENER_WITHLISTENER; // Event buffer is now in use by the listener
gfxSemSignal(&pl->waitqueue); gfxSemSignal(&pl->waitqueue);
} }
} }
@ -119,38 +116,36 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh) {
} }
GEvent *geventEventWait(GListener *pl, delaytime_t timeout) { GEvent *geventEventWait(GListener *pl, delaytime_t timeout) {
gfxMutexEnter(&geventMutex); /* NOTE:
// Don't allow waiting if we are on callbacks or if there is already a thread waiting We no longer try to protect against two threads trying to listen on the
if (pl->callback || (pl->flags & GLISTENER_WAITING)) { one listener. This was never allowed, it makes little sense to try to do so,
gfxMutexExit(&geventMutex); and the testing caused strange multi-thread windows of opportunity.
In practice it is probably safer than it used to be - the only potential
issue is that the buffer may be prematurely marked as not in use by the listener.
If the calling code can guarantee that the event buffer is free when either thread
calls the event wait - it is now safe for them to do so.
ie. it is the implicit geventEventComplete() that is the only thing that now raises
possible multi-thread issues.
*/
// Don't allow waiting if we are on callbacks
if (pl->callback)
return 0; return 0;
}
// Event buffer is not in use by the listener - this is an implicit geventEventComplete() call
pl->flags &= ~GLISTENER_WITHLISTENER;
// Check to see if there is a pending event ready for us // Wait for an event
if ((pl->flags & GLISTENER_PENDING)) { if (!gfxSemWait(&pl->waitqueue, timeout))
pl->flags &= ~GLISTENER_PENDING; // We have now got this return 0; // Timeout
pl->flags |= GLISTENER_EVENTBUSY; // Event buffer is definitely busy
gfxMutexExit(&geventMutex);
return &pl->event;
}
// No - wait for one. return &pl->event;
pl->flags &= ~GLISTENER_EVENTBUSY; // Event buffer is definitely not busy
pl->flags |= GLISTENER_WAITING; // We will now be waiting on the thread
gfxMutexExit(&geventMutex);
if (gfxSemWait(&pl->waitqueue, timeout))
return &pl->event;
// Timeout - clear the waiting flag.
// We know this is safe as any other thread will still think there is someone waiting.
gfxMutexEnter(&geventMutex);
pl->flags &= ~GLISTENER_WAITING;
gfxMutexExit(&geventMutex);
return 0;
} }
void geventEventComplete(GListener *pl) { void geventEventComplete(GListener *pl) {
pl->flags &= ~GLISTENER_EVENTBUSY; // The listener is done with the buffer
pl->flags &= ~GLISTENER_WITHLISTENER;
} }
void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) { void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
@ -160,7 +155,7 @@ void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
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 (fn) if (fn)
pl->flags &= ~GLISTENER_EVENTBUSY; // The event buffer is immediately available pl->flags &= ~GLISTENER_WITHLISTENER; // The event buffer is immediately available
gfxMutexExit(&geventMutex); gfxMutexExit(&geventMutex);
} }
} }
@ -176,7 +171,7 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las
// Unlock the last listener event buffer if it wasn't used. // Unlock the last listener event buffer if it wasn't used.
if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE)) if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE))
lastlr->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); lastlr->pListener->flags &= ~GLISTENER_WITHSOURCE;
// 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++) {
@ -191,14 +186,13 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las
GEvent *geventGetEventBuffer(GSourceListener *psl) { GEvent *geventGetEventBuffer(GSourceListener *psl) {
gfxMutexEnter(&geventMutex); gfxMutexEnter(&geventMutex);
if ((psl->pListener->flags & GLISTENER_EVENTBUSY)) { if ((psl->pListener->flags & (GLISTENER_WITHLISTENER|GLISTENER_WITHSOURCE))) {
// Oops - event buffer is still in use
gfxMutexExit(&geventMutex); gfxMutexExit(&geventMutex);
return 0; return 0;
} }
// Allocate the event buffer // Allocate the event buffer to the source
psl->pListener->flags |= (GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY); psl->pListener->flags |= GLISTENER_WITHSOURCE;
gfxMutexExit(&geventMutex); gfxMutexExit(&geventMutex);
return &psl->pListener->event; return &psl->pListener->event;
} }
@ -209,7 +203,7 @@ void geventSendEvent(GSourceListener *psl) {
// Mark it back as free and as sent. This is early to be marking as free but it protects // 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 // if the callback alters the listener in any way
psl->pListener->flags &= ~(GLISTENER_WITHSOURCE|GLISTENER_EVENTBUSY|GLISTENER_PENDING); psl->pListener->flags = 0;
gfxMutexExit(&geventMutex); gfxMutexExit(&geventMutex);
// Do the callback // Do the callback
@ -217,14 +211,8 @@ void geventSendEvent(GSourceListener *psl) {
} else { } else {
// Wake up the listener // Wake up the listener
psl->pListener->flags &= ~GLISTENER_WITHSOURCE; psl->pListener->flags = GLISTENER_WITHLISTENER;
if ((psl->pListener->flags & GLISTENER_WAITING)) { gfxSemSignal(&psl->pListener->waitqueue);
psl->pListener->flags &= ~(GLISTENER_WAITING|GLISTENER_PENDING);
gfxSemSignal(&psl->pListener->waitqueue);
} else
psl->pListener->flags |= GLISTENER_PENDING;
// The listener thread will free the event buffer when ready
gfxMutexExit(&geventMutex); gfxMutexExit(&geventMutex);
} }
} }

View File

@ -146,7 +146,6 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh);
* @brief Wait for an event on a listener from an assigned source. * @brief Wait 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 * @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. * be typecast to the actual event type if it needs to be processed.
* 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 returned GEvent is released when this routine is called again * @note The returned GEvent is released when this routine is called again
@ -154,9 +153,11 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh);
* allows the GEvent object to be reused earlier which can reduce missed events. The GEvent * 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 * object MUST NOT be used after this function is called (and is blocked waiting for the next
* event) or after geventEventComplete() is called. * event) or after geventEventComplete() is called.
* The one listener object should not be waited on using more than one thread simultanously
* because of the implicit geventEventComplete() that occurs when this function is called.
* *
* @param[in] pl The listener * @param[in] pl The listener
* @param[in] timeout The timeout * @param[in] timeout The timeout in milliseconds
* *
* @return NULL on timeout * @return NULL on timeout
*/ */
@ -216,7 +217,7 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las
* *
* @param[in] psl The source listener * @param[in] psl The source listener
* *
* @return NULL if the listener is not currently listening. * @return NULL if the event buffer for this listener is currently in use.
*/ */
GEvent *geventGetEventBuffer(GSourceListener *psl); GEvent *geventGetEventBuffer(GSourceListener *psl);