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
10Module Name:
11
12 threadsusp.cpp
13
14Abstract:
15
16 Implementation of functions related to threads.
17
18Revision 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
39using namespace CorUnix;
40
41/* ------------------- Definitions ------------------------------*/
42SET_DEFAULT_DEBUG_CHANNEL(THREAD);
43
44/* This code is written to the blocking pipe of a thread that was created
45 in suspended state in order to resume it. */
46CONST BYTE WAKEUPCODE=0x2A;
47
48// #define USE_GLOBAL_LOCK_FOR_SUSPENSION // Uncomment this define to use the global suspension lock.
49/* The global suspension lock can be used in place of each thread having its own
50suspension mutex or spinlock. The downside is that it restricts us to only
51performing one suspension or resumption in the PAL at a time. */
52#ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION
53static LONG g_ssSuspensionLock = 0;
54#endif
55
56/*++
57Function:
58 InternalSuspendNewThreadFromData
59
60 On platforms where we use pipes for starting threads suspended, this
61 function sets the blocking pipe for the thread and blocks until the
62 wakeup code is written to the pipe by ResumeThread.
63
64--*/
65PAL_ERROR
66CThreadSuspensionInfo::InternalSuspendNewThreadFromData(
67 CPalThread *pThread
68 )
69{
70 PAL_ERROR palError = NO_ERROR;
71
72 AcquireSuspensionLock(pThread);
73 pThread->suspensionInfo.SetSelfSusp(TRUE);
74 ReleaseSuspensionLock(pThread);
75
76 int pipe_descs[2];
77 int pipeRv =
78#if HAVE_PIPE2
79 pipe2(pipe_descs, O_CLOEXEC);
80#else
81 pipe(pipe_descs);
82#endif // HAVE_PIPE2
83 if (pipeRv == -1)
84 {
85 ERROR("pipe() failed! error is %d (%s)\n", errno, strerror(errno));
86 return ERROR_NOT_ENOUGH_MEMORY;
87 }
88#if !HAVE_PIPE2
89 fcntl(pipe_descs[0], F_SETFD, FD_CLOEXEC); // make pipe non-inheritable, if possible
90 fcntl(pipe_descs[1], F_SETFD, FD_CLOEXEC);
91#endif // !HAVE_PIPE2
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/*++
132Function:
133
134 ResumeThread
135
136See MSDN doc.
137--*/
138DWORD
139PALAPI
140ResumeThread(
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/*++
174Function:
175 InternalResumeThread
176
177InternalResumeThread converts the handle of the target thread to a
178CPalThread, and passes both the resumer and target thread references
179to InternalResumeThreadFromData. A reference to the suspend count from
180the resumption attempt is passed back to the caller of this function.
181--*/
182PAL_ERROR
183CorUnix::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/*++
219Function:
220 InternalResumeThreadFromData
221
222InternalResumeThreadFromData resumes the target thread. First, the suspension
223mutexes of the threads are acquired. Next, there's a check to ensure that the
224target thread was actually suspended. Finally, the resume attempt is made
225and the suspension mutexes are released. The suspend count of the
226target thread is passed back to the caller of this function.
227
228Note that ReleaseSuspensionLock(s) is called before hitting ASSERTs in error
229paths. Currently, this seems unnecessary since asserting within
230InternalResumeThreadFromData will not cause cleanup to occur. However,
231this may change since it would be preferable to perform cleanup. Thus, calls
232to release suspension locks remain in the error paths.
233--*/
234PAL_ERROR
235CThreadSuspensionInfo::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
324InternalResumeThreadFromDataExit:
325
326 if (NO_ERROR == palError)
327 {
328 *pdwSuspendCount = 1;
329 }
330
331 return palError;
332}
333
334/*++
335Function:
336 TryAcquireSuspensionLock
337
338TryAcquireSuspensionLock is a utility function that tries to acquire a thread's
339suspension mutex or spinlock. If it succeeds, the function returns TRUE.
340Otherwise, it returns FALSE. This function is used in AcquireSuspensionLocks.
341Note that the global lock cannot be acquired in this function since it makes
342no sense to do so. A thread holding the global lock is the only thread that
343can perform suspend or resume operations so it doesn't need to acquire
344a second lock.
345--*/
346BOOL
347CThreadSuspensionInfo::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/*++
368Function:
369 AcquireSuspensionLock
370
371AcquireSuspensionLock acquires a thread's suspension mutex or spinlock.
372If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it will acquire the global lock.
373A thread in this function blocks until it acquires
374its lock, unlike in TryAcquireSuspensionLock.
375--*/
376void
377CThreadSuspensionInfo::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/*++
403Function:
404 ReleaseSuspensionLock
405
406ReleaseSuspensionLock is a function that releases a thread's suspension mutex
407or spinlock. If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined,
408it will release the global lock.
409--*/
410void
411CThreadSuspensionInfo::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/*++
437Function:
438 AcquireSuspensionLocks
439
440AcquireSuspensionLocks is used to acquire the suspension locks
441of a suspender (or resumer) and target thread. The thread will
442perform a blocking call to acquire its own suspension lock
443and will then try to acquire the target thread's lock without blocking.
444If it fails to acquire the target's lock, it releases its own lock
445and the thread will try to acquire both locks again. The key
446is that both locks must be acquired together.
447
448Originally, only blocking calls were used to acquire the suspender
449and the target lock. However, this was problematic since a thread
450could acquire its own lock and then block on acquiring the target
451lock. In the meantime, the target could have already acquired its
452own lock and be attempting to suspend the suspender thread. This
453clearly causes deadlock. A second approach used locking hierarchies,
454where locks were acquired use thread id ordering. This was better but
455suffered from the scenario where thread A acquires thread B's
456suspension mutex first. In the meantime, thread C acquires thread A's
457suspension mutex and its own. Thus, thread A is suspended while
458holding thread B's mutex. This is problematic if thread C now wants
459to suspend thread B. The issue here is that a thread can be
460suspended while holding someone else's mutex but not holding its own.
461In the end, the correct approach is to always acquire your suspension
462mutex first. This prevents you from being suspended while holding the
463target's mutex. Then, attempt to acquire the target's mutex. If the mutex
464cannot be acquired, release your own and try again. This all or nothing
465approach is the safest and avoids nasty race conditions.
466
467If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, the calling thread
468will acquire the global lock when possible.
469--*/
470VOID
471CThreadSuspensionInfo::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/*++
522Function:
523 ReleaseSuspensionLocks
524
525ReleaseSuspensionLocks releases both thread's suspension mutexes.
526Note that the locks are released in the opposite order they're acquired.
527This prevents a suspending or resuming thread from being suspended
528while holding the target's lock.
529If USE_GLOBAL_LOCK_FOR_SUSPENSION is defined, it simply releases the global lock.
530--*/
531VOID
532CThreadSuspensionInfo::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/*++
549Function:
550 PostOnSuspendSemaphore
551
552PostOnSuspendSemaphore is a utility function for a thread
553to post on its POSIX or SysV suspension semaphore.
554--*/
555void
556CThreadSuspensionInfo::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/*++
605Function:
606 WaitOnSuspendSemaphore
607
608WaitOnSuspendSemaphore is a utility function for a thread
609to wait on its POSIX or SysV suspension semaphore.
610--*/
611void
612CThreadSuspensionInfo::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/*++
659Function:
660 PostOnResumeSemaphore
661
662PostOnResumeSemaphore is a utility function for a thread
663to post on its POSIX or SysV resume semaphore.
664--*/
665void
666CThreadSuspensionInfo::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/*++
715Function:
716 WaitOnResumeSemaphore
717
718WaitOnResumeSemaphore is a utility function for a thread
719to wait on its POSIX or SysV resume semaphore.
720--*/
721void
722CThreadSuspensionInfo::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/*++
769Function:
770 InitializeSuspensionLock
771
772InitializeSuspensionLock initializes a thread's suspension spinlock
773or suspension mutex. It is called from the CThreadSuspensionInfo
774constructor.
775--*/
776VOID
777CThreadSuspensionInfo::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/*++
793Function:
794 InitializePreCreate
795
796InitializePreCreate initializes the semaphores and signal masks used
797for thread suspension. At the end, it sets the calling thread's
798signal mask to the default signal mask.
799--*/
800PAL_ERROR
801CThreadSuspensionInfo::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
939InitializePreCreateExit:
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
962CThreadSuspensionInfo::~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/*++
1008Function:
1009 DestroySemaphoreIds
1010
1011DestroySemaphoreIds is called from the CThreadSuspensionInfo destructor and
1012from PROCCleanupThreadSemIds. If a thread exits before shutdown or is suspended
1013during shutdown, its destructor will be invoked and the semaphore ids destroyed.
1014In assert or exceptions situations that are suspension unsafe,
1015PROCCleanupThreadSemIds is called, which uses DestroySemaphoreIds.
1016--*/
1017void
1018CThreadSuspensionInfo::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
1047