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 | using namespace CorUnix; |
40 | |
41 | /* ------------------- Definitions ------------------------------*/ |
42 | SET_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. */ |
46 | CONST 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 |
50 | suspension mutex or spinlock. The downside is that it restricts us to only |
51 | performing one suspension or resumption in the PAL at a time. */ |
52 | #ifdef USE_GLOBAL_LOCK_FOR_SUSPENSION |
53 | static LONG g_ssSuspensionLock = 0; |
54 | #endif |
55 | |
56 | /*++ |
57 | Function: |
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 | --*/ |
65 | PAL_ERROR |
66 | CThreadSuspensionInfo::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 | /*++ |
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 |
1047 | |