1 |
|
---|
2 | /*
|
---|
3 | *@@sourcefile semaphores.c:
|
---|
4 | * implements read-write semaphores.
|
---|
5 | *
|
---|
6 | * Read-write semaphores are similar to the regular
|
---|
7 | * OS/2 mutex semaphores in that they are used to
|
---|
8 | * serialize access to a resource. See CPREF for an
|
---|
9 | * introduction to mutex semaphores -- do not use
|
---|
10 | * the things in this file if you have never used
|
---|
11 | * regular mutexes.
|
---|
12 | *
|
---|
13 | * Regular mutexes are inefficient though if most
|
---|
14 | * of the access to the protected resource is
|
---|
15 | * read-only. It is not dangerous if several threads
|
---|
16 | * read from the same resource at the same time,
|
---|
17 | * as long as none of the threads actually modifies
|
---|
18 | * the resource. Still, with regular mutexes, a
|
---|
19 | * reading thread will be blocked out while another
|
---|
20 | * thread is reading, which isn't really necessary.
|
---|
21 | *
|
---|
22 | * So read-write mutexes differentiate between read
|
---|
23 | * and write access. After creating a read-write
|
---|
24 | * semaphore using semCreateRWMutex,
|
---|
25 | *
|
---|
26 | * -- to request read access, call semRequestRead
|
---|
27 | * (and semReleaseRead when done); this will
|
---|
28 | * let the thread in as long as no other thread
|
---|
29 | * has write access;
|
---|
30 | *
|
---|
31 | * -- to request write access, call semRequestWrite
|
---|
32 | * (and semReleaseWrite when done); this will
|
---|
33 | * let the thread in _only_ if no other thread
|
---|
34 | * currently has requested either read or write
|
---|
35 | * access.
|
---|
36 | *
|
---|
37 | * In other words, only write access is exclusive as with
|
---|
38 | * regular mutexes.
|
---|
39 | *
|
---|
40 | * This file is new with V0.9.12 (2001-05-24) [umoeller].
|
---|
41 | *
|
---|
42 | * Usage: All PM programs.
|
---|
43 | *
|
---|
44 | * Function prefix:
|
---|
45 | *
|
---|
46 | * -- sem*: semaphore helpers.
|
---|
47 | *
|
---|
48 | *@@added V0.9.12 (2001-05-24) [umoeller]
|
---|
49 | *@@header "helpers\semaphores.h"
|
---|
50 | */
|
---|
51 |
|
---|
52 | #define OS2EMX_PLAIN_CHAR
|
---|
53 | // this is needed for "os2emx.h"; if this is defined,
|
---|
54 | // emx will define PSZ as _signed_ char, otherwise
|
---|
55 | // as unsigned char
|
---|
56 |
|
---|
57 | #define INCL_DOSERRORS
|
---|
58 | #define INCL_DOSSEMAPHORES
|
---|
59 |
|
---|
60 | #define INCL_WINMESSAGEMGR
|
---|
61 | #include <os2.h>
|
---|
62 |
|
---|
63 | #include <stdlib.h>
|
---|
64 |
|
---|
65 | #include "setup.h" // code generation and debugging options
|
---|
66 |
|
---|
67 | #include "helpers\dosh.h"
|
---|
68 | #include "helpers\semaphores.h"
|
---|
69 | #include "helpers\standards.h"
|
---|
70 | #include "helpers\tree.h"
|
---|
71 |
|
---|
72 | #pragma hdrstop
|
---|
73 |
|
---|
74 | /*
|
---|
75 | *@@category: Helpers\Control program helpers\Semaphores
|
---|
76 | * see semaphores.c.
|
---|
77 | */
|
---|
78 |
|
---|
79 | /* ******************************************************************
|
---|
80 | *
|
---|
81 | * Private declarations
|
---|
82 | *
|
---|
83 | ********************************************************************/
|
---|
84 |
|
---|
85 | /*
|
---|
86 | *@@ RWMUTEX:
|
---|
87 | * read-write mutex, as created by
|
---|
88 | * mtxCreateRWMutex.
|
---|
89 | *
|
---|
90 | * The HRW handle is really a PRWMUTEX.
|
---|
91 | */
|
---|
92 |
|
---|
93 | typedef struct _RWMUTEX
|
---|
94 | {
|
---|
95 | ULONG cReaders;
|
---|
96 | // current no. of readers on all threads,
|
---|
97 | // including nested read requests (0 if none)
|
---|
98 | TREE *ReaderThreadsTree;
|
---|
99 | // red-black tree (tree.c) containing a
|
---|
100 | // READERTREENODE for each thread which
|
---|
101 | // ever requested read access; items are
|
---|
102 | // only added, but never removed (for speed)
|
---|
103 | ULONG cReaderThreads;
|
---|
104 | // tree item count
|
---|
105 | // (this is NOT the same as cReaders)
|
---|
106 |
|
---|
107 | ULONG cWriters;
|
---|
108 | // current no. of writers (0 if none);
|
---|
109 | // this will only be > 1 if the same
|
---|
110 | // thread did a nested write request,
|
---|
111 | // since only one thread can ever have
|
---|
112 | // write access at a time
|
---|
113 | ULONG tidWriter;
|
---|
114 | // TID of current writer or 0 if none
|
---|
115 |
|
---|
116 | HEV hevWriterDone;
|
---|
117 | // posted after writers count goes to 0;
|
---|
118 | // semRequestRead blocks on this
|
---|
119 |
|
---|
120 | HEV hevReadersDone;
|
---|
121 | // posted after readers or writers count
|
---|
122 | // goes to 0; semRequestWrite blocks on this
|
---|
123 |
|
---|
124 | } RWMUTEX, *PRWMUTEX;
|
---|
125 |
|
---|
126 | /*
|
---|
127 | *@@ READERTREENODE:
|
---|
128 | * tree item structure which describes
|
---|
129 | * a thread which requested read access.
|
---|
130 | *
|
---|
131 | * These nodes are stored in RWMUTEX.ReadersTree
|
---|
132 | * and only ever allocated, but never removed
|
---|
133 | * from the tree for speed.
|
---|
134 | *
|
---|
135 | * Since TREE.id holds the thread ID, this
|
---|
136 | * tree is sorted by thread IDs.
|
---|
137 | */
|
---|
138 |
|
---|
139 | typedef struct _READERTREENODE
|
---|
140 | {
|
---|
141 | TREE Tree; // tree base struct; "id" member
|
---|
142 | // has the TID
|
---|
143 |
|
---|
144 | ULONG cRequests; // read requests from this thread;
|
---|
145 | // 0 after the last read request
|
---|
146 | // was released (since tree node
|
---|
147 | // won't be freed then)
|
---|
148 |
|
---|
149 | } READERTREENODE, *PREADERTREENODE;
|
---|
150 |
|
---|
151 | /* ******************************************************************
|
---|
152 | *
|
---|
153 | * Global variables
|
---|
154 | *
|
---|
155 | ********************************************************************/
|
---|
156 |
|
---|
157 | static HMTX G_hmtxGlobal = NULLHANDLE;
|
---|
158 |
|
---|
159 | /* ******************************************************************
|
---|
160 | *
|
---|
161 | * Private helpers
|
---|
162 | *
|
---|
163 | ********************************************************************/
|
---|
164 |
|
---|
165 | /*
|
---|
166 | *@@ LockGlobal:
|
---|
167 | *
|
---|
168 | * WARNING: As opposed to most other Lock* functions
|
---|
169 | * I have created, this returns an APIRET.
|
---|
170 | *
|
---|
171 | */
|
---|
172 |
|
---|
173 | static APIRET LockGlobal(VOID)
|
---|
174 | {
|
---|
175 | if (!G_hmtxGlobal)
|
---|
176 | // first call:
|
---|
177 | return (DosCreateMutexSem(NULL,
|
---|
178 | &G_hmtxGlobal,
|
---|
179 | 0,
|
---|
180 | TRUE)); // request!
|
---|
181 |
|
---|
182 | return (WinRequestMutexSem(G_hmtxGlobal, SEM_INDEFINITE_WAIT));
|
---|
183 | }
|
---|
184 |
|
---|
185 | /*
|
---|
186 | *@@ UnlockGlobal:
|
---|
187 | *
|
---|
188 | */
|
---|
189 |
|
---|
190 | static VOID UnlockGlobal(VOID)
|
---|
191 | {
|
---|
192 | DosReleaseMutexSem(G_hmtxGlobal);
|
---|
193 | }
|
---|
194 |
|
---|
195 | /* ******************************************************************
|
---|
196 | *
|
---|
197 | * Public interfaces
|
---|
198 | *
|
---|
199 | ********************************************************************/
|
---|
200 |
|
---|
201 | /*
|
---|
202 | *@@ semCreateRWMutex:
|
---|
203 | * creates a read-write mutex.
|
---|
204 | *
|
---|
205 | * If this returns NO_ERROR, a new RWMUTEX
|
---|
206 | * has been created in *ppMutex. You must
|
---|
207 | * use semDeleteRWMutex to free it again.
|
---|
208 | */
|
---|
209 |
|
---|
210 | APIRET semCreateRWMutex(PHRW phrw)
|
---|
211 | {
|
---|
212 | APIRET arc = NO_ERROR;
|
---|
213 |
|
---|
214 | if (!(arc = LockGlobal()))
|
---|
215 | {
|
---|
216 | PRWMUTEX pMutex = NEW(RWMUTEX);
|
---|
217 | if (!pMutex)
|
---|
218 | arc = ERROR_NOT_ENOUGH_MEMORY;
|
---|
219 | else
|
---|
220 | {
|
---|
221 | ZERO(pMutex);
|
---|
222 |
|
---|
223 | if ( (!(arc = DosCreateEventSem(NULL,
|
---|
224 | &pMutex->hevWriterDone,
|
---|
225 | 0,
|
---|
226 | FALSE)))
|
---|
227 | && (!(arc = DosCreateEventSem(NULL,
|
---|
228 | &pMutex->hevReadersDone,
|
---|
229 | 0,
|
---|
230 | FALSE)))
|
---|
231 | )
|
---|
232 | {
|
---|
233 | treeInit(&pMutex->ReaderThreadsTree, NULL);
|
---|
234 | }
|
---|
235 | }
|
---|
236 |
|
---|
237 | if (arc)
|
---|
238 | semDeleteRWMutex((PHRW)&pMutex);
|
---|
239 | else
|
---|
240 | *phrw = (HRW)pMutex;
|
---|
241 |
|
---|
242 | UnlockGlobal();
|
---|
243 | }
|
---|
244 |
|
---|
245 | return arc;
|
---|
246 | }
|
---|
247 |
|
---|
248 | /*
|
---|
249 | *@@ semDeleteRWMutex:
|
---|
250 | * deletes a RW mutex previously created by
|
---|
251 | * semCreateRWMutex.
|
---|
252 | *
|
---|
253 | * Returns:
|
---|
254 | *
|
---|
255 | * -- NO_ERROR: sem was deleted, and *phrw
|
---|
256 | * was set to NULLHANDLE.
|
---|
257 | *
|
---|
258 | * -- ERROR_SEM_BUSY: semaphore is currently
|
---|
259 | * requested.
|
---|
260 | */
|
---|
261 |
|
---|
262 | APIRET semDeleteRWMutex(PHRW phrw) // in/out: rwsem handle
|
---|
263 | {
|
---|
264 | APIRET arc = NO_ERROR;
|
---|
265 |
|
---|
266 | if (!(arc = LockGlobal()))
|
---|
267 | {
|
---|
268 | PRWMUTEX pMutex;
|
---|
269 | if ( (phrw)
|
---|
270 | && (pMutex = (PRWMUTEX)(*phrw))
|
---|
271 | )
|
---|
272 | {
|
---|
273 | if ( (pMutex->cReaders)
|
---|
274 | || (pMutex->cWriters)
|
---|
275 | )
|
---|
276 | arc = ERROR_SEM_BUSY;
|
---|
277 | else
|
---|
278 | {
|
---|
279 | if ( (!(arc = DosCloseEventSem(pMutex->hevWriterDone)))
|
---|
280 | && (!(arc = DosCloseEventSem(pMutex->hevReadersDone)))
|
---|
281 | )
|
---|
282 | {
|
---|
283 | LONG cItems = pMutex->cReaderThreads;
|
---|
284 | TREE **papNodes = treeBuildArray(pMutex->ReaderThreadsTree,
|
---|
285 | &cItems);
|
---|
286 | if (papNodes)
|
---|
287 | {
|
---|
288 | ULONG ul;
|
---|
289 | for (ul = 0; ul < cItems; ul++)
|
---|
290 | free(papNodes[ul]);
|
---|
291 |
|
---|
292 | free(papNodes);
|
---|
293 | }
|
---|
294 |
|
---|
295 | free(pMutex);
|
---|
296 | *phrw = NULLHANDLE;
|
---|
297 | }
|
---|
298 | }
|
---|
299 | }
|
---|
300 | else
|
---|
301 | arc = ERROR_INVALID_PARAMETER;
|
---|
302 |
|
---|
303 | UnlockGlobal();
|
---|
304 | }
|
---|
305 |
|
---|
306 | return arc;
|
---|
307 | }
|
---|
308 |
|
---|
309 | /*
|
---|
310 | *@@ semRequestRead:
|
---|
311 | * requests read access from the read-write mutex.
|
---|
312 | *
|
---|
313 | * Returns:
|
---|
314 | *
|
---|
315 | * -- NO_ERROR: caller has read access and must
|
---|
316 | * call semReleaseRead when done.
|
---|
317 | *
|
---|
318 | * -- ERROR_INVALID_PARAMETER
|
---|
319 | *
|
---|
320 | * -- ERROR_TIMEOUT
|
---|
321 | *
|
---|
322 | * This function will block only if another thread
|
---|
323 | * currently holds write access.
|
---|
324 | *
|
---|
325 | * It will not block if other threads also have
|
---|
326 | * write access, or it is the current thread which
|
---|
327 | * holds write access, or if this is a nested read
|
---|
328 | * request on the same thread.
|
---|
329 | *
|
---|
330 | * If this function returns NO_ERROR, the calling
|
---|
331 | * thread is stored as a reader in the read-write
|
---|
332 | * mutex and will block out other threads which
|
---|
333 | * call semRequestWrite.
|
---|
334 | */
|
---|
335 |
|
---|
336 | APIRET semRequestRead(HRW hrw, // in: rw-sem created by semCreateRWMutex
|
---|
337 | ULONG ulTimeout) // in: timeout in ms, or SEM_INDEFINITE_WAIT
|
---|
338 | {
|
---|
339 | APIRET arc = NO_ERROR;
|
---|
340 | BOOL fLocked = FALSE;
|
---|
341 |
|
---|
342 | // protect the RW by requesting the global mutex;
|
---|
343 | // note, we ignore ulTimeout here, since this request
|
---|
344 | // will only ever take a very short time
|
---|
345 | if (!(arc = LockGlobal()))
|
---|
346 | {
|
---|
347 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
348 | fLocked = TRUE;
|
---|
349 |
|
---|
350 | if (!pMutex)
|
---|
351 | arc = ERROR_INVALID_PARAMETER;
|
---|
352 | else
|
---|
353 | {
|
---|
354 | // get our own thread ID
|
---|
355 | ULONG tidMyself = doshMyTID();
|
---|
356 |
|
---|
357 | // if there are any writers in the RW
|
---|
358 | // besides our own thread, wait for the
|
---|
359 | // writer to release write
|
---|
360 | if ( (pMutex->cWriters)
|
---|
361 | && (pMutex->tidWriter != tidMyself)
|
---|
362 | )
|
---|
363 | {
|
---|
364 | while ( (pMutex->cWriters)
|
---|
365 | && (!arc)
|
---|
366 | )
|
---|
367 | {
|
---|
368 | ULONG ul;
|
---|
369 | DosResetEventSem(pMutex->hevWriterDone, &ul);
|
---|
370 |
|
---|
371 | // while we're waiting on the writer to post
|
---|
372 | // "writers done", release the global mutex
|
---|
373 | UnlockGlobal();
|
---|
374 | fLocked = FALSE;
|
---|
375 |
|
---|
376 | // block on "writer done"; this gets posted from
|
---|
377 | // semReleaseWrite after the writer has released
|
---|
378 | // its last write request...
|
---|
379 | // so after this unblocks, we must check cWriters
|
---|
380 | // again, in case another writer has come in between
|
---|
381 | if (!(arc = WinWaitEventSem(pMutex->hevWriterDone, ulTimeout)))
|
---|
382 | // writer done:
|
---|
383 | // request global mutex again
|
---|
384 | if (!(arc = LockGlobal()))
|
---|
385 | fLocked = TRUE;
|
---|
386 | // else: probably timeout, do not loop again
|
---|
387 | }
|
---|
388 | }
|
---|
389 |
|
---|
390 | if (!arc)
|
---|
391 | {
|
---|
392 | PREADERTREENODE pReader;
|
---|
393 |
|
---|
394 | // add readers count
|
---|
395 | (pMutex->cReaders)++;
|
---|
396 |
|
---|
397 | // check if this thread has a reader entry already
|
---|
398 | if (pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
|
---|
399 | tidMyself, // ID to look for
|
---|
400 | treeCompareKeys))
|
---|
401 | {
|
---|
402 | // yes:
|
---|
403 | // this is either
|
---|
404 | // -- a nested read request for the same thread
|
---|
405 | // -- or a tree item from a previous read request
|
---|
406 | // which went to 0, but wasn't deleted for speed
|
---|
407 | // (cRequests is then 0)
|
---|
408 | (pReader->cRequests)++;
|
---|
409 | }
|
---|
410 | else
|
---|
411 | {
|
---|
412 | // no entry for this thread yet:
|
---|
413 | // add a new one
|
---|
414 | pReader = NEW(READERTREENODE);
|
---|
415 | if (!pReader)
|
---|
416 | arc = ERROR_NOT_ENOUGH_MEMORY;
|
---|
417 | else
|
---|
418 | {
|
---|
419 | // store the thread ID as the tree ID to
|
---|
420 | // sort by (so we can find by TID)
|
---|
421 | pReader->Tree.ulKey = tidMyself;
|
---|
422 | // set requests count to 1
|
---|
423 | pReader->cRequests = 1;
|
---|
424 |
|
---|
425 | treeInsert(&pMutex->ReaderThreadsTree,
|
---|
426 | NULL,
|
---|
427 | (TREE*)pReader,
|
---|
428 | treeCompareKeys);
|
---|
429 | (pMutex->cReaderThreads)++;
|
---|
430 | }
|
---|
431 | }
|
---|
432 | }
|
---|
433 | }
|
---|
434 | } // end if (!(arc = LockGlobal()))
|
---|
435 |
|
---|
436 | if (fLocked)
|
---|
437 | UnlockGlobal();
|
---|
438 |
|
---|
439 | return arc;
|
---|
440 | }
|
---|
441 |
|
---|
442 | /*
|
---|
443 | *@@ semReleaseRead:
|
---|
444 | * releases read access previously requested
|
---|
445 | * by semRequestRead.
|
---|
446 | *
|
---|
447 | * This may unblock other threads which have
|
---|
448 | * blocked in semRequestWrite.
|
---|
449 | */
|
---|
450 |
|
---|
451 | APIRET semReleaseRead(HRW hrw) // in: rw-sem created by semCreateRWMutex
|
---|
452 | {
|
---|
453 | APIRET arc = NO_ERROR;
|
---|
454 |
|
---|
455 | // protect the RW by requesting global mutex
|
---|
456 | if (!(arc = LockGlobal()))
|
---|
457 | {
|
---|
458 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
459 |
|
---|
460 | if (!pMutex)
|
---|
461 | arc = ERROR_INVALID_PARAMETER;
|
---|
462 | else
|
---|
463 | {
|
---|
464 | // get our own thread ID
|
---|
465 | ULONG tidMyself = doshMyTID();
|
---|
466 |
|
---|
467 | PREADERTREENODE pReader;
|
---|
468 |
|
---|
469 | // find the READERTREENODE for our TID
|
---|
470 | if ( (pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
|
---|
471 | tidMyself, // ID to look for
|
---|
472 | treeCompareKeys))
|
---|
473 | && (pReader->cRequests)
|
---|
474 | )
|
---|
475 | {
|
---|
476 | // lower user count then (will be zero now,
|
---|
477 | // unless read requests were nested)
|
---|
478 | (pReader->cRequests)--;
|
---|
479 |
|
---|
480 | // note: we don't delete the tree item,
|
---|
481 | // since it will probably be reused soon
|
---|
482 | // (speed)
|
---|
483 |
|
---|
484 | // lower total requests count
|
---|
485 | (pMutex->cReaders)--;
|
---|
486 |
|
---|
487 | if (pMutex->cReaders == 0)
|
---|
488 | // no more readers now:
|
---|
489 | // post "readers done" semaphore
|
---|
490 | DosPostEventSem(pMutex->hevReadersDone);
|
---|
491 | // this sets all threads blocking
|
---|
492 | // in semRequestWrite to "ready"
|
---|
493 | }
|
---|
494 | else
|
---|
495 | // excessive releases for this thread,
|
---|
496 | // or this wasn't requested at all:
|
---|
497 | arc = ERROR_NOT_OWNER;
|
---|
498 | }
|
---|
499 |
|
---|
500 | UnlockGlobal();
|
---|
501 |
|
---|
502 | } // end if (!(arc = LockGlobal()))
|
---|
503 |
|
---|
504 | return arc;
|
---|
505 | }
|
---|
506 |
|
---|
507 | /*
|
---|
508 | *@@ semQueryRead:
|
---|
509 | * checks if the thread currently has read
|
---|
510 | * access to the read-write semaphore.
|
---|
511 | *
|
---|
512 | * Returns:
|
---|
513 | *
|
---|
514 | * -- NO_ERROR if the same thread request
|
---|
515 | * read access before and thus is a reader.
|
---|
516 | *
|
---|
517 | * -- ERROR_NOT_OWNER if the thread does not
|
---|
518 | * currently have read access.
|
---|
519 | */
|
---|
520 |
|
---|
521 | APIRET semQueryRead(HRW hrw) // in: rw-sem created by semCreateRWMutex
|
---|
522 | {
|
---|
523 | APIRET arc = NO_ERROR;
|
---|
524 |
|
---|
525 | // protect the RW by requesting global mutex
|
---|
526 | if (!(arc = LockGlobal()))
|
---|
527 | {
|
---|
528 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
529 |
|
---|
530 | if (!pMutex)
|
---|
531 | arc = ERROR_INVALID_PARAMETER;
|
---|
532 | else
|
---|
533 | {
|
---|
534 | // get our own thread ID
|
---|
535 | ULONG tidMyself = doshMyTID();
|
---|
536 |
|
---|
537 | PREADERTREENODE pReader;
|
---|
538 |
|
---|
539 | // find the READERTREENODE for our TID
|
---|
540 | if ( (!(pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
|
---|
541 | tidMyself, // ID to look for
|
---|
542 | treeCompareKeys)))
|
---|
543 | || (pReader->cRequests == 0)
|
---|
544 | )
|
---|
545 | arc = ERROR_NOT_OWNER;
|
---|
546 | // else: pReader exists, and pReader->cRequests > 0 --> NO_ERROR
|
---|
547 | }
|
---|
548 |
|
---|
549 | UnlockGlobal();
|
---|
550 |
|
---|
551 | } // end if (!(arc = LockGlobal()))
|
---|
552 |
|
---|
553 | return arc;
|
---|
554 | }
|
---|
555 |
|
---|
556 | /*
|
---|
557 | *@@ semRequestWrite:
|
---|
558 | * requests write access from the read-write mutex.
|
---|
559 | *
|
---|
560 | * Returns:
|
---|
561 | *
|
---|
562 | * -- NO_ERROR: caller has write access and must
|
---|
563 | * call semReleaseWrite when done.
|
---|
564 | *
|
---|
565 | * -- ERROR_INVALID_PARAMETER
|
---|
566 | *
|
---|
567 | * -- ERROR_TIMEOUT
|
---|
568 | *
|
---|
569 | * This function will block if any other thread
|
---|
570 | * currently has read or write access.
|
---|
571 | *
|
---|
572 | * It will not block if the current thread is the
|
---|
573 | * only thread that has requested read access
|
---|
574 | * before, or if this is a nested write request
|
---|
575 | * on the same thread.
|
---|
576 | *
|
---|
577 | * If this function returns NO_ERROR, the calling
|
---|
578 | * thread owns the read-write mutex all alone,
|
---|
579 | * as if it were a regular mutex. While write
|
---|
580 | * access is held, other threads are blocked in
|
---|
581 | * semRequestRead or semRequestWrite.
|
---|
582 | */
|
---|
583 |
|
---|
584 | APIRET semRequestWrite(HRW hrw, // in: rw-sem created by semCreateRWMutex
|
---|
585 | ULONG ulTimeout) // in: timeout in ms, or SEM_INDEFINITE_WAIT
|
---|
586 | {
|
---|
587 | APIRET arc = NO_ERROR;
|
---|
588 | BOOL fLocked = FALSE;
|
---|
589 |
|
---|
590 | // protect the RW by requesting global mutex;
|
---|
591 | // note, we ignore ulTimeout here, since this request
|
---|
592 | // will only ever take a very short time
|
---|
593 | if (!(arc = LockGlobal()))
|
---|
594 | {
|
---|
595 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
596 | fLocked = TRUE;
|
---|
597 |
|
---|
598 | if (!pMutex)
|
---|
599 | arc = ERROR_INVALID_PARAMETER;
|
---|
600 | else
|
---|
601 | {
|
---|
602 | // get our own thread ID
|
---|
603 | ULONG tidMyself = doshMyTID();
|
---|
604 |
|
---|
605 | while (!arc)
|
---|
606 | {
|
---|
607 | // check if current TID holds read request also
|
---|
608 | PREADERTREENODE pReader
|
---|
609 | = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
|
---|
610 | tidMyself,
|
---|
611 | treeCompareKeys);
|
---|
612 | // != NULL if this TID has a reader already
|
---|
613 |
|
---|
614 | // let the writer in if one of the three is true:
|
---|
615 | if (
|
---|
616 | // 1) no readers and no writers at all currently
|
---|
617 | ( (pMutex->cWriters == 0)
|
---|
618 | && (pMutex->cReaders == 0)
|
---|
619 | )
|
---|
620 | // or 2) there is a writer (which implies that there
|
---|
621 | // are no readers), but the writer has the
|
---|
622 | // same TID as the caller --> nested writer call
|
---|
623 | // on the same thread
|
---|
624 | || ( (pMutex->cWriters)
|
---|
625 | && (pMutex->tidWriter == tidMyself)
|
---|
626 | )
|
---|
627 | // or 3) a reader tree item was found above, and
|
---|
628 | // current thread is the only reader
|
---|
629 | || ( (pReader)
|
---|
630 | && (pReader->cRequests)
|
---|
631 | && (pReader->cRequests == pMutex->cReaders)
|
---|
632 | )
|
---|
633 | )
|
---|
634 | {
|
---|
635 | // we're safe!
|
---|
636 | break;
|
---|
637 | }
|
---|
638 | else
|
---|
639 | {
|
---|
640 | // we're NOT safe:
|
---|
641 | // this means that
|
---|
642 | // 1) a writer other than current thread is active, or
|
---|
643 | // 2) readers exist and we're not the only reader...
|
---|
644 | // block then until "readers done" is posted
|
---|
645 | ULONG ul;
|
---|
646 | DosResetEventSem(pMutex->hevReadersDone, &ul);
|
---|
647 |
|
---|
648 | // while we're waiting on the last reader to post
|
---|
649 | // "readers done", release the global mutex
|
---|
650 | UnlockGlobal();
|
---|
651 | fLocked = FALSE;
|
---|
652 |
|
---|
653 | // wait for all readers and writers to finish;
|
---|
654 | // this gets posted by
|
---|
655 | // -- semReleaseRead if pMutex->cReaders goes to 0
|
---|
656 | // -- semReleaseWrite after another writer has
|
---|
657 | // released its last write request
|
---|
658 | if (!(arc = WinWaitEventSem(pMutex->hevReadersDone, ulTimeout)))
|
---|
659 | // readers done:
|
---|
660 | // request global mutex again
|
---|
661 | if (!(arc = LockGlobal()))
|
---|
662 | fLocked = TRUE;
|
---|
663 | // else: probably timeout, do not loop again
|
---|
664 | }
|
---|
665 | } // end while (!arc)
|
---|
666 |
|
---|
667 | if (!arc)
|
---|
668 | {
|
---|
669 | // OK, raise writers count
|
---|
670 | (pMutex->cWriters)++;
|
---|
671 | // and store our TID as the current writer
|
---|
672 | pMutex->tidWriter = tidMyself;
|
---|
673 | }
|
---|
674 | }
|
---|
675 |
|
---|
676 | if (fLocked)
|
---|
677 | UnlockGlobal();
|
---|
678 |
|
---|
679 | } // end if (!(arc = LockGlobal()))
|
---|
680 |
|
---|
681 | return arc;
|
---|
682 | }
|
---|
683 |
|
---|
684 | /*
|
---|
685 | *@@ semReleaseWrite:
|
---|
686 | * releases write access previously requested
|
---|
687 | * by semRequestWrite.
|
---|
688 | *
|
---|
689 | * This may unblock other threads which have
|
---|
690 | * blocked in either semRequestRead or
|
---|
691 | * semRequestWrite.
|
---|
692 | */
|
---|
693 |
|
---|
694 | APIRET semReleaseWrite(HRW hrw) // in: rw-sem created by semCreateRWMutex
|
---|
695 | {
|
---|
696 | APIRET arc = NO_ERROR;
|
---|
697 |
|
---|
698 | // protect the RW by requesting the global mutex
|
---|
699 | if (!(arc = LockGlobal()))
|
---|
700 | {
|
---|
701 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
702 |
|
---|
703 | if (!pMutex)
|
---|
704 | arc = ERROR_INVALID_PARAMETER;
|
---|
705 | else
|
---|
706 | {
|
---|
707 | // get our own thread ID
|
---|
708 | ULONG tidMyself = doshMyTID();
|
---|
709 |
|
---|
710 | if ( (pMutex->cWriters)
|
---|
711 | && (pMutex->tidWriter == tidMyself)
|
---|
712 | )
|
---|
713 | {
|
---|
714 | (pMutex->cWriters)--;
|
---|
715 |
|
---|
716 | if (pMutex->cWriters == 0)
|
---|
717 | {
|
---|
718 | ULONG ul;
|
---|
719 | // last write request released:
|
---|
720 | // post the "writer done" semaphore
|
---|
721 | DosResetEventSem(pMutex->hevWriterDone, &ul);
|
---|
722 | DosPostEventSem(pMutex->hevWriterDone);
|
---|
723 | // this sets all threads blocking
|
---|
724 | // in semRequestRead to "ready"
|
---|
725 |
|
---|
726 | // and post the "reader done" semaphore
|
---|
727 | // as well, in case another thread is
|
---|
728 | // waiting for write request
|
---|
729 | DosResetEventSem(pMutex->hevReadersDone, &ul);
|
---|
730 | DosPostEventSem(pMutex->hevReadersDone);
|
---|
731 | // this sets all threads blocking
|
---|
732 | // in semRequestWrite to "ready"
|
---|
733 |
|
---|
734 | // and set tidWriter to 0
|
---|
735 | pMutex->tidWriter = 0;
|
---|
736 | }
|
---|
737 | // else: nested write request on this
|
---|
738 | // thread (there can only ever be one
|
---|
739 | // writer thread)
|
---|
740 | }
|
---|
741 | else
|
---|
742 | // excessive releases for this thread,
|
---|
743 | // or this wasn't requested at all:
|
---|
744 | arc = ERROR_NOT_OWNER;
|
---|
745 | }
|
---|
746 |
|
---|
747 | UnlockGlobal();
|
---|
748 |
|
---|
749 | } // end if (!(arc = LockGlobal()))
|
---|
750 |
|
---|
751 | return arc;
|
---|
752 | }
|
---|
753 |
|
---|
754 | /*
|
---|
755 | *@@ semQueryWrite:
|
---|
756 | * checks if the thread currently has write
|
---|
757 | * access to the read-write semaphore.
|
---|
758 | *
|
---|
759 | * Returns:
|
---|
760 | *
|
---|
761 | * -- NO_ERROR if the same thread request
|
---|
762 | * write access before and thus is a writer.
|
---|
763 | *
|
---|
764 | * -- ERROR_NOT_OWNER if the thread does not
|
---|
765 | * currently have write access.
|
---|
766 | */
|
---|
767 |
|
---|
768 | APIRET semQueryWrite(HRW hrw) // in: rw-sem created by semCreateRWMutex
|
---|
769 | {
|
---|
770 | APIRET arc = NO_ERROR;
|
---|
771 |
|
---|
772 | // protect the RW by requesting the global mutex
|
---|
773 | if (!(arc = LockGlobal()))
|
---|
774 | {
|
---|
775 | PRWMUTEX pMutex = (PRWMUTEX)hrw;
|
---|
776 |
|
---|
777 | if (!pMutex)
|
---|
778 | arc = ERROR_INVALID_PARAMETER;
|
---|
779 | else
|
---|
780 | {
|
---|
781 | // get our own thread ID
|
---|
782 | ULONG tidMyself = doshMyTID();
|
---|
783 |
|
---|
784 | if ( (pMutex->cWriters == 0)
|
---|
785 | || (pMutex->tidWriter != tidMyself)
|
---|
786 | )
|
---|
787 | arc = ERROR_NOT_OWNER;
|
---|
788 | }
|
---|
789 |
|
---|
790 | UnlockGlobal();
|
---|
791 |
|
---|
792 | } // end if (!(arc = LockGlobal()))
|
---|
793 |
|
---|
794 | return arc;
|
---|
795 | }
|
---|
796 |
|
---|
797 |
|
---|