1 /* FTHREADS.C (c) Copyright "Fish" (David B. Trout), 2001-2009 */
2 /* Fish's WIN32 version of pthreads */
3
4 ////////////////////////////////////////////////////////////////////////////////////
5 // (c) Copyright "Fish" (David B. Trout), 2001-2009. Released under the Q Public License
6 // (http://www.hercules-390.org/herclic.html) as modifications to Hercules.
7 ////////////////////////////////////////////////////////////////////////////////////
8
9 #include "hstdinc.h"
10
11 #define _FTHREADS_C_
12 #define _HUTIL_DLL_
13
14 #include "hercules.h"
15 #include "fthreads.h"
16
17 #if defined(OPTION_FTHREADS)
18
19 ////////////////////////////////////////////////////////////////////////////////////
20 // Private internal fthreads structures...
21
22 typedef struct _tagFT_MUTEX // fthread "mutex" structure
23 {
24 CRITICAL_SECTION MutexLock; // (lock for accessing this data)
25 DWORD dwMutexMagic; // (magic number)
26 HANDLE hUnlockedEvent; // (signalled while NOT locked)
27 DWORD dwMutexType; // (type of mutex (normal, etc))
28 DWORD dwLockOwner; // (thread-id of who owns it)
29 int nLockedCount; // (#of times lock acquired)
30 }
31 FT_MUTEX, *PFT_MUTEX;
32
33 typedef struct _tagFT_COND_VAR // fthread "condition variable" structure
34 {
35 CRITICAL_SECTION CondVarLock; // (lock for accessing this data)
36 DWORD dwCondMagic; // (magic number)
37 HANDLE hSigXmitEvent; // set during signal transmission
38 HANDLE hSigRecvdEvent; // set once signal received by every-
39 // one that's supposed to receive it.
40 BOOL bBroadcastSig; // TRUE = "broadcast", FALSE = "signal"
41 int nNumWaiting; // #of threads waiting to receive signal
42 }
43 FT_COND_VAR, *PFT_COND_VAR;
44
45 ////////////////////////////////////////////////////////////////////////////////////
46 // So we can tell whether our structures have been properly initialized or not...
47
48 #define FT_MUTEX_MAGIC 0x4D767478 // "Mutx" in ASCII
49 #define FT_COND_MAGIC 0x436F6E64 // "Cond" in ASCII
50 #define FT_ATTR_MAGIC 0x41747472 // "Attr" in ASCII
51
52 ////////////////////////////////////////////////////////////////////////////////////
53 ////////////////////////////////////////////////////////////////////////////////////
54 ////////////////////////////////////////////////////////////////////////////////////
55 // Private internal fthreads functions...
56
IsValidMutexType(DWORD dwMutexType)57 static BOOL IsValidMutexType ( DWORD dwMutexType )
58 {
59 return (0
60 // || FTHREAD_MUTEX_DEFAULT == dwMutexType // (FTHREAD_MUTEX_RECURSIVE)
61 || FTHREAD_MUTEX_RECURSIVE == dwMutexType
62 || FTHREAD_MUTEX_ERRORCHECK == dwMutexType
63 // || FTHREAD_MUTEX_NORMAL == dwMutexType // (not currently supported)
64 );
65 }
66
67 ////////////////////////////////////////////////////////////////////////////////////
68
MallocFT_MUTEX()69 static FT_MUTEX* MallocFT_MUTEX ( )
70 {
71 FT_MUTEX* pFT_MUTEX = (FT_MUTEX*) malloc ( sizeof ( FT_MUTEX ) );
72 if ( !pFT_MUTEX ) return NULL;
73 memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
74 return pFT_MUTEX;
75 }
76
77 ////////////////////////////////////////////////////////////////////////////////////
78
InitializeFT_MUTEX(FT_MUTEX * pFT_MUTEX,DWORD dwMutexType)79 static BOOL InitializeFT_MUTEX
80 (
81 FT_MUTEX* pFT_MUTEX,
82 DWORD dwMutexType
83 )
84 {
85 // Note: UnlockedEvent created initially signalled
86
87 if ( !(pFT_MUTEX->hUnlockedEvent = MyCreateEvent ( NULL, TRUE, TRUE, NULL )) )
88 {
89 memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
90 return FALSE;
91 }
92
93 MyInitializeCriticalSection ( &pFT_MUTEX->MutexLock );
94
95 pFT_MUTEX->dwMutexMagic = FT_MUTEX_MAGIC;
96 pFT_MUTEX->dwMutexType = dwMutexType;
97 pFT_MUTEX->dwLockOwner = 0;
98 pFT_MUTEX->nLockedCount = 0;
99
100 return TRUE;
101 }
102
103 ////////////////////////////////////////////////////////////////////////////////////
104
UninitializeFT_MUTEX(FT_MUTEX * pFT_MUTEX)105 static BOOL UninitializeFT_MUTEX
106 (
107 FT_MUTEX* pFT_MUTEX
108 )
109 {
110 if ( pFT_MUTEX->nLockedCount > 0 )
111 return FALSE; // (still in use)
112
113 ASSERT( IsEventSet ( pFT_MUTEX->hUnlockedEvent ) );
114
115 MyDeleteEvent ( pFT_MUTEX->hUnlockedEvent );
116 MyDeleteCriticalSection ( &pFT_MUTEX->MutexLock );
117
118 memset ( pFT_MUTEX, 0xCD, sizeof ( FT_MUTEX ) );
119
120 return TRUE;
121 }
122
123 ////////////////////////////////////////////////////////////////////////////////////
124
MallocFT_COND_VAR()125 static FT_COND_VAR* MallocFT_COND_VAR ( )
126 {
127 FT_COND_VAR* pFT_COND_VAR = (FT_COND_VAR*) malloc ( sizeof ( FT_COND_VAR ) );
128 if ( !pFT_COND_VAR ) return NULL;
129 memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
130 return pFT_COND_VAR;
131 }
132
133 ////////////////////////////////////////////////////////////////////////////////////
134
InitializeFT_COND_VAR(FT_COND_VAR * pFT_COND_VAR)135 static BOOL InitializeFT_COND_VAR
136 (
137 FT_COND_VAR* pFT_COND_VAR
138 )
139 {
140 if ( ( pFT_COND_VAR->hSigXmitEvent = MyCreateEvent ( NULL, TRUE, FALSE, NULL ) ) )
141 {
142 // Note: hSigRecvdEvent created initially signaled
143
144 if ( ( pFT_COND_VAR->hSigRecvdEvent = MyCreateEvent ( NULL, TRUE, TRUE, NULL ) ) )
145 {
146 MyInitializeCriticalSection ( &pFT_COND_VAR->CondVarLock );
147
148 pFT_COND_VAR->dwCondMagic = FT_COND_MAGIC;
149 pFT_COND_VAR->bBroadcastSig = FALSE;
150 pFT_COND_VAR->nNumWaiting = 0;
151
152 return TRUE;
153 }
154
155 MyDeleteEvent ( pFT_COND_VAR->hSigXmitEvent );
156 }
157
158 memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
159
160 return FALSE;
161 }
162
163 ////////////////////////////////////////////////////////////////////////////////////
164
UninitializeFT_COND_VAR(FT_COND_VAR * pFT_COND_VAR)165 static BOOL UninitializeFT_COND_VAR
166 (
167 FT_COND_VAR* pFT_COND_VAR
168 )
169 {
170 if (0
171 || pFT_COND_VAR->nNumWaiting
172 || IsEventSet ( pFT_COND_VAR->hSigXmitEvent )
173 || !IsEventSet ( pFT_COND_VAR->hSigRecvdEvent )
174 )
175 return FALSE;
176
177 MyDeleteEvent ( pFT_COND_VAR->hSigXmitEvent );
178 MyDeleteEvent ( pFT_COND_VAR->hSigRecvdEvent );
179
180 MyDeleteCriticalSection ( &pFT_COND_VAR->CondVarLock );
181
182 memset ( pFT_COND_VAR, 0xCD, sizeof ( FT_COND_VAR ) );
183
184 return TRUE;
185 }
186
187 ////////////////////////////////////////////////////////////////////////////////////
188
TryEnterFT_MUTEX(FT_MUTEX * pFT_MUTEX)189 static BOOL TryEnterFT_MUTEX
190 (
191 FT_MUTEX* pFT_MUTEX
192 )
193 {
194 BOOL bSuccess;
195 DWORD dwThreadId = GetCurrentThreadId();
196
197 if ( hostinfo.trycritsec_avail )
198 {
199 bSuccess = MyTryEnterCriticalSection ( &pFT_MUTEX->MutexLock );
200
201 if ( bSuccess )
202 {
203 pFT_MUTEX->nLockedCount++;
204 ASSERT( pFT_MUTEX->nLockedCount > 0 );
205 pFT_MUTEX->dwLockOwner = dwThreadId;
206 }
207 }
208 else
209 {
210 MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
211
212 ASSERT ( pFT_MUTEX->nLockedCount >= 0 );
213
214 bSuccess = ( pFT_MUTEX->nLockedCount <= 0 || pFT_MUTEX->dwLockOwner == dwThreadId );
215
216 if ( bSuccess )
217 {
218 pFT_MUTEX->nLockedCount++;
219 ASSERT ( pFT_MUTEX->nLockedCount > 0 );
220 pFT_MUTEX->dwLockOwner = dwThreadId;
221 MyResetEvent ( pFT_MUTEX->hUnlockedEvent );
222 }
223
224 MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
225 }
226
227 return bSuccess;
228 }
229
230 ////////////////////////////////////////////////////////////////////////////////////
231
EnterFT_MUTEX(FT_MUTEX * pFT_MUTEX)232 static void EnterFT_MUTEX
233 (
234 FT_MUTEX* pFT_MUTEX
235 )
236 {
237 DWORD dwThreadId = GetCurrentThreadId();
238
239 if ( hostinfo.trycritsec_avail )
240 {
241 MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
242 pFT_MUTEX->dwLockOwner = dwThreadId;
243 pFT_MUTEX->nLockedCount++;
244 ASSERT ( pFT_MUTEX->nLockedCount > 0 );
245 }
246 else
247 {
248 for (;;)
249 {
250 MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
251 ASSERT ( pFT_MUTEX->nLockedCount >= 0 );
252 if ( pFT_MUTEX->nLockedCount <= 0 || pFT_MUTEX->dwLockOwner == dwThreadId ) break;
253 MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
254 MyWaitForSingleObject ( pFT_MUTEX->hUnlockedEvent, INFINITE );
255 }
256
257 MyResetEvent ( pFT_MUTEX->hUnlockedEvent );
258 pFT_MUTEX->dwLockOwner = dwThreadId;
259 pFT_MUTEX->nLockedCount++;
260 ASSERT ( pFT_MUTEX->nLockedCount > 0 );
261 MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
262 }
263 }
264
265 ////////////////////////////////////////////////////////////////////////////////////
266
LeaveFT_MUTEX(FT_MUTEX * pFT_MUTEX)267 static void LeaveFT_MUTEX
268 (
269 FT_MUTEX* pFT_MUTEX
270 )
271 {
272 if ( hostinfo.trycritsec_avail )
273 {
274 ASSERT ( pFT_MUTEX->nLockedCount > 0 );
275 pFT_MUTEX->nLockedCount--;
276 if ( pFT_MUTEX->nLockedCount <= 0 )
277 pFT_MUTEX->dwLockOwner = 0;
278 MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
279 }
280 else
281 {
282 MyEnterCriticalSection ( &pFT_MUTEX->MutexLock );
283 ASSERT ( pFT_MUTEX->nLockedCount > 0 );
284 pFT_MUTEX->nLockedCount--;
285 if ( pFT_MUTEX->nLockedCount <= 0 )
286 {
287 pFT_MUTEX->dwLockOwner = 0;
288 MySetEvent ( pFT_MUTEX->hUnlockedEvent );
289 }
290 MyLeaveCriticalSection ( &pFT_MUTEX->MutexLock );
291 }
292 }
293
294 ////////////////////////////////////////////////////////////////////////////////////
295 ////////////////////////////////////////////////////////////////////////////////////
296 ////////////////////////////////////////////////////////////////////////////////////
297 // Now we get to the "meat" of fthreads...
298 //
299 // The below function atomically releases the caller's mutex and registers the fact
300 // that the caller wishes to wait on their condition variable (or rather the other
301 // way around: it first registers the fact that the caller wishes to wait on the
302 // condition by first acquiring the condition variable lock and then registering
303 // the wait and THEN afterwards (once the wait has been registered and condition
304 // variable lock acquired) releases the original mutex). This ensures that no one
305 // can "sneak a signal past us" from the time this function is called until we can
306 // manage to register the wait request since no signals can ever be sent while the
307 // condition variable is locked.
308
BeginWait(FT_COND_VAR * pFT_COND_VAR,fthread_mutex_t * pFTUSER_MUTEX)309 static int BeginWait
310 (
311 FT_COND_VAR* pFT_COND_VAR,
312 fthread_mutex_t* pFTUSER_MUTEX
313 )
314 {
315 int rc;
316 FT_MUTEX* pFT_MUTEX;
317
318 if (0
319 || !pFT_COND_VAR // (invalid ptr)
320 || pFT_COND_VAR -> dwCondMagic != FT_COND_MAGIC // (not initialized)
321 || !pFTUSER_MUTEX // (invalid ptr)
322 || pFTUSER_MUTEX-> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
323 || !(pFT_MUTEX = pFTUSER_MUTEX->hMutex) // (invalid ptr)
324 // || !pFT_MUTEX // (invalid ptr)
325 || pFT_MUTEX -> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
326 )
327 return RC(EINVAL);
328
329 if (0
330 || pFT_MUTEX -> dwLockOwner != GetCurrentThreadId() // (mutex not owned)
331 || pFT_MUTEX -> nLockedCount <= 0 // (mutex not locked)
332 )
333 return RC(EPERM);
334
335 // First, acquire the fthreads condition variable lock...
336
337 for (;;)
338 {
339 MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
340
341 // It is always safe to proceed if the prior signal was completely
342 // processed (received by everyone who was supposed to receive it)
343
344 if ( IsEventSet ( pFT_COND_VAR->hSigRecvdEvent ) )
345 break;
346
347 // Prior signal not completely received yet... Verify that it is
348 // still being transmitted...
349
350 ASSERT ( IsEventSet ( pFT_COND_VAR->hSigXmitEvent ) );
351
352 // If no one is currently waiting to receive [this signal not yet
353 // completely received and still being transmitted], then we can
354 // go ahead and receive it right now *regardless* of what type of
355 // signal it is ("signal" or "broadcast") since we're *obviously*
356 // the one who is supposed to receive it (since we ARE trying to
357 // wait on it after all and it IS being transmitted. The 'xmit'
358 // event is *always* turned off once everyone [who is *supposed*
359 // to receive the signal] *has* received the signal. Thus, since
360 // it's still being transmitted, that means *not* everyone who
361 // *should* receive it *has* received it yet, and thus we can be
362 // absolutely certain that we indeed *should* therefore receive it
363 // since we *are* after all waiting for it).
364
365 // Otherwise (prior signal not completely processed AND there are
366 // still others waiting to receive it too (as well as us)), then if
367 // it's a "broadcast" type signal, we can go ahead and receive that
368 // type of signal too as well (along with the others); we just came
369 // to the party a little bit late (but nevertheless in the nick of
370 // time!), that's all...
371
372 if ( !pFT_COND_VAR->nNumWaiting || pFT_COND_VAR->bBroadcastSig )
373 break;
374
375 // Otherwise it's a "signal" type signal (and not a broadcast type)
376 // that hasn't been completely received yet, meaning only ONE thread
377 // should be released. Thus, since there's already a thread (or more
378 // than one thread) already waiting/trying to receive it, we need to
379 // let [one of] THEM receive it and NOT US. Thus we go back to sleep
380 // and wait for the signal processing currently in progress to finish
381 // releasing the proper number of threads first. Only once that has
382 // happened can we then be allowed to try and catch whatever signal
383 // happens to come along next...
384
385 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
386
387 // (Programming Note: technically we should really be checking our
388 // return code from the below wait call too)
389
390 MyWaitForSingleObject ( pFT_COND_VAR->hSigRecvdEvent, INFINITE );
391 }
392
393 // Register the caller's wait request while we still have control
394 // over this condition variable...
395
396 pFT_COND_VAR->nNumWaiting++; // (register wait request)
397
398 // Now release the original mutex and thus any potential signalers...
399 // (but note that no signal can actually ever be sent per se until
400 // the condition variable which we currently have locked is first
401 // released, which gets done in the WaitForTransmission function).
402
403 if
404 (
405 (
406 rc = fthread_mutex_unlock
407 (
408 pFTUSER_MUTEX
409 )
410 )
411 != 0
412 )
413 {
414 // Oops! Something went wrong. We couldn't release the original
415 // caller's original mutex. Since we've already registered their
416 // wait and already have the condition variable locked, we need
417 // to first de-register their wait and release the condition var-
418 // iable lock before returning our error (i.e. we essentially
419 // need to back out what we previously did just above).
420
421 logmsg("fthreads: BeginWait: fthread_mutex_unlock failed! rc=%d\n"
422 ,rc );
423
424 pFT_COND_VAR->nNumWaiting--; // (de-register wait request)
425
426 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
427
428 return RC(rc);
429 }
430
431 // Our "begin-to-wait-on-condition-variable" task has been successfully
432 // completed. We have essentially atomically released the originally mutex
433 // and "begun our wait" on it (by registering the fact that there's someone
434 // wanting to wait on it). Return to OUR caller with the condition variable
435 // still locked (so no signals can be sent nor any threads released until
436 // our caller calls the below WaitForTransmission function)...
437
438 return RC(0); // (success)
439 }
440
441 ////////////////////////////////////////////////////////////////////////////////////
442 // Wait for the condition variable in question to receive a transmission...
443
WaitForTransmission(FT_COND_VAR * pFT_COND_VAR,struct timespec * pTimeTimeout)444 static int WaitForTransmission
445 (
446 FT_COND_VAR* pFT_COND_VAR,
447 struct timespec* pTimeTimeout // (NULL == INFINITE wait)
448 )
449 {
450 DWORD dwWaitRetCode, dwWaitMilliSecs;
451
452 // If the signal has already arrived (i.e. is still being transmitted)
453 // then there's no need to wait for it. Simply return success with the
454 // condition variable still locked...
455
456 if ( IsEventSet ( pFT_COND_VAR->hSigXmitEvent ) )
457 {
458 // There's no need to wait for the signal (transmission)
459 // to arrive because it's already been sent! Just return.
460
461 return 0; // (transmission received!)
462 }
463
464 // Our loop to wait for our transmission to arrive...
465
466 do
467 {
468 // Release condition var lock (so signal (transmission) can
469 // be sent) and then wait for the signal (transmission)...
470
471 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
472
473 // Need to calculate a timeout value if this is a
474 // timed condition wait as opposed to a normal wait...
475
476 // Note that we unfortunately need to do this on each iteration
477 // because Window's wait API requires a relative timeout value
478 // rather than an absolute TOD timeout value like pthreads...
479
480 if ( !pTimeTimeout )
481 {
482 dwWaitMilliSecs = INFINITE;
483 }
484 else
485 {
486 struct timeval TimeNow;
487
488 gettimeofday ( &TimeNow, NULL );
489
490 if (TimeNow.tv_sec > pTimeTimeout->tv_sec
491 ||
492 (
493 TimeNow.tv_sec == pTimeTimeout->tv_sec
494 &&
495 (TimeNow.tv_usec * 1000) > pTimeTimeout->tv_nsec
496 )
497 )
498 {
499 dwWaitMilliSecs = 0;
500 }
501 else
502 {
503 dwWaitMilliSecs =
504 ((pTimeTimeout->tv_sec - TimeNow.tv_sec) * 1000) +
505 ((pTimeTimeout->tv_nsec - (TimeNow.tv_usec * 1000)) / 1000000);
506 }
507 }
508
509 // Finally we get to do the actual wait...
510
511 dwWaitRetCode =
512 MyWaitForSingleObject ( pFT_COND_VAR->hSigXmitEvent, dwWaitMilliSecs );
513
514 // A signal (transmission) has been sent; reacquire our condition var lock
515 // and receive the transmission (if it's still being transmitted that is)...
516
517 MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
518
519 // The "WAIT_OBJECT_0 == dwWaitRetCode && ..." clause in the below 'while'
520 // statement ensures that we will always break out of our wait loop whenever
521 // either a timeout occurs or our actual MyWaitForSingleObject call fails
522 // for any reason. As long as dwWaitRetCode is WAIT_OBJECT_0 though *AND*
523 // our event has still not been signaled yet, then we'll continue looping
524 // to wait for a signal (transmission) that we're supposed to receive...
525
526 // Also note that one might at first think/ask: "Gee, Fish, why do we need
527 // to check to see if the condition variable's "hSigXmitEvent" event has
528 // been set each time (via the 'IsEventSet' macro)? Shouldn't it *always*
529 // be set if the above MyWaitForSingleObject call returns??" The answer is
530 // of course no, it might NOT [still] be signaled. This is because whenever
531 // someone *does* happen to signal it, we will of course be released from
532 // our wait (the above MyWaitForSingleObject call) BUT... someone else who
533 // was also waiting for it may have managed to grab our condition variable
534 // lock before we could and they could have reset it. Thus we need to check
535 // it again each time.
536 }
537 while ( WAIT_OBJECT_0 == dwWaitRetCode && !IsEventSet( pFT_COND_VAR->hSigXmitEvent ) );
538
539 // Our signal (transmission) has either [finally] arrived or else we got
540 // tired of waiting for it (i.e. we timed out) or else there was an error...
541
542 if ( WAIT_OBJECT_0 == dwWaitRetCode ) return RC(0);
543 if ( WAIT_TIMEOUT == dwWaitRetCode ) return RC(ETIMEDOUT);
544
545 // Our wait failed! Something is VERY wrong! Maybe the condition variable
546 // was prematurely destroyed by someone? <shrug> In any case there's not
547 // much we can do about it other than log the fact that it occurred and
548 // return the error back to the caller. Their wait request has, believe
549 // it or not, actually been completed (although not as they expected it
550 // would in all likelihood!)...
551
552 logmsg ( "fthreads: WaitForTransmission: MyWaitForSingleObject failed! dwWaitRetCode=%d (0x%8.8X)\n"
553 ,dwWaitRetCode ,dwWaitRetCode );
554
555 return RC(EFAULT);
556 }
557
558 ////////////////////////////////////////////////////////////////////////////////////
559 // Send a "signal" or "broadcast" to a condition variable...
560
QueueTransmission(FT_COND_VAR * pFT_COND_VAR,BOOL bXmitType)561 static int QueueTransmission
562 (
563 FT_COND_VAR* pFT_COND_VAR,
564 BOOL bXmitType
565 )
566 {
567 if (0
568 || !pFT_COND_VAR // (invalid ptr)
569 || pFT_COND_VAR->dwCondMagic != FT_COND_MAGIC // (not initialized)
570 )
571 {
572 return RC(EINVAL); // (invalid parameters were passed)
573 }
574
575 // Wait for the condition variable to become free so we can begin transmitting
576 // our signal... If the condition variable is still "busy" (in use), then that
577 // means threads are still in the process of being released as a result of some
578 // prior signal (transmission). Thus we must wait until that work is completed
579 // first before we can send our new signal (transmission)...
580
581 for (;;)
582 {
583 MyEnterCriticalSection ( &pFT_COND_VAR->CondVarLock );
584 if ( IsEventSet ( pFT_COND_VAR->hSigRecvdEvent ) ) break;
585 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
586 MyWaitForSingleObject ( pFT_COND_VAR->hSigRecvdEvent, INFINITE );
587 }
588
589 // Turn on our transmitter... (i.e. start transmitting our "signal" to
590 // all threads who might be waiting to receive it (if there are any)...
591
592 // If no one has registered their interest in receiving any transmissions
593 // associated with this particular condition variable, then they are unfor-
594 // tunately a little too late in doing so because we're ready to start our
595 // transmission right now! If there's no one to receive our transmission,
596 // then it simply gets lost (i.e. a "missed signal" situation has essenti-
597 // ally occurred), but then that's not our concern here; our only concern
598 // here is to transmit the signal (transmission) and nothing more. Tough
599 // beans if there's no one listening to receive it... <shrug>
600
601 if ( pFT_COND_VAR->nNumWaiting ) // (anyone interested?)
602 {
603 pFT_COND_VAR->bBroadcastSig = bXmitType; // (yep! set xmit type)
604 MySetEvent ( pFT_COND_VAR->hSigXmitEvent ); // (turn on transmitter)
605 MyResetEvent ( pFT_COND_VAR->hSigRecvdEvent ); // (serialize reception)
606 }
607
608 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
609
610 return RC(0);
611 }
612
613 ////////////////////////////////////////////////////////////////////////////////////
614 // A thread has been released as a result of someone's signal or broadcast...
615
ReceiveXmission(FT_COND_VAR * pFT_COND_VAR)616 static void ReceiveXmission
617 (
618 FT_COND_VAR* pFT_COND_VAR
619 )
620 {
621 // If we were the only ones supposed to receive the transmission, (or
622 // if no one remains to receive any transmissions), then turn off the
623 // transmitter (i.e. stop sending the signal) and indicate that it has
624 // been completely received all interested parties (i.e. by everyone
625 // who was supposed to receive it)...
626
627 pFT_COND_VAR->nNumWaiting--; // (de-register wait since transmission
628 // has been successfully received now)
629
630 // Determine whether any more waiters (threads) should also receive
631 // this transmission (i.e. also be released) or whether we should
632 // reset (turn off) our transmitter so as to not release any other
633 // thread(s) besides ourselves...
634
635 if (0
636 || !pFT_COND_VAR->bBroadcastSig // ("signal" == only us)
637 || pFT_COND_VAR->nNumWaiting <= 0 // (no one else == only us)
638 )
639 {
640 MyResetEvent ( pFT_COND_VAR->hSigXmitEvent ); // (turn off transmitter)
641 MySetEvent ( pFT_COND_VAR->hSigRecvdEvent ); // (transmission complete)
642 }
643 }
644
645 ////////////////////////////////////////////////////////////////////////////////////
646 // The following function is called just before returning back to the caller. It
647 // first releases the condition variable lock (since we're now done with it) and
648 // then reacquires the caller's original mutex before returning [back to the caller].
649
650 // We MUST do things in that order! 1) release our condition variable lock, and THEN
651 // 2) try to reacquire the caller's original mutex. Otherwise a deadlock could occur!
652
653 // If we still had the condition variable locked before trying to acquire the caller's
654 // original mutex, we could easily become blocked if some other thread still already
655 // owned (had locked) the caller's mutex. We would then be unable to ever release our
656 // condition variable lock until whoever had the mutex locked first released it, but
657 // they would never be able to release it because we still had the condition variable
658 // still locked! Recall that upon entry to a wait call (see previous BeginWait function)
659 // we acquire the condition variable lock *first* (in order to register the caller's
660 // wait) before releasing their mutex. Thus, we MUST therefore release our condition
661 // variable lock FIRST and THEN try reacquiring their original mutex before returning.
662
ReturnFromWait(FT_COND_VAR * pFT_COND_VAR,fthread_mutex_t * pFTUSER_MUTEX,int nRetCode)663 static int ReturnFromWait
664 (
665 FT_COND_VAR* pFT_COND_VAR,
666 fthread_mutex_t* pFTUSER_MUTEX,
667 int nRetCode
668 )
669 {
670 int rc;
671 FT_MUTEX* pFT_MUTEX;
672
673 if (0
674 || !pFT_COND_VAR // (invalid ptr)
675 || pFT_COND_VAR -> dwCondMagic != FT_COND_MAGIC // (not initialized)
676 || !pFTUSER_MUTEX // (invalid ptr)
677 || pFTUSER_MUTEX-> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
678 || !(pFT_MUTEX = pFTUSER_MUTEX->hMutex) // (invalid ptr)
679 // || !pFT_MUTEX // (invalid ptr)
680 || pFT_MUTEX -> dwMutexMagic != FT_MUTEX_MAGIC // (not initialized)
681 )
682 return RC(EINVAL);
683
684 // (let other threads access this condition variable now...)
685
686 MyLeaveCriticalSection ( &pFT_COND_VAR->CondVarLock );
687
688 // (reacquire original mutex before returning back to the original caller...)
689
690 if
691 (
692 (
693 rc = fthread_mutex_lock
694 (
695 pFTUSER_MUTEX
696 )
697 )
698 != 0
699 )
700 {
701 // Oops! We were unable to reacquire the caller's original mutex! This
702 // is actually a catastrophic type of error! The caller expects their
703 // mutex to still be owned (locked) by themselves upon return, but we
704 // were unable to reacquire it for them! Unfortunately there's nothing
705 // we can do about this; the system is essentially hosed at this point.
706 // Just log the fact that something went wrong and return. <shrug>
707
708 logmsg("fthreads: ReturnFromWait: fthread_mutex_lock failed! rc=%d\n"
709 ,rc );
710
711 return RC(rc); // (what went wrong)
712 }
713
714 // Return to caller with the requested return code. (The caller passes to us
715 // the return code they wish to pass back to the original caller, so we just
716 // return that same return code back to OUR caller (so they can then pass it
717 // back to the original fthreads caller)).
718
719 return RC(nRetCode); // (as requested)
720 }
721
722 ////////////////////////////////////////////////////////////////////////////////////
723 ////////////////////////////////////////////////////////////////////////////////////
724 ////////////////////////////////////////////////////////////////////////////////////
725 // Threading functions...
726
727 LIST_ENTRY ThreadListHead; // head list entry of list of joinable threads
728 CRITICAL_SECTION ThreadListLock; // lock for accessing list of joinable threads
729
730 #define LockThreadsList() EnterCriticalSection ( &ThreadListLock )
731 #define UnlockThreadsList() LeaveCriticalSection ( &ThreadListLock )
732
733 ////////////////////////////////////////////////////////////////////////////////////
734 // internal joinable thread information
735
736 typedef struct _tagFTHREAD
737 {
738 LIST_ENTRY ThreadListLink; // (links entries together in a chain)
739 DWORD dwThreadID; // (thread-id)
740 HANDLE hThreadHandle; // (Win32 thread handle)
741 BOOL bJoinable; // (whether thread is joinable or detached)
742 int nJoinedCount; // (#of threads that did join on this one)
743 void* ExitVal; // (saved thread exit value)
744 jmp_buf JumpBuf; // (jump buffer for fthread_exit)
745 }
746 FTHREAD, *PFTHREAD;
747
748 ////////////////////////////////////////////////////////////////////////////////////
749 // (Note: returns with thread list lock still held if found; not held if not found)
750
FindFTHREAD(DWORD dwThreadID)751 static FTHREAD* FindFTHREAD ( DWORD dwThreadID )
752 {
753 FTHREAD* pFTHREAD;
754 LIST_ENTRY* pListEntry;
755
756 LockThreadsList(); // (acquire thread list lock)
757
758 pListEntry = ThreadListHead.Flink;
759
760 while ( pListEntry != &ThreadListHead )
761 {
762 pFTHREAD = CONTAINING_RECORD ( pListEntry, FTHREAD, ThreadListLink );
763
764 pListEntry = pListEntry->Flink;
765
766 if ( pFTHREAD->dwThreadID != dwThreadID )
767 continue;
768
769 return pFTHREAD; // (return with thread list lock still held)
770 }
771
772 UnlockThreadsList(); // (release thread list lock)
773
774 return NULL; // (not found)
775 }
776
777 ////////////////////////////////////////////////////////////////////////////////////
778
779 typedef struct _ftCallThreadParms
780 {
781 PFT_THREAD_FUNC pfnTheirThreadFunc;
782 void* pvTheirThreadArgs;
783 char* pszTheirThreadName;
784 FTHREAD* pFTHREAD;
785 }
786 FT_CALL_THREAD_PARMS;
787
788 //----------------------------------------------------------------------------------
789
FTWin32ThreadFunc(void * pMyArgs)790 static DWORD __stdcall FTWin32ThreadFunc
791 (
792 void* pMyArgs
793 )
794 {
795 FT_CALL_THREAD_PARMS* pCallTheirThreadParms;
796 PFT_THREAD_FUNC pfnTheirThreadFunc;
797 void* pvTheirThreadArgs;
798 FTHREAD* pFTHREAD;
799
800 pCallTheirThreadParms = (FT_CALL_THREAD_PARMS*) pMyArgs;
801
802 pfnTheirThreadFunc = pCallTheirThreadParms->pfnTheirThreadFunc;
803 pvTheirThreadArgs = pCallTheirThreadParms->pvTheirThreadArgs;
804 pFTHREAD = pCallTheirThreadParms->pFTHREAD;
805
806 // PROGRAMMING NOTE: -1 == "current calling thread"
807 SET_THREAD_NAME_ID ( -1, pCallTheirThreadParms->pszTheirThreadName );
808
809 free ( pCallTheirThreadParms );
810
811 if ( setjmp ( pFTHREAD->JumpBuf ) == 0 )
812 pFTHREAD->ExitVal = pfnTheirThreadFunc ( pvTheirThreadArgs );
813
814 LockThreadsList();
815
816 if ( !pFTHREAD->bJoinable )
817 {
818 // If we are not a joinable thread, we must free our
819 // own resources ourselves, but ONLY IF the 'joined'
820 // count is zero. If the 'joined' count is NOT zero,
821 // then, however it occurred, there is still someone
822 // waiting in the join function for us to exit, and
823 // thus, we cannot free our resources at this time
824 // (since the thread that did the join and which is
825 // waiting for us to exit still needs access to our
826 // resources). In such a situation the actual freeing
827 // of resources is deferred and will be done by the
828 // join function itself whenever it's done with them.
829
830 if ( pFTHREAD->nJoinedCount <= 0 )
831 {
832 CloseHandle ( pFTHREAD->hThreadHandle );
833 RemoveListEntry ( &pFTHREAD->ThreadListLink );
834 free ( pFTHREAD );
835 }
836 }
837
838 UnlockThreadsList();
839
840 MyExitThread ( 0 );
841
842 return 0; // (make compiler happy)
843 }
844
845 ////////////////////////////////////////////////////////////////////////////////////
846 // Create a new thread...
847
848 DLL_EXPORT
fthread_create(fthread_t * pdwThreadID,fthread_attr_t * pThreadAttr,PFT_THREAD_FUNC pfnThreadFunc,void * pvThreadArgs,char * pszThreadName)849 int fthread_create
850 (
851 fthread_t* pdwThreadID,
852 fthread_attr_t* pThreadAttr,
853 PFT_THREAD_FUNC pfnThreadFunc,
854 void* pvThreadArgs,
855 char* pszThreadName
856 )
857 {
858 static BOOL bDidInit = FALSE;
859 FT_CALL_THREAD_PARMS* pCallTheirThreadParms;
860 size_t nStackSize;
861 int nDetachState;
862 FTHREAD* pFTHREAD;
863 HANDLE hThread;
864 DWORD dwThreadID;
865
866 if ( !bDidInit )
867 {
868 bDidInit = TRUE;
869 InitializeListHead ( &ThreadListHead );
870 InitializeCriticalSection ( &ThreadListLock );
871 }
872
873 if (0
874 || !pdwThreadID
875 || !pfnThreadFunc
876 )
877 return RC(EINVAL);
878
879 if ( pThreadAttr )
880 {
881 if ( pThreadAttr->dwAttrMagic != FT_ATTR_MAGIC ||
882 ( pThreadAttr->nDetachState != FTHREAD_CREATE_DETACHED &&
883 pThreadAttr->nDetachState != FTHREAD_CREATE_JOINABLE ) )
884 return RC(EINVAL);
885
886 nStackSize = pThreadAttr->nStackSize;
887 nDetachState = pThreadAttr->nDetachState;
888 }
889 else
890 {
891 nStackSize = 0;
892 nDetachState = FTHREAD_CREATE_DEFAULT;
893 }
894
895 pCallTheirThreadParms = (FT_CALL_THREAD_PARMS*)
896 malloc ( sizeof ( FT_CALL_THREAD_PARMS ) );
897
898 if ( !pCallTheirThreadParms )
899 {
900 logmsg("fthread_create: malloc(FT_CALL_THREAD_PARMS) failed\n");
901 return RC(ENOMEM); // (out of memory)
902 }
903
904 pFTHREAD = (FTHREAD*)
905 malloc ( sizeof( FTHREAD ) );
906
907 if ( !pFTHREAD )
908 {
909 logmsg("fthread_create: malloc(FTHREAD) failed\n");
910 free ( pCallTheirThreadParms );
911 return RC(ENOMEM); // (out of memory)
912 }
913
914 pCallTheirThreadParms->pfnTheirThreadFunc = pfnThreadFunc;
915 pCallTheirThreadParms->pvTheirThreadArgs = pvThreadArgs;
916 pCallTheirThreadParms->pszTheirThreadName = pszThreadName;
917 pCallTheirThreadParms->pFTHREAD = pFTHREAD;
918
919 InitializeListLink(&pFTHREAD->ThreadListLink);
920
921 pFTHREAD->dwThreadID = 0;
922 pFTHREAD->hThreadHandle = NULL;
923 pFTHREAD->bJoinable = ((FTHREAD_CREATE_JOINABLE == nDetachState) ? (TRUE) : (FALSE));
924 pFTHREAD->nJoinedCount = 0;
925 pFTHREAD->ExitVal = NULL;
926
927 LockThreadsList();
928
929 hThread =
930 MyCreateThread ( NULL, nStackSize, FTWin32ThreadFunc, pCallTheirThreadParms, 0, &dwThreadID );
931
932 if ( !hThread )
933 {
934 UnlockThreadsList();
935 logmsg("fthread_create: MyCreateThread failed\n");
936 free ( pCallTheirThreadParms );
937 free ( pFTHREAD );
938 return RC(EAGAIN); // (unable to obtain required resources)
939 }
940
941 pFTHREAD->hThreadHandle = hThread;
942 pFTHREAD->dwThreadID = dwThreadID;
943 *pdwThreadID = dwThreadID;
944
945 InsertListHead ( &ThreadListHead, &pFTHREAD->ThreadListLink );
946
947 UnlockThreadsList();
948
949 return RC(0);
950 }
951
952 ////////////////////////////////////////////////////////////////////////////////////
953 // Exit from a thread...
954
955 DLL_EXPORT
fthread_exit(void * ExitVal)956 void fthread_exit
957 (
958 void* ExitVal
959 )
960 {
961 FTHREAD* pFTHREAD;
962 VERIFY ( pFTHREAD = FindFTHREAD ( GetCurrentThreadId() ) );
963 pFTHREAD->ExitVal = ExitVal;
964 UnlockThreadsList();
965 longjmp ( pFTHREAD->JumpBuf, 1 );
966 }
967
968 ////////////////////////////////////////////////////////////////////////////////////
969 // Join a thread (i.e. wait for a thread's termination)...
970
971 DLL_EXPORT
fthread_join(fthread_t dwThreadID,void ** pExitVal)972 int fthread_join
973 (
974 fthread_t dwThreadID,
975 void** pExitVal
976 )
977 {
978 HANDLE hThread;
979 FTHREAD* pFTHREAD;
980
981 if ( GetCurrentThreadId() == dwThreadID )
982 return RC(EDEADLK); // (can't join self!)
983
984 if ( !(pFTHREAD = FindFTHREAD ( dwThreadID ) ) )
985 return RC(ESRCH); // (thread not found)
986
987 // (Note: threads list lock still held at this point
988 // since thread was found...)
989
990 if ( !pFTHREAD->bJoinable )
991 {
992 UnlockThreadsList();
993 return RC(EINVAL); // (not a joinable thread)
994 }
995
996 ASSERT ( pFTHREAD->nJoinedCount >= 0 );
997 pFTHREAD->nJoinedCount++;
998 hThread = pFTHREAD->hThreadHandle;
999
1000 // Wait for thread to exit...
1001
1002 UnlockThreadsList();
1003 {
1004 WaitForSingleObject ( hThread, INFINITE );
1005 }
1006 LockThreadsList();
1007
1008 if ( pExitVal )
1009 *pExitVal = pFTHREAD->ExitVal; // (pass back thread's exit value)
1010
1011 ASSERT ( pFTHREAD->nJoinedCount > 0 );
1012 pFTHREAD->nJoinedCount--;
1013
1014 // If this is the last thread to be resumed after having been suspended
1015 // (as a result of doing the join), then we need to do the detach (i.e.
1016 // to free resources), BUT ONLY IF the detach for the thread in question
1017 // has already been done by someone (which can be determined by virtue of
1018 // the "joinable" flag having already been changed back to non-joinable).
1019
1020 // The idea here is that the 'detach' function purposely does not free
1021 // the resources unless the 'joined' count is zero. If the joined count
1022 // is NOT zero whenever the detach function is called, then the resources
1023 // cannot be freed since there's still a thread waiting to be resumed
1024 // from its join (perhaps us!), and it obviously still needs access to
1025 // the resources in question. Thus, in such a situation (i.e. there still
1026 // being a thread remaining to be woken up from its join when the detach
1027 // is done), the freeing of resources normally done by the detach function
1028 // is deferred so that WE can do the resource freeing ourselves once we
1029 // are done with them.
1030
1031 if ( !pFTHREAD->bJoinable && pFTHREAD->nJoinedCount <= 0 )
1032 {
1033 CloseHandle ( pFTHREAD->hThreadHandle );
1034 RemoveListEntry ( &pFTHREAD->ThreadListLink );
1035 free ( pFTHREAD );
1036 }
1037
1038 UnlockThreadsList();
1039
1040 return RC(0);
1041 }
1042
1043 ////////////////////////////////////////////////////////////////////////////////////
1044 // Detach a thread (i.e. ignore a thread's termination)...
1045
1046 DLL_EXPORT
fthread_detach(fthread_t dwThreadID)1047 int fthread_detach
1048 (
1049 fthread_t dwThreadID
1050 )
1051 {
1052 FTHREAD* pFTHREAD;
1053
1054 if ( !( pFTHREAD = FindFTHREAD ( dwThreadID ) ) )
1055 return RC(ESRCH); // (thread not found)
1056
1057 // (Note: threads list lock still held at this point
1058 // since thread was found...)
1059
1060 if ( !pFTHREAD->bJoinable )
1061 {
1062 UnlockThreadsList();
1063 return RC(EINVAL); // (not a joinable thread)
1064 }
1065
1066 // If the thread has not yet exited, then IT will free its
1067 // own resources itself whenever it eventually does exit by
1068 // virtue of our changing it to a non-joinable thread type.
1069
1070 pFTHREAD->bJoinable = FALSE; // (indicate detach was done)
1071
1072 // Otherwise we need to free its resources ourselves since
1073 // it obviously can't (since it has already exited).
1074
1075 // Note that we cannot free the resources ourselves even if the
1076 // thread has already exited if there are still other threads
1077 // waiting to be woken up from their own join (since they will
1078 // still need to have access to the resources). In other words,
1079 // even if the thread has already exited (and thus it would seem
1080 // that our freeing the resources would be the proper thing to
1081 // do), we CANNOT do so if the 'join' count is non-zero.
1082
1083 // In such a situation (the join count being non-zero indicating
1084 // there is still another thread waiting to be resumed from its
1085 // own join), we simply defer the actual freeing of resources to
1086 // the thread still waiting to be woken up from its join. Whenever
1087 // it does eventually wake up from its join, it will free the
1088 // resources for us, as long as we remember to reset the 'joinable'
1089 // flag back to non-joinable (which we've already done just above).
1090
1091 if ( IsEventSet ( pFTHREAD->hThreadHandle ) && pFTHREAD->nJoinedCount <= 0 )
1092 {
1093 CloseHandle ( pFTHREAD->hThreadHandle );
1094 RemoveListEntry ( &pFTHREAD->ThreadListLink );
1095 free ( pFTHREAD );
1096 }
1097
1098 UnlockThreadsList();
1099
1100 return RC(0);
1101 }
1102
1103 ////////////////////////////////////////////////////////////////////////////////////
1104 // Initialize a "thread attribute"...
1105
1106 DLL_EXPORT
fthread_attr_init(fthread_attr_t * pThreadAttr)1107 int fthread_attr_init
1108 (
1109 fthread_attr_t* pThreadAttr
1110 )
1111 {
1112 if ( !pThreadAttr )
1113 return RC(EINVAL); // (invalid ptr)
1114
1115 if ( FT_ATTR_MAGIC == pThreadAttr->dwAttrMagic )
1116 return RC(EBUSY); // (already initialized)
1117
1118 pThreadAttr->dwAttrMagic = FT_ATTR_MAGIC;
1119 pThreadAttr->nDetachState = FTHREAD_CREATE_DEFAULT;
1120 pThreadAttr->nStackSize = 0;
1121
1122 return RC(0);
1123 }
1124
1125 ////////////////////////////////////////////////////////////////////////////////////
1126 // Destroy a "thread attribute"...
1127
1128 DLL_EXPORT
fthread_attr_destroy(fthread_attr_t * pThreadAttr)1129 int fthread_attr_destroy
1130 (
1131 fthread_attr_t* pThreadAttr
1132 )
1133 {
1134 if ( !pThreadAttr )
1135 return RC(EINVAL); // (invalid ptr)
1136
1137 if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
1138 return RC(EINVAL); // (not initialized)
1139
1140 pThreadAttr->dwAttrMagic = 0;
1141 pThreadAttr->nDetachState = 0;
1142 pThreadAttr->nStackSize = 0;
1143
1144 return RC(0);
1145 }
1146
1147 ////////////////////////////////////////////////////////////////////////////////////
1148 // Set a thread's "detachstate" attribute...
1149
1150 DLL_EXPORT
fthread_attr_setdetachstate(fthread_attr_t * pThreadAttr,int nDetachState)1151 int fthread_attr_setdetachstate
1152 (
1153 fthread_attr_t* pThreadAttr,
1154 int nDetachState
1155 )
1156 {
1157 if ( !pThreadAttr )
1158 return RC(EINVAL); // (invalid ptr)
1159
1160 if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
1161 return RC(EINVAL); // (not initialized)
1162
1163 if ( FTHREAD_CREATE_DETACHED != nDetachState &&
1164 FTHREAD_CREATE_JOINABLE != nDetachState )
1165 return RC(EINVAL); // (invalid detach state)
1166
1167 pThreadAttr->nDetachState = nDetachState;
1168
1169 return RC(0);
1170 }
1171
1172 ////////////////////////////////////////////////////////////////////////////////////
1173 // Retrieve a thread's "detachstate" attribute...
1174
1175 DLL_EXPORT
fthread_attr_getdetachstate(const fthread_attr_t * pThreadAttr,int * pnDetachState)1176 int fthread_attr_getdetachstate
1177 (
1178 const fthread_attr_t* pThreadAttr,
1179 int* pnDetachState
1180 )
1181 {
1182 if ( !pThreadAttr || !pnDetachState )
1183 return RC(EINVAL); // (invalid ptr)
1184
1185 if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
1186 return RC(EINVAL); // (not initialized)
1187
1188 if ( FTHREAD_CREATE_DETACHED != pThreadAttr->nDetachState &&
1189 FTHREAD_CREATE_JOINABLE != pThreadAttr->nDetachState )
1190 return RC(EINVAL); // (invalid detach state)
1191
1192 *pnDetachState = pThreadAttr->nDetachState;
1193
1194 return RC(0);
1195 }
1196
1197 ////////////////////////////////////////////////////////////////////////////////////
1198 // Set a thread's initial stack size...
1199
1200 DLL_EXPORT
fthread_attr_setstacksize(fthread_attr_t * pThreadAttr,size_t nStackSize)1201 int fthread_attr_setstacksize
1202 (
1203 fthread_attr_t* pThreadAttr,
1204 size_t nStackSize
1205 )
1206 {
1207 if ( !pThreadAttr )
1208 return RC(EINVAL); // (invalid ptr)
1209
1210 if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
1211 return RC(EINVAL); // (not initialized)
1212
1213 pThreadAttr->nStackSize = nStackSize;
1214
1215 return RC(0);
1216 }
1217
1218 ////////////////////////////////////////////////////////////////////////////////////
1219 // Retrieve a thread's initial stack size...
1220
1221 DLL_EXPORT
fthread_attr_getstacksize(const fthread_attr_t * pThreadAttr,size_t * pnStackSize)1222 int fthread_attr_getstacksize
1223 (
1224 const fthread_attr_t* pThreadAttr,
1225 size_t* pnStackSize
1226 )
1227 {
1228 if ( !pThreadAttr || !pnStackSize )
1229 return RC(EINVAL); // (invalid ptr)
1230
1231 if ( FT_ATTR_MAGIC != pThreadAttr->dwAttrMagic )
1232 return RC(EINVAL); // (not initialized)
1233
1234 *pnStackSize = pThreadAttr->nStackSize;
1235
1236 return RC(0);
1237 }
1238
1239 ////////////////////////////////////////////////////////////////////////////////////
1240 // (thread signalling not [currently] supported (yet); always returns ENOTSUP...)
1241
1242 DLL_EXPORT
fthread_kill(int dummy1,int dummy2)1243 int fthread_kill // FIXME: TODO:
1244 (
1245 int dummy1,
1246 int dummy2
1247 )
1248 {
1249 UNREFERENCED ( dummy1 );
1250 UNREFERENCED ( dummy2 );
1251 return RC(ENOTSUP);
1252 }
1253
1254 ////////////////////////////////////////////////////////////////////////////////////
1255 // Return thread-id...
1256
1257 DLL_EXPORT
fthread_self()1258 fthread_t fthread_self ()
1259 {
1260 return GetCurrentThreadId();
1261 }
1262
1263 ////////////////////////////////////////////////////////////////////////////////////
1264 // Compare thread-ids...
1265
1266 DLL_EXPORT
fthread_equal(fthread_t pdwThreadID_1,fthread_t pdwThreadID_2)1267 int fthread_equal
1268 (
1269 fthread_t pdwThreadID_1,
1270 fthread_t pdwThreadID_2
1271 )
1272 {
1273 return ( pdwThreadID_1 == pdwThreadID_2 );
1274 }
1275
1276 ////////////////////////////////////////////////////////////////////////////////////
1277 ////////////////////////////////////////////////////////////////////////////////////
1278 ////////////////////////////////////////////////////////////////////////////////////
1279 // Initialize a lock...
1280
1281 DLL_EXPORT
fthread_mutex_init(fthread_mutex_t * pFTUSER_MUTEX,const fthread_mutexattr_t * pFT_MUTEX_ATTR)1282 int fthread_mutex_init
1283 (
1284 fthread_mutex_t* pFTUSER_MUTEX,
1285 const fthread_mutexattr_t* pFT_MUTEX_ATTR
1286 )
1287 {
1288 DWORD dwMutexType = 0;
1289
1290 if ( !pFTUSER_MUTEX )
1291 return RC(EINVAL); // (invalid mutex ptr)
1292
1293 if ( FT_MUTEX_MAGIC == pFTUSER_MUTEX->dwMutexMagic )
1294 return RC(EBUSY); // (mutex already initialized)
1295
1296 if ( pFT_MUTEX_ATTR && !IsValidMutexType ( dwMutexType = *pFT_MUTEX_ATTR ) )
1297 return RC(EINVAL); // (invalid mutex attr ptr or mutex attr type)
1298
1299 if ( !(pFTUSER_MUTEX->hMutex = MallocFT_MUTEX()) )
1300 return RC(ENOMEM); // (out of memory)
1301
1302 if ( !InitializeFT_MUTEX
1303 (
1304 pFTUSER_MUTEX->hMutex,
1305 pFT_MUTEX_ATTR ? dwMutexType : FTHREAD_MUTEX_DEFAULT
1306 ))
1307 {
1308 free ( pFTUSER_MUTEX->hMutex );
1309
1310 pFTUSER_MUTEX->dwMutexMagic = 0;
1311 pFTUSER_MUTEX->hMutex = NULL;
1312
1313 return RC(EAGAIN); // (unable to obtain required resources)
1314 }
1315
1316 pFTUSER_MUTEX->dwMutexMagic = FT_MUTEX_MAGIC;
1317
1318 return RC(0);
1319 }
1320
1321 ////////////////////////////////////////////////////////////////////////////////////
1322 // Destroy a lock...
1323
1324 DLL_EXPORT
fthread_mutex_destroy(fthread_mutex_t * pFTUSER_MUTEX)1325 int fthread_mutex_destroy
1326 (
1327 fthread_mutex_t* pFTUSER_MUTEX
1328 )
1329 {
1330 if ( !pFTUSER_MUTEX )
1331 return RC(EINVAL); // (invalid ptr)
1332
1333 if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
1334 return RC(EINVAL); // (not initialized)
1335
1336 if ( !UninitializeFT_MUTEX
1337 (
1338 pFTUSER_MUTEX->hMutex
1339 ))
1340 return RC(EBUSY); // (still in use)
1341
1342 free ( pFTUSER_MUTEX->hMutex );
1343
1344 pFTUSER_MUTEX->dwMutexMagic = 0;
1345 pFTUSER_MUTEX->hMutex = NULL;
1346
1347 return RC(0);
1348 }
1349
1350 ////////////////////////////////////////////////////////////////////////////////////
1351 // Try to lock a "mutex"...
1352
1353 DLL_EXPORT
fthread_mutex_trylock(fthread_mutex_t * pFTUSER_MUTEX)1354 int fthread_mutex_trylock
1355 (
1356 fthread_mutex_t* pFTUSER_MUTEX
1357 )
1358 {
1359 if ( !pFTUSER_MUTEX )
1360 return RC(EINVAL); // (invalid ptr)
1361
1362 if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
1363 return RC(EINVAL); // (not initialized)
1364
1365 // Try to acquire the requested mutex...
1366
1367 if
1368 (
1369 !TryEnterFT_MUTEX
1370 (
1371 pFTUSER_MUTEX->hMutex
1372 )
1373 )
1374 // We could not acquire the mutex; return 'busy'...
1375
1376 return RC(EBUSY);
1377
1378 // We successfully acquired the mutex... If the mutex type is recursive,
1379 // or, if not recursive (i.e. error-check), if this was the first/initial
1380 // lock on the mutex, then return success...
1381
1382 if (0
1383 || FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
1384 || ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
1385 )
1386 return RC(0);
1387
1388 ASSERT ( FTHREAD_MUTEX_ERRORCHECK == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
1389 && ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount > 1 );
1390
1391 // The mutex type is error-check and we already previously had the mutex locked
1392 // before (i.e. this was the *second* time we acquired this same mutex). Return
1393 // 'busy' after first releasing the mutex once (to decrement the locked count
1394 // back down to what it was (i.e. 1 (one))).
1395
1396 LeaveFT_MUTEX
1397 (
1398 pFTUSER_MUTEX->hMutex
1399 );
1400
1401 return RC(EBUSY);
1402 }
1403
1404 ////////////////////////////////////////////////////////////////////////////////////
1405 // Lock a "mutex"...
1406
1407 DLL_EXPORT
fthread_mutex_lock(fthread_mutex_t * pFTUSER_MUTEX)1408 int fthread_mutex_lock
1409 (
1410 fthread_mutex_t* pFTUSER_MUTEX
1411 )
1412 {
1413 if ( !pFTUSER_MUTEX )
1414 return RC(EINVAL); // (invalid ptr)
1415
1416 if ( FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic )
1417 return RC(EINVAL); // (not initialized)
1418
1419 // Try to acquire the requested mutex...
1420
1421 if
1422 (
1423 !TryEnterFT_MUTEX
1424 (
1425 pFTUSER_MUTEX->hMutex
1426 )
1427 )
1428 {
1429 // We could not acquire the mutex. This means someone already owns the mutex,
1430 // so just do a normal acquire on the mutex. Both recursive and error-check
1431 // types will block until such time as the mutex is successfully acquired...
1432
1433 EnterFT_MUTEX
1434 (
1435 pFTUSER_MUTEX->hMutex
1436 );
1437
1438 return RC(0);
1439 }
1440
1441 // We successfully acquired the mutex... If the mutex type is recursive,
1442 // or, if not recursive (i.e. error-check), if this was the first/initial
1443 // lock on the mutex, then return success...
1444
1445 if (0
1446 || FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
1447 || ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
1448 )
1449 return RC(0);
1450
1451 ASSERT ( FTHREAD_MUTEX_ERRORCHECK == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType
1452 && ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount > 1 );
1453
1454 // The mutex type is error-check and we already previously had the mutex locked
1455 // before (i.e. this was the *second* time we acquired this same mutex). Return
1456 // 'deadlock' after first releasing the mutex once (to decrement the locked count
1457 // back down to what it was (i.e. 1 (one))).
1458
1459 LeaveFT_MUTEX
1460 (
1461 pFTUSER_MUTEX->hMutex
1462 );
1463
1464 return RC(EDEADLK);
1465 }
1466
1467 ////////////////////////////////////////////////////////////////////////////////////
1468 // Unlock a "mutex"...
1469
1470 DLL_EXPORT
fthread_mutex_unlock(fthread_mutex_t * pFTUSER_MUTEX)1471 int fthread_mutex_unlock
1472 (
1473 fthread_mutex_t* pFTUSER_MUTEX
1474 )
1475 {
1476 if (0
1477 || !pFTUSER_MUTEX // (invalid ptr)
1478 || FT_MUTEX_MAGIC != pFTUSER_MUTEX->dwMutexMagic // (not initialized)
1479 )
1480 return RC(EINVAL);
1481
1482 if (0
1483 || GetCurrentThreadId() != ((PFT_MUTEX)pFTUSER_MUTEX->hMutex)->dwLockOwner // (not owned)
1484 || ((PFT_MUTEX)pFTUSER_MUTEX->hMutex)->nLockedCount <= 0 // (not locked)
1485 )
1486 return RC(EPERM);
1487
1488 ASSERT ( ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->nLockedCount <= 1
1489 || FTHREAD_MUTEX_RECURSIVE == ((FT_MUTEX*)pFTUSER_MUTEX->hMutex)->dwMutexType );
1490
1491 LeaveFT_MUTEX
1492 (
1493 pFTUSER_MUTEX->hMutex
1494 );
1495
1496 return RC(0);
1497 }
1498
1499 ////////////////////////////////////////////////////////////////////////////////////
1500 // Initialize a "condition"...
1501
1502 DLL_EXPORT
fthread_cond_init(fthread_cond_t * pFT_COND_VAR)1503 int fthread_cond_init
1504 (
1505 fthread_cond_t* pFT_COND_VAR
1506 )
1507 {
1508 if ( !pFT_COND_VAR )
1509 return RC(EINVAL); // (invalid ptr)
1510
1511 if ( FT_COND_MAGIC == pFT_COND_VAR->dwCondMagic )
1512 return RC(EBUSY); // (already initialized)
1513
1514 if ( !(pFT_COND_VAR->hCondVar = MallocFT_COND_VAR()) )
1515 return RC(ENOMEM); // (out of memory)
1516
1517 if ( !InitializeFT_COND_VAR
1518 (
1519 pFT_COND_VAR->hCondVar
1520 ))
1521 {
1522 free ( pFT_COND_VAR->hCondVar );
1523
1524 pFT_COND_VAR->dwCondMagic = 0;
1525 pFT_COND_VAR->hCondVar = NULL;
1526
1527 return RC(EAGAIN); // (unable to obtain required resources)
1528 }
1529
1530 pFT_COND_VAR->dwCondMagic = FT_COND_MAGIC;
1531
1532 return RC(0);
1533 }
1534
1535 ////////////////////////////////////////////////////////////////////////////////////
1536 // Destroy a "condition"...
1537
1538 DLL_EXPORT
fthread_cond_destroy(fthread_cond_t * pFT_COND_VAR)1539 int fthread_cond_destroy
1540 (
1541 fthread_cond_t* pFT_COND_VAR
1542 )
1543 {
1544 if ( !pFT_COND_VAR )
1545 return RC(EINVAL); // (invalid ptr)
1546
1547 if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
1548 return RC(EINVAL); // (not initialized)
1549
1550 if ( !UninitializeFT_COND_VAR
1551 (
1552 pFT_COND_VAR->hCondVar
1553 ))
1554 return RC(EBUSY); // (still in use)
1555
1556 free ( pFT_COND_VAR->hCondVar );
1557
1558 pFT_COND_VAR->dwCondMagic = 0;
1559 pFT_COND_VAR->hCondVar = NULL;
1560
1561 return RC(0);
1562 }
1563
1564 ////////////////////////////////////////////////////////////////////////////////////
1565 // 'Signal' a "condition"... (causes ONE waiting thread to be released)
1566
1567 DLL_EXPORT
fthread_cond_signal(fthread_cond_t * pFT_COND_VAR)1568 int fthread_cond_signal
1569 (
1570 fthread_cond_t* pFT_COND_VAR
1571 )
1572 {
1573 if ( !pFT_COND_VAR )
1574 return RC(EINVAL); // (invalid ptr)
1575
1576 if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
1577 return RC(EINVAL); // (not initialized)
1578
1579 return QueueTransmission
1580 (
1581 pFT_COND_VAR->hCondVar,
1582 FALSE // (FALSE == not "broadcast")
1583 );
1584 }
1585
1586 ////////////////////////////////////////////////////////////////////////////////////
1587 // 'Broadcast' a "condition"... (causes ALL waiting threads to be released)
1588
1589 DLL_EXPORT
fthread_cond_broadcast(fthread_cond_t * pFT_COND_VAR)1590 int fthread_cond_broadcast
1591 (
1592 fthread_cond_t* pFT_COND_VAR
1593 )
1594 {
1595 if ( !pFT_COND_VAR )
1596 return RC(EINVAL); // (invalid ptr)
1597
1598 if ( FT_COND_MAGIC != pFT_COND_VAR->dwCondMagic )
1599 return RC(EINVAL); // (not initialized)
1600
1601 return QueueTransmission
1602 (
1603 pFT_COND_VAR->hCondVar,
1604 TRUE // (TRUE == "broadcast" type)
1605 );
1606 }
1607
1608 ////////////////////////////////////////////////////////////////////////////////////
1609 // Wait for a "condition" to occur...
1610
1611 DLL_EXPORT
fthread_cond_wait(fthread_cond_t * pFT_COND_VAR,fthread_mutex_t * pFTUSER_MUTEX)1612 int fthread_cond_wait
1613 (
1614 fthread_cond_t* pFT_COND_VAR,
1615 fthread_mutex_t* pFTUSER_MUTEX
1616 )
1617 {
1618 int rc;
1619
1620 if (0
1621 || !pFT_COND_VAR // (invalid ptr)
1622 || FT_COND_MAGIC != pFT_COND_VAR -> dwCondMagic // (not initialized)
1623 || !pFTUSER_MUTEX // (invalid ptr)
1624 || FT_MUTEX_MAGIC != pFTUSER_MUTEX -> dwMutexMagic // (not initialized)
1625 )
1626 return RC(EINVAL);
1627
1628 // The following call essentially atomically releases the caller's mutex
1629 // and does a wait. Of course, it doesn't really do the wait though; that's
1630 // actually done further below. BUT, it does atomically register the fact
1631 // that this thread *wishes* to do a wait by acquiring the condition variable
1632 // lock and then incrementing the #of waiters counter before it releases the
1633 // original mutex. Thus, whenever the below function call returns back to us,
1634 // we can be assured that: 1) our request to wait on this condition variable
1635 // has been registered AND 2) we have control of the condition variable in
1636 // question (i.e. we still hold the condition variable lock thus preventing
1637 // anyone from trying to send a signal just yet)...
1638
1639 if
1640 (
1641 (
1642 rc = BeginWait
1643 (
1644 pFT_COND_VAR -> hCondVar,
1645 pFTUSER_MUTEX
1646 )
1647 )
1648 != 0
1649 )
1650 {
1651 // OOPS! Something went wrong. The original mutex has NOT been released
1652 // and we did NOT acquire our condition variable lock (and thus our wait
1653 // was not registered). Thus we can safely return back to the caller with
1654 // the original mutex still owned (held) by the caller.
1655
1656 return RC(rc); // (return error code to caller; their wait failed)
1657 }
1658
1659 // We only reach here if the condition var was successfully acquired AND our
1660 // wait was registered AND the original mutex was released so the signal can
1661 // be sent (but the signal (transmission) of course cannot ever be sent until
1662 // we first release our lock on our condition variable, which is of course is
1663 // what the below WaitForTransmission function call does within its wait loop)...
1664
1665 rc = WaitForTransmission // (wait for "signal" or "broadcast"...)
1666 (
1667 pFT_COND_VAR->hCondVar,
1668 NULL
1669 );
1670
1671 // A signal (transmission) was sent and we're one of the ones (or the
1672 // only one) that's supposed to receive it...
1673
1674 // If we're the only one that's supposed to receive this transmission,
1675 // then we need to turn off the transmitter (stop "sending" the signal)
1676 // so that no other threads get "woken up" (released) as a result of
1677 // this particular "signal" (transmission)...
1678
1679 // (Note that the below call also de-registers our wait too)
1680
1681 ReceiveXmission // (reset transmitter)
1682 (
1683 pFT_COND_VAR->hCondVar
1684 );
1685
1686 // Release the condition var lock (since we're done with it) and then
1687 // reacquire the caller's original mutex (if possible) and then return
1688 // back to the original caller with their original mutex held with what-
1689 // ever return code got set by the above wait call...
1690
1691 return ReturnFromWait
1692 (
1693 pFT_COND_VAR -> hCondVar,
1694 pFTUSER_MUTEX,
1695 rc
1696 );
1697 }
1698
1699 ////////////////////////////////////////////////////////////////////////////////////
1700 // Wait (but not forever) for a "condition" to occur...
1701
1702 // Refer to the comments in the above 'fthread_cond_wait' function (as well as all
1703 // of our other internal functions too of course (BeginWait, WaitForTransmission,
1704 // ReceiveXmission and ReturnFromWait)) for details regarding what's going on here
1705 // (i.e. what we're doing below and why). The below function is essentially identical
1706 // to the above 'fthread_cond_wait' function except that we don't wait forever; we
1707 // only wait for a limited amount of time. Other than that they're exactly identical
1708 // so there's no sense in repeating myself here...
1709
1710 DLL_EXPORT
fthread_cond_timedwait(fthread_cond_t * pFT_COND_VAR,fthread_mutex_t * pFTUSER_MUTEX,struct timespec * pTimeTimeout)1711 int fthread_cond_timedwait
1712 (
1713 fthread_cond_t* pFT_COND_VAR,
1714 fthread_mutex_t* pFTUSER_MUTEX,
1715 struct timespec* pTimeTimeout
1716 )
1717 {
1718 int rc;
1719
1720 if (0
1721 || !pFT_COND_VAR
1722 || FT_COND_MAGIC != pFT_COND_VAR -> dwCondMagic
1723 || !pFTUSER_MUTEX
1724 || FT_MUTEX_MAGIC != pFTUSER_MUTEX -> dwMutexMagic
1725 || !pTimeTimeout
1726 )
1727 return RC(EINVAL);
1728
1729 if
1730 (
1731 (
1732 rc = BeginWait
1733 (
1734 pFT_COND_VAR -> hCondVar,
1735 pFTUSER_MUTEX
1736 )
1737 )
1738 != 0
1739 )
1740 return rc;
1741
1742 rc = WaitForTransmission
1743 (
1744 pFT_COND_VAR->hCondVar,
1745 pTimeTimeout
1746 );
1747
1748 ReceiveXmission
1749 (
1750 pFT_COND_VAR->hCondVar
1751 );
1752
1753 return ReturnFromWait
1754 (
1755 pFT_COND_VAR -> hCondVar,
1756 pFTUSER_MUTEX,
1757 rc
1758 );
1759 }
1760
1761 ////////////////////////////////////////////////////////////////////////////////////
1762 ////////////////////////////////////////////////////////////////////////////////////
1763 ////////////////////////////////////////////////////////////////////////////////////
1764 // Initialize a "mutex" attribute...
1765
1766 DLL_EXPORT
fthread_mutexattr_init(fthread_mutexattr_t * pFT_MUTEX_ATTR)1767 int fthread_mutexattr_init ( fthread_mutexattr_t* pFT_MUTEX_ATTR )
1768 {
1769 if ( !pFT_MUTEX_ATTR )
1770 return RC(EINVAL);
1771 *pFT_MUTEX_ATTR = FTHREAD_MUTEX_DEFAULT;
1772 return RC(0);
1773 }
1774
1775 ////////////////////////////////////////////////////////////////////////////////////
1776 // Destroy a "mutex" attribute...
1777
1778 DLL_EXPORT
fthread_mutexattr_destroy(fthread_mutexattr_t * pFT_MUTEX_ATTR)1779 int fthread_mutexattr_destroy ( fthread_mutexattr_t* pFT_MUTEX_ATTR )
1780 {
1781 if ( !pFT_MUTEX_ATTR )
1782 return RC(EINVAL);
1783 *pFT_MUTEX_ATTR = 0xCDCDCDCD;
1784 return RC(0);
1785 }
1786
1787 ////////////////////////////////////////////////////////////////////////////////////
1788 // Retrieve "mutex" attribute type...
1789
1790 DLL_EXPORT
fthread_mutexattr_gettype(const fthread_mutexattr_t * pFT_MUTEX_ATTR,int * pnMutexType)1791 int fthread_mutexattr_gettype
1792 (
1793 const fthread_mutexattr_t* pFT_MUTEX_ATTR,
1794 int* pnMutexType
1795 )
1796 {
1797 DWORD dwMutexType;
1798 if ( !pFT_MUTEX_ATTR || !pnMutexType || !IsValidMutexType ( dwMutexType = *pFT_MUTEX_ATTR ) )
1799 return RC(EINVAL);
1800 *pnMutexType = (int) dwMutexType;
1801 return RC(0);
1802 }
1803
1804 ////////////////////////////////////////////////////////////////////////////////////
1805 // Set "mutex" attribute type...
1806
1807 DLL_EXPORT
fthread_mutexattr_settype(fthread_mutexattr_t * pFT_MUTEX_ATTR,int nMutexType)1808 int fthread_mutexattr_settype
1809 (
1810 fthread_mutexattr_t* pFT_MUTEX_ATTR,
1811 int nMutexType
1812 )
1813 {
1814 if ( !pFT_MUTEX_ATTR || !IsValidMutexType ( (DWORD) nMutexType ) )
1815 return RC(EINVAL);
1816 *pFT_MUTEX_ATTR = (DWORD) nMutexType;
1817 return RC(0);
1818 }
1819
1820 ////////////////////////////////////////////////////////////////////////////////////
1821 ////////////////////////////////////////////////////////////////////////////////////
1822 ////////////////////////////////////////////////////////////////////////////////////
1823
1824 #endif // !defined(OPTION_FTHREADS)
1825