b31b88da59476e5b97302f800e0d0bec5e62671c
[platform/upstream/coreclr.git] / src / pal / src / thread / threadsusp.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5
6 /*++
7
8
9
10 Module Name:
11
12     threadsusp.cpp
13
14 Abstract:
15
16     Implementation of functions related to threads.
17
18 Revision History:
19
20
21
22 --*/
23
24 #include "pal/corunix.hpp"
25 #include "pal/thread.hpp"
26 #include "pal/mutex.hpp"
27 #include "pal/seh.hpp"
28 #include "pal/init.h"
29 #include "pal/dbgmsg.h"
30
31 #include <pthread.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <stddef.h>
35 #include <sys/stat.h>
36 #include <limits.h>
37 #include <debugmacrosext.h>
38
39 #if defined(_AIX)
40 // AIX requires explicit definition of the union semun (see semctl man page)
41 union semun 
42 {
43     int val;
44     struct semid_ds * buf;
45     unsigned short * array;
46 };
47 #endif 
48
49 using namespace CorUnix;
50
51 /* ------------------- Definitions ------------------------------*/
52 SET_DEFAULT_DEBUG_CHANNEL(THREAD);
53
54 /* This code is written to the blocking pipe of a thread that was created
55    in suspended state in order to resume it. */
56 CONST BYTE WAKEUPCODE=0x2A;
57
58 // #define USE_GLOBAL_LOCK_FOR_SUSPENSION // Uncomment this define to use the global suspension lock. 
59 /* The global suspension lock can be used in place of each thread having its own
60 suspension mutex or spinlock. The downside is that it restricts us to only
61 performing one suspension or resumption in the PAL at a time. */
62 #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
63 static LONG g_ssSuspensionLock = 0;
64 #endif
65
66 /*++
67 Function:
68   InternalSuspendNewThreadFromData
69
70   On platforms where we use pipes for starting threads suspended, this
71   function sets the blocking pipe for the thread and blocks until the
72   wakeup code is written to the pipe by ResumeThread.
73
74 --*/
75 PAL_ERROR
76 CThreadSuspensionInfo::InternalSuspendNewThreadFromData(
77     CPalThread *pThread
78     )
79 {
80     PAL_ERROR palError = NO_ERROR;
81
82     AcquireSuspensionLock(pThread);
83     pThread->suspensionInfo.SetSelfSusp(TRUE);
84     ReleaseSuspensionLock(pThread);
85
86     int pipe_descs[2];
87     if (pipe(pipe_descs) == -1)
88     {
89         ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
90         return ERROR_NOT_ENOUGH_MEMORY;
91     }
92
93     // [0] is the read end of the pipe, and [1] is the write end.
94     pThread->suspensionInfo.SetBlockingPipe(pipe_descs[1]);
95     pThread->SetStartStatus(TRUE);
96
97     BYTE resume_code = 0;
98     ssize_t read_ret;
99     
100     // Block until ResumeThread writes something to the pipe
101     while ((read_ret = read(pipe_descs[0], &resume_code, sizeof(resume_code))) != sizeof(resume_code))
102     {
103         if (read_ret != -1 || EINTR != errno)
104         {
105             // read might return 0 (with EAGAIN) if the other end of the pipe gets closed
106             palError = ERROR_INTERNAL_ERROR;
107             break;
108         }
109     }
110
111     if (palError == NO_ERROR && resume_code != WAKEUPCODE)
112     {
113         // If we did read successfully but the byte didn't match WAKEUPCODE, we treat it as a failure.
114         palError = ERROR_INTERNAL_ERROR;
115     }
116
117     if (palError == NO_ERROR)
118     {
119         AcquireSuspensionLock(pThread);
120         pThread->suspensionInfo.SetSelfSusp(FALSE);
121         ReleaseSuspensionLock(pThread);
122     }
123
124     // Close the pipes regardless of whether we were successful.
125     close(pipe_descs[0]);
126     close(pipe_descs[1]);
127
128     return palError;
129 }
130
131 /*++
132 Function:
133
134   ResumeThread
135
136 See MSDN doc.
137 --*/
138 DWORD
139 PALAPI
140 ResumeThread(
141          IN HANDLE hThread
142          )
143 {
144     PAL_ERROR palError;
145     CPalThread *pthrResumer;
146     DWORD dwSuspendCount = (DWORD)-1;
147
148     PERF_ENTRY(ResumeThread);
149     ENTRY("ResumeThread(hThread=%p)\n", hThread);
150
151     pthrResumer = InternalGetCurrentThread();
152     palError = InternalResumeThread(
153         pthrResumer,
154         hThread,
155         &dwSuspendCount
156         );
157
158     if (NO_ERROR != palError)
159     {
160         pthrResumer->SetLastError(palError);
161         dwSuspendCount = (DWORD) -1;
162     }
163     else
164     {
165         _ASSERT_MSG(dwSuspendCount != static_cast<DWORD>(-1), "InternalResumeThread returned success but dwSuspendCount did not change.\n");   
166     }
167
168     LOGEXIT("ResumeThread returns DWORD %u\n", dwSuspendCount);
169     PERF_EXIT(ResumeThread);
170     return dwSuspendCount;
171 }
172
173 /*++
174 Function:
175   InternalResumeThread
176
177 InternalResumeThread converts the handle of the target thread to a 
178 CPalThread, and passes both the resumer and target thread references
179 to InternalResumeThreadFromData. A reference to the suspend count from
180 the resumption attempt is passed back to the caller of this function.
181 --*/
182 PAL_ERROR
183 CorUnix::InternalResumeThread(
184     CPalThread *pthrResumer,
185     HANDLE hTargetThread,
186     DWORD *pdwSuspendCount
187     )
188 {
189     PAL_ERROR palError = NO_ERROR;
190     CPalThread *pthrTarget = NULL;
191     IPalObject *pobjThread = NULL;
192
193     palError = InternalGetThreadDataFromHandle(
194         pthrResumer,
195         hTargetThread,
196         0, // THREAD_SUSPEND_RESUME
197         &pthrTarget,
198         &pobjThread
199         );
200
201     if (NO_ERROR == palError)
202     {
203         palError = pthrResumer->suspensionInfo.InternalResumeThreadFromData(
204             pthrResumer,
205             pthrTarget,
206             pdwSuspendCount
207             );
208     }
209
210     if (NULL != pobjThread)
211     {
212         pobjThread->ReleaseReference(pthrResumer);
213     }
214
215     return palError;
216 }
217
218 /*++
219 Function:
220   InternalResumeThreadFromData
221
222 InternalResumeThreadFromData resumes the target thread. First, the suspension
223 mutexes of the threads are acquired. Next, there's a check to ensure that the
224 target thread was actually suspended. Finally, the resume attempt is made
225 and the suspension mutexes are released. The suspend count of the 
226 target thread is passed back to the caller of this function.
227
228 Note that ReleaseSuspensionLock(s) is called before hitting ASSERTs in error
229 paths. Currently, this seems unnecessary since asserting within 
230 InternalResumeThreadFromData will not cause cleanup to occur. However,
231 this may change since it would be preferable to perform cleanup. Thus, calls
232 to release suspension locks remain in the error paths.
233 --*/
234 PAL_ERROR
235 CThreadSuspensionInfo::InternalResumeThreadFromData(
236     CPalThread *pthrResumer,
237     CPalThread *pthrTarget,
238     DWORD *pdwSuspendCount
239     )
240 {
241     PAL_ERROR palError = NO_ERROR;
242
243     int nWrittenBytes = -1;
244
245     if (SignalHandlerThread == pthrTarget->GetThreadType())
246     {
247         ASSERT("Attempting to resume the signal handling thread, which can never be suspended.\n");
248         palError = ERROR_INVALID_HANDLE;
249         goto InternalResumeThreadFromDataExit;
250     }
251
252     // Acquire suspension mutex
253     AcquireSuspensionLocks(pthrResumer, pthrTarget);
254
255     // Check target thread's state to ensure it hasn't died. 
256     // Setting a thread's state to TS_DONE is protected by the 
257     // target's suspension mutex.
258     if (pthrTarget->synchronizationInfo.GetThreadState() == TS_DONE)
259     {
260         palError = ERROR_INVALID_HANDLE;
261         ReleaseSuspensionLocks(pthrResumer, pthrTarget);
262         goto InternalResumeThreadFromDataExit;
263     }
264
265     // If this is a dummy thread, then it represents a process that was created with CREATE_SUSPENDED
266     // and it should have a blocking pipe set. If GetBlockingPipe returns -1 for a dummy thread, then
267     // something is wrong - either CREATE_SUSPENDED wasn't used or the process was already resumed.
268     if (pthrTarget->IsDummy() && -1 == pthrTarget->suspensionInfo.GetBlockingPipe())
269     {
270         palError = ERROR_INVALID_HANDLE;
271         ERROR("Tried to wake up dummy thread without a blocking pipe.\n");
272         ReleaseSuspensionLocks(pthrResumer, pthrTarget);
273         goto InternalResumeThreadFromDataExit;
274     }
275
276     // If there is a blocking pipe on this thread, resume it by writing the wake up code to that pipe.
277     if (-1 != pthrTarget->suspensionInfo.GetBlockingPipe())
278     {
279         // If write() is interrupted by a signal before writing data, 
280         // it returns -1 and sets errno to EINTR. In this case, we
281         // attempt the write() again.
282         writeAgain:
283         nWrittenBytes = write(pthrTarget->suspensionInfo.GetBlockingPipe(), &WAKEUPCODE, sizeof(WAKEUPCODE));
284
285         // The size of WAKEUPCODE is 1 byte. If write returns 0, we'll treat it as an error.
286         if (sizeof(WAKEUPCODE) != nWrittenBytes)
287         {
288             // If we are here during process creation, this is most likely caused by the target 
289             // process dying before reaching this point and thus breaking the pipe.
290             if (nWrittenBytes == -1 && EPIPE == errno)
291             {
292                 palError = ERROR_INVALID_HANDLE;
293                 ReleaseSuspensionLocks(pthrResumer, pthrTarget);
294                 ERROR("Write failed with EPIPE\n");
295                 goto InternalResumeThreadFromDataExit;
296             }
297             else if (nWrittenBytes == 0 || (nWrittenBytes == -1 && EINTR == errno))
298             {
299                 TRACE("write() failed with EINTR; re-attempting write\n");
300                 goto writeAgain;
301             }
302             else
303             {
304                 // Some other error occurred; need to release suspension mutexes before leaving ResumeThread.
305                 palError = ERROR_INTERNAL_ERROR;
306                 ReleaseSuspensionLocks(pthrResumer, pthrTarget);
307                 ASSERT("Write() failed; error is %d (%s)\n", errno, strerror(errno));
308                 goto InternalResumeThreadFromDataExit;
309             }
310         }
311
312         // Reset blocking pipe to -1 since we're done using it.
313         pthrTarget->suspensionInfo.SetBlockingPipe(-1);
314         
315         ReleaseSuspensionLocks(pthrResumer, pthrTarget);
316         goto InternalResumeThreadFromDataExit;
317     }
318     else
319     {
320         *pdwSuspendCount = 0;
321         palError = ERROR_BAD_COMMAND;
322     }
323
324 InternalResumeThreadFromDataExit:
325
326     if (NO_ERROR == palError)
327     {
328         *pdwSuspendCount = 1;
329     }
330
331     return palError;
332 }
333
334 /*++
335 Function:
336   TryAcquireSuspensionLock
337
338 TryAcquireSuspensionLock is a utility function that tries to acquire a thread's
339 suspension mutex or spinlock. If it succeeds, the function returns TRUE. 
340 Otherwise, it returns FALSE. This function is used in AcquireSuspensionLocks.
341 Note that the global lock cannot be acquired in this function since it makes
342 no sense to do so. A thread holding the global lock is the only thread that
343 can perform suspend or resume operations so it doesn't need to acquire
344 a second lock.
345 --*/
346 BOOL 
347 CThreadSuspensionInfo::TryAcquireSuspensionLock(
348     CPalThread* pthrTarget
349     )
350 {
351     int iPthreadRet = 0;
352 #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
353 {
354     iPthreadRet = SPINLOCKTryAcquire(pthrTarget->suspensionInfo.GetSuspensionSpinlock());
355 }
356 #else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
357 {
358     iPthreadRet = pthread_mutex_trylock(pthrTarget->suspensionInfo.GetSuspensionMutex());
359     _ASSERT_MSG(iPthreadRet == 0 || iPthreadRet == EBUSY, "pthread_mutex_trylock returned %d\n", iPthreadRet);
360 }
361 #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
362
363     // If iPthreadRet is 0, lock acquisition was successful. Otherwise, it failed.
364     return (iPthreadRet == 0);
365 }
366
367 /*++
368 Function:
369   AcquireSuspensionLock
370
371 AcquireSuspensionLock acquires a thread's suspension mutex or spinlock. 
372 If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it will acquire the global lock. 
373 A thread in this function blocks until it acquires
374 its lock, unlike in TryAcquireSuspensionLock.
375 --*/
376 void 
377 CThreadSuspensionInfo::AcquireSuspensionLock(
378     CPalThread* pthrCurrent
379     )
380 {
381 #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
382 {
383     SPINLOCKAcquire(&g_ssSuspensionLock, 0);
384 }
385 #else // USE_GLOBAL_LOCK_FOR_SUSPENSION
386 {
387     #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
388     {
389         SPINLOCKAcquire(&pthrCurrent->suspensionInfo.m_nSpinlock, 0);
390     }
391     #else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
392     {
393         INDEBUG(int iPthreadError = )
394         pthread_mutex_lock(&pthrCurrent->suspensionInfo.m_ptmSuspmutex);
395         _ASSERT_MSG(iPthreadError == 0, "pthread_mutex_lock returned %d\n", iPthreadError);
396     }
397     #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
398 }
399 #endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
400 }
401
402 /*++
403 Function:
404   ReleaseSuspensionLock
405
406 ReleaseSuspensionLock is a function that releases a thread's suspension mutex
407 or spinlock. If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, 
408 it will release the global lock.
409 --*/
410 void 
411 CThreadSuspensionInfo::ReleaseSuspensionLock(
412     CPalThread* pthrCurrent
413     )
414 {
415 #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
416 {
417     SPINLOCKRelease(&g_ssSuspensionLock);
418 }
419 #else // USE_GLOBAL_LOCK_FOR_SUSPENSION
420 {
421     #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 
422     {
423         SPINLOCKRelease(&pthrCurrent->suspensionInfo.m_nSpinlock);
424     }
425     #else // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 
426     {
427         INDEBUG(int iPthreadError = )
428         pthread_mutex_unlock(&pthrCurrent->suspensionInfo.m_ptmSuspmutex);
429         _ASSERT_MSG(iPthreadError == 0, "pthread_mutex_unlock returned %d\n", iPthreadError);
430     }
431     #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX 
432 }
433 #endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
434 }
435
436 /*++
437 Function:
438   AcquireSuspensionLocks
439
440 AcquireSuspensionLocks is used to acquire the suspension locks
441 of a suspender (or resumer) and target thread. The thread will 
442 perform a blocking call to acquire its own suspension lock
443 and will then try to acquire the target thread's lock without blocking. 
444 If it fails to acquire the target's lock, it releases its own lock 
445 and the thread will try to acquire both locks again. The key 
446 is that both locks must be acquired together.
447
448 Originally, only blocking calls were used to acquire the suspender
449 and the target lock. However, this was problematic since a thread
450 could acquire its own lock and then block on acquiring the target
451 lock. In the meantime, the target could have already acquired its
452 own lock and be attempting to suspend the suspender thread. This 
453 clearly causes deadlock. A second approach used locking hierarchies,
454 where locks were acquired use thread id ordering. This was better but
455 suffered from the scenario where thread A acquires thread B's
456 suspension mutex first. In the meantime, thread C acquires thread A's
457 suspension mutex and its own. Thus, thread A is suspended while
458 holding thread B's mutex. This is problematic if thread C now wants
459 to suspend thread B. The issue here is that a thread can be
460 suspended while holding someone else's mutex but not holding its own.
461 In the end, the correct approach is to always acquire your suspension 
462 mutex first. This prevents you from being suspended while holding the 
463 target's mutex. Then, attempt to acquire the target's mutex. If the mutex 
464 cannot be acquired, release your own and try again. This all or nothing 
465 approach is the safest and avoids nasty race conditions.
466
467 If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, the calling thread 
468 will acquire the global lock when possible.
469 --*/
470 VOID
471 CThreadSuspensionInfo::AcquireSuspensionLocks(
472     CPalThread *pthrSuspender,
473     CPalThread *pthrTarget
474     )
475 {
476     BOOL fReacquire = FALSE;
477
478 #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
479     AcquireSuspensionLock(pthrSuspender);
480 #else // USE_GLOBAL_LOCK_FOR_SUSPENSION
481     do
482     {
483         fReacquire = FALSE;
484         AcquireSuspensionLock(pthrSuspender);
485         if (!TryAcquireSuspensionLock(pthrTarget))
486         {
487             // pthread_mutex_trylock returned EBUSY so release the first lock and try again.
488             ReleaseSuspensionLock(pthrSuspender);           
489             fReacquire = TRUE;
490             sched_yield();
491         }
492     } while (fReacquire);
493 #endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
494
495     // Whenever the native implementation for the wait subsystem's thread 
496     // blocking requires a lock as protection (as pthread conditions do with 
497     // the associated mutex), we need to grab that lock to prevent the target 
498     // thread from being suspended while holding the lock.
499     // Failing to do so can lead to a multiple threads deadlocking such as the 
500     // one described in VSW 363793.
501     // In general, in similar scenarios, we need to grab the protecting lock 
502     // every time suspension safety/unsafety is unbalanced on the two sides 
503     // using the same condition (or any other native blocking support which 
504     // needs an associated native lock), i.e. when either the signaling 
505     // thread(s) is(are) signaling from an unsafe area and the waiting 
506     // thread(s) is(are) waiting from a safe one, or vice versa (the scenario
507     // described in VSW 363793 is a good example of the first type of 
508     // unbalanced suspension safety/unsafety).
509     // Instead, whenever signaling and waiting sides are both marked safe or 
510     // unsafe, the deadlock cannot take place since either the suspending 
511     // thread will suspend them anyway (regardless of the native lock), or it 
512     // won't suspend any of them, since they are both marked unsafe.
513     // Such a balanced scenario applies, for instance, to critical sections 
514     // where depending on whether the target CS is internal or not, both the
515     // signaling and the waiting side will access the mutex/condition from 
516     // respectively an unsafe or safe region.
517     
518     pthrTarget->AcquireNativeWaitLock();
519 }
520
521 /*++
522 Function:
523   ReleaseSuspensionLocks
524
525 ReleaseSuspensionLocks releases both thread's suspension mutexes.
526 Note that the locks are released in the opposite order they're acquired.
527 This prevents a suspending or resuming thread from being suspended
528 while holding the target's lock.
529 If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it simply releases the global lock.
530 --*/
531 VOID
532 CThreadSuspensionInfo::ReleaseSuspensionLocks(
533     CPalThread *pthrSuspender,
534     CPalThread *pthrTarget
535     )
536 {
537     // See comment in AcquireSuspensionLocks    
538     pthrTarget->ReleaseNativeWaitLock();
539
540 #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
541     ReleaseSuspensionLock(pthrSuspender);
542 #else // USE_GLOBAL_LOCK_FOR_SUSPENSION
543     ReleaseSuspensionLock(pthrTarget);
544     ReleaseSuspensionLock(pthrSuspender);
545 #endif // USE_GLOBAL_LOCK_FOR_SUSPENSION
546 }
547
548 /*++
549 Function:
550   PostOnSuspendSemaphore
551
552 PostOnSuspendSemaphore is a utility function for a thread
553 to post on its POSIX or SysV suspension semaphore.
554 --*/
555 void
556 CThreadSuspensionInfo::PostOnSuspendSemaphore()
557 {
558 #if USE_POSIX_SEMAPHORES
559     if (sem_post(&m_semSusp) == -1)
560     {
561         ASSERT("sem_post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
562     }
563 #elif USE_SYSV_SEMAPHORES
564     if (semop(m_nSemsuspid, &m_sbSempost, 1) == -1)
565     {
566         ASSERT("semop - post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
567     }
568 #elif USE_PTHREAD_CONDVARS
569     int status;
570
571     // The suspending thread may not have entered the wait yet, in which case the cond var
572     // signal below will be a no-op. To prevent the race condition we set m_fSuspended to
573     // TRUE first (which the suspender will take as an indication that no wait is required).
574     // But the setting of the flag and the signal must appear atomic to the suspender (as
575     // reading the flag and potentially waiting must appear to us) to avoid the race
576     // condition where the suspender reads the flag as FALSE, we set it and signal and the
577     // suspender then waits.
578
579     // Acquire the suspend mutex. Once we enter the critical section the suspender has
580     // either gotten there before us (and is waiting for our signal) or is yet to even
581     // check the flag (so we can set it here to stop them attempting a wait).
582     status = pthread_mutex_lock(&m_mutexSusp);
583     if (status != 0)
584     {
585         ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
586     }
587
588     m_fSuspended = TRUE;
589
590     status = pthread_cond_signal(&m_condSusp);
591     if (status != 0)
592     {
593         ASSERT("pthread_cond_signal returned %d (%s)\n", status, strerror(status));
594     }
595
596     status = pthread_mutex_unlock(&m_mutexSusp);
597     if (status != 0)
598     {
599         ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
600     }
601 #endif // USE_POSIX_SEMAPHORES
602 }
603
604 /*++
605 Function:
606   WaitOnSuspendSemaphore
607
608 WaitOnSuspendSemaphore is a utility function for a thread
609 to wait on its POSIX or SysV suspension semaphore. 
610 --*/
611 void
612 CThreadSuspensionInfo::WaitOnSuspendSemaphore()
613 {
614 #if USE_POSIX_SEMAPHORES
615     while (sem_wait(&m_semSusp) == -1)
616     {
617         ASSERT("sem_wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
618     }
619 #elif USE_SYSV_SEMAPHORES
620     while (semop(m_nSemsuspid, &m_sbSemwait, 1) == -1)
621     {
622         ASSERT("semop wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
623     }
624 #elif USE_PTHREAD_CONDVARS
625     int status;
626
627     // By the time we wait the target thread may have already signalled its suspension (in
628     // which case m_fSuspended will be TRUE and we shouldn't wait on the cond var). But we
629     // must check the flag and potentially wait atomically to avoid the race where we read
630     // the flag and the target thread sets it and signals before we have a chance to wait.
631
632     status = pthread_mutex_lock(&m_mutexSusp);
633     if (status != 0)
634     {
635         ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
636     }
637
638     // If the target has already acknowledged the suspend we shouldn't wait.
639     while (!m_fSuspended)
640     {
641         // We got here before the target could signal. Wait on them (which atomically releases
642         // the mutex during the wait).
643         status = pthread_cond_wait(&m_condSusp, &m_mutexSusp);
644         if (status != 0)
645         {
646             ASSERT("pthread_cond_wait returned %d (%s)\n", status, strerror(status));
647         }
648     }
649
650     status = pthread_mutex_unlock(&m_mutexSusp);
651     if (status != 0)
652     {
653         ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
654     }
655 #endif // USE_POSIX_SEMAPHORES
656 }
657
658 /*++
659 Function:
660   PostOnResumeSemaphore
661
662 PostOnResumeSemaphore is a utility function for a thread
663 to post on its POSIX or SysV resume semaphore.
664 --*/
665 void
666 CThreadSuspensionInfo::PostOnResumeSemaphore()
667 {
668 #if USE_POSIX_SEMAPHORES
669     if (sem_post(&m_semResume) == -1)
670     {
671         ASSERT("sem_post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
672     }
673 #elif USE_SYSV_SEMAPHORES
674     if (semop(m_nSemrespid, &m_sbSempost, 1) == -1)
675     {
676         ASSERT("semop - post returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
677     }
678 #elif USE_PTHREAD_CONDVARS
679     int status;
680
681     // The resuming thread may not have entered the wait yet, in which case the cond var
682     // signal below will be a no-op. To prevent the race condition we set m_fResumed to
683     // TRUE first (which the resumer will take as an indication that no wait is required).
684     // But the setting of the flag and the signal must appear atomic to the resumer (as
685     // reading the flag and potentially waiting must appear to us) to avoid the race
686     // condition where the resumer reads the flag as FALSE, we set it and signal and the
687     // resumer then waits.
688
689     // Acquire the resume mutex. Once we enter the critical section the resumer has
690     // either gotten there before us (and is waiting for our signal) or is yet to even
691     // check the flag (so we can set it here to stop them attempting a wait).
692     status = pthread_mutex_lock(&m_mutexResume);
693     if (status != 0)
694     {
695         ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
696     }
697
698     m_fResumed = TRUE;
699
700     status = pthread_cond_signal(&m_condResume);
701     if (status != 0)
702     {
703         ASSERT("pthread_cond_signal returned %d (%s)\n", status, strerror(status));
704     }
705
706     status = pthread_mutex_unlock(&m_mutexResume);
707     if (status != 0)
708     {
709         ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
710     }
711 #endif // USE_POSIX_SEMAPHORES
712 }
713
714 /*++
715 Function:
716   WaitOnResumeSemaphore
717
718 WaitOnResumeSemaphore is a utility function for a thread
719 to wait on its POSIX or SysV resume semaphore.
720 --*/
721 void
722 CThreadSuspensionInfo::WaitOnResumeSemaphore()
723 {
724 #if USE_POSIX_SEMAPHORES
725     while (sem_wait(&m_semResume) == -1)
726     {
727         ASSERT("sem_wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
728     }
729 #elif USE_SYSV_SEMAPHORES
730     while (semop(m_nSemrespid, &m_sbSemwait, 1) == -1)
731     {
732         ASSERT("semop wait returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
733     }
734 #elif USE_PTHREAD_CONDVARS
735     int status;
736
737     // By the time we wait the target thread may have already signalled its resumption (in
738     // which case m_fResumed will be TRUE and we shouldn't wait on the cond var). But we
739     // must check the flag and potentially wait atomically to avoid the race where we read
740     // the flag and the target thread sets it and signals before we have a chance to wait.
741
742     status = pthread_mutex_lock(&m_mutexResume);
743     if (status != 0)
744     {
745         ASSERT("pthread_mutex_lock returned %d (%s)\n", status, strerror(status));
746     }
747
748     // If the target has already acknowledged the resume we shouldn't wait.
749     while (!m_fResumed)
750     {
751         // We got here before the target could signal. Wait on them (which atomically releases
752         // the mutex during the wait).
753         status = pthread_cond_wait(&m_condResume, &m_mutexResume);
754         if (status != 0)
755         {
756             ASSERT("pthread_cond_wait returned %d (%s)\n", status, strerror(status));
757         }
758     }
759
760     status = pthread_mutex_unlock(&m_mutexResume);
761     if (status != 0)
762     {
763         ASSERT("pthread_mutex_unlock returned %d (%s)\n", status, strerror(status));
764     }
765 #endif // USE_POSIX_SEMAPHORES
766 }
767
768 /*++
769 Function:
770   InitializeSuspensionLock
771
772 InitializeSuspensionLock initializes a thread's suspension spinlock
773 or suspension mutex. It is called from the CThreadSuspensionInfo
774 constructor.
775 --*/
776 VOID
777 CThreadSuspensionInfo::InitializeSuspensionLock()
778 {
779 #if DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
780     SPINLOCKInit(&m_nSpinlock);
781 #else
782     int iError = pthread_mutex_init(&m_ptmSuspmutex, NULL);
783     if (0 != iError )
784     {
785         ASSERT("pthread_mutex_init(&suspmutex) returned %d\n", iError);
786         return;
787     }
788     m_fSuspmutexInitialized = TRUE;
789 #endif // DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
790 }
791
792 /*++
793 Function:
794   InitializePreCreate
795
796 InitializePreCreate initializes the semaphores and signal masks used 
797 for thread suspension. At the end, it sets the calling thread's 
798 signal mask to the default signal mask. 
799 --*/
800 PAL_ERROR
801 CThreadSuspensionInfo::InitializePreCreate()
802 {
803     PAL_ERROR palError = ERROR_INTERNAL_ERROR;
804     int iError = 0;
805 #if SEM_INIT_MODIFIES_ERRNO
806     int nStoredErrno;
807 #endif  // SEM_INIT_MODIFIES_ERRNO
808
809 #if USE_POSIX_SEMAPHORES
810
811 #if SEM_INIT_MODIFIES_ERRNO
812     nStoredErrno = errno;
813 #endif  // SEM_INIT_MODIFIES_ERRNO
814
815     // initialize suspension semaphore
816     iError = sem_init(&m_semSusp, 0, 0);  
817
818 #if SEM_INIT_MODIFIES_ERRNO
819     if (iError == 0)
820     {
821         // Restore errno if sem_init succeeded.
822         errno = nStoredErrno;
823     }
824 #endif  // SEM_INIT_MODIFIES_ERRNO
825
826     if (0 != iError )
827     {
828         ASSERT("sem_init(&suspsem) returned %d\n", iError);
829         goto InitializePreCreateExit;
830     }
831
832 #if SEM_INIT_MODIFIES_ERRNO
833     nStoredErrno = errno;
834 #endif  // SEM_INIT_MODIFIES_ERRNO
835
836     // initialize resume semaphore
837     iError = sem_init(&m_semResume, 0, 0);
838
839 #if SEM_INIT_MODIFIES_ERRNO
840     if (iError == 0)
841     {
842         // Restore errno if sem_init succeeded.
843         errno = nStoredErrno;
844     }
845 #endif  // SEM_INIT_MODIFIES_ERRNO
846
847     if (0 != iError )
848     {
849         ASSERT("sem_init(&suspsem) returned %d\n", iError);
850         sem_destroy(&m_semSusp);
851         goto InitializePreCreateExit;
852     }
853
854     m_fSemaphoresInitialized = TRUE;
855
856 #elif USE_SYSV_SEMAPHORES
857     // preparing to initialize the SysV semaphores.
858     union semun semunData;
859     m_nSemsuspid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
860     if (m_nSemsuspid == -1)
861     {
862         ASSERT("semget for suspension sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
863         goto InitializePreCreateExit;
864     }
865     
866     m_nSemrespid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
867     if (m_nSemrespid == -1)
868     {
869         ASSERT("semget for resumption sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
870         goto InitializePreCreateExit;
871     }
872
873     if (m_nSemsuspid == m_nSemrespid)
874     {
875         ASSERT("Suspension and Resumption Semaphores have the same id\n");
876         goto InitializePreCreateExit;
877     }
878
879     semunData.val = 0;
880     iError = semctl(m_nSemsuspid, 0, SETVAL, semunData);
881     if (iError == -1)
882     {
883         ASSERT("semctl for suspension sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
884         goto InitializePreCreateExit;
885     }
886
887     semunData.val = 0;
888     iError = semctl(m_nSemrespid, 0, SETVAL, semunData);
889     if (iError == -1)
890     {
891         ASSERT("semctl for resumption sem id returned -1 and set errno to %d (%s)\n", errno, strerror(errno));
892         goto InitializePreCreateExit;
893     }
894     
895     // initialize suspend semaphore
896     m_sbSemwait.sem_num = 0;
897     m_sbSemwait.sem_op = -1;
898     m_sbSemwait.sem_flg = 0;
899
900     // initialize resume semaphore
901     m_sbSempost.sem_num = 0;
902     m_sbSempost.sem_op = 1;
903     m_sbSempost.sem_flg = 0;    
904 #elif USE_PTHREAD_CONDVARS
905     iError = pthread_cond_init(&m_condSusp, NULL);
906     if (iError != 0)
907     {
908         ASSERT("pthread_cond_init for suspension returned %d (%s)\n", iError, strerror(iError));
909         goto InitializePreCreateExit;
910     }
911
912     iError = pthread_mutex_init(&m_mutexSusp, NULL);
913     if (iError != 0)
914     {
915         ASSERT("pthread_mutex_init for suspension returned %d (%s)\n", iError, strerror(iError));
916         goto InitializePreCreateExit;
917     }
918
919     iError = pthread_cond_init(&m_condResume, NULL);
920     if (iError != 0)
921     {
922         ASSERT("pthread_cond_init for resume returned %d (%s)\n", iError, strerror(iError));
923         goto InitializePreCreateExit;
924     }
925
926     iError = pthread_mutex_init(&m_mutexResume, NULL);
927     if (iError != 0)
928     {
929         ASSERT("pthread_mutex_init for resume returned %d (%s)\n", iError, strerror(iError));
930         goto InitializePreCreateExit;
931     }
932
933     m_fSemaphoresInitialized = TRUE;
934 #endif // USE_POSIX_SEMAPHORES
935
936     // Initialization was successful.
937     palError = NO_ERROR;
938     
939 InitializePreCreateExit:
940
941     if (NO_ERROR == palError && 0 != iError)
942     {
943         switch (iError)
944         {
945             case ENOMEM:
946             case EAGAIN:
947             {
948                 palError = ERROR_OUTOFMEMORY;
949                 break;
950             }
951             default:
952             {
953                 ASSERT("A pthrSuspender init call returned %d (%s)\n", iError, strerror(iError));
954                 palError = ERROR_INTERNAL_ERROR;
955             }
956         }
957     }
958
959     return palError;
960 }
961
962 CThreadSuspensionInfo::~CThreadSuspensionInfo()
963 {
964 #if !DEADLOCK_WHEN_THREAD_IS_SUSPENDED_WHILE_BLOCKED_ON_MUTEX
965     if (m_fSuspmutexInitialized)
966     {
967         INDEBUG(int iError = )
968         pthread_mutex_destroy(&m_ptmSuspmutex);
969         _ASSERT_MSG(0 == iError, "pthread_mutex_destroy returned %d (%s)\n", iError, strerror(iError));
970     }
971 #endif
972
973 #if USE_POSIX_SEMAPHORES
974     if (m_fSemaphoresInitialized)
975     {
976         int iError;
977
978         iError = sem_destroy(&m_semSusp);
979         _ASSERT_MSG(0 == iError, "sem_destroy failed and set errno to %d (%s)\n", errno, strerror(errno));
980
981         iError = sem_destroy(&m_semResume);
982         _ASSERT_MSG(0 == iError, "sem_destroy failed and set errno to %d (%s)\n", errno, strerror(errno));
983     }
984 #elif USE_SYSV_SEMAPHORES
985     DestroySemaphoreIds();
986 #elif USE_PTHREAD_CONDVARS
987     if (m_fSemaphoresInitialized)
988     {
989         int iError;
990
991         iError = pthread_cond_destroy(&m_condSusp);
992         _ASSERT_MSG(0 == iError, "pthread_cond_destroy failed with %d (%s)\n", iError, strerror(iError));
993
994         iError = pthread_mutex_destroy(&m_mutexSusp);
995         _ASSERT_MSG(0 == iError, "pthread_mutex_destroy failed with %d (%s)\n", iError, strerror(iError));
996
997         iError = pthread_cond_destroy(&m_condResume);
998         _ASSERT_MSG(0 == iError, "pthread_cond_destroy failed with %d (%s)\n", iError, strerror(iError));
999
1000         iError = pthread_mutex_destroy(&m_mutexResume);
1001         _ASSERT_MSG(0 == iError, "pthread_mutex_destroy failed with %d (%s)\n", iError, strerror(iError));
1002     }
1003 #endif  // USE_POSIX_SEMAPHORES
1004 }
1005
1006 #if USE_SYSV_SEMAPHORES
1007 /*++
1008 Function:
1009   DestroySemaphoreIds
1010   
1011 DestroySemaphoreIds is called from the CThreadSuspensionInfo destructor and
1012 from PROCCleanupThreadSemIds. If a thread exits before shutdown or is suspended
1013 during shutdown, its destructor will be invoked and the semaphore ids destroyed. 
1014 In assert or exceptions situations that are suspension unsafe, 
1015 PROCCleanupThreadSemIds is called, which uses DestroySemaphoreIds.
1016 --*/
1017 void
1018 CThreadSuspensionInfo::DestroySemaphoreIds()
1019 {
1020     union semun semunData;
1021     if (m_nSemsuspid != 0)
1022     {
1023         semunData.val = 0;
1024         if (0 != semctl(m_nSemsuspid, 0, IPC_RMID, semunData))
1025         {
1026             ERROR("semctl(Semsuspid) failed and set errno to %d (%s)\n", errno, strerror(errno));
1027         }
1028         else
1029         {
1030             m_nSemsuspid = 0;
1031         }
1032     }
1033     if (this->m_nSemrespid)
1034     {
1035         semunData.val = 0;
1036         if (0 != semctl(m_nSemrespid, 0, IPC_RMID, semunData))
1037         {
1038             ERROR("semctl(Semrespid) failed and set errno to %d (%s)\n", errno, strerror(errno));
1039         }
1040         else
1041         {
1042             m_nSemrespid = 0;
1043         }
1044     }
1045 }
1046 #endif // USE_SYSV_SEMAPHORES