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