µGFX  2.9
version 2.9
gevent.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GEVENT || defined(__DOXYGEN__)
11 
12 #if GEVENT_ASSERT_NO_RESOURCE
13  #define GEVENT_ASSERT(x) assert(x)
14 #else
15  #define GEVENT_ASSERT(x)
16 #endif
17 
18 /* Flags in the listener structure */
19 #define GLISTENER_WITHLISTENER 0x0001 // The listener is current using the buffer
20 #define GLISTENER_WITHSOURCE 0x0002 // The source is currently using the buffer
21 
22 /* This mutex protects access to our tables */
23 static gMutex geventMutex;
24 
25 /* Our table of listener/source pairs */
26 static GSourceListener Assignments[GEVENT_MAX_SOURCE_LISTENERS];
27 
28 /* Send an exit event if possible. */
29 /* We already have the geventMutex */
30 static void doExitEvent(GListener *pl) {
31  // Don't do the exit if someone else currently is using the buffer
32  if (!(pl->flags & GLISTENER_WITHLISTENER)) {
33  pl->event.type = GEVENT_EXIT; // Set up the EXIT event
34  pl->flags = GLISTENER_WITHLISTENER; // Event buffer is now in use by the listener
35  gfxSemSignal(&pl->waitqueue);
36  }
37 }
38 
39 /* Loop through the assignment table deleting this listener/source pair. */
40 /* Null is treated as a wildcard. */
41 /* We already have the geventMutex */
42 static void deleteAssignments(GListener *pl, GSourceHandle gsh) {
43  GSourceListener *psl;
44 
45  for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
46  if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) {
47  doExitEvent(psl->pListener);
48  psl->pListener = 0;
49  psl->pSource = 0;
50  }
51  }
52 }
53 
54 void _geventInit(void)
55 {
56  gfxMutexInit(&geventMutex);
57 }
58 
59 void _geventDeinit(void)
60 {
61  gfxMutexDestroy(&geventMutex);
62 }
63 
64 void geventListenerInit(GListener *pl) {
65  gfxSemInit(&pl->waitqueue, 0, gSemMaxCount); // Next wait'er will block
66  pl->callback = 0; // No callback active
67  pl->event.type = GEVENT_NULL; // Always safety
68  pl->flags = 0;
69 }
70 
71 gBool geventAttachSource(GListener *pl, GSourceHandle gsh, gU32 flags) {
72  GSourceListener *psl, *pslfree;
73 
74  // Safety first
75  if (!pl || !gsh) {
76  GEVENT_ASSERT(gFalse);
77  return gFalse;
78  }
79 
80  gfxMutexEnter(&geventMutex);
81 
82  // Check if this pair is already in the table (scan for a free slot at the same time)
83  pslfree = 0;
84  for(psl = Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
85 
86  if (pl == psl->pListener && gsh == psl->pSource) {
87  // Just update the flags
88  psl->listenflags = flags;
89  gfxMutexExit(&geventMutex);
90  return gTrue;
91  }
92  if (!pslfree && !psl->pListener)
93  pslfree = psl;
94  }
95 
96  // A free slot was found - allocate it
97  if (pslfree) {
98  pslfree->pListener = pl;
99  pslfree->pSource = gsh;
100  pslfree->listenflags = flags;
101  pslfree->srcflags = 0;
102  }
103  gfxMutexExit(&geventMutex);
104  GEVENT_ASSERT(pslfree != 0);
105  return pslfree != 0;
106 }
107 
108 void geventDetachSource(GListener *pl, GSourceHandle gsh) {
109  if (pl) {
110  gfxMutexEnter(&geventMutex);
111  deleteAssignments(pl, gsh);
112  if (!gsh)
113  doExitEvent(pl);
114  gfxMutexExit(&geventMutex);
115  }
116 }
117 
118 GEvent *geventEventWait(GListener *pl, gDelay timeout) {
119  /* NOTE:
120  We no longer try to protect against two threads trying to listen on the
121  one listener. This was never allowed, it makes little sense to try to do so,
122  and the testing caused strange multi-thread windows of opportunity.
123 
124  In practice it is probably safer than it used to be - the only potential
125  issue is that the buffer may be prematurely marked as not in use by the listener.
126  If the calling code can guarantee that the event buffer is free when either thread
127  calls the event wait - it is now safe for them to do so.
128  ie. it is the implicit geventEventComplete() that is the only thing that now raises
129  possible multi-thread issues.
130  */
131 
132  // Don't allow waiting if we are on callbacks
133  if (pl->callback)
134  return 0;
135 
136  // Event buffer is not in use by the listener - this is an implicit geventEventComplete() call
137  pl->flags &= ~GLISTENER_WITHLISTENER;
138 
139  // Wait for an event
140  if (!gfxSemWait(&pl->waitqueue, timeout))
141  return 0; // Timeout
142 
143  return &pl->event;
144 }
145 
146 void geventEventComplete(GListener *pl) {
147  // The listener is done with the buffer
148  pl->flags &= ~GLISTENER_WITHLISTENER;
149 }
150 
151 void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
152  if (pl) {
153  gfxMutexEnter(&geventMutex);
154  doExitEvent(pl);
155  pl->param = param; // Set the param
156  pl->callback = fn; // Set the callback function
157  if (fn)
158  pl->flags &= ~GLISTENER_WITHLISTENER; // The event buffer is immediately available
159  gfxMutexExit(&geventMutex);
160  }
161 }
162 
163 GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr) {
164  GSourceListener *psl;
165 
166  // Safety first
167  if (!gsh)
168  return 0;
169 
170  gfxMutexEnter(&geventMutex);
171 
172  // Unlock the last listener event buffer if it wasn't used.
173  if (lastlr && lastlr->pListener && (lastlr->pListener->flags & GLISTENER_WITHSOURCE))
174  lastlr->pListener->flags &= ~GLISTENER_WITHSOURCE;
175 
176  // Loop through the table looking for attachments to this source
177  for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+GEVENT_MAX_SOURCE_LISTENERS; psl++) {
178  if (gsh == psl->pSource) {
179  gfxMutexExit(&geventMutex);
180  return psl;
181  }
182  }
183  gfxMutexExit(&geventMutex);
184  return 0;
185 }
186 
187 GEvent *geventGetEventBuffer(GSourceListener *psl) {
188  gfxMutexEnter(&geventMutex);
189  if ((psl->pListener->flags & (GLISTENER_WITHLISTENER|GLISTENER_WITHSOURCE))) {
190  gfxMutexExit(&geventMutex);
191  return 0;
192  }
193 
194  // Allocate the event buffer to the source
195  psl->pListener->flags |= GLISTENER_WITHSOURCE;
196  gfxMutexExit(&geventMutex);
197  return &psl->pListener->event;
198 }
199 
200 void geventSendEvent(GSourceListener *psl) {
201  gfxMutexEnter(&geventMutex);
202  if (psl->pListener->callback) {
203 
204  // Mark it back as free and as sent. This is early to be marking as free but it protects
205  // if the callback alters the listener in any way
206  psl->pListener->flags = 0;
207  gfxMutexExit(&geventMutex);
208 
209  // Do the callback
210  psl->pListener->callback(psl->pListener->param, &psl->pListener->event);
211 
212  } else {
213  // Wake up the listener
214  psl->pListener->flags = GLISTENER_WITHLISTENER;
215  gfxSemSignal(&psl->pListener->waitqueue);
216  gfxMutexExit(&geventMutex);
217  }
218 }
219 
220 void geventDetachSourceListeners(GSourceHandle gsh) {
221  gfxMutexEnter(&geventMutex);
222  deleteAssignments(0, gsh);
223  gfxMutexExit(&geventMutex);
224 }
225 
226 #endif /* GFX_USE_GEVENT */
void geventListenerInit(GListener *pl)
Create a Listener.
Definition: gevent.c:64
#define GEVENT_MAX_SOURCE_LISTENERS
Defines the maximum Source/Listener pairs in the system.
GEvent * geventGetEventBuffer(GSourceListener *psl)
Get the event buffer from the GSourceListener.
Definition: gevent.c:187
void geventDetachSource(GListener *pl, GSourceHandle gsh)
Detach a source from a listener.
Definition: gevent.c:108
void geventSendEvent(GSourceListener *psl)
Called by a source to indicate the listener's event buffer has been filled.
Definition: gevent.c:200
GEvent * geventEventWait(GListener *pl, gDelay timeout)
Wait for an event on a listener from an assigned source.
Definition: gevent.c:118
gBool geventAttachSource(GListener *pl, GSourceHandle gsh, gU32 flags)
Attach a source to a listener.
Definition: gevent.c:71
GSourceListener * geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr)
Called by a source with a possible event to get a listener record.
Definition: gevent.c:163
void geventDetachSourceListeners(GSourceHandle gsh)
Detach any listener that has this source attached.
Definition: gevent.c:220
void geventEventComplete(GListener *pl)
Release the GEvent buffer associated with a listener.
Definition: gevent.c:146
void gfxSemSignal(gSem *psem)
Signal a semaphore.
void gfxMutexExit(gMutex *pmutex)
Exit the critical code region protected by the mutex.
gBool gfxSemWait(gSem *psem, gDelay ms)
Wait on a semaphore.
void gfxMutexEnter(gMutex *pmutex)
Enter the critical code region protected by the mutex.
void gfxMutexInit(gMutex *pmutex)
Initialise a mutex to protect a region of code from other threads.
void gfxSemInit(gSem *psem, gSemcount val, gSemcount limit)
Initialise a Counted Semaphore.
void gfxMutexDestroy(gMutex *pmutex)
Destroy a Mutex.
A mutex.
Definition: gos.h:110