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
9Module Name:
10
11 synchmanager.cpp
12
13Abstract:
14 Implementation of Synchronization Manager and related objects
15
16
17
18--*/
19
20#include "pal/dbgmsg.h"
21
22SET_DEFAULT_DEBUG_CHANNEL(SYNC); // some headers have code with asserts, so do this first
23
24#include "synchmanager.hpp"
25#include "pal/file.hpp"
26
27#include <sys/types.h>
28#include <sys/time.h>
29#include <sys/stat.h>
30#include <sys/wait.h>
31#include <unistd.h>
32#include <limits.h>
33#include <sched.h>
34#include <signal.h>
35#include <errno.h>
36#if HAVE_POLL
37#include <poll.h>
38#else
39#include "pal/fakepoll.h"
40#endif // HAVE_POLL
41
42#include <algorithm>
43
44const int CorUnix::CThreadSynchronizationInfo::PendingSignalingsArraySize;
45
46// We use the synchronization manager's worker thread to handle
47// process termination requests. It does so by calling the
48// registered handler function.
49PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = NULL;
50
51// Set the handler for process termination requests.
52VOID PALAPI PAL_SetTerminationRequestHandler(
53 IN PTERMINATION_REQUEST_HANDLER terminationHandler)
54{
55 g_terminationRequestHandler = terminationHandler;
56}
57
58namespace CorUnix
59{
60 /////////////////////////////////
61 // //
62 // WaitingThreadsListNode //
63 // //
64 /////////////////////////////////
65#ifdef SYNCH_OBJECT_VALIDATION
66 _WaitingThreadsListNode::_WaitingThreadsListNode()
67 {
68 ValidateEmptyObject();
69 dwDebugHeadSignature = HeadSignature;
70 dwDebugTailSignature = TailSignature;
71 }
72 _WaitingThreadsListNode::~_WaitingThreadsListNode()
73 {
74 ValidateObject();
75 InvalidateObject();
76 }
77 void _WaitingThreadsListNode::ValidateObject()
78 {
79 TRACE("Verifying WaitingThreadsListNode @ %p\n", this);
80 _ASSERT_MSG(HeadSignature == dwDebugHeadSignature,
81 "WaitingThreadsListNode header signature corruption [p=%p]",
82 this);
83 _ASSERT_MSG(TailSignature == dwDebugTailSignature,
84 "WaitingThreadsListNode trailer signature corruption [p=%p]",
85 this);
86 }
87 void _WaitingThreadsListNode::ValidateEmptyObject()
88 {
89 _ASSERT_MSG(HeadSignature != dwDebugHeadSignature,
90 "WaitingThreadsListNode header previously signed [p=%p]",
91 this);
92 _ASSERT_MSG(TailSignature != dwDebugTailSignature,
93 "WaitingThreadsListNode trailer previously signed [p=%p]",
94 this);
95 }
96 void _WaitingThreadsListNode::InvalidateObject()
97 {
98 TRACE("Invalidating WaitingThreadsListNode @ %p\n", this);
99 dwDebugHeadSignature = EmptySignature;
100 dwDebugTailSignature = EmptySignature;
101 }
102#endif // SYNCH_OBJECT_VALIDATION
103
104 //////////////////////////////
105 // //
106 // CPalSynchMgrController //
107 // //
108 //////////////////////////////
109
110 /*++
111 Method:
112 CPalSynchMgrController::CreatePalSynchronizationManager
113
114 Creates the Synchronization Manager. It must be called once per process.
115 --*/
116 IPalSynchronizationManager * CPalSynchMgrController::CreatePalSynchronizationManager()
117 {
118 return CPalSynchronizationManager::CreatePalSynchronizationManager();
119 };
120
121 /*++
122 Method:
123 CPalSynchMgrController::StartWorker
124
125 Starts the Synchronization Manager's Worker Thread
126 --*/
127 PAL_ERROR CPalSynchMgrController::StartWorker(
128 CPalThread * pthrCurrent)
129 {
130 return CPalSynchronizationManager::StartWorker(pthrCurrent);
131 }
132
133 /*++
134 Method:
135 CPalSynchMgrController::PrepareForShutdown
136
137 This method performs the part of Synchronization Manager's shutdown that
138 needs to be carried out when core PAL subsystems are still active
139 --*/
140 PAL_ERROR CPalSynchMgrController::PrepareForShutdown()
141 {
142 return CPalSynchronizationManager::PrepareForShutdown();
143 }
144
145 //////////////////////////////////
146 // //
147 // CPalSynchronizationManager //
148 // //
149 //////////////////////////////////
150
151 IPalSynchronizationManager * g_pSynchronizationManager = NULL;
152
153 CPalSynchronizationManager * CPalSynchronizationManager::s_pObjSynchMgr = NULL;
154 Volatile<LONG> CPalSynchronizationManager::s_lInitStatus = SynchMgrStatusIdle;
155 CRITICAL_SECTION CPalSynchronizationManager::s_csSynchProcessLock;
156 CRITICAL_SECTION CPalSynchronizationManager::s_csMonitoredProcessesLock;
157
158 CPalSynchronizationManager::CPalSynchronizationManager()
159 : m_dwWorkerThreadTid(0),
160 m_pipoThread(NULL),
161 m_pthrWorker(NULL),
162 m_iProcessPipeRead(-1),
163 m_iProcessPipeWrite(-1),
164 m_pmplnMonitoredProcesses(NULL),
165 m_lMonitoredProcessesCount(0),
166 m_pmplnExitedNodes(NULL),
167 m_cacheWaitCtrlrs(CtrlrsCacheMaxSize),
168 m_cacheStateCtrlrs(CtrlrsCacheMaxSize),
169 m_cacheSynchData(SynchDataCacheMaxSize),
170 m_cacheSHRSynchData(SynchDataCacheMaxSize),
171 m_cacheWTListNodes(WTListNodeCacheMaxSize),
172 m_cacheSHRWTListNodes(WTListNodeCacheMaxSize),
173 m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize),
174 m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize)
175 {
176#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
177 m_iKQueue = -1;
178 // Initialize data to 0 and flags to EV_EOF
179 EV_SET(&m_keProcessPipeEvent, 0, 0, EV_EOF, 0, 0, 0);
180#endif // HAVE_KQUEUE
181 }
182
183 CPalSynchronizationManager::~CPalSynchronizationManager()
184 {
185 }
186
187 /*++
188 Method:
189 CPalSynchronizationManager::BlockThread
190
191 Called by a thread to go to sleep for a wait or a sleep
192
193 NOTE: This method must must be called without holding any
194 synchronization lock (as well as other locks)
195 --*/
196 PAL_ERROR CPalSynchronizationManager::BlockThread(
197 CPalThread *pthrCurrent,
198 DWORD dwTimeout,
199 bool fAlertable,
200 bool fIsSleep,
201 ThreadWakeupReason *ptwrWakeupReason,
202 DWORD * pdwSignaledObject)
203 {
204 PAL_ERROR palErr = NO_ERROR;
205 ThreadWakeupReason twrWakeupReason = WaitFailed;
206 DWORD * pdwWaitState;
207 DWORD dwWaitState = 0;
208 DWORD dwSigObjIdx = 0;
209 bool fRaceAlerted = false;
210 bool fEarlyDeath = false;
211
212 pdwWaitState = SharedIDToTypePointer(DWORD,
213 pthrCurrent->synchronizationInfo.m_shridWaitAwakened);
214
215 _ASSERT_MSG(NULL != pdwWaitState,
216 "Got NULL pdwWaitState from m_shridWaitAwakened=%p\n",
217 (VOID *)pthrCurrent->synchronizationInfo.m_shridWaitAwakened);
218
219 if (fIsSleep)
220 {
221 // If fIsSleep is true we are being called by Sleep/SleepEx
222 // and we need to switch the wait state to TWS_WAITING or
223 // TWS_ALERTABLE (according to fAlertable)
224
225 if (fAlertable)
226 {
227 // If we are in alertable mode we need to grab the lock to
228 // make sure that no APC is queued right before the
229 // InterlockedCompareExchange.
230 // If there are APCs queued at this time, no native wakeup
231 // will be posted, so we need to skip the native wait
232
233 // Lock
234 AcquireLocalSynchLock(pthrCurrent);
235 AcquireSharedSynchLock(pthrCurrent);
236
237 if (AreAPCsPending(pthrCurrent))
238 {
239 // APCs have been queued when the thread wait status was
240 // still TWS_ACTIVE, therefore the queueing thread will not
241 // post any native wakeup: we need to skip the actual
242 // native wait
243 fRaceAlerted = true;
244 }
245 }
246
247 if (!fRaceAlerted)
248 {
249 // Setting the thread in wait state
250 dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);
251
252 TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u [current *pdwWaitState=%u]\n",
253 pdwWaitState, dwWaitState, *pdwWaitState);
254
255 dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState,
256 dwWaitState,
257 TWS_ACTIVE);
258
259 if ((DWORD)TWS_ACTIVE != dwWaitState)
260 {
261 if (fAlertable)
262 {
263 // Unlock
264 ReleaseSharedSynchLock(pthrCurrent);
265 ReleaseLocalSynchLock(pthrCurrent);
266 }
267
268 if ((DWORD)TWS_EARLYDEATH == dwWaitState)
269 {
270 // Process is terminating, this thread will soon be suspended (by SuspendOtherThreads).
271 WARN("Thread is about to get suspended by TerminateProcess\n");
272
273 fEarlyDeath = true;
274 palErr = WAIT_FAILED;
275 }
276 else
277 {
278 ASSERT("Unexpected thread wait state %u\n", dwWaitState);
279 palErr = ERROR_INTERNAL_ERROR;
280 }
281
282 goto BT_exit;
283 }
284 }
285
286 if (fAlertable)
287 {
288 // Unlock
289 ReleaseSharedSynchLock(pthrCurrent);
290 ReleaseLocalSynchLock(pthrCurrent);
291 }
292 }
293
294 if (fRaceAlerted)
295 {
296 twrWakeupReason = Alerted;
297 }
298 else
299 {
300 TRACE("Current thread is about to block for waiting\n");
301
302 palErr = ThreadNativeWait(
303 &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
304 dwTimeout,
305 &twrWakeupReason,
306 &dwSigObjIdx);
307
308 if (NO_ERROR != palErr)
309 {
310 ERROR("ThreadNativeWait() failed [palErr=%d]\n", palErr);
311 twrWakeupReason = WaitFailed;
312 goto BT_exit;
313 }
314
315 TRACE("ThreadNativeWait returned {WakeupReason=%u "
316 "dwSigObjIdx=%u}\n", twrWakeupReason, dwSigObjIdx);
317 }
318
319 if (WaitTimeout == twrWakeupReason)
320 {
321 // timeout reached. set wait state back to 'active'
322 dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);
323
324 TRACE("Current thread awakened for timeout: switching wait "
325 "state [%p] from %u to TWS_ACTIVE [current *pdwWaitState=%u]\n",
326 pdwWaitState, dwWaitState, *pdwWaitState);
327
328 DWORD dwOldWaitState = InterlockedCompareExchange(
329 (LONG *)pdwWaitState,
330 TWS_ACTIVE, (LONG)dwWaitState);
331
332 switch (dwOldWaitState)
333 {
334 case TWS_ACTIVE:
335 // We were already ACTIVE; someone decided to wake up this
336 // thread sometime between the moment the native wait
337 // timed out and here. Since the signaling side succeeded
338 // its InterlockedCompareExchange, it will signal the
339 // condition/predicate pair (we just raced overtaking it);
340 // therefore we need to clear the condition/predicate
341 // by waiting on it one more time.
342 // That will also cause this method to report a signal
343 // rather than a timeout.
344 // In the remote signaling scenario, this second wait
345 // also makes sure that the shared id passed over the
346 // process pipe is valid for the entire duration of time
347 // in which the worker thread deals with it
348 TRACE("Current thread already ACTIVE: a signaling raced "
349 "with the timeout: re-waiting natively to clear the "
350 "predicate\n");
351
352 palErr = ThreadNativeWait(
353 &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
354 SecondNativeWaitTimeout,
355 &twrWakeupReason,
356 &dwSigObjIdx);
357
358 if (NO_ERROR != palErr)
359 {
360 ERROR("ThreadNativeWait() failed [palErr=%d]\n",
361 palErr);
362 twrWakeupReason = WaitFailed;
363 }
364
365 if (WaitTimeout == twrWakeupReason)
366 {
367 ERROR("Second native wait timed out\n");
368 }
369
370 break;
371 case TWS_EARLYDEATH:
372 // Thread is about to be suspended by TerminateProcess.
373 // Anyway, if the wait timed out, we still want to
374 // (try to) unregister the wait (especially if it
375 // involves shared objects)
376 WARN("Thread is about to be suspended by TerminateProcess\n");
377 fEarlyDeath = true;
378 palErr = WAIT_FAILED;
379 break;
380 case TWS_WAITING:
381 case TWS_ALERTABLE:
382 default:
383 _ASSERT_MSG(dwOldWaitState == dwWaitState,
384 "Unexpected wait status: actual=%u, expected=%u\n",
385 dwOldWaitState, dwWaitState);
386 break;
387 }
388 }
389
390 switch (twrWakeupReason)
391 {
392 case WaitTimeout:
393 {
394 // Awakened for timeout: we need to unregister the wait
395 ThreadWaitInfo * ptwiWaitInfo;
396
397 TRACE("Current thread awakened for timeout: unregistering the wait\n");
398
399 // Local lock
400 AcquireLocalSynchLock(pthrCurrent);
401
402 ptwiWaitInfo = GetThreadWaitInfo(pthrCurrent);
403
404 // Unregister the wait
405 // Note: UnRegisterWait will take care of grabbing the shared synch lock, if needed.
406 UnRegisterWait(pthrCurrent, ptwiWaitInfo, false);
407
408 // Unlock
409 ReleaseLocalSynchLock(pthrCurrent);
410
411 break;
412 }
413 case WaitSucceeded:
414 case MutexAbondoned:
415 *pdwSignaledObject = dwSigObjIdx;
416 break;
417 default:
418 // 'Alerted' and 'WaitFailed' go through this case
419 break;
420 }
421
422 // Set the returned wakeup reason
423 *ptwrWakeupReason = twrWakeupReason;
424
425 TRACE("Current thread is now active [WakeupReason=%u SigObjIdx=%u]\n",
426 twrWakeupReason, dwSigObjIdx);
427
428 _ASSERT_MSG(TWS_ACTIVE == VolatileLoad(pdwWaitState) ||
429 TWS_EARLYDEATH == VolatileLoad(pdwWaitState),
430 "Unexpected thread wait state %u\n", VolatileLoad(pdwWaitState));
431
432 BT_exit:
433 if (fEarlyDeath)
434 {
435 ThreadPrepareForShutdown();
436 }
437
438 return palErr;
439 }
440
441 PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
442 ThreadNativeWaitData * ptnwdNativeWaitData,
443 DWORD dwTimeout,
444 ThreadWakeupReason * ptwrWakeupReason,
445 DWORD * pdwSignaledObject)
446 {
447 PAL_ERROR palErr = NO_ERROR;
448 int iRet, iWaitRet = 0;
449 struct timespec tsAbsTmo;
450
451 TRACE("ThreadNativeWait(ptnwdNativeWaitData=%p, dwTimeout=%u, ...)\n",
452 ptnwdNativeWaitData, dwTimeout);
453
454 if (dwTimeout != INFINITE)
455 {
456 // Calculate absolute timeout
457 palErr = GetAbsoluteTimeout(dwTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
458 if (NO_ERROR != palErr)
459 {
460 ERROR("Failed to convert timeout to absolute timeout\n");
461 goto TNW_exit;
462 }
463 }
464
465 // Lock the mutex
466 iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
467 if (0 != iRet)
468 {
469 ERROR("Internal Error: cannot lock mutex\n");
470 palErr = ERROR_INTERNAL_ERROR;
471 *ptwrWakeupReason = WaitFailed;
472 goto TNW_exit;
473 }
474
475 while (FALSE == ptnwdNativeWaitData->iPred)
476 {
477 if (INFINITE == dwTimeout)
478 {
479 iWaitRet = pthread_cond_wait(&ptnwdNativeWaitData->cond,
480 &ptnwdNativeWaitData->mutex);
481 }
482 else
483 {
484 iWaitRet = pthread_cond_timedwait(&ptnwdNativeWaitData->cond,
485 &ptnwdNativeWaitData->mutex,
486 &tsAbsTmo);
487 }
488
489 if (ETIMEDOUT == iWaitRet)
490 {
491 _ASSERT_MSG(INFINITE != dwTimeout,
492 "Got ETIMEDOUT despite timeout being INFINITE\n");
493 break;
494 }
495 else if (0 != iWaitRet)
496 {
497 ERROR("pthread_cond_%swait returned %d [errno=%d (%s)]\n",
498 (INFINITE == dwTimeout) ? "" : "timed",
499 iWaitRet, errno, strerror(errno));
500 palErr = ERROR_INTERNAL_ERROR;
501 break;
502 }
503 }
504
505 // Reset the predicate
506 if (0 == iWaitRet)
507 {
508 // We don't want to reset the predicate if pthread_cond_timedwait
509 // timed out racing with a pthread_cond_signal. When
510 // pthread_cond_timedwait times out, it needs to grab the mutex
511 // before returning. At timeout time, it may happen that the
512 // signaling thread just grabbed the mutex, but it hasn't called
513 // pthread_cond_signal yet. In this scenario pthread_cond_timedwait
514 // will have to wait for the signaling side to release the mutex.
515 // As a result it will return with error timeout, but the predicate
516 // will be set. Since pthread_cond_timedwait timed out, the
517 // predicate value is intended for the next signal. In case of a
518 // object signaling racing with a wait timeout this predicate value
519 // will be picked up by the 'second native wait' (see comments in
520 // BlockThread).
521
522 ptnwdNativeWaitData->iPred = FALSE;
523 }
524
525 // Unlock the mutex
526 iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
527 if (0 != iRet)
528 {
529 ERROR("Cannot unlock mutex [err=%d]\n", iRet);
530 palErr = ERROR_INTERNAL_ERROR;
531 goto TNW_exit;
532 }
533
534 _ASSERT_MSG(ETIMEDOUT != iRet || INFINITE != dwTimeout, "Got timeout return code with INFINITE timeout\n");
535
536 if (0 == iWaitRet)
537 {
538 *ptwrWakeupReason = ptnwdNativeWaitData->twrWakeupReason;
539 *pdwSignaledObject = ptnwdNativeWaitData->dwObjectIndex;
540 }
541 else if (ETIMEDOUT == iWaitRet)
542 {
543 *ptwrWakeupReason = WaitTimeout;
544 }
545
546 TNW_exit:
547 TRACE("ThreadNativeWait: returning %u [WakeupReason=%u]\n", palErr, *ptwrWakeupReason);
548 return palErr;
549 }
550
551 /*++
552 Method:
553 CPalSynchronizationManager::AbandonObjectsOwnedByThread
554
555 This method is called by a thread at thread-exit time to abandon
556 any currently owned waitable object (mutexes). If pthrTarget is
557 different from pthrCurrent, AbandonObjectsOwnedByThread assumes
558 to be called whether by TerminateThread or at shutdown time. See
559 comments below for more details
560 --*/
561 PAL_ERROR CPalSynchronizationManager::AbandonObjectsOwnedByThread(
562 CPalThread * pthrCurrent,
563 CPalThread * pthrTarget)
564 {
565 PAL_ERROR palErr = NO_ERROR;
566 OwnedObjectsListNode * poolnItem;
567 bool fSharedSynchLock = false;
568 CThreadSynchronizationInfo * pSynchInfo = &pthrTarget->synchronizationInfo;
569 CPalSynchronizationManager * pSynchManager = GetInstance();
570
571 // Local lock
572 AcquireLocalSynchLock(pthrCurrent);
573
574 // Abandon owned objects
575 while (NULL != (poolnItem = pSynchInfo->RemoveFirstObjectFromOwnedList()))
576 {
577 CSynchData * psdSynchData = poolnItem->pPalObjSynchData;
578
579 _ASSERT_MSG(NULL != psdSynchData,
580 "NULL psdSynchData pointer in ownership list node\n");
581
582 VALIDATEOBJECT(psdSynchData);
583
584 TRACE("Abandoning object with SynchData at %p\n", psdSynchData);
585
586 if (!fSharedSynchLock &&
587 (SharedObject == psdSynchData->GetObjectDomain()))
588 {
589 AcquireSharedSynchLock(pthrCurrent);
590 fSharedSynchLock = true;
591 }
592
593 // Reset ownership data
594 psdSynchData->ResetOwnership();
595
596 // Set abandoned status; in case there is a thread to be released:
597 // - if the thread is local, ReleaseFirstWaiter will reset the
598 // abandoned status
599 // - if the thread is remote, the remote worker thread will use
600 // the value and reset it
601 psdSynchData->SetAbandoned(true);
602
603 // Signal the object and trigger thread awakening
604 psdSynchData->Signal(pthrCurrent, 1, false);
605
606 // Release reference to to SynchData
607 psdSynchData->Release(pthrCurrent);
608
609 // Return node to the cache
610 pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, poolnItem);
611 }
612
613 // Abandon owned named mutexes
614 while (true)
615 {
616 NamedMutexProcessData *processData = pSynchInfo->RemoveFirstOwnedNamedMutex();
617 if (processData == nullptr)
618 {
619 break;
620 }
621 processData->Abandon();
622 }
623
624 if (pthrTarget != pthrCurrent)
625 {
626 // If the target thead is not the current one, we are being called
627 // at shutdown time, right before the target thread is suspended,
628 // or anyway the target thread is being terminated.
629 // In this case we switch its wait state to TWS_EARLYDEATH so that,
630 // if the thread is currently waiting/sleeping and it wakes up
631 // before shutdown code manage to suspend it, it will be rerouted
632 // to ThreadPrepareForShutdown (that will be done without holding
633 // any internal lock, in a way to accomodate shutdown time thread
634 // suspension).
635 // At this time we also unregister the wait, so no dummy nodes are
636 // left around on waiting objects.
637 // The TWS_EARLYDEATH wait-state will also prevent the thread from
638 // successfully registering for a possible new wait in the same
639 // time window.
640 LONG lTWState;
641 DWORD * pdwWaitState;
642
643 pdwWaitState = SharedIDToTypePointer(DWORD, pthrTarget->synchronizationInfo.m_shridWaitAwakened);
644 lTWState = InterlockedExchange((LONG *)pdwWaitState, TWS_EARLYDEATH);
645
646 if (( ((LONG)TWS_WAITING == lTWState) || ((LONG)TWS_ALERTABLE == lTWState) ) &&
647 (0 < pSynchInfo->m_twiWaitInfo.lObjCount))
648 {
649 // Unregister the wait
650 // Note: UnRegisterWait will take care of grabbing the shared synch lock, if needed.
651 UnRegisterWait(pthrCurrent, &pSynchInfo->m_twiWaitInfo, fSharedSynchLock);
652 }
653 }
654
655 // Unlock
656 if (fSharedSynchLock)
657 {
658 ReleaseSharedSynchLock(pthrCurrent);
659 fSharedSynchLock = false;
660 }
661
662 ReleaseLocalSynchLock(pthrCurrent);
663 DiscardAllPendingAPCs(pthrCurrent, pthrTarget);
664
665 return palErr;
666 }
667
668 /*++
669 Method:
670 CPalSynchronizationManager::GetSynchWaitControllersForObjects
671
672 Returns an array of wait controllers, one for each of the objects
673 in rgObjects
674 --*/
675 PAL_ERROR CPalSynchronizationManager::GetSynchWaitControllersForObjects(
676 CPalThread *pthrCurrent,
677 IPalObject *rgObjects[],
678 DWORD dwObjectCount,
679 ISynchWaitController * rgControllers[])
680 {
681 return GetSynchControllersForObjects(pthrCurrent,
682 rgObjects,
683 dwObjectCount,
684 (void **)rgControllers,
685 CSynchControllerBase::WaitController);
686 }
687
688 /*++
689 Method:
690 CPalSynchronizationManager::GetSynchStateControllersForObjects
691
692 Returns an array of state controllers, one for each of the objects
693 in rgObjects
694 --*/
695 PAL_ERROR CPalSynchronizationManager::GetSynchStateControllersForObjects(
696 CPalThread *pthrCurrent,
697 IPalObject *rgObjects[],
698 DWORD dwObjectCount,
699 ISynchStateController *rgControllers[])
700 {
701 return GetSynchControllersForObjects(pthrCurrent,
702 rgObjects,
703 dwObjectCount,
704 (void **)rgControllers,
705 CSynchControllerBase::StateController);
706 }
707
708 /*++
709 Method:
710 CPalSynchronizationManager::GetSynchControllersForObjects
711
712 Internal common implementation for GetSynchWaitControllersForObjects and
713 GetSynchStateControllersForObjects
714 --*/
715 PAL_ERROR CPalSynchronizationManager::GetSynchControllersForObjects(
716 CPalThread *pthrCurrent,
717 IPalObject *rgObjects[],
718 DWORD dwObjectCount,
719 void ** ppvControllers,
720 CSynchControllerBase::ControllerType ctCtrlrType)
721 {
722 PAL_ERROR palErr = NO_ERROR;
723 unsigned int uIdx, uCount = 0, uSharedObjectCount = 0;
724 WaitDomain wdWaitDomain = LocalWait;
725 CObjectType * potObjectType = NULL;
726 unsigned int uErrCleanupIdxFirstNotInitializedCtrlr = 0;
727 unsigned int uErrCleanupIdxLastCtrlr = 0;
728 bool fLocalSynchLock = false;
729
730 union
731 {
732 CSynchWaitController * pWaitCtrlrs[MAXIMUM_WAIT_OBJECTS];
733 CSynchStateController * pStateCtrlrs[MAXIMUM_WAIT_OBJECTS];
734 } Ctrlrs;
735
736 if ((dwObjectCount <= 0) || (dwObjectCount > MAXIMUM_WAIT_OBJECTS))
737 {
738 palErr = ERROR_INVALID_PARAMETER;
739 goto GSCFO_exit;
740 }
741
742 if (CSynchControllerBase::WaitController == ctCtrlrType)
743 {
744 uCount = (unsigned int)m_cacheWaitCtrlrs.Get(pthrCurrent,
745 dwObjectCount,
746 Ctrlrs.pWaitCtrlrs);
747 }
748 else
749 {
750 uCount = (unsigned int)m_cacheStateCtrlrs.Get(pthrCurrent,
751 dwObjectCount,
752 Ctrlrs.pStateCtrlrs);
753 }
754
755 if (uCount < dwObjectCount)
756 {
757 // We got less controllers (uCount) than we asked for (dwObjectCount),
758 // probably because of low memory.
759 // None of these controllers is initialized, so they must be all
760 // returned directly to the cache
761 uErrCleanupIdxLastCtrlr = uCount;
762
763 palErr = ERROR_NOT_ENOUGH_MEMORY;
764 goto GSCFO_error_cleanup;
765 }
766
767 //
768 // We need to acquire the local synch lock before evaluating object domains
769 //
770 AcquireLocalSynchLock(pthrCurrent);
771 fLocalSynchLock = true;
772
773 for (uIdx=0; uIdx<dwObjectCount; uIdx++)
774 {
775 if (SharedObject == rgObjects[uIdx]->GetObjectDomain())
776 {
777 ++uSharedObjectCount;
778 }
779
780 if (uSharedObjectCount > 0 && uSharedObjectCount <= uIdx)
781 {
782 wdWaitDomain = MixedWait;
783 break;
784 }
785 }
786
787 if (dwObjectCount == uSharedObjectCount)
788 {
789 wdWaitDomain = SharedWait;
790 }
791
792 for (uIdx=0;uIdx<dwObjectCount;uIdx++)
793 {
794 void * pvSData;
795 CSynchData * psdSynchData;
796 ObjectDomain odObjectDomain = rgObjects[uIdx]->GetObjectDomain();
797
798 palErr = rgObjects[uIdx]->GetObjectSynchData((void **)&pvSData);
799 if (NO_ERROR != palErr)
800 {
801 break;
802 }
803
804 psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
805 CSynchData, reinterpret_cast<SharedID>(pvSData)) :
806 static_cast<CSynchData *>(pvSData);
807
808 VALIDATEOBJECT(psdSynchData);
809
810 potObjectType = rgObjects[uIdx]->GetObjectType();
811
812 if (CSynchControllerBase::WaitController == ctCtrlrType)
813 {
814 Ctrlrs.pWaitCtrlrs[uIdx]->Init(pthrCurrent,
815 ctCtrlrType,
816 odObjectDomain,
817 potObjectType,
818 psdSynchData,
819 wdWaitDomain);
820 }
821 else
822 {
823 Ctrlrs.pStateCtrlrs[uIdx]->Init(pthrCurrent,
824 ctCtrlrType,
825 odObjectDomain,
826 potObjectType,
827 psdSynchData,
828 wdWaitDomain);
829 }
830
831 if (CSynchControllerBase::WaitController == ctCtrlrType &&
832 otiProcess == potObjectType->GetId())
833 {
834 CProcProcessLocalData * pProcLocData;
835 IDataLock * pDataLock;
836
837 palErr = rgObjects[uIdx]->GetProcessLocalData(
838 pthrCurrent,
839 ReadLock,
840 &pDataLock,
841 (void **)&pProcLocData);
842
843 if (NO_ERROR != palErr)
844 {
845 // In case of failure here, bail out of the loop, but
846 // keep track (by incrementing the counter 'uIdx') of the
847 // fact that this controller has already being initialized
848 // and therefore need to be Release'd rather than just
849 // returned to the cache
850 uIdx++;
851 break;
852 }
853
854 Ctrlrs.pWaitCtrlrs[uIdx]->SetProcessData(rgObjects[uIdx], pProcLocData);
855 pDataLock->ReleaseLock(pthrCurrent, false);
856 }
857 }
858 if (NO_ERROR != palErr)
859 {
860 // An error occurred while initializing the (uIdx+1)-th controller,
861 // i.e. the one at index uIdx; therefore the first uIdx controllers
862 // must be Release'd, while the remaining uCount-uIdx must be returned
863 // directly to the cache.
864 uErrCleanupIdxFirstNotInitializedCtrlr = uIdx;
865 uErrCleanupIdxLastCtrlr = dwObjectCount;
866
867 goto GSCFO_error_cleanup;
868 }
869
870 // Succeeded
871 if (CSynchControllerBase::WaitController == ctCtrlrType)
872 {
873 for (uIdx=0;uIdx<dwObjectCount;uIdx++)
874 {
875 // The multiple cast is NEEDED, though currently it does not
876 // change the value ot the pointer. Anyway, if in the future
877 // a virtual method should be added to the base class
878 // CSynchControllerBase, both derived classes would have two
879 // virtual tables, therefore a static cast from, for instance,
880 // a CSynchWaitController* to a ISynchWaitController* would
881 // return the given pointer incremented by the size of a
882 // generic pointer on the specific platform
883 ppvControllers[uIdx] = reinterpret_cast<void *>(
884 static_cast<ISynchWaitController *>(Ctrlrs.pWaitCtrlrs[uIdx]));
885 }
886 }
887 else
888 {
889 for (uIdx=0;uIdx<dwObjectCount;uIdx++)
890 {
891 // See comment above
892 ppvControllers[uIdx] = reinterpret_cast<void *>(
893 static_cast<ISynchStateController *>(Ctrlrs.pStateCtrlrs[uIdx]));
894 }
895 }
896
897 // Succeeded: skip error cleanup
898 goto GSCFO_exit;
899
900 GSCFO_error_cleanup:
901 if (CSynchControllerBase::WaitController == ctCtrlrType)
902 {
903 // Release already initialized wait controllers
904 for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
905 {
906 Ctrlrs.pWaitCtrlrs[uIdx]->Release();
907 }
908
909 // Return to the cache not yet initialized wait controllers
910 for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
911 {
912 m_cacheWaitCtrlrs.Add(pthrCurrent, Ctrlrs.pWaitCtrlrs[uIdx]);
913 }
914 }
915 else
916 {
917 // Release already initialized state controllers
918 for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
919 {
920 Ctrlrs.pStateCtrlrs[uIdx]->Release();
921 }
922
923 // Return to the cache not yet initialized state controllers
924 for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
925 {
926 m_cacheStateCtrlrs.Add(pthrCurrent, Ctrlrs.pStateCtrlrs[uIdx]);
927 }
928 }
929
930 GSCFO_exit:
931 if (fLocalSynchLock)
932 {
933 ReleaseLocalSynchLock(pthrCurrent);
934 }
935 return palErr;
936 }
937
938 /*++
939 Method:
940 CPalSynchronizationManager::AllocateObjectSynchData
941
942 Returns a new SynchData for an object of given type and domain
943 --*/
944 PAL_ERROR CPalSynchronizationManager::AllocateObjectSynchData(
945 CObjectType *potObjectType,
946 ObjectDomain odObjectDomain,
947 VOID **ppvSynchData)
948 {
949 PAL_ERROR palErr = NO_ERROR;
950 CSynchData * psdSynchData = NULL;
951 CPalThread * pthrCurrent = InternalGetCurrentThread();
952
953 if (SharedObject == odObjectDomain)
954 {
955 SharedID shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
956 if (NULL == shridSynchData)
957 {
958 ERROR("Unable to allocate shared memory\n");
959 return ERROR_NOT_ENOUGH_MEMORY;
960 }
961
962 psdSynchData = SharedIDToTypePointer(CSynchData, shridSynchData);
963
964 VALIDATEOBJECT(psdSynchData);
965
966 _ASSERT_MSG(NULL != psdSynchData, "Bad shared memory pointer\n");
967
968 // Initialize waiting list pointers
969 psdSynchData->SetWTLHeadShrPtr(NULL);
970 psdSynchData->SetWTLTailShrPtr(NULL);
971
972 // Store shared pointer to this object
973 psdSynchData->SetSharedThis(shridSynchData);
974
975 *ppvSynchData = reinterpret_cast<void *>(shridSynchData);
976 }
977 else
978 {
979 psdSynchData = m_cacheSynchData.Get(pthrCurrent);
980 if (NULL == psdSynchData)
981 {
982 ERROR("Unable to allocate memory\n");
983 return ERROR_NOT_ENOUGH_MEMORY;
984 }
985
986 // Initialize waiting list pointers
987 psdSynchData->SetWTLHeadPtr(NULL);
988 psdSynchData->SetWTLTailPtr(NULL);
989
990 // Set shared this pointer to NULL
991 psdSynchData->SetSharedThis(NULL);
992
993 *ppvSynchData = static_cast<void *>(psdSynchData);
994 }
995
996 // Initialize object domain and object type;
997 psdSynchData->SetObjectDomain(odObjectDomain);
998 psdSynchData->SetObjectType(potObjectType);
999
1000 return palErr;
1001 }
1002
1003 /*++
1004 Method:
1005 CPalSynchronizationManager::FreeObjectSynchData
1006
1007 Called to return a no longer used SynchData to the Synchronization Manager.
1008 The SynchData may actually survive this call, since it is a ref-counted
1009 object and at FreeObjectSynchData time it may still be used from within
1010 the Synchronization Manager itself (e.g. the worker thread).
1011 --*/
1012 void CPalSynchronizationManager::FreeObjectSynchData(
1013 CObjectType *potObjectType,
1014 ObjectDomain odObjectDomain,
1015 VOID *pvSynchData)
1016 {
1017 CSynchData * psdSynchData;
1018 CPalThread * pthrCurrent = InternalGetCurrentThread();
1019
1020 if (odObjectDomain == SharedObject)
1021 {
1022 psdSynchData = SharedIDToTypePointer(CSynchData,
1023 reinterpret_cast<SharedID>(pvSynchData));
1024
1025 if (NULL == psdSynchData)
1026 {
1027 ASSERT("Bad shared memory pointer\n");
1028 return;
1029 }
1030 }
1031 else
1032 {
1033 psdSynchData = static_cast<CSynchData *>(pvSynchData);
1034 }
1035
1036 psdSynchData->Release(pthrCurrent);
1037 }
1038
1039 /*++
1040 Method:
1041 CPalSynchronizationManager::CreateSynchStateController
1042
1043 Creates a state controller for the given object
1044 --*/
1045 PAL_ERROR CPalSynchronizationManager::CreateSynchStateController(
1046 CPalThread *pthrCurrent,
1047 CObjectType *potObjectType,
1048 VOID *pvSynchData,
1049 ObjectDomain odObjectDomain,
1050 ISynchStateController **ppStateController)
1051 {
1052 PAL_ERROR palErr = NO_ERROR;
1053 CSynchStateController * pCtrlr = NULL;
1054 WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
1055 CSynchData * psdSynchData;
1056
1057 psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(CSynchData, reinterpret_cast<SharedID>(pvSynchData))
1058 : static_cast<CSynchData *>(pvSynchData);
1059
1060 VALIDATEOBJECT(psdSynchData);
1061
1062 pCtrlr = m_cacheStateCtrlrs.Get(pthrCurrent);
1063 if (NULL == pCtrlr)
1064 {
1065 return ERROR_NOT_ENOUGH_MEMORY;
1066 }
1067
1068 pCtrlr->Init(pthrCurrent,
1069 CSynchControllerBase::StateController,
1070 odObjectDomain,
1071 potObjectType,
1072 psdSynchData,
1073 wdWaitDomain);
1074
1075 // Succeeded
1076 *ppStateController = (ISynchStateController *)pCtrlr;
1077
1078 if (NO_ERROR != palErr)
1079 {
1080 m_cacheStateCtrlrs.Add(pthrCurrent, pCtrlr);
1081 }
1082
1083 return palErr;
1084 }
1085
1086 /*++
1087 Method:
1088 CPalSynchronizationManager::CreateSynchWaitController
1089
1090 Creates a wait controller for the given object
1091 --*/
1092 PAL_ERROR CPalSynchronizationManager::CreateSynchWaitController(
1093 CPalThread *pthrCurrent,
1094 CObjectType *potObjectType,
1095 VOID *pvSynchData,
1096 ObjectDomain odObjectDomain,
1097 ISynchWaitController **ppWaitController)
1098 {
1099 CSynchWaitController * pCtrlr = NULL;
1100 WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
1101 CSynchData * psdSynchData;
1102
1103 psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
1104 CSynchData, reinterpret_cast<SharedID>(pvSynchData)) :
1105 static_cast<CSynchData *>(pvSynchData);
1106
1107 VALIDATEOBJECT(psdSynchData);
1108
1109 pCtrlr = m_cacheWaitCtrlrs.Get(pthrCurrent);
1110 if (NULL == pCtrlr)
1111 {
1112 return ERROR_NOT_ENOUGH_MEMORY;
1113 }
1114
1115 pCtrlr->Init(pthrCurrent,
1116 CSynchControllerBase::WaitController,
1117 odObjectDomain,
1118 potObjectType,
1119 psdSynchData,
1120 wdWaitDomain);
1121
1122 // Succeeded
1123 *ppWaitController = (ISynchWaitController *)pCtrlr;
1124
1125 return NO_ERROR;
1126 }
1127
1128 /*++
1129 Method:
1130 CPalSynchronizationManager::QueueUserAPC
1131
1132 Internal implementation of QueueUserAPC
1133 --*/
1134 PAL_ERROR CPalSynchronizationManager::QueueUserAPC(CPalThread * pthrCurrent,
1135 CPalThread * pthrTarget,
1136 PAPCFUNC pfnAPC,
1137 ULONG_PTR uptrData)
1138 {
1139 PAL_ERROR palErr = NO_ERROR;
1140 ThreadApcInfoNode * ptainNode = NULL;
1141 DWORD dwWaitState;
1142 DWORD * pdwWaitState;
1143 ThreadWaitInfo * pTargetTWInfo = GetThreadWaitInfo(pthrTarget);
1144 bool fLocalSynchLock = false;
1145 bool fSharedSynchLock = false;
1146 bool fThreadLock = false;
1147
1148 ptainNode = m_cacheThreadApcInfoNodes.Get(pthrCurrent);
1149 if (NULL == ptainNode)
1150 {
1151 ERROR("No memory for new APCs linked list entry\n");
1152 palErr = ERROR_NOT_ENOUGH_MEMORY;
1153 goto QUAPC_exit;
1154 }
1155
1156 ptainNode->pfnAPC = pfnAPC;
1157 ptainNode->pAPCData = uptrData;
1158 ptainNode->pNext = NULL;
1159
1160 AcquireLocalSynchLock(pthrCurrent);
1161 fLocalSynchLock = true;
1162
1163 if (LocalWait != pTargetTWInfo->wdWaitDomain)
1164 {
1165 AcquireSharedSynchLock(pthrCurrent);
1166 fSharedSynchLock = true;
1167 }
1168
1169 pthrTarget->Lock(pthrCurrent);
1170 fThreadLock = true;
1171
1172 if (TS_DONE == pthrTarget->synchronizationInfo.GetThreadState())
1173 {
1174 ERROR("Thread %#x has terminated; can't queue an APC on it\n",
1175 pthrTarget->GetThreadId());
1176 palErr = ERROR_INVALID_PARAMETER;
1177 goto QUAPC_exit;
1178 }
1179 pdwWaitState = SharedIDToTypePointer(DWORD,
1180 pthrTarget->synchronizationInfo.m_shridWaitAwakened);
1181 if (TWS_EARLYDEATH == VolatileLoad(pdwWaitState))
1182 {
1183 ERROR("Thread %#x is about to be suspended for process shutdwon, "
1184 "can't queue an APC on it\n", pthrTarget->GetThreadId());
1185 palErr = ERROR_INVALID_PARAMETER;
1186 goto QUAPC_exit;
1187 }
1188
1189 if (NULL == pthrTarget->apcInfo.m_ptainTail)
1190 {
1191 _ASSERT_MSG(NULL == pthrTarget->apcInfo.m_ptainHead, "Corrupted APC list\n");
1192
1193 pthrTarget->apcInfo.m_ptainHead = ptainNode;
1194 pthrTarget->apcInfo.m_ptainTail = ptainNode;
1195 }
1196 else
1197 {
1198 pthrTarget->apcInfo.m_ptainTail->pNext = ptainNode;
1199 pthrTarget->apcInfo.m_ptainTail = ptainNode;
1200 }
1201
1202 // Set ptainNode to NULL so it won't be readded to the cache
1203 ptainNode = NULL;
1204
1205 TRACE("APC %p with parameter %p added to APC queue\n", pfnAPC, uptrData);
1206
1207 dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState,
1208 (LONG)TWS_ACTIVE,
1209 (LONG)TWS_ALERTABLE);
1210
1211 // Release thread lock
1212 pthrTarget->Unlock(pthrCurrent);
1213 fThreadLock = false;
1214
1215 if (TWS_ALERTABLE == dwWaitState)
1216 {
1217 // Unregister the wait
1218 UnRegisterWait(pthrCurrent, pTargetTWInfo, fSharedSynchLock);
1219
1220 // Wake up target thread
1221 palErr = WakeUpLocalThread(
1222 pthrCurrent,
1223 pthrTarget,
1224 Alerted,
1225 0);
1226
1227 if (NO_ERROR != palErr)
1228 {
1229 ERROR("Failed to wakeup local thread %#x for dispatching APCs [err=%u]\n",
1230 pthrTarget->GetThreadId(), palErr);
1231 }
1232 }
1233
1234 QUAPC_exit:
1235 if (fThreadLock)
1236 {
1237 pthrTarget->Unlock(pthrCurrent);
1238 }
1239
1240 if (fSharedSynchLock)
1241 {
1242 ReleaseSharedSynchLock(pthrCurrent);
1243 }
1244
1245 if (fLocalSynchLock)
1246 {
1247 ReleaseLocalSynchLock(pthrCurrent);
1248 }
1249
1250 if (ptainNode)
1251 {
1252 m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
1253 }
1254
1255 return palErr;
1256 }
1257
1258 /*++
1259 Method:
1260 CPalSynchronizationManager::SendTerminationRequestToWorkerThread
1261
1262 Send a request to the worker thread to initiate process termination.
1263 --*/
1264 PAL_ERROR CPalSynchronizationManager::SendTerminationRequestToWorkerThread()
1265 {
1266 PAL_ERROR palErr = GetInstance()->WakeUpLocalWorkerThread(SynchWorkerCmdTerminationRequest);
1267 if (palErr != NO_ERROR)
1268 {
1269 ERROR("Failed to wake up worker thread [errno=%d {%s%}]\n",
1270 errno, strerror(errno));
1271 palErr = ERROR_INTERNAL_ERROR;
1272 }
1273
1274 return palErr;
1275 }
1276
1277 /*++
1278 Method:
1279 CPalSynchronizationManager::AreAPCsPending
1280
1281 Returns 'true' if there are APCs currently pending for the target
1282 thread (normally the current one)
1283 --*/
1284 bool CPalSynchronizationManager::AreAPCsPending(
1285 CPalThread * pthrTarget)
1286 {
1287 // No need to lock here
1288 return (NULL != pthrTarget->apcInfo.m_ptainHead);
1289 }
1290
1291 /*++
1292 Method:
1293 CPalSynchronizationManager::DispatchPendingAPCs
1294
1295 Executes any pending APC for the current thread
1296 --*/
1297 PAL_ERROR CPalSynchronizationManager::DispatchPendingAPCs(
1298 CPalThread * pthrCurrent)
1299 {
1300 ThreadApcInfoNode * ptainNode, * ptainLocalHead;
1301 int iAPCsCalled = 0;
1302
1303 while (TRUE)
1304 {
1305 // Lock
1306 pthrCurrent->Lock(pthrCurrent);
1307 ptainLocalHead = pthrCurrent->apcInfo.m_ptainHead;
1308 if (ptainLocalHead)
1309 {
1310 pthrCurrent->apcInfo.m_ptainHead = NULL;
1311 pthrCurrent->apcInfo.m_ptainTail = NULL;
1312 }
1313
1314 // Unlock
1315 pthrCurrent->Unlock(pthrCurrent);
1316
1317 if (NULL == ptainLocalHead)
1318 {
1319 break;
1320 }
1321
1322 while (ptainLocalHead)
1323 {
1324 ptainNode = ptainLocalHead;
1325 ptainLocalHead = ptainNode->pNext;
1326
1327#if _ENABLE_DEBUG_MESSAGES_
1328 // reset ENTRY nesting level back to zero while
1329 // inside the callback ...
1330 int iOldLevel = DBG_change_entrylevel(0);
1331#endif /* _ENABLE_DEBUG_MESSAGES_ */
1332
1333 TRACE("Calling APC %p with parameter %#x\n",
1334 ptainNode->pfnAPC, ptainNode->pfnAPC);
1335
1336 // Actual APC call
1337 ptainNode->pfnAPC(ptainNode->pAPCData);
1338
1339#if _ENABLE_DEBUG_MESSAGES_
1340 // ... and set nesting level back to what it was
1341 DBG_change_entrylevel(iOldLevel);
1342#endif /* _ENABLE_DEBUG_MESSAGES_ */
1343
1344 iAPCsCalled++;
1345 m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
1346 }
1347 }
1348
1349 return (iAPCsCalled > 0) ? NO_ERROR : ERROR_NOT_FOUND;
1350 }
1351
1352 /*++
1353 Method:
1354 CPalSynchronizationManager::DiscardAllPendingAPCs
1355
1356 Discards any pending APC for the target pthrTarget thread
1357 --*/
1358 void CPalSynchronizationManager::DiscardAllPendingAPCs(
1359 CPalThread * pthrCurrent,
1360 CPalThread * pthrTarget)
1361 {
1362 ThreadApcInfoNode * ptainNode, * ptainLocalHead;
1363
1364 // Lock
1365 pthrTarget->Lock(pthrCurrent);
1366 ptainLocalHead = pthrTarget->apcInfo.m_ptainHead;
1367 if (ptainLocalHead)
1368 {
1369 pthrTarget->apcInfo.m_ptainHead = NULL;
1370 pthrTarget->apcInfo.m_ptainTail = NULL;
1371 }
1372
1373 // Unlock
1374 pthrTarget->Unlock(pthrCurrent);
1375
1376 while (ptainLocalHead)
1377 {
1378 ptainNode = ptainLocalHead;
1379 ptainLocalHead = ptainNode->pNext;
1380
1381 m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
1382 }
1383 }
1384
1385 /*++
1386 Method:
1387 CPalSynchronizationManager::CreatePalSynchronizationManager
1388
1389 Creates the Synchronization Manager.
1390 Private method, it is called only by CPalSynchMgrController.
1391 --*/
1392 IPalSynchronizationManager * CPalSynchronizationManager::CreatePalSynchronizationManager()
1393 {
1394 if (s_pObjSynchMgr != NULL)
1395 {
1396 ASSERT("Multiple PAL Synchronization manager initializations\n");
1397 return NULL;
1398 }
1399
1400 Initialize();
1401 return static_cast<IPalSynchronizationManager *>(s_pObjSynchMgr);
1402 }
1403
1404 /*++
1405 Method:
1406 CPalSynchronizationManager::Initialize
1407
1408 Internal Synchronization Manager initialization
1409 --*/
1410 PAL_ERROR CPalSynchronizationManager::Initialize()
1411 {
1412 PAL_ERROR palErr = NO_ERROR;
1413 LONG lInit;
1414 CPalSynchronizationManager * pSynchManager = NULL;
1415
1416 lInit = InterlockedCompareExchange(&s_lInitStatus,
1417 (LONG)SynchMgrStatusInitializing,
1418 (LONG)SynchMgrStatusIdle);
1419
1420 if ((LONG)SynchMgrStatusIdle != lInit)
1421 {
1422 ASSERT("Synchronization Manager already being initialized");
1423 palErr = ERROR_INTERNAL_ERROR;
1424 goto I_exit;
1425 }
1426
1427 InternalInitializeCriticalSection(&s_csSynchProcessLock);
1428 InternalInitializeCriticalSection(&s_csMonitoredProcessesLock);
1429
1430 pSynchManager = InternalNew<CPalSynchronizationManager>();
1431 if (NULL == pSynchManager)
1432 {
1433 ERROR("Failed to allocate memory for Synchronization Manager");
1434 palErr = ERROR_NOT_ENOUGH_MEMORY;
1435 goto I_exit;
1436 }
1437
1438 if (!pSynchManager->CreateProcessPipe())
1439 {
1440 ERROR("Unable to create process pipe \n");
1441 palErr = ERROR_OPEN_FAILED;
1442 goto I_exit;
1443 }
1444
1445 s_pObjSynchMgr = pSynchManager;
1446
1447 // Initialization was successful
1448 g_pSynchronizationManager =
1449 static_cast<IPalSynchronizationManager *>(pSynchManager);
1450 s_lInitStatus = (LONG)SynchMgrStatusRunning;
1451
1452 I_exit:
1453 if (NO_ERROR != palErr)
1454 {
1455 s_lInitStatus = (LONG)SynchMgrStatusError;
1456 if (NULL != pSynchManager)
1457 {
1458 pSynchManager->ShutdownProcessPipe();
1459 }
1460
1461 s_pObjSynchMgr = NULL;
1462 g_pSynchronizationManager = NULL;
1463 InternalDelete(pSynchManager);
1464 }
1465
1466 return palErr;
1467 }
1468
1469 /*++
1470 Method:
1471 CPalSynchronizationManager::StartWorker
1472
1473 Starts the Synchronization Manager's Worker Thread.
1474 Private method, it is called only by CPalSynchMgrController.
1475 --*/
1476 PAL_ERROR CPalSynchronizationManager::StartWorker(
1477 CPalThread * pthrCurrent)
1478 {
1479 PAL_ERROR palErr = NO_ERROR;
1480 CPalSynchronizationManager * pSynchManager = GetInstance();
1481
1482 if ((NULL == pSynchManager) || ((LONG)SynchMgrStatusRunning != s_lInitStatus))
1483 {
1484 ERROR("Trying to to create worker thread in invalid state\n");
1485 return ERROR_INTERNAL_ERROR;
1486 }
1487
1488 HANDLE hWorkerThread = NULL;
1489 palErr = InternalCreateThread(pthrCurrent,
1490 NULL,
1491 0,
1492 &WorkerThread,
1493 (PVOID)pSynchManager,
1494 0,
1495 PalWorkerThread,
1496 &pSynchManager->m_dwWorkerThreadTid,
1497 &hWorkerThread);
1498
1499 if (NO_ERROR == palErr)
1500 {
1501 palErr = InternalGetThreadDataFromHandle(pthrCurrent,
1502 hWorkerThread,
1503 0,
1504 &pSynchManager->m_pthrWorker,
1505 &pSynchManager->m_pipoThread);
1506 if (NO_ERROR != palErr)
1507 {
1508 ERROR("Unable to get worker thread data\n");
1509 }
1510 }
1511 else
1512 {
1513 ERROR("Unable to create worker thread\n");
1514 }
1515
1516 if (NULL != hWorkerThread)
1517 {
1518 CloseHandle(hWorkerThread);
1519 }
1520
1521 return palErr;
1522 }
1523
1524 /*++
1525 Method:
1526 CPalSynchronizationManager::PrepareForShutdown
1527
1528 This method performs the part of Synchronization Manager's shutdown that
1529 needs to be carried out when core PAL subsystems are still active.
1530 Private method, it is called only by CPalSynchMgrController.
1531 --*/
1532 PAL_ERROR CPalSynchronizationManager::PrepareForShutdown()
1533 {
1534 PAL_ERROR palErr = NO_ERROR;
1535 CPalSynchronizationManager * pSynchManager = GetInstance();
1536 CPalThread * pthrCurrent = InternalGetCurrentThread();
1537 int iRet;
1538 ThreadNativeWaitData * ptnwdWorkerThreadNativeData;
1539 struct timespec tsAbsTmo = { 0, 0 };
1540
1541 LONG lInit = InterlockedCompareExchange(&s_lInitStatus,
1542 (LONG)SynchMgrStatusShuttingDown, (LONG)SynchMgrStatusRunning);
1543
1544 if ((LONG)SynchMgrStatusRunning != lInit)
1545 {
1546 ASSERT("Unexpected initialization status found "
1547 "in PrepareForShutdown [expected=%d current=%d]\n",
1548 SynchMgrStatusRunning, lInit);
1549 // We intentionally not set s_lInitStatus to SynchMgrStatusError
1550 // cause this could interfere with a previous thread already
1551 // executing shutdown
1552 palErr = ERROR_INTERNAL_ERROR;
1553 goto PFS_exit;
1554 }
1555
1556 // Discard process monitoring for process waits
1557 pSynchManager->DiscardMonitoredProcesses(pthrCurrent);
1558
1559 if (NULL == pSynchManager->m_pipoThread)
1560 {
1561 // If m_pipoThread is NULL here, that means that StartWorker has
1562 // never been called. That may happen if PAL_Initialize fails
1563 // sometime after having called CreatePalSynchronizationManager,
1564 // but before calling StartWorker. Nothing else to do here.
1565 goto PFS_exit;
1566 }
1567
1568 palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdShutdown);
1569 if (NO_ERROR != palErr)
1570 {
1571 ERROR("Failed stopping worker thread [palErr=%u]\n", palErr);
1572 s_lInitStatus = SynchMgrStatusError;
1573 goto PFS_exit;
1574 }
1575
1576 ptnwdWorkerThreadNativeData =
1577 &pSynchManager->m_pthrWorker->synchronizationInfo.m_tnwdNativeData;
1578
1579 palErr = GetAbsoluteTimeout(WorkerThreadTerminationTimeout, &tsAbsTmo, /*fPreferMonotonicClock*/ TRUE);
1580 if (NO_ERROR != palErr)
1581 {
1582 ERROR("Failed to convert timeout to absolute timeout\n");
1583 s_lInitStatus = SynchMgrStatusError;
1584 goto PFS_exit;
1585 }
1586
1587 // Using the worker thread's predicate/condition/mutex
1588 // to wait for worker thread to be done
1589 iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
1590 if (0 != iRet)
1591 {
1592 // pthread calls might fail if the shutdown is called
1593 // from a signal handler. In this case just don't wait
1594 // for the worker thread
1595 ERROR("Cannot lock mutex [err=%d]\n", iRet);
1596 palErr = ERROR_INTERNAL_ERROR;
1597 s_lInitStatus = SynchMgrStatusError;
1598 goto PFS_exit;
1599 }
1600
1601 while (FALSE == ptnwdWorkerThreadNativeData->iPred)
1602 {
1603 iRet = pthread_cond_timedwait(&ptnwdWorkerThreadNativeData->cond,
1604 &ptnwdWorkerThreadNativeData->mutex,
1605 &tsAbsTmo);
1606 if (0 != iRet)
1607 {
1608 if (ETIMEDOUT == iRet)
1609 {
1610 WARN("Timed out waiting for worker thread to exit "
1611 "(tmo=%u ms)\n", WorkerThreadTerminationTimeout);
1612 }
1613 else
1614 {
1615 ERROR("pthread_cond_timedwait returned %d [errno=%d (%s)]\n",
1616 iRet, errno, strerror(errno));
1617 }
1618 break;
1619 }
1620 }
1621 if (0 == iRet)
1622 {
1623 ptnwdWorkerThreadNativeData->iPred = FALSE;
1624 }
1625 iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);
1626 if (0 != iRet)
1627 {
1628 ERROR("Cannot unlock mutex [err=%d]\n", iRet);
1629 palErr = ERROR_INTERNAL_ERROR;
1630 s_lInitStatus = SynchMgrStatusError;
1631 goto PFS_exit;
1632 }
1633
1634 PFS_exit:
1635 if (NO_ERROR == palErr)
1636 {
1637 if (NULL != pSynchManager->m_pipoThread)
1638 {
1639 pSynchManager->m_pipoThread->ReleaseReference(pthrCurrent);
1640
1641 // After this release both m_pipoThread and m_pthrWorker
1642 // are no longer valid
1643 pSynchManager->m_pipoThread = NULL;
1644 pSynchManager->m_pthrWorker = NULL;
1645 }
1646
1647 // Ready for process shutdown
1648 s_lInitStatus = SynchMgrStatusReadyForProcessShutDown;
1649 }
1650
1651 return palErr;
1652 }
1653
1654 // Entry point routine for the thread that initiates process termination.
1655 DWORD PALAPI TerminationRequestHandlingRoutine(LPVOID pArg)
1656 {
1657 // Call the termination request handler if one is registered.
1658 if (g_terminationRequestHandler != NULL)
1659 {
1660 g_terminationRequestHandler();
1661 }
1662
1663 return 0;
1664 }
1665
1666 /*++
1667 Method:
1668 CPalSynchronizationManager::WorkerThread
1669
1670 Synchronization Manager's Worker Thread
1671 --*/
1672 DWORD PALAPI CPalSynchronizationManager::WorkerThread(LPVOID pArg)
1673 {
1674 PAL_ERROR palErr;
1675 bool fShuttingDown = false;
1676 bool fWorkerIsDone = false;
1677 int iPollTimeout = INFTIM;
1678 SynchWorkerCmd swcCmd;
1679 ThreadWakeupReason twrWakeUpReason;
1680 SharedID shridMarshaledData;
1681 DWORD dwData;
1682 CPalSynchronizationManager * pSynchManager =
1683 reinterpret_cast<CPalSynchronizationManager*>(pArg);
1684 CPalThread * pthrWorker = InternalGetCurrentThread();
1685
1686 while (!fWorkerIsDone)
1687 {
1688 LONG lProcessCount;
1689
1690 palErr = pSynchManager->ReadCmdFromProcessPipe(iPollTimeout,
1691 &swcCmd,
1692 &shridMarshaledData,
1693 &dwData);
1694 if (NO_ERROR != palErr)
1695 {
1696 ERROR("Received error %x from ReadCmdFromProcessPipe()\n",
1697 palErr);
1698 continue;
1699 }
1700 switch (swcCmd)
1701 {
1702 case SynchWorkerCmdTerminationRequest:
1703 // This worker thread is being asked to initiate process termination
1704
1705 HANDLE hTerminationRequestHandlingThread;
1706 palErr = InternalCreateThread(pthrWorker,
1707 NULL,
1708 0,
1709 &TerminationRequestHandlingRoutine,
1710 NULL,
1711 0,
1712 PalWorkerThread,
1713 NULL,
1714 &hTerminationRequestHandlingThread);
1715
1716 if (NO_ERROR != palErr)
1717 {
1718 ERROR("Unable to create worker thread\n");
1719 }
1720
1721 if (hTerminationRequestHandlingThread != NULL)
1722 {
1723 CloseHandle(hTerminationRequestHandlingThread);
1724 }
1725
1726 break;
1727 case SynchWorkerCmdNop:
1728 TRACE("Synch Worker: received SynchWorkerCmdNop\n");
1729 if (fShuttingDown)
1730 {
1731 TRACE("Synch Worker: received a timeout when "
1732 "fShuttingDown==true: worker is done, bailing "
1733 "out from the loop\n");
1734
1735 // Whether WorkerThreadShuttingDownTimeout has elapsed
1736 // or the last process with a descriptor opened for
1737 // write on our process pipe, has just closed it,
1738 // causing an EOF on the read fd (that can happen only
1739 // at shutdown time since during normal run time we
1740 // hold a fd opened for write within this process).
1741 // In both the case it is time to go for the worker
1742 // thread.
1743 fWorkerIsDone = true;
1744 }
1745 else
1746 {
1747 lProcessCount = pSynchManager->DoMonitorProcesses(pthrWorker);
1748 if (lProcessCount > 0)
1749 {
1750 iPollTimeout = WorkerThreadProcMonitoringTimeout;
1751 }
1752 else
1753 {
1754 iPollTimeout = INFTIM;
1755 }
1756 }
1757 break;
1758 case SynchWorkerCmdRemoteSignal:
1759 {
1760 // Note: this cannot be a wait all
1761 WaitingThreadsListNode * pWLNode;
1762 ThreadWaitInfo * ptwiWaitInfo;
1763 DWORD dwObjIndex;
1764 bool fSharedSynchLock = false;
1765
1766 // Lock
1767 AcquireLocalSynchLock(pthrWorker);
1768 AcquireSharedSynchLock(pthrWorker);
1769 fSharedSynchLock = true;
1770
1771 pWLNode = SharedIDToTypePointer(WaitingThreadsListNode,
1772 shridMarshaledData);
1773
1774 _ASSERT_MSG(NULL != pWLNode, "Received bad Shared ID %p\n",
1775 shridMarshaledData);
1776 _ASSERT_MSG(gPID == pWLNode->dwProcessId,
1777 "Remote signal apparently sent to the wrong "
1778 "process [target pid=%u current pid=%u]\n",
1779 pWLNode->dwProcessId, gPID);
1780 _ASSERT_MSG(0 == (WTLN_FLAG_WAIT_ALL & pWLNode->dwFlags),
1781 "Wait all with remote awakening delegated "
1782 "through SynchWorkerCmdRemoteSignal rather than "
1783 "SynchWorkerCmdDelegatedObjectSignaling\n");
1784
1785
1786 // Get the object index
1787 dwObjIndex = pWLNode->dwObjIndex;
1788
1789 // Get the WaitInfo
1790 ptwiWaitInfo = pWLNode->ptwiWaitInfo;
1791
1792 // Initialize the WakeUpReason to WaitSucceeded
1793 twrWakeUpReason = WaitSucceeded;
1794
1795 CSynchData * psdSynchData =
1796 SharedIDToTypePointer(CSynchData,
1797 pWLNode->ptrOwnerObjSynchData.shrid);
1798
1799 TRACE("Synch Worker: received REMOTE SIGNAL cmd "
1800 "[WInfo=%p {Type=%u Domain=%u ObjCount=%d TgtThread=%x} "
1801 "SynchData={shriId=%p p=%p} {SigCount=%d IsAbandoned=%d}\n",
1802 ptwiWaitInfo, ptwiWaitInfo->wtWaitType, ptwiWaitInfo->wdWaitDomain,
1803 ptwiWaitInfo->lObjCount, ptwiWaitInfo->pthrOwner->GetThreadId(),
1804 (VOID *)pWLNode->ptrOwnerObjSynchData.shrid, psdSynchData,
1805 psdSynchData->GetSignalCount(), psdSynchData->IsAbandoned());
1806
1807 if (CObjectType::OwnershipTracked ==
1808 psdSynchData->GetObjectType()->GetOwnershipSemantics())
1809 {
1810 // Abandoned status is not propagated through process
1811 // pipe: need to get it from the object itself before
1812 // resetting the data by acquiring the object ownership
1813 if (psdSynchData->IsAbandoned())
1814 {
1815 twrWakeUpReason = MutexAbondoned;
1816 }
1817
1818 // Acquire ownership
1819 palErr = psdSynchData->AssignOwnershipToThread(
1820 pthrWorker,
1821 ptwiWaitInfo->pthrOwner);
1822 if (NO_ERROR != palErr)
1823 {
1824 ERROR("Synch Worker: AssignOwnershipToThread "
1825 "failed with error %u; ownership data on "
1826 "object with SynchData %p may be "
1827 "corrupted\n", palErr, psdSynchData);
1828 }
1829 }
1830
1831 // Unregister the wait
1832 pSynchManager->UnRegisterWait(pthrWorker,
1833 ptwiWaitInfo,
1834 fSharedSynchLock);
1835
1836 // pWLNode is no longer valid after UnRegisterWait
1837 pWLNode = NULL;
1838
1839 TRACE("Synch Worker: Waking up local thread %x "
1840 "{WakeUpReason=%u ObjIndex=%u}\n",
1841 ptwiWaitInfo->pthrOwner->GetThreadId(),
1842 twrWakeUpReason, dwObjIndex);
1843
1844 // Wake up the target thread
1845 palErr = WakeUpLocalThread(
1846 pthrWorker,
1847 ptwiWaitInfo->pthrOwner,
1848 twrWakeUpReason,
1849 dwObjIndex);
1850 if (NO_ERROR != palErr)
1851 {
1852 ERROR("Synch Worker: Failed to wake up local thread "
1853 "%#x while propagating remote signaling: "
1854 "object signaling may be lost\n",
1855 ptwiWaitInfo->pthrOwner->GetThreadId());
1856 }
1857
1858 // Unlock
1859 ReleaseSharedSynchLock(pthrWorker);
1860 fSharedSynchLock = false;
1861 ReleaseLocalSynchLock(pthrWorker);
1862
1863 break;
1864 }
1865 case SynchWorkerCmdDelegatedObjectSignaling:
1866 {
1867 CSynchData * psdSynchData;
1868
1869 TRACE("Synch Worker: received "
1870 "SynchWorkerCmdDelegatedObjectSignaling\n");
1871
1872 psdSynchData = SharedIDToTypePointer(CSynchData,
1873 shridMarshaledData);
1874
1875 _ASSERT_MSG(NULL != psdSynchData, "Received bad Shared ID %p\n",
1876 shridMarshaledData);
1877 _ASSERT_MSG(0 < dwData && (DWORD)INT_MAX > dwData,
1878 "Received remote signaling with invalid signal "
1879 "count\n");
1880
1881 // Lock
1882 AcquireLocalSynchLock(pthrWorker);
1883 AcquireSharedSynchLock(pthrWorker);
1884
1885 TRACE("Synch Worker: received DELEGATED OBJECT SIGNALING "
1886 "cmd [SynchData={shriId=%p p=%p} SigCount=%u] [Current obj SigCount=%d "
1887 "IsAbandoned=%d]\n", (VOID *)shridMarshaledData,
1888 psdSynchData, dwData, psdSynchData->GetSignalCount(),
1889 psdSynchData->IsAbandoned());
1890
1891 psdSynchData->Signal(pthrWorker,
1892 psdSynchData->GetSignalCount() + dwData,
1893 true);
1894
1895 // Current SynchData has been AddRef'd by remote process in
1896 // order to be marshaled to the current one, therefore at
1897 // this point we need to release it
1898 psdSynchData->Release(pthrWorker);
1899
1900 // Unlock
1901 ReleaseSharedSynchLock(pthrWorker);
1902 ReleaseLocalSynchLock(pthrWorker);
1903
1904 break;
1905 }
1906 case SynchWorkerCmdShutdown:
1907 TRACE("Synch Worker: received SynchWorkerCmdShutdown\n");
1908
1909 // Shutdown the process pipe: this will cause the process
1910 // pipe to be unlinked and its write-only file descriptor
1911 // to be closed, so that when the last fd opened for write
1912 // on the fifo (from another process) will be closed, we
1913 // will receive an EOF on the read end (i.e. poll in
1914 // ReadBytesFromProcessPipe will return 1 with no data to
1915 // be read). That will allow the worker thread to process
1916 // possible commands already successfully written to the
1917 // pipe by some other process, before shutting down.
1918 pSynchManager->ShutdownProcessPipe();
1919
1920 // Shutting down: this will cause the worker thread to
1921 // fetch residual cmds from the process pipe until an
1922 // EOF is converted to a SynchWorkerCmdNop or the
1923 // WorkerThreadShuttingDownTimeout has elapsed without
1924 // receiving any cmd.
1925 fShuttingDown = true;
1926
1927 // Set the timeout to WorkerThreadShuttingDownTimeout
1928 iPollTimeout = WorkerThreadShuttingDownTimeout;
1929 break;
1930 default:
1931 ASSERT("Synch Worker: Unknown worker cmd [swcWorkerCmd=%d]\n",
1932 swcCmd);
1933 break;
1934 }
1935 }
1936
1937 int iRet;
1938 ThreadNativeWaitData * ptnwdWorkerThreadNativeData =
1939 &pthrWorker->synchronizationInfo.m_tnwdNativeData;
1940
1941 // Using the worker thread's predicate/condition/mutex
1942 // (that normally are never used) to signal the shutting
1943 // down thread that the worker thread is done
1944 iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
1945 _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);
1946
1947 ptnwdWorkerThreadNativeData->iPred = TRUE;
1948
1949 iRet = pthread_cond_signal(&ptnwdWorkerThreadNativeData->cond);
1950 if (0 != iRet)
1951 {
1952 ERROR ("pthread_cond_signal returned %d [errno=%d (%s)]\n",
1953 iRet, errno, strerror(errno));
1954 }
1955
1956 iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);
1957 _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);
1958
1959 // Sleep forever
1960 ThreadPrepareForShutdown();
1961
1962 return 0;
1963 }
1964
1965 /*++
1966 Method:
1967 CPalSynchronizationManager::ReadCmdFromProcessPipe
1968
1969 Reads a worker thread cmd from the process pipe. If there is no data
1970 to be read on the pipe, it blocks until there is data available or the
1971 timeout expires.
1972 --*/
1973 PAL_ERROR CPalSynchronizationManager::ReadCmdFromProcessPipe(
1974 int iPollTimeout,
1975 SynchWorkerCmd * pswcWorkerCmd,
1976 SharedID * pshridMarshaledData,
1977 DWORD * pdwData)
1978 {
1979 int iRet;
1980 BYTE byVal;
1981 SynchWorkerCmd swcWorkerCmd = SynchWorkerCmdNop;
1982
1983 _ASSERTE(NULL != pswcWorkerCmd);
1984 _ASSERTE(NULL != pshridMarshaledData);
1985 _ASSERTE(NULL != pdwData);
1986
1987 iRet = ReadBytesFromProcessPipe(iPollTimeout, &byVal, sizeof(BYTE));
1988
1989 if (0 > iRet)
1990 {
1991 ERROR("Failed polling the process pipe [ret=%d errno=%d (%s)]\n",
1992 iRet, errno, strerror(errno));
1993
1994 return ERROR_INTERNAL_ERROR;
1995 }
1996
1997 if (iRet != 0)
1998 {
1999 _ASSERT_MSG(sizeof(BYTE) == iRet,
2000 "Got %d bytes from process pipe while expecting for %d\n",
2001 iRet, sizeof(BYTE));
2002
2003 swcWorkerCmd = (SynchWorkerCmd)byVal;
2004
2005 if (SynchWorkerCmdLast <= swcWorkerCmd)
2006 {
2007 ERROR("Got unknown worker command code %d from the process "
2008 "pipe!\n", swcWorkerCmd);
2009
2010 return ERROR_INTERNAL_ERROR;
2011 }
2012
2013 _ASSERT_MSG(SynchWorkerCmdNop == swcWorkerCmd ||
2014 SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
2015 SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd ||
2016 SynchWorkerCmdShutdown == swcWorkerCmd ||
2017 SynchWorkerCmdTerminationRequest == swcWorkerCmd,
2018 "Unknown worker command code %u\n", swcWorkerCmd);
2019
2020 TRACE("Got cmd %u from process pipe\n", swcWorkerCmd);
2021 }
2022
2023 if (SynchWorkerCmdRemoteSignal == swcWorkerCmd ||
2024 SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
2025 {
2026 SharedID shridMarshaledId = NULL;
2027
2028 TRACE("Received %s cmd\n",
2029 (swcWorkerCmd == SynchWorkerCmdRemoteSignal) ?
2030 "REMOTE SIGNAL" : "DELEGATED OBJECT SIGNALING" );
2031
2032 iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
2033 (BYTE *)&shridMarshaledId,
2034 sizeof(shridMarshaledId));
2035 if (sizeof(shridMarshaledId) != iRet)
2036 {
2037 ERROR("Unable to read marshaled Shared ID from the "
2038 "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
2039 m_iProcessPipeRead, iRet, errno, strerror(errno));
2040
2041 return ERROR_INTERNAL_ERROR;
2042 }
2043
2044 TRACE("Received marshaled shrid=%p\n", (VOID *)shridMarshaledId);
2045
2046 *pshridMarshaledData = shridMarshaledId;
2047 }
2048
2049 if (SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
2050 {
2051 DWORD dwData;
2052
2053 iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout,
2054 (BYTE *)&dwData,
2055 sizeof(dwData));
2056 if (sizeof(dwData) != iRet)
2057 {
2058 ERROR("Unable to read signal count from the "
2059 "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
2060 m_iProcessPipeRead, iRet, errno, strerror(errno));
2061
2062 return ERROR_INTERNAL_ERROR;
2063 }
2064
2065 TRACE("Received signal count %u\n", dwData);
2066
2067 *pdwData = dwData;
2068 }
2069
2070 *pswcWorkerCmd = swcWorkerCmd;
2071 return NO_ERROR;
2072 }
2073
2074 /*++
2075 Method:
2076 CPalSynchronizationManager::ReadBytesFromProcessPipe
2077
2078 Reads the specified number of bytes from the process pipe. If there is
2079 no data to be read on the pipe, it blocks until there is data available
2080 or the timeout expires.
2081 --*/
2082 int CPalSynchronizationManager::ReadBytesFromProcessPipe(
2083 int iTimeout,
2084 BYTE * pRecvBuf,
2085 LONG iBytes)
2086 {
2087#if !HAVE_KQUEUE
2088 struct pollfd Poll;
2089#endif // !HAVE_KQUEUE
2090 int iRet = -1;
2091 int iConsecutiveEintrs = 0;
2092 LONG iBytesRead = 0;
2093 BYTE * pPos = pRecvBuf;
2094#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
2095 struct kevent keChanges;
2096 struct timespec ts, *pts;
2097 int iNChanges;
2098#endif // HAVE_KQUEUE
2099
2100 _ASSERTE(0 <= iBytes);
2101
2102 do
2103 {
2104 while (TRUE)
2105 {
2106 int iErrno = 0;
2107#if HAVE_KQUEUE
2108#if HAVE_BROKEN_FIFO_KEVENT
2109#if HAVE_BROKEN_FIFO_SELECT
2110#error Found no way to wait on a FIFO.
2111#endif
2112
2113 timeval *ptv;
2114 timeval tv;
2115
2116 if (INFTIM == iTimeout)
2117 {
2118 ptv = NULL;
2119 }
2120 else
2121 {
2122 tv.tv_usec = (iTimeout % tccSecondsToMillieSeconds) *
2123 tccMillieSecondsToMicroSeconds;
2124 tv.tv_sec = iTimeout / tccSecondsToMillieSeconds;
2125 ptv = &tv;
2126 }
2127
2128 fd_set readfds;
2129 FD_ZERO(&readfds);
2130 FD_SET(m_iProcessPipeRead, &readfds);
2131 iRet = select(m_iProcessPipeRead + 1, &readfds, NULL, NULL, ptv);
2132
2133#else // HAVE_BROKEN_FIFO_KEVENT
2134
2135 // Note: FreeBSD needs to use kqueue/kevent support here, since on this
2136 // platform the EOF notification on FIFOs is not surfaced through poll,
2137 // and process pipe shutdown relies on this feature.
2138 // If a thread is polling a FIFO or a pipe for POLLIN, when the last
2139 // write descriptor for that pipe is closed, poll() is supposed to
2140 // return with a POLLIN event but no data to be read on the FIFO/pipe,
2141 // which means EOF.
2142 // On FreeBSD such feature works for pipes but it doesn't for FIFOs.
2143 // Using kevent the EOF is instead surfaced correctly.
2144
2145 if (iBytes > m_keProcessPipeEvent.data)
2146 {
2147 if (INFTIM == iTimeout)
2148 {
2149 pts = NULL;
2150 }
2151 else
2152 {
2153 ts.tv_nsec = (iTimeout % tccSecondsToMillieSeconds) *
2154 tccMillieSecondsToNanoSeconds;
2155 ts.tv_sec = iTimeout / tccSecondsToMillieSeconds;
2156 pts = &ts;
2157 }
2158
2159 if (0 != (EV_EOF & m_keProcessPipeEvent.flags))
2160 {
2161 TRACE("Refreshing kevent settings\n");
2162 EV_SET(&keChanges, m_iProcessPipeRead, EVFILT_READ,
2163 EV_ADD | EV_CLEAR, 0, 0, 0);
2164 iNChanges = 1;
2165 }
2166 else
2167 {
2168 iNChanges = 0;
2169 }
2170
2171 iRet = kevent(m_iKQueue, &keChanges, iNChanges,
2172 &m_keProcessPipeEvent, 1, pts);
2173
2174 if (0 < iRet)
2175 {
2176 _ASSERTE(1 == iRet);
2177 _ASSERTE(EVFILT_READ == m_keProcessPipeEvent.filter);
2178
2179 if (EV_ERROR & m_keProcessPipeEvent.flags)
2180 {
2181 ERROR("EV_ERROR from kevent [ident=%d filter=%d flags=%x]\n", m_keProcessPipeEvent.ident, m_keProcessPipeEvent.filter, m_keProcessPipeEvent.flags);
2182 iRet = -1;
2183 iErrno = m_keProcessPipeEvent.data;
2184 m_keProcessPipeEvent.data = 0;
2185 }
2186 }
2187 else if (0 > iRet)
2188 {
2189 iErrno = errno;
2190 }
2191
2192 TRACE("Woken up from kevent() with ret=%d flags=%#x data=%d "
2193 "[iTimeout=%d]\n", iRet, m_keProcessPipeEvent.flags,
2194 m_keProcessPipeEvent.data, iTimeout);
2195 }
2196 else
2197 {
2198 // There is enough data already available in the buffer, just use that.
2199 iRet = 1;
2200 }
2201
2202#endif // HAVE_BROKEN_FIFO_KEVENT
2203#else // HAVE_KQUEUE
2204
2205 Poll.fd = m_iProcessPipeRead;
2206 Poll.events = POLLIN;
2207 Poll.revents = 0;
2208
2209 iRet = poll(&Poll, 1, iTimeout);
2210
2211 TRACE("Woken up from poll() with ret=%d [iTimeout=%d]\n",
2212 iRet, iTimeout);
2213
2214 if (1 == iRet &&
2215 ((POLLERR | POLLHUP | POLLNVAL) & Poll.revents))
2216 {
2217 // During PAL shutdown the pipe gets closed and Poll.revents is set to POLLHUP
2218 // (note: no other flags are set). We will also receive an EOF on from the read call.
2219 // Please see the comment for SynchWorkerCmdShutdown in CPalSynchronizationManager::WorkerThread.
2220 if (!PALIsShuttingDown() || (Poll.revents != POLLHUP))
2221 {
2222 ERROR("Unexpected revents=%x while polling pipe %d\n",
2223 Poll.revents, Poll.fd);
2224 iErrno = EINVAL;
2225 iRet = -1;
2226 }
2227 }
2228 else if (0 > iRet)
2229 {
2230 iErrno = errno;
2231 }
2232
2233#endif // HAVE_KQUEUE
2234
2235 if (0 == iRet || 1 == iRet)
2236 {
2237 // 0 == wait timed out
2238 // 1 == FIFO has data available
2239 break;
2240 }
2241 else
2242 {
2243 if (1 < iRet)
2244 {
2245 // Unexpected iRet > 1
2246 ASSERT("Unexpected return code %d from blocking poll/kevent call\n",
2247 iRet);
2248 goto RBFPP_exit;
2249 }
2250
2251 if (EINTR != iErrno)
2252 {
2253 // Unexpected error
2254 ASSERT("Unexpected error from blocking poll/kevent call: %d (%s)\n",
2255 iErrno, strerror(iErrno));
2256 goto RBFPP_exit;
2257 }
2258
2259 iConsecutiveEintrs++;
2260 TRACE("poll() failed with EINTR; re-polling\n");
2261
2262 if (iConsecutiveEintrs >= MaxWorkerConsecutiveEintrs)
2263 {
2264 if (iTimeout != INFTIM)
2265 {
2266 WARN("Receiving too many EINTRs; converting one of them "
2267 "to a timeout");
2268 iRet = 0;
2269 break;
2270 }
2271 else if (0 == (iConsecutiveEintrs % MaxWorkerConsecutiveEintrs))
2272 {
2273 WARN("Receiving too many EINTRs [%d so far]",
2274 iConsecutiveEintrs);
2275 }
2276 }
2277 }
2278 }
2279
2280 if (0 == iRet)
2281 {
2282 // Time out
2283 break;
2284 }
2285 else
2286 {
2287#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
2288 if (0 != (EV_EOF & m_keProcessPipeEvent.flags) && 0 == m_keProcessPipeEvent.data)
2289 {
2290 // EOF
2291 TRACE("Received an EOF on process pipe via kevent\n");
2292 goto RBFPP_exit;
2293 }
2294#endif // HAVE_KQUEUE
2295
2296 iRet = read(m_iProcessPipeRead, pPos, iBytes - iBytesRead);
2297
2298 if (0 == iRet)
2299 {
2300 // Poll returned 1 and read returned zero: this is an EOF,
2301 // i.e. no other process has the pipe still open for write
2302 TRACE("Received an EOF on process pipe via poll\n");
2303 goto RBFPP_exit;
2304 }
2305 else if (0 > iRet)
2306 {
2307 ERROR("Unable to read %d bytes from the the process pipe "
2308 "[pipe=%d ret=%d errno=%d (%s)]\n", iBytes - iBytesRead,
2309 m_iProcessPipeRead, iRet, errno, strerror(errno));
2310 goto RBFPP_exit;
2311 }
2312
2313 TRACE("Read %d bytes from process pipe\n", iRet);
2314
2315 iBytesRead += iRet;
2316 pPos += iRet;
2317
2318#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
2319 // Update available data count
2320 m_keProcessPipeEvent.data -= iRet;
2321 _ASSERTE(0 <= m_keProcessPipeEvent.data);
2322#endif // HAVE_KQUEUE
2323 }
2324 } while(iBytesRead < iBytes);
2325
2326 RBFPP_exit:
2327 return (iRet < 0) ? iRet : iBytesRead;
2328 }
2329
2330 /*++
2331 Method:
2332 CPalSynchronizationManager::WakeUpLocalThread
2333
2334 Wakes up a local thead currently sleeping for a wait or a sleep
2335 --*/
2336 PAL_ERROR CPalSynchronizationManager::WakeUpLocalThread(
2337 CPalThread * pthrCurrent,
2338 CPalThread * pthrTarget,
2339 ThreadWakeupReason twrWakeupReason,
2340 DWORD dwObjectIndex)
2341 {
2342 PAL_ERROR palErr = NO_ERROR;
2343 ThreadNativeWaitData * ptnwdNativeWaitData =
2344 pthrTarget->synchronizationInfo.GetNativeData();
2345
2346 TRACE("Waking up a local thread [WakeUpReason=%u ObjectIndex=%u "
2347 "ptnwdNativeWaitData=%p]\n", twrWakeupReason, dwObjectIndex,
2348 ptnwdNativeWaitData);
2349
2350 // Set wakeup reason and signaled object index
2351 ptnwdNativeWaitData->twrWakeupReason = twrWakeupReason;
2352 ptnwdNativeWaitData->dwObjectIndex = dwObjectIndex;
2353
2354#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
2355 if (0 < GetLocalSynchLockCount(pthrCurrent))
2356 {
2357 // Defer the actual thread signaling to right after
2358 // releasing the synch lock(s), so that signaling
2359 // can happen from a thread-suspension safe area
2360 palErr = DeferThreadConditionSignaling(pthrCurrent, pthrTarget);
2361 }
2362 else
2363 {
2364 // Signal the target thread's condition
2365 palErr = SignalThreadCondition(ptnwdNativeWaitData);
2366 }
2367#else // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
2368 // Signal the target thread's condition
2369 palErr = SignalThreadCondition(ptnwdNativeWaitData);
2370#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
2371
2372 return palErr;
2373 }
2374
2375#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
2376 /*++
2377 Method:
2378 CPalSynchronizationManager::DeferThreadConditionSignaling
2379
2380 Defers thread signaling to the final release of synchronization
2381 lock(s), so that condition signaling can happen when the signaling
2382 thread is marked as safe for thread suspension.
2383 --*/
2384 PAL_ERROR CPalSynchronizationManager::DeferThreadConditionSignaling(
2385 CPalThread * pthrCurrent,
2386 CPalThread * pthrTarget)
2387 {
2388 PAL_ERROR palErr = NO_ERROR;
2389 LONG lCount = pthrCurrent->synchronizationInfo.m_lPendingSignalingCount;
2390
2391 _ASSERTE(pthrTarget != pthrCurrent);
2392
2393 if (CThreadSynchronizationInfo::PendingSignalingsArraySize > lCount)
2394 {
2395 // If there is available room, add the target thread object to
2396 // the array of pending thread signalings.
2397 pthrCurrent->synchronizationInfo.m_rgpthrPendingSignalings[lCount] = pthrTarget;
2398 }
2399 else
2400 {
2401 // If the array is full, add the target thread object at the end
2402 // of the overflow list
2403 DeferredSignalingListNode * pdsln =
2404 InternalNew<DeferredSignalingListNode>();
2405
2406 if (pdsln)
2407 {
2408 pdsln->pthrTarget = pthrTarget;
2409
2410 // Add the note to the end of the list.
2411 // Note: no need to synchronize the access to this list since
2412 // it is meant to be accessed only by the owner thread.
2413 InsertTailList(&pthrCurrent->synchronizationInfo.m_lePendingSignalingsOverflowList,
2414 &pdsln->Link);
2415 }
2416 else
2417 {
2418 palErr = ERROR_NOT_ENOUGH_MEMORY;
2419 }
2420 }
2421
2422 if (NO_ERROR == palErr)
2423 {
2424 // Increment the count of pending signalings
2425 pthrCurrent->synchronizationInfo.m_lPendingSignalingCount += 1;
2426
2427 // Add a reference to the target CPalThread object; this is
2428 // needed since deferring signaling after releasing the synch
2429 // locks implies accessing the target thread object without
2430 // holding the local synch lock. In rare circumstances, the
2431 // target thread may have already exited while deferred signaling
2432 // takes place, therefore invalidating the thread object. The
2433 // reference added here ensures that the thread object is still
2434 // good, even if the target thread has exited.
2435 pthrTarget->AddThreadReference();
2436 }
2437
2438 return palErr;
2439 }
2440#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
2441
2442 /*++
2443 Method:
2444 CPalSynchronizationManager::SignalThreadCondition
2445
2446 Performs the actual condition signaling in to wake up the target thread
2447 --*/
2448 PAL_ERROR CPalSynchronizationManager::SignalThreadCondition(
2449 ThreadNativeWaitData * ptnwdNativeWaitData)
2450 {
2451 PAL_ERROR palErr = NO_ERROR;
2452 int iRet;
2453
2454 // Lock the mutex
2455 iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
2456 if (0 != iRet)
2457 {
2458 ERROR("Cannot lock mutex [err=%d]\n", iRet);
2459 return ERROR_INTERNAL_ERROR;
2460 }
2461
2462 // Set the predicate
2463 ptnwdNativeWaitData->iPred = TRUE;
2464
2465 // Signal the condition
2466 iRet = pthread_cond_signal(&ptnwdNativeWaitData->cond);
2467 if (0 != iRet)
2468 {
2469 ERROR("Failed to signal condition: pthread_cond_signal "
2470 "returned %d [errno=%d (%s)]\n", iRet, errno,
2471 strerror(errno));
2472 palErr = ERROR_INTERNAL_ERROR;
2473 // Continue in order to unlock the mutex anyway
2474 }
2475
2476 // Unlock the mutex
2477 iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
2478 if (0 != iRet)
2479 {
2480 ERROR("Cannot unlock mutex [err=%d]\n", iRet);
2481 return ERROR_INTERNAL_ERROR;
2482 }
2483
2484 return palErr;
2485 }
2486
2487 /*++
2488 Method:
2489 CPalSynchronizationManager::ReadBytesFromProcessPipe
2490
2491 Wakes up a remote thead currently sleeping for a wait or a sleep
2492 by sending the appropriate cmd to the remote process' worker
2493 thread, which will take care to convert this command into a
2494 WakeUpLocalThread in the remote process
2495 --*/
2496 PAL_ERROR CPalSynchronizationManager::WakeUpRemoteThread(
2497 SharedID shridWLNode)
2498 {
2499 const int MsgSize = sizeof(BYTE) + sizeof(SharedID);
2500 PAL_ERROR palErr = NO_ERROR;
2501 BYTE rgSendBuf[MsgSize];
2502 BYTE * pbySrc, * pbyDst = rgSendBuf;
2503 WaitingThreadsListNode * pWLNode = SharedIDToTypePointer(WaitingThreadsListNode, shridWLNode);
2504
2505 _ASSERT_MSG(gPID != pWLNode->dwProcessId, "WakeUpRemoteThread called on local thread\n");
2506 _ASSERT_MSG(NULL != shridWLNode, "NULL shared identifier\n");
2507 _ASSERT_MSG(NULL != pWLNode, "Bad shared wait list node identifier (%p)\n", (VOID*)shridWLNode);
2508 _ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
2509
2510 TRACE("Waking up remote thread {pid=%x, tid=%x} by sending cmd=%u and shridWLNode=%p over process pipe\n",
2511 pWLNode->dwProcessId, pWLNode->dwThreadId, SynchWorkerCmdRemoteSignal, (VOID *)shridWLNode);
2512
2513 // Prepare the message
2514 // Cmd
2515 *pbyDst++ = (BYTE)(SynchWorkerCmdRemoteSignal & 0xFF);
2516
2517 // WaitingThreadsListNode (not aligned, copy byte by byte)
2518 pbySrc = (BYTE *)&shridWLNode;
2519 for (int i = 0; i < (int)sizeof(SharedID); i++)
2520 {
2521 *pbyDst++ = *pbySrc++;
2522 }
2523
2524 _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");
2525
2526 // Send the message
2527 palErr = SendMsgToRemoteWorker(pWLNode->dwProcessId, rgSendBuf, MsgSize);
2528 if (NO_ERROR != palErr)
2529 {
2530 ERROR("Failed sending message to remote worker in process %u\n", pWLNode->dwProcessId);
2531 }
2532
2533 return palErr;
2534 }
2535
2536 /*++
2537 Method:
2538 CPalSynchronizationManager::DelegateSignalingToRemoteProcess
2539
2540 This method transfers an object signaling operation to a remote process,
2541 where it will be performed by the worker thread. Such delegation takes
2542 place when the currently processed thread (among those waiting on the
2543 signald object) lives in a different process as the signaling thread,
2544 and it is performing a wait all. In this case generally is not possible
2545 to find out whether or not the wait all is satisfied, therefore the
2546 signaling operation must be continued in the target process.
2547 --*/
2548 PAL_ERROR CPalSynchronizationManager::DelegateSignalingToRemoteProcess(
2549 CPalThread * pthrCurrent,
2550 DWORD dwTargetProcessId,
2551 SharedID shridSynchData)
2552 {
2553 const int MsgSize = sizeof(BYTE) + sizeof(SharedID) + sizeof(DWORD);
2554 int i;
2555 PAL_ERROR palErr = NO_ERROR;
2556 BYTE rgSendBuf[MsgSize];
2557 BYTE * pbySrc, * pbyDst = rgSendBuf;
2558 DWORD dwSigCount;
2559 CSynchData * psdSynchData =
2560 SharedIDToTypePointer(CSynchData, shridSynchData);
2561
2562 _ASSERT_MSG(gPID != dwTargetProcessId, " called on local thread\n");
2563 _ASSERT_MSG(NULL != shridSynchData, "NULL shared identifier\n");
2564 _ASSERT_MSG(NULL != psdSynchData, "Bad shared SynchData identifier (%p)\n", (VOID*)shridSynchData);
2565 _ASSERT_MSG(MsgSize <= PIPE_BUF, "Message too long [MsgSize=%d PIPE_BUF=%d]\n", MsgSize, (int)PIPE_BUF);
2566
2567 TRACE("Transfering wait all signaling to remote process pid=%x by sending cmd=%u and shridSynchData=%p over process pipe\n",
2568 dwTargetProcessId, SynchWorkerCmdDelegatedObjectSignaling, (VOID *)shridSynchData);
2569
2570 dwSigCount = psdSynchData->GetSignalCount();
2571
2572 // AddRef SynchData to be marshaled to remote process
2573 psdSynchData->AddRef();
2574
2575 //
2576 // Prepare the message
2577 //
2578
2579 // Cmd
2580 *pbyDst++ = (BYTE)(SynchWorkerCmdDelegatedObjectSignaling & 0xFF);
2581
2582 // CSynchData (not aligned, copy byte by byte)
2583 pbySrc = (BYTE *)&shridSynchData;
2584 for (i=0; i<(int)sizeof(SharedID); i++)
2585 {
2586 *pbyDst++ = *pbySrc++;
2587 }
2588
2589 // Signal Count (not aligned, copy byte by byte)
2590 pbySrc = (BYTE *)&dwSigCount;
2591 for (i=0; i<(int)sizeof(DWORD); i++)
2592 {
2593 *pbyDst++ = *pbySrc++;
2594 }
2595
2596 _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");
2597
2598 // Send the message
2599 palErr = SendMsgToRemoteWorker(dwTargetProcessId, rgSendBuf, MsgSize);
2600 if (NO_ERROR != palErr)
2601 {
2602 TRACE("Failed sending message to remote worker in process %u\n", dwTargetProcessId);
2603
2604 // Undo refcounting
2605 psdSynchData->Release(pthrCurrent);
2606 }
2607
2608 return palErr;
2609 }
2610
2611 /*++
2612 Method:
2613 CPalSynchronizationManager::SendMsgToRemoteWorker
2614
2615 Sends a message (command + data) to a remote process's worker thread.
2616 --*/
2617 PAL_ERROR CPalSynchronizationManager::SendMsgToRemoteWorker(
2618 DWORD dwProcessId,
2619 BYTE * pMsg,
2620 int iMsgSize)
2621 {
2622#ifndef CORECLR
2623 PAL_ERROR palErr = NO_ERROR;
2624 int iProcessPipe, iBytesToWrite, iRetryCount;
2625 ssize_t sszRet;
2626 char strPipeFilename[MAX_PATH];
2627 BYTE * pPos = pMsg;
2628 bool fRet;
2629 CPalThread *pthrCurrent = InternalGetCurrentThread();
2630
2631 _ASSERT_MSG(gPID != dwProcessId, "SendMsgToRemoteWorker called with local process as target process\n");
2632
2633 fRet = GetProcessPipeName(strPipeFilename, MAX_PATH, dwProcessId);
2634
2635 _ASSERT_MSG(fRet, "Failed to retrieve process pipe's name!\n");
2636
2637 iProcessPipe = InternalOpen(strPipeFilename, O_WRONLY);
2638 if (-1 == iProcessPipe)
2639 {
2640 ERROR("Unable to open a process pipe to wake up a remote thread "
2641 "[pid=%u errno=%d (%s) PipeFilename=%s]\n", dwProcessId,
2642 errno, strerror(errno), strPipeFilename);
2643 palErr = ERROR_INTERNAL_ERROR;
2644 goto SMTRW_exit;
2645 }
2646
2647 pPos = pMsg;
2648 iBytesToWrite = iMsgSize;
2649 while (0 < iBytesToWrite)
2650 {
2651 iRetryCount = 0;
2652 do
2653 {
2654 sszRet = write(iProcessPipe, pPos, iBytesToWrite);
2655 } while (-1 == sszRet &&
2656 EAGAIN == errno &&
2657 ++iRetryCount < MaxConsecutiveEagains &&
2658 0 == sched_yield());
2659
2660 if (0 >= sszRet)
2661 {
2662 ERROR("Error writing message to process pipe %d [target_pid=%u "
2663 "bytes_to_write=%d bytes_written=%d ret=%d errno=%d (%s) "
2664 "PipeFilename=%s]\n", iProcessPipe, dwProcessId, iMsgSize,
2665 iMsgSize - iBytesToWrite, (int)sszRet, errno, strerror(errno),
2666 strPipeFilename);
2667 palErr = ERROR_INTERNAL_ERROR;
2668 break;
2669 }
2670 iBytesToWrite -= (int)sszRet;
2671 pPos += sszRet;
2672
2673 _ASSERT_MSG(0 == iBytesToWrite,
2674 "Interleaved messages while writing to process pipe %d\n",
2675 iProcessPipe);
2676 }
2677
2678 // Close the opened pipe
2679 close(iProcessPipe);
2680
2681 SMTRW_exit:
2682 return palErr;
2683#else // !CORECLR
2684 ASSERT("There should never be a reason to send a message to a remote worker\n");
2685 return ERROR_INTERNAL_ERROR;
2686#endif // !CORECLR
2687 }
2688
2689 /*++
2690 Method:
2691 CPalSynchronizationManager::WakeUpLocalWorkerThread
2692
2693 Wakes up the local worker thread by writing a 'nop' cmd to the
2694 process pipe.
2695 --*/
2696 PAL_ERROR CPalSynchronizationManager::WakeUpLocalWorkerThread(
2697 SynchWorkerCmd swcWorkerCmd)
2698 {
2699 PAL_ERROR palErr = NO_ERROR;
2700
2701 _ASSERT_MSG((swcWorkerCmd & 0xFF) == swcWorkerCmd,
2702 "Value too big for swcWorkerCmd\n");
2703
2704 _ASSERT_MSG((SynchWorkerCmdNop == swcWorkerCmd) ||
2705 (SynchWorkerCmdShutdown == swcWorkerCmd) ||
2706 (SynchWorkerCmdTerminationRequest == swcWorkerCmd),
2707 "WakeUpLocalWorkerThread supports only SynchWorkerCmdNop, SynchWorkerCmdShutdown, and SynchWorkerCmdTerminationRequest."
2708 "[received cmd=%d]\n", swcWorkerCmd);
2709
2710 BYTE byCmd = (BYTE)(swcWorkerCmd & 0xFF);
2711
2712 TRACE("Waking up Synch Worker Thread for %u [byCmd=%u]\n",
2713 swcWorkerCmd, (unsigned int)byCmd);
2714
2715 // As long as we use pipes and we keep the message size
2716 // within PIPE_BUF, there's no need to lock here, since the
2717 // write is guaranteed not to be interleaved with/into other
2718 // writes of PIPE_BUF bytes or less.
2719 _ASSERT_MSG(sizeof(BYTE) <= PIPE_BUF, "Message too long\n");
2720
2721 int iRetryCount = 0;
2722 ssize_t sszWritten;
2723 do
2724 {
2725 sszWritten = write(m_iProcessPipeWrite, &byCmd, sizeof(BYTE));
2726 } while (-1 == sszWritten &&
2727 EAGAIN == errno &&
2728 ++iRetryCount < MaxConsecutiveEagains &&
2729 0 == sched_yield());
2730
2731 if (sszWritten != sizeof(BYTE))
2732 {
2733 ERROR("Unable to write to the process pipe to wake up the "
2734 "worker thread [errno=%d (%s)]\n", errno, strerror(errno));
2735 palErr = ERROR_INTERNAL_ERROR;
2736 }
2737
2738 return palErr;
2739 }
2740
2741 /*++
2742 Method:
2743 CPalSynchronizationManager::GetThreadWaitInfo
2744
2745 Returns a pointer to the WaitInfo structure for the passed CPalThread object
2746 --*/
2747 ThreadWaitInfo * CPalSynchronizationManager::GetThreadWaitInfo(
2748 CPalThread * pthrCurrent)
2749 {
2750 return &pthrCurrent->synchronizationInfo.m_twiWaitInfo;
2751 }
2752
2753 /*++
2754 Method:
2755 CPalSynchronizationManager::UnRegisterWait
2756
2757 Unregister the wait described by ptwiWaitInfo that in general involves
2758 a thread other than the current one (most of the times the deregistration
2759 is performed by the signaling thread)
2760
2761 Note: this method must be called while holding the local process
2762 synchronization lock.
2763 --*/
2764 void CPalSynchronizationManager::UnRegisterWait(
2765 CPalThread * pthrCurrent,
2766 ThreadWaitInfo * ptwiWaitInfo,
2767 bool fHaveSharedLock)
2768 {
2769 int i = 0;
2770 CSynchData * psdSynchData = NULL;
2771 bool fSharedSynchLock = false;
2772
2773 if (!fHaveSharedLock && LocalWait != ptwiWaitInfo->wdWaitDomain)
2774 {
2775 AcquireSharedSynchLock(pthrCurrent);
2776 fSharedSynchLock = true;
2777 }
2778
2779 TRACE("Unregistering wait for thread=%u [ObjCount=%d WaitType=%u WaitDomain=%u]\n",
2780 ptwiWaitInfo->pthrOwner->GetThreadId(),
2781 ptwiWaitInfo->lObjCount, ptwiWaitInfo->wtWaitType,
2782 ptwiWaitInfo->wdWaitDomain);
2783
2784 for (i=0; i < ptwiWaitInfo->lObjCount; i++)
2785 {
2786 WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
2787
2788 VALIDATEOBJECT(pwtlnItem);
2789
2790 if (pwtlnItem->dwFlags & WTLN_FLAG_OWNER_OBJECT_IS_SHARED)
2791 {
2792 // Shared object
2793 WaitingThreadsListNode * pwtlnItemNext, * pwtlnItemPrev;
2794
2795 psdSynchData = SharedIDToTypePointer(CSynchData,
2796 pwtlnItem->ptrOwnerObjSynchData.shrid);
2797
2798 VALIDATEOBJECT(psdSynchData);
2799
2800 pwtlnItemNext = SharedIDToTypePointer(WaitingThreadsListNode,
2801 pwtlnItem->ptrNext.shrid);
2802 pwtlnItemPrev = SharedIDToTypePointer(WaitingThreadsListNode,
2803 pwtlnItem->ptrPrev.shrid);
2804 if (pwtlnItemPrev)
2805 {
2806 VALIDATEOBJECT(pwtlnItemPrev);
2807 pwtlnItemPrev->ptrNext.shrid = pwtlnItem->ptrNext.shrid;
2808 }
2809 else
2810 {
2811 psdSynchData->SetWTLHeadShrPtr(pwtlnItem->ptrNext.shrid);
2812 }
2813
2814 if (pwtlnItemNext)
2815 {
2816 VALIDATEOBJECT(pwtlnItemNext);
2817 pwtlnItemNext->ptrPrev.shrid = pwtlnItem->ptrPrev.shrid;
2818 }
2819 else
2820 {
2821 psdSynchData->SetWTLTailShrPtr(pwtlnItem->ptrPrev.shrid);
2822 }
2823
2824 m_cacheSHRWTListNodes.Add(pthrCurrent, pwtlnItem->shridSHRThis);
2825 }
2826 else
2827 {
2828 // Local object
2829 psdSynchData = pwtlnItem->ptrOwnerObjSynchData.ptr;
2830
2831 VALIDATEOBJECT(psdSynchData);
2832
2833 if (pwtlnItem->ptrPrev.ptr)
2834 {
2835 VALIDATEOBJECT(pwtlnItem);
2836 pwtlnItem->ptrPrev.ptr->ptrNext.ptr = pwtlnItem->ptrNext.ptr;
2837 }
2838 else
2839 {
2840 psdSynchData->SetWTLHeadPtr(pwtlnItem->ptrNext.ptr);
2841 }
2842
2843 if (pwtlnItem->ptrNext.ptr)
2844 {
2845 VALIDATEOBJECT(pwtlnItem);
2846 pwtlnItem->ptrNext.ptr->ptrPrev.ptr = pwtlnItem->ptrPrev.ptr;
2847 }
2848 else
2849 {
2850 psdSynchData->SetWTLTailPtr(pwtlnItem->ptrPrev.ptr);
2851 }
2852
2853 m_cacheWTListNodes.Add(pthrCurrent, pwtlnItem);
2854 }
2855
2856 // Release the node's refcount on the synch data, and decerement
2857 // waiting thread count
2858 psdSynchData->DecrementWaitingThreadCount();
2859 psdSynchData->Release(pthrCurrent);
2860 }
2861
2862 // Reset wait data in ThreadWaitInfo structure: it is enough
2863 // to reset lObjCount, lSharedObjCount and wdWaitDomain.
2864 ptwiWaitInfo->lObjCount = 0;
2865 ptwiWaitInfo->lSharedObjCount = 0;
2866 ptwiWaitInfo->wdWaitDomain = LocalWait;
2867
2868 // Done
2869 if (fSharedSynchLock)
2870 {
2871 ReleaseSharedSynchLock(pthrCurrent);
2872 }
2873
2874 return;
2875 }
2876
2877 /*++
2878 Method:
2879 CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll
2880
2881 Unsignals all the objects involved in a wait all, except the target
2882 one (i.e. psdTgtObjectSynchData)
2883
2884 Note: this method must be called while holding the synchronization locks
2885 appropriate to all the objects involved in the wait-all. If any
2886 of the objects is shared, the caller must own both local and
2887 shared synch locks; if no shared object is involved in the wait,
2888 only the local synch lock is needed.
2889 --*/
2890 void CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
2891 CPalThread * pthrCurrent,
2892 CPalThread * pthrTarget,
2893 WaitingThreadsListNode * pwtlnNode,
2894 CSynchData * psdTgtObjectSynchData)
2895 {
2896 PAL_ERROR palErr = NO_ERROR;
2897 CSynchData * psdSynchDataItem = NULL;
2898
2899#ifdef _DEBUG
2900 bool bOriginatingNodeFound = false;
2901#endif
2902
2903 VALIDATEOBJECT(psdTgtObjectSynchData);
2904 VALIDATEOBJECT(pwtlnNode);
2905
2906 _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags),
2907 "UnsignalRestOfLocalAwakeningWaitAll() called on a normal (non wait all) wait");
2908
2909 _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
2910 "UnsignalRestOfLocalAwakeningWaitAll() called on a wait all with remote awakening");
2911
2912 ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
2913
2914 int iObjCount = ptwiWaitInfo->lObjCount;
2915 for (int i = 0; i < iObjCount; i++)
2916 {
2917 WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];
2918
2919 VALIDATEOBJECT(pwtlnItem);
2920
2921 if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags))
2922 {
2923 psdSynchDataItem = SharedIDToTypePointer(CSynchData, pwtlnItem->ptrOwnerObjSynchData.shrid);
2924 }
2925 else
2926 {
2927 psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr;
2928 }
2929
2930 VALIDATEOBJECT(psdSynchDataItem);
2931
2932 // Skip originating node
2933 if (psdTgtObjectSynchData == psdSynchDataItem)
2934 {
2935#ifdef _DEBUG
2936 bOriginatingNodeFound = true;
2937#endif
2938 continue;
2939 }
2940
2941 palErr = psdSynchDataItem->ReleaseWaiterWithoutBlocking(pthrCurrent, pthrTarget);
2942 if (NO_ERROR != palErr)
2943 {
2944 ERROR("ReleaseWaiterWithoutBlocking failed on SynchData @ %p [palErr = %u]\n", psdSynchDataItem, palErr);
2945 }
2946 }
2947
2948 _ASSERT_MSG(bOriginatingNodeFound, "Couldn't find originating node while unsignaling rest of the wait all\n");
2949 }
2950
2951 /*++
2952 Method:
2953 CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress
2954
2955 Marks all the thread waiting list nodes involved in the the current wait-all
2956 for "delegated object signaling in progress", so that this wait cannot be
2957 involved in another delegated object signaling that may happen while the
2958 current object singaling is being tranfered to the target process (while
2959 transfering it, synchronization locks are released in this process and later
2960 grabbed again in the target process; in this time window another thread
2961 could signal another object part of the same wait-all. In this case no
2962 signal delegation must take place.
2963
2964 Note: this method must be called while holding the synchronization locks
2965 appropriate to the target object described by pwtlnNode (i.e. the
2966 local process synch lock if the target object is local, both local
2967 and shared one if the object is shared).
2968 --*/
2969 void CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress(
2970 CPalThread * pthrCurrent,
2971 WaitingThreadsListNode * pwtlnNode)
2972 {
2973 bool fSharedSynchLock = false;
2974 bool fTargetObjectIsShared = (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags));
2975
2976 VALIDATEOBJECT(pwtlnNode);
2977
2978 _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
2979 "MarkWaitForDelegatedObjectSignalingInProgress() called from the wrong process");
2980
2981 ThreadWaitInfo *ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;
2982
2983 if (!fSharedSynchLock && !fTargetObjectIsShared &&
2984 LocalWait != ptwiWaitInfo->wdWaitDomain)
2985 {
2986 AcquireSharedSynchLock(pthrCurrent);
2987 fSharedSynchLock = true;
2988 }
2989
2990 _ASSERT_MSG(MultipleObjectsWaitAll == ptwiWaitInfo->wtWaitType,
2991 "MarkWaitForDelegatedObjectSignalingInProgress() called on a normal (non wait-all) wait");
2992
2993 // Unmark all nodes other than the target one
2994 int iTgtCount = ptwiWaitInfo->lObjCount;
2995 for (int i = 0; i < iTgtCount; i++)
2996 {
2997 VALIDATEOBJECT(ptwiWaitInfo->rgpWTLNodes[i]);
2998 ptwiWaitInfo->rgpWTLNodes[i]->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
2999 }
3000
3001 // Mark the target node
3002 pwtlnNode->dwFlags |= WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
3003
3004 // Done
3005 if (fSharedSynchLock)
3006 {
3007 ReleaseSharedSynchLock(pthrCurrent);
3008 }
3009
3010 return;
3011 }
3012
3013 /*++
3014 Method:
3015 CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress
3016
3017 Resets the "delegated object signaling in progress" flags in all the
3018 nodes of the thread waitin list for the target waitable objects (represented
3019 by its SynchData)
3020
3021 Note: this method must be called while holding the appropriate
3022 synchronization locks (the local process synch lock if the target
3023 object is local, both local and shared one if the object is shared).
3024 --*/
3025 void CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress(
3026 CSynchData * pTgtObjectSynchData)
3027 {
3028 bool fSharedObject = (SharedObject == pTgtObjectSynchData->GetObjectDomain());
3029 WaitingThreadsListNode * pwtlnNode;
3030
3031 VALIDATEOBJECT(pTgtObjectSynchData);
3032
3033 pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pTgtObjectSynchData->GetWTLHeadShmPtr())
3034 : pTgtObjectSynchData->GetWTLHeadPtr();
3035
3036 while (pwtlnNode)
3037 {
3038 VALIDATEOBJECT(pwtlnNode);
3039
3040 pwtlnNode->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
3041 pwtlnNode = fSharedObject ? SharedIDToTypePointer(WaitingThreadsListNode, pwtlnNode->ptrNext.shrid)
3042 : pwtlnNode->ptrNext.ptr;
3043 }
3044 }
3045
3046 /*++
3047 Method:
3048 CPalSynchronizationManager::RegisterProcessForMonitoring
3049
3050 Registers the process object represented by the passed psdSynchData and
3051 pProcLocalData. The worker thread will monitor the actual process and,
3052 upon process termination, it will set the exit code in pProcLocalData,
3053 and it will signal the process object, by signaling its psdSynchData.
3054 --*/
3055 PAL_ERROR CPalSynchronizationManager::RegisterProcessForMonitoring(
3056 CPalThread * pthrCurrent,
3057 CSynchData *psdSynchData,
3058 IPalObject *pProcessObject,
3059 CProcProcessLocalData * pProcLocalData)
3060 {
3061 PAL_ERROR palErr = NO_ERROR;
3062 MonitoredProcessesListNode * pmpln;
3063 bool fWakeUpWorker = false;
3064 bool fMonitoredProcessesLock = false;
3065
3066 VALIDATEOBJECT(psdSynchData);
3067
3068 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3069
3070 fMonitoredProcessesLock = true;
3071
3072 pmpln = m_pmplnMonitoredProcesses;
3073 while (pmpln)
3074 {
3075 if (psdSynchData == pmpln->psdSynchData)
3076 {
3077 _ASSERT_MSG(pmpln->dwPid == pProcLocalData->dwProcessId, "Invalid node in Monitored Processes List\n");
3078 break;
3079 }
3080
3081 pmpln = pmpln->pNext;
3082 }
3083
3084 if (pmpln)
3085 {
3086 pmpln->lRefCount++;
3087 }
3088 else
3089 {
3090 pmpln = InternalNew<MonitoredProcessesListNode>();
3091 if (NULL == pmpln)
3092 {
3093 ERROR("No memory to allocate MonitoredProcessesListNode structure\n");
3094 palErr = ERROR_NOT_ENOUGH_MEMORY;
3095 goto RPFM_exit;
3096 }
3097
3098 pmpln->lRefCount = 1;
3099 pmpln->dwPid = pProcLocalData->dwProcessId;
3100 pmpln->dwExitCode = 0;
3101 pmpln->pProcessObject = pProcessObject;
3102 pmpln->pProcessObject->AddReference();
3103 pmpln->pProcLocalData = pProcLocalData;
3104
3105 // Acquire SynchData and AddRef it
3106 pmpln->psdSynchData = psdSynchData;
3107 psdSynchData->AddRef();
3108
3109 pmpln->pNext = m_pmplnMonitoredProcesses;
3110 m_pmplnMonitoredProcesses = pmpln;
3111 m_lMonitoredProcessesCount++;
3112
3113 fWakeUpWorker = true;
3114 }
3115
3116 // Unlock
3117 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3118 fMonitoredProcessesLock = false;
3119
3120 if (fWakeUpWorker)
3121 {
3122 CPalSynchronizationManager * pSynchManager = GetInstance();
3123
3124 palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdNop);
3125 if (NO_ERROR != palErr)
3126 {
3127 ERROR("Failed waking up worker thread for process "
3128 "monitoring registration [errno=%d {%s%}]\n",
3129 errno, strerror(errno));
3130 palErr = ERROR_INTERNAL_ERROR;
3131 }
3132 }
3133
3134 RPFM_exit:
3135 if (fMonitoredProcessesLock)
3136 {
3137 InternalLeaveCriticalSection(pthrCurrent,
3138 &s_csMonitoredProcessesLock);
3139 }
3140
3141 return palErr;
3142 }
3143
3144 /*++
3145 Method:
3146 CPalSynchronizationManager::UnRegisterProcessForMonitoring
3147
3148 Unregisters a process object currently monitored by the worker thread
3149 (typically called if the wait timed out before the process exited, or
3150 if the wait was a normal (i.e. non wait-all) wait that involved othter
3151 objects, and another object has been signaled).
3152 --*/
3153 PAL_ERROR CPalSynchronizationManager::UnRegisterProcessForMonitoring(
3154 CPalThread * pthrCurrent,
3155 CSynchData *psdSynchData,
3156 DWORD dwPid)
3157 {
3158 PAL_ERROR palErr = NO_ERROR;
3159 MonitoredProcessesListNode * pmpln, * pmplnPrev = NULL;
3160
3161 VALIDATEOBJECT(psdSynchData);
3162
3163 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3164
3165 pmpln = m_pmplnMonitoredProcesses;
3166 while (pmpln)
3167 {
3168 if (psdSynchData == pmpln->psdSynchData)
3169 {
3170 _ASSERT_MSG(dwPid == pmpln->dwPid, "Invalid node in Monitored Processes List\n");
3171 break;
3172 }
3173
3174 pmplnPrev = pmpln;
3175 pmpln = pmpln->pNext;
3176 }
3177
3178 if (pmpln)
3179 {
3180 if (0 == --pmpln->lRefCount)
3181 {
3182 if (NULL != pmplnPrev)
3183 {
3184 pmplnPrev->pNext = pmpln->pNext;
3185 }
3186 else
3187 {
3188 m_pmplnMonitoredProcesses = pmpln->pNext;
3189 }
3190
3191 m_lMonitoredProcessesCount--;
3192 pmpln->pProcessObject->ReleaseReference(pthrCurrent);
3193 pmpln->psdSynchData->Release(pthrCurrent);
3194 InternalDelete(pmpln);
3195 }
3196 }
3197 else
3198 {
3199 palErr = ERROR_NOT_FOUND;
3200 }
3201
3202 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3203 return palErr;
3204 }
3205
3206 /*++
3207 Method:
3208 CPalSynchronizationManager::ThreadPrepareForShutdown
3209
3210 Used to hijack thread execution from known spots within the
3211 Synchronization Manager in case a PAL shutdown is initiated
3212 or the thread is being terminated by another thread.
3213 --*/
3214 void CPalSynchronizationManager::ThreadPrepareForShutdown()
3215 {
3216 TRACE("The Synchronization Manager hijacked the current thread "
3217 "for process shutdown or thread termination\n");
3218 while (true)
3219 {
3220 poll(NULL, 0, INFTIM);
3221 sched_yield();
3222 }
3223
3224 ASSERT("This code should never be executed\n");
3225 }
3226
3227 /*++
3228 Method:
3229 CPalSynchronizationManager::DoMonitorProcesses
3230
3231 This method is called by the worker thread to execute one step of
3232 monitoring for all the process currently registered for monitoring
3233 --*/
3234 LONG CPalSynchronizationManager::DoMonitorProcesses(
3235 CPalThread * pthrCurrent)
3236 {
3237 MonitoredProcessesListNode * pNode, * pPrev = NULL, * pNext;
3238 LONG lInitialNodeCount;
3239 LONG lRemovingCount = 0;
3240 bool fLocalSynchLock = false;
3241 bool fSharedSynchLock = false;
3242 bool fMonitoredProcessesLock = false;
3243
3244 // Note: we first need to grab the monitored processes lock to walk
3245 // the list of monitored processes, and then, if there is any
3246 // which exited, to grab the synchronization lock(s) to signal
3247 // the process object. Anyway we cannot grab the synchronization
3248 // lock(s) while holding the monitored processes lock; that
3249 // would cause deadlock, since RegisterProcessForMonitoring and
3250 // UnRegisterProcessForMonitoring call stacks grab the locks
3251 // in the opposite order. Grabbing the synch lock(s) first (and
3252 // therefore all the times) would cause unacceptable contention
3253 // (process monitoring is done in polling mode).
3254 // Therefore we need to remove list nodes for processes that
3255 // exited copying them to the exited array, while holding only
3256 // the monitored processes lock, and then to signal them from that
3257 // array holding synch lock(s) and monitored processes lock,
3258 // acquired in this order. Holding again the monitored processes
3259 // lock is needed in order to support object promotion.
3260
3261 // Grab the monitored processes lock
3262 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3263 fMonitoredProcessesLock = true;
3264
3265 lInitialNodeCount = m_lMonitoredProcessesCount;
3266
3267 pNode = m_pmplnMonitoredProcesses;
3268 while (pNode)
3269 {
3270 pNext = pNode->pNext;
3271
3272 if (HasProcessExited(pNode->dwPid,
3273 &pNode->dwExitCode,
3274 &pNode->fIsActualExitCode))
3275 {
3276 TRACE("Process %u exited with return code %u\n",
3277 pNode->dwPid,
3278 pNode->fIsActualExitCode ? "actual" : "guessed",
3279 pNode->dwExitCode);
3280
3281 if (NULL != pPrev)
3282 {
3283 pPrev->pNext = pNext;
3284 }
3285 else
3286 {
3287 m_pmplnMonitoredProcesses = pNext;
3288 }
3289
3290 m_lMonitoredProcessesCount--;
3291
3292 // Insert in the list of nodes for exited processes
3293 pNode->pNext = m_pmplnExitedNodes;
3294 m_pmplnExitedNodes = pNode;
3295 lRemovingCount++;
3296 }
3297 else
3298 {
3299 pPrev = pNode;
3300 }
3301
3302 // Go to the next
3303 pNode = pNext;
3304 }
3305
3306 // Release the monitored processes lock
3307 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3308 fMonitoredProcessesLock = false;
3309
3310 if (lRemovingCount > 0)
3311 {
3312 // First grab the local synch lock
3313 AcquireLocalSynchLock(pthrCurrent);
3314 fLocalSynchLock = true;
3315
3316 // Acquire the monitored processes lock
3317 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3318 fMonitoredProcessesLock = true;
3319
3320 if (!fSharedSynchLock)
3321 {
3322 bool fSharedSynchLockIsNeeded = false;
3323
3324 // See if the shared lock is needed
3325 pNode = m_pmplnExitedNodes;
3326 while (pNode)
3327 {
3328 if (SharedObject == pNode->psdSynchData->GetObjectDomain())
3329 {
3330 fSharedSynchLockIsNeeded = true;
3331 break;
3332 }
3333
3334 pNode = pNode->pNext;
3335 }
3336
3337 if (fSharedSynchLockIsNeeded)
3338 {
3339 // Release the monitored processes lock
3340 InternalLeaveCriticalSection(pthrCurrent,
3341 &s_csMonitoredProcessesLock);
3342 fMonitoredProcessesLock = false;
3343
3344 // Acquire the shared synch lock
3345 AcquireSharedSynchLock(pthrCurrent);
3346 fSharedSynchLock = true;
3347
3348 // Acquire again the monitored processes lock
3349 InternalEnterCriticalSection(pthrCurrent,
3350 &s_csMonitoredProcessesLock);
3351 fMonitoredProcessesLock = true;
3352 }
3353 }
3354
3355 // Start from the beginning of the exited processes list
3356 pNode = m_pmplnExitedNodes;
3357
3358 // Invalidate the list
3359 m_pmplnExitedNodes = NULL;
3360
3361 while (pNode)
3362 {
3363 pNext = pNode->pNext;
3364
3365 TRACE("Process pid=%u exited with exitcode=%u\n",
3366 pNode->dwPid, pNode->dwExitCode);
3367
3368 // Store the exit code in the process local data
3369 if (pNode->fIsActualExitCode)
3370 {
3371 pNode->pProcLocalData->dwExitCode = pNode->dwExitCode;
3372 }
3373
3374 // Set process status to PS_DONE
3375 pNode->pProcLocalData->ps = PS_DONE;
3376
3377 // Set signal count
3378 pNode->psdSynchData->SetSignalCount(1);
3379
3380 // Releasing all local waiters
3381 //
3382 // We just called directly in CSynchData::SetSignalCount(), so
3383 // we need to take care of waking up waiting threads according
3384 // to the Process object semantics (i.e. every thread must be
3385 // awakend). Anyway if a process object is shared among two or
3386 // more processes and threads from different processes are
3387 // waiting on it, the object will be registered for monitoring
3388 // in each of the processes. As result its signal count will
3389 // be set to one more times (which is not a problem, given the
3390 // process object semantics) and each worker thread will wake
3391 // up waiting threads. Therefore we need to make sure that each
3392 // worker wakes up only threads in its own process: we do that
3393 // by calling ReleaseAllLocalWaiters
3394 pNode->psdSynchData->ReleaseAllLocalWaiters(pthrCurrent);
3395
3396 // We are done with pProcLocalData, so we can release the process object
3397 pNode->pProcessObject->ReleaseReference(pthrCurrent);
3398
3399 // Release the reference to the SynchData
3400 pNode->psdSynchData->Release(pthrCurrent);
3401
3402 // Delete the node
3403 InternalDelete(pNode);
3404
3405 // Go to the next
3406 pNode = pNext;
3407 }
3408 }
3409
3410 if (fMonitoredProcessesLock)
3411 {
3412 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3413 }
3414
3415 if (fSharedSynchLock)
3416 {
3417 ReleaseSharedSynchLock(pthrCurrent);
3418 }
3419
3420 if (fLocalSynchLock)
3421 {
3422 ReleaseLocalSynchLock(pthrCurrent);
3423 }
3424
3425 return (lInitialNodeCount - lRemovingCount);
3426 }
3427
3428 /*++
3429 Method:
3430 CPalSynchronizationManager::DiscardMonitoredProcesses
3431
3432 This method is called at shutdown time to discard all the registration
3433 for the processes currently monitored by the worker thread.
3434 This method must be called at shutdown time, otherwise some shared memory
3435 may be leaked at process shutdown.
3436 --*/
3437 void CPalSynchronizationManager::DiscardMonitoredProcesses(
3438 CPalThread * pthrCurrent)
3439 {
3440 MonitoredProcessesListNode * pNode;
3441
3442 // Grab the monitored processes lock
3443 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3444
3445 while (m_pmplnMonitoredProcesses)
3446 {
3447 pNode = m_pmplnMonitoredProcesses;
3448 m_pmplnMonitoredProcesses = pNode->pNext;
3449 pNode->pProcessObject->ReleaseReference(pthrCurrent);
3450 pNode->psdSynchData->Release(pthrCurrent);
3451 InternalDelete(pNode);
3452 }
3453
3454 // Release the monitored processes lock
3455 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3456 }
3457
3458 /*++
3459 Method:
3460 CPalSynchronizationManager::CreateProcessPipe
3461
3462 Creates the process pipe for the current process
3463 --*/
3464 bool CPalSynchronizationManager::CreateProcessPipe()
3465 {
3466 bool fRet = true;
3467#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
3468 int iKq = -1;
3469#endif // HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
3470
3471#ifndef CORECLR
3472 int iPipeRd = -1, iPipeWr = -1;
3473 char szPipeFilename[MAX_PATH];
3474
3475 /* Create the blocking pipe */
3476 if (!GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
3477 {
3478 ERROR("couldn't get process pipe's name\n");
3479 szPipeFilename[0] = 0;
3480 fRet = false;
3481 goto CPP_exit;
3482 }
3483
3484 /* create the pipe, with full access to the owner only */
3485 if (mkfifo(szPipeFilename, S_IRWXU) == -1)
3486 {
3487 if (errno == EEXIST)
3488 {
3489 /* Some how no one deleted the pipe, perhaps it was left behind
3490 from a crash?? Delete the pipe and try again. */
3491 if (-1 == unlink(szPipeFilename))
3492 {
3493 ERROR( "Unable to delete the process pipe that was left behind.\n" );
3494 fRet = false;
3495 goto CPP_exit;
3496 }
3497 else
3498 {
3499 if (mkfifo(szPipeFilename, S_IRWXU) == -1)
3500 {
3501 ERROR( "Still unable to create the process pipe...giving up!\n" );
3502 fRet = false;
3503 goto CPP_exit;
3504 }
3505 }
3506 }
3507 else
3508 {
3509 ERROR( "Unable to create the process pipe.\n" );
3510 fRet = false;
3511 goto CPP_exit;
3512 }
3513 }
3514
3515 iPipeRd = InternalOpen(szPipeFilename, O_RDONLY | O_NONBLOCK);
3516 if (iPipeRd == -1)
3517 {
3518 ERROR("Unable to open the process pipe for read\n");
3519 fRet = false;
3520 goto CPP_exit;
3521 }
3522
3523 iPipeWr = InternalOpen(szPipeFilename, O_WRONLY | O_NONBLOCK);
3524 if (iPipeWr == -1)
3525 {
3526 ERROR("Unable to open the process pipe for write\n");
3527 fRet = false;
3528 goto CPP_exit;
3529 }
3530#else // !CORECLR
3531 int rgiPipe[] = { -1, -1 };
3532 int pipeRv =
3533#if HAVE_PIPE2
3534 pipe2(rgiPipe, O_CLOEXEC);
3535#else
3536 pipe(rgiPipe);
3537#endif // HAVE_PIPE2
3538 if (pipeRv == -1)
3539 {
3540 ERROR("Unable to create the process pipe\n");
3541 fRet = false;
3542 goto CPP_exit;
3543 }
3544#if !HAVE_PIPE2
3545 fcntl(rgiPipe[0], F_SETFD, FD_CLOEXEC); // make pipe non-inheritable, if possible
3546 fcntl(rgiPipe[1], F_SETFD, FD_CLOEXEC);
3547#endif // !HAVE_PIPE2
3548#endif // !CORECLR
3549
3550#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
3551 iKq = kqueue();
3552 if (-1 == iKq)
3553 {
3554 ERROR("Failed to create kqueue associated to process pipe\n");
3555 fRet = false;
3556 goto CPP_exit;
3557 }
3558#endif // HAVE_KQUEUE
3559
3560 CPP_exit:
3561 if (fRet)
3562 {
3563 // Succeeded
3564#ifndef CORECLR
3565 m_iProcessPipeRead = iPipeRd;
3566 m_iProcessPipeWrite = iPipeWr;
3567#else // !CORECLR
3568 m_iProcessPipeRead = rgiPipe[0];
3569 m_iProcessPipeWrite = rgiPipe[1];
3570#endif // !CORECLR
3571#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
3572 m_iKQueue = iKq;
3573#endif // HAVE_KQUEUE
3574 }
3575 else
3576 {
3577#ifndef CORECLR
3578 // Failed
3579 if (0 != szPipeFilename[0])
3580 {
3581 unlink(szPipeFilename);
3582 }
3583 if (-1 != iPipeRd)
3584 {
3585 close(iPipeRd);
3586 }
3587 if (-1 != iPipeWr)
3588 {
3589 close(iPipeWr);
3590 }
3591#else // !CORECLR
3592 if (-1 != rgiPipe[0])
3593 {
3594 close(rgiPipe[0]);
3595 close(rgiPipe[1]);
3596 }
3597#endif // !CORECLR
3598#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
3599 if (-1 != iKq)
3600 {
3601 close(iKq);
3602 }
3603#endif // HAVE_KQUEUE
3604 }
3605
3606 return fRet;
3607 }
3608
3609 /*++
3610 Method:
3611 CPalSynchronizationManager::ShutdownProcessPipe
3612
3613 Shuts down the process pipe and removes the fifo so that other processes
3614 can no longer open it. It also closes the local write end of the pipe (see
3615 comment below). From this moment on the worker thread will process any
3616 possible data already received in the pipe (but not yet consumed) and any
3617 data written by processes that still have a opened write end of this pipe;
3618 it will wait (with timeout) until the last remote process which has a write
3619 end opened closes it, and then it will yield to process shutdown.
3620 --*/
3621 PAL_ERROR CPalSynchronizationManager::ShutdownProcessPipe()
3622 {
3623 PAL_ERROR palErr = NO_ERROR;
3624#ifndef CORECLR
3625 char szPipeFilename[MAX_PATH];
3626
3627 if (GetProcessPipeName(szPipeFilename, MAX_PATH, gPID))
3628 {
3629 if (unlink(szPipeFilename) == -1)
3630 {
3631 ERROR("Unable to unlink the pipe file name errno=%d (%s)\n",
3632 errno, strerror(errno));
3633 palErr = ERROR_INTERNAL_ERROR;
3634 // go on anyway
3635 }
3636 }
3637 else
3638 {
3639 ERROR("Couldn't get the process pipe's name\n");
3640 palErr = ERROR_INTERNAL_ERROR;
3641 // go on anyway
3642 }
3643#endif // CORECLR
3644
3645 if (-1 != m_iProcessPipeWrite)
3646 {
3647 // Closing the write end of the process pipe. When the last process
3648 // that still has a open write-fd on this pipe will close it, the
3649 // worker thread will receive an EOF; the worker thread will wait
3650 // for this EOF before shutting down, so to ensure to process any
3651 // possible data already written to the pipe by other processes
3652 // when the shutdown has been initiated in the current process.
3653 // Note: no need here to worry about platforms where close(pipe)
3654 // blocks on outstanding syscalls, since we are the only one using
3655 // this fd.
3656 TRACE("Closing the write end of process pipe\n");
3657 if (close(m_iProcessPipeWrite) == -1)
3658 {
3659 ERROR("Unable to close the write end of process pipe\n");
3660 palErr = ERROR_INTERNAL_ERROR;
3661 }
3662
3663 m_iProcessPipeWrite = -1;
3664 }
3665
3666 return palErr;
3667 }
3668
3669#ifndef CORECLR
3670 /*++
3671 Method:
3672 CPalSynchronizationManager::GetProcessPipeName
3673
3674 Returns the process pipe name for the target process (identified by its PID)
3675 --*/
3676 bool CPalSynchronizationManager::GetProcessPipeName(
3677 LPSTR pDest,
3678 int iDestSize,
3679 DWORD dwPid)
3680 {
3681 CHAR config_dir[MAX_PATH];
3682 int needed_size;
3683
3684 _ASSERT_MSG(NULL != pDest, "Destination pointer is NULL!\n");
3685 _ASSERT_MSG(0 < iDestSize,"Invalid buffer size %d\n", iDestSize);
3686
3687 if (!PALGetPalConfigDir(config_dir, MAX_PATH))
3688 {
3689 ASSERT("Unable to determine the PAL config directory.\n");
3690 pDest[0] = '\0';
3691 return false;
3692 }
3693 needed_size = snprintf(pDest, iDestSize, "%s/%s-%u", config_dir,
3694 PROCESS_PIPE_NAME_PREFIX, dwPid);
3695 pDest[iDestSize-1] = 0;
3696 if(needed_size >= iDestSize)
3697 {
3698 ERROR("threadpipe name needs %d characters, buffer only has room for "
3699 "%d\n", needed_size, iDestSize+1);
3700 return false;
3701 }
3702 return true;
3703 }
3704#endif // !CORECLR
3705
3706 /*++
3707 Method:
3708 CPalSynchronizationManager::AcquireProcessLock
3709
3710 Acquires the local Process Lock (which currently is the same as the
3711 the local Process Synch Lock)
3712 --*/
3713 void CPalSynchronizationManager::AcquireProcessLock(CPalThread * pthrCurrent)
3714 {
3715 AcquireLocalSynchLock(pthrCurrent);
3716 }
3717
3718 /*++
3719 Method:
3720 CPalSynchronizationManager::ReleaseProcessLock
3721
3722 Releases the local Process Lock (which currently is the same as the
3723 the local Process Synch Lock)
3724 --*/
3725 void CPalSynchronizationManager::ReleaseProcessLock(CPalThread * pthrCurrent)
3726 {
3727 ReleaseLocalSynchLock(pthrCurrent);
3728 }
3729
3730 /*++
3731 Method:
3732 CPalSynchronizationManager::PromoteObjectSynchData
3733
3734 Promotes an object's synchdata from local to shared
3735 --*/
3736 PAL_ERROR CPalSynchronizationManager::PromoteObjectSynchData(
3737 CPalThread *pthrCurrent,
3738 VOID *pvLocalSynchData,
3739 VOID **ppvSharedSynchData)
3740 {
3741 PAL_ERROR palError = NO_ERROR;
3742 CSynchData *psdLocal = reinterpret_cast<CSynchData *>(pvLocalSynchData);
3743 CSynchData *psdShared = NULL;
3744 SharedID shridSynchData = NULL;
3745 SharedID *rgshridWTLNodes = NULL;
3746 CObjectType *pot = NULL;
3747 ULONG ulcWaitingThreads;
3748
3749 _ASSERTE(NULL != pthrCurrent);
3750 _ASSERTE(NULL != pvLocalSynchData);
3751 _ASSERTE(NULL != ppvSharedSynchData);
3752 _ASSERTE(ProcessLocalObject == psdLocal->GetObjectDomain());
3753
3754#if _DEBUG
3755
3756 //
3757 // TODO: Verify that the proper locks are held
3758 //
3759#endif
3760
3761 //
3762 // Allocate shared memory CSynchData and map to local memory
3763 //
3764
3765 shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent);
3766 if (NULL == shridSynchData)
3767 {
3768 ERROR("Unable to allocate shared memory\n");
3769 palError = ERROR_NOT_ENOUGH_MEMORY;
3770 goto POSD_exit;
3771 }
3772
3773 psdShared = SharedIDToTypePointer(CSynchData, shridSynchData);
3774 _ASSERTE(NULL != psdShared);
3775
3776 //
3777 // Allocate shared memory WaitingThreadListNodes if there are
3778 // any threads currently waiting on this object
3779 //
3780
3781 ulcWaitingThreads = psdLocal->GetWaitingThreadCount();
3782 if (0 < ulcWaitingThreads)
3783 {
3784 int i;
3785
3786 rgshridWTLNodes = InternalNewArray<SharedID>(ulcWaitingThreads);
3787 if (NULL == rgshridWTLNodes)
3788 {
3789 palError = ERROR_OUTOFMEMORY;
3790 goto POSD_exit;
3791 }
3792
3793 i = m_cacheSHRWTListNodes.Get(
3794 pthrCurrent,
3795 ulcWaitingThreads,
3796 rgshridWTLNodes
3797 );
3798
3799 if (static_cast<ULONG>(i) != ulcWaitingThreads)
3800 {
3801 for (i -= 1; i >= 0; i -= 1)
3802 {
3803 m_cacheSHRWTListNodes.Add(pthrCurrent, rgshridWTLNodes[i]);
3804 }
3805
3806 palError = ERROR_OUTOFMEMORY;
3807 goto POSD_exit;
3808 }
3809 }
3810
3811 //
3812 // If the synch data is for a process object we need to grab
3813 // the monitored process list lock here
3814 //
3815
3816 pot = psdLocal->GetObjectType();
3817 _ASSERTE(NULL != pot);
3818
3819 if (otiProcess == pot->GetId())
3820 {
3821 InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3822 }
3823
3824 //
3825 // Copy pertinent CSynchData info to the shared memory version (and
3826 // initialize other members)
3827 //
3828
3829 psdShared->SetSharedThis(shridSynchData);
3830 psdShared->SetObjectDomain(SharedObject);
3831 psdShared->SetObjectType(psdLocal->GetObjectType());
3832 psdShared->SetSignalCount(psdLocal->GetSignalCount());
3833
3834#ifdef SYNCH_STATISTICS
3835 psdShared->SetStatContentionCount(psdLocal->GetStatContentionCount());
3836 psdShared->SetStatWaitCount(psdLocal->GetStatWaitCount());
3837#endif
3838
3839 //
3840 // Rebuild the waiting thread list, and update the wait domain
3841 // for the waiting threads
3842 //
3843
3844 psdShared->SetWTLHeadShrPtr(NULL);
3845 psdShared->SetWTLTailShrPtr(NULL);
3846
3847 if (0 < ulcWaitingThreads)
3848 {
3849 WaitingThreadsListNode *pwtlnOld;
3850 WaitingThreadsListNode *pwtlnNew;
3851 int i = 0;
3852
3853 for (pwtlnOld = psdLocal->GetWTLHeadPtr();
3854 pwtlnOld != NULL;
3855 pwtlnOld = pwtlnOld->ptrNext.ptr, i += 1)
3856 {
3857 pwtlnNew = SharedIDToTypePointer(
3858 WaitingThreadsListNode,
3859 rgshridWTLNodes[i]
3860 );
3861
3862 _ASSERTE(NULL != pwtlnNew);
3863
3864 pwtlnNew->shridSHRThis = rgshridWTLNodes[i];
3865 pwtlnNew->ptrOwnerObjSynchData.shrid = shridSynchData;
3866
3867 pwtlnNew->dwThreadId = pwtlnOld->dwThreadId;
3868 pwtlnNew->dwProcessId = pwtlnOld->dwProcessId;
3869 pwtlnNew->dwObjIndex = pwtlnOld->dwObjIndex;
3870 pwtlnNew->dwFlags = pwtlnOld->dwFlags | WTLN_FLAG_OWNER_OBJECT_IS_SHARED;
3871 pwtlnNew->shridWaitingState = pwtlnOld->shridWaitingState;
3872 pwtlnNew->ptwiWaitInfo = pwtlnOld->ptwiWaitInfo;
3873
3874 psdShared->SharedWaiterEnqueue(rgshridWTLNodes[i], false);
3875 psdShared->AddRef();
3876
3877 _ASSERTE(pwtlnOld = pwtlnOld->ptwiWaitInfo->rgpWTLNodes[pwtlnOld->dwObjIndex]);
3878 pwtlnNew->ptwiWaitInfo->rgpWTLNodes[pwtlnNew->dwObjIndex] = pwtlnNew;
3879
3880 pwtlnNew->ptwiWaitInfo->lSharedObjCount += 1;
3881 if (pwtlnNew->ptwiWaitInfo->lSharedObjCount
3882 == pwtlnNew->ptwiWaitInfo->lObjCount)
3883 {
3884 pwtlnNew->ptwiWaitInfo->wdWaitDomain = SharedWait;
3885 }
3886 else
3887 {
3888 _ASSERTE(pwtlnNew->ptwiWaitInfo->lSharedObjCount
3889 < pwtlnNew->ptwiWaitInfo->lObjCount);
3890
3891 pwtlnNew->ptwiWaitInfo->wdWaitDomain = MixedWait;
3892 }
3893 }
3894
3895 _ASSERTE(psdShared->GetWaitingThreadCount() == ulcWaitingThreads);
3896 }
3897
3898 //
3899 // If the object tracks ownership and has a current owner update
3900 // the OwnedObjectsListNode to point to the shared memory synch
3901 // data
3902 //
3903
3904 if (CObjectType::OwnershipTracked == pot->GetOwnershipSemantics())
3905 {
3906 OwnedObjectsListNode *pooln;
3907
3908 pooln = psdLocal->GetOwnershipListNode();
3909 if (NULL != pooln)
3910 {
3911 pooln->pPalObjSynchData = psdShared;
3912 psdShared->SetOwnershipListNode(pooln);
3913 psdShared->AddRef();
3914
3915 //
3916 // Copy over other ownership info.
3917 //
3918
3919 psdShared->SetOwner(psdLocal->GetOwnerThread());
3920 psdShared->SetOwnershipCount(psdLocal->GetOwnershipCount());
3921 _ASSERTE(!psdShared->IsAbandoned());
3922 }
3923 else
3924 {
3925 _ASSERTE(0 == psdLocal->GetOwnershipCount());
3926 _ASSERTE(0 == psdShared->GetOwnershipCount());
3927 psdShared->SetAbandoned(psdLocal->IsAbandoned());
3928 }
3929 }
3930
3931 //
3932 // If the synch data is for a process object update the monitored
3933 // process list nodes to point to the shared memory object data,
3934 // and release the monitored process list lock
3935 //
3936
3937 if (otiProcess == pot->GetId())
3938 {
3939 MonitoredProcessesListNode *pmpn;
3940
3941 pmpn = m_pmplnMonitoredProcesses;
3942 while (NULL != pmpn)
3943 {
3944 if (psdLocal == pmpn->psdSynchData)
3945 {
3946 pmpn->psdSynchData = psdShared;
3947 psdShared->AddRef();
3948 }
3949
3950 pmpn = pmpn->pNext;
3951 }
3952
3953 pmpn = m_pmplnExitedNodes;
3954 while (NULL != pmpn)
3955 {
3956 if (psdLocal == pmpn->psdSynchData)
3957 {
3958 pmpn->psdSynchData = psdShared;
3959 psdShared->AddRef();
3960 }
3961
3962 pmpn = pmpn->pNext;
3963 }
3964
3965 InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
3966 }
3967
3968 *ppvSharedSynchData = reinterpret_cast<VOID*>(shridSynchData);
3969
3970 //
3971 // Free the local memory items to caches
3972 //
3973
3974 if (0 < ulcWaitingThreads)
3975 {
3976 WaitingThreadsListNode *pwtln;
3977
3978 pwtln = psdLocal->GetWTLHeadPtr();
3979 while (NULL != pwtln)
3980 {
3981 WaitingThreadsListNode *pwtlnTemp;
3982
3983 pwtlnTemp = pwtln;
3984 pwtln = pwtln->ptrNext.ptr;
3985 m_cacheWTListNodes.Add(pthrCurrent, pwtlnTemp);
3986 }
3987 }
3988
3989 m_cacheSynchData.Add(pthrCurrent, psdLocal);
3990
3991 POSD_exit:
3992
3993 if (NULL != rgshridWTLNodes)
3994 {
3995 InternalDeleteArray(rgshridWTLNodes);
3996 }
3997
3998 return palError;
3999 }
4000
4001
4002 /////////////////////////////
4003 // //
4004 // _ThreadNativeWaitData //
4005 // //
4006 /////////////////////////////
4007
4008 _ThreadNativeWaitData::~_ThreadNativeWaitData()
4009 {
4010 if (fInitialized)
4011 {
4012 fInitialized = false;
4013 pthread_cond_destroy(&cond);
4014 pthread_mutex_destroy(&mutex);
4015 }
4016 }
4017
4018
4019 //////////////////////////////////
4020 // //
4021 // CThreadSynchronizationInfo //
4022 // //
4023 //////////////////////////////////
4024
4025 CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
4026 m_tsThreadState(TS_IDLE),
4027 m_shridWaitAwakened(NULL),
4028 m_lLocalSynchLockCount(0),
4029 m_lSharedSynchLockCount(0),
4030 m_ownedNamedMutexListHead(nullptr)
4031 {
4032 InitializeListHead(&m_leOwnedObjsList);
4033 InitializeCriticalSection(&m_ownedNamedMutexListLock);
4034
4035#ifdef SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4036 m_lPendingSignalingCount = 0;
4037 InitializeListHead(&m_lePendingSignalingsOverflowList);
4038#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4039 }
4040
4041 CThreadSynchronizationInfo::~CThreadSynchronizationInfo()
4042 {
4043 DeleteCriticalSection(&m_ownedNamedMutexListLock);
4044 if (NULL != m_shridWaitAwakened)
4045 {
4046 free(m_shridWaitAwakened);
4047 }
4048 }
4049
4050 void CThreadSynchronizationInfo::AcquireNativeWaitLock()
4051 {
4052#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4053 int iRet;
4054 iRet = pthread_mutex_lock(&m_tnwdNativeData.mutex);
4055 _ASSERT_MSG(0 == iRet, "pthread_mutex_lock failed with error=%d\n", iRet);
4056#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4057 }
4058
4059 void CThreadSynchronizationInfo::ReleaseNativeWaitLock()
4060 {
4061#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4062 int iRet;
4063 iRet = pthread_mutex_unlock(&m_tnwdNativeData.mutex);
4064 _ASSERT_MSG(0 == iRet, "pthread_mutex_unlock failed with error=%d\n", iRet);
4065#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4066 }
4067
4068 bool CThreadSynchronizationInfo::TryAcquireNativeWaitLock()
4069 {
4070 bool fRet = true;
4071#if !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4072 int iRet;
4073 iRet = pthread_mutex_trylock(&m_tnwdNativeData.mutex);
4074 _ASSERT_MSG(0 == iRet || EBUSY == iRet,
4075 "pthread_mutex_trylock failed with error=%d\n", iRet);
4076 fRet = (0 == iRet);
4077#endif // !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4078 return fRet;
4079 }
4080
4081 /*++
4082 Method:
4083 CThreadSynchronizationInfo::InitializePreCreate
4084
4085 Part of CThreadSynchronizationInfo's initialization to be carried out
4086 before actual thread creation
4087 --*/
4088 PAL_ERROR CThreadSynchronizationInfo::InitializePreCreate(void)
4089 {
4090 PAL_ERROR palErr = NO_ERROR;
4091 DWORD * pdwWaitState = NULL;
4092 int iRet;
4093 const int MaxUnavailableResourceRetries = 10;
4094 int iEagains;
4095 pthread_condattr_t attrs;
4096 pthread_condattr_t *attrsPtr = nullptr;
4097
4098 m_shridWaitAwakened = malloc(sizeof(DWORD));
4099 if (NULL == m_shridWaitAwakened)
4100 {
4101 ERROR("Fail allocating thread wait status shared object\n");
4102 palErr = ERROR_NOT_ENOUGH_MEMORY;
4103 goto IPrC_exit;
4104 }
4105
4106 pdwWaitState = SharedIDToTypePointer(DWORD,
4107 m_shridWaitAwakened);
4108
4109 _ASSERT_MSG(NULL != pdwWaitState,
4110 "Unable to map shared wait state: bad shared ID [shrid=%p]\n", (VOID*)m_shridWaitAwakened);
4111
4112 VolatileStore<DWORD>(pdwWaitState, TWS_ACTIVE);
4113 m_tsThreadState = TS_STARTING;
4114
4115#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
4116 attrsPtr = &attrs;
4117 iRet = pthread_condattr_init(&attrs);
4118 if (0 != iRet)
4119 {
4120 ERROR("Failed to initialize thread synchronization condition attribute "
4121 "[error=%d (%s)]\n", iRet, strerror(iRet));
4122 if (ENOMEM == iRet)
4123 {
4124 palErr = ERROR_NOT_ENOUGH_MEMORY;
4125 }
4126 else
4127 {
4128 palErr = ERROR_INTERNAL_ERROR;
4129 }
4130 goto IPrC_exit;
4131 }
4132
4133 // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC
4134 iRet = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
4135 if (0 != iRet)
4136 {
4137 ERROR("Failed set thread synchronization condition timed wait clock "
4138 "[error=%d (%s)]\n", iRet, strerror(iRet));
4139 palErr = ERROR_INTERNAL_ERROR;
4140 pthread_condattr_destroy(&attrs);
4141 goto IPrC_exit;
4142 }
4143#endif // HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
4144
4145 iEagains = 0;
4146 Mutex_retry:
4147 iRet = pthread_mutex_init(&m_tnwdNativeData.mutex, NULL);
4148 if (0 != iRet)
4149 {
4150 ERROR("Failed creating thread synchronization mutex [error=%d (%s)]\n", iRet, strerror(iRet));
4151 if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
4152 {
4153 poll(NULL, 0, std::min(100,10*iEagains));
4154 goto Mutex_retry;
4155 }
4156 else if (ENOMEM == iRet)
4157 {
4158 palErr = ERROR_NOT_ENOUGH_MEMORY;
4159 }
4160 else
4161 {
4162 palErr = ERROR_INTERNAL_ERROR;
4163 }
4164
4165 goto IPrC_exit;
4166 }
4167
4168 iEagains = 0;
4169 Cond_retry:
4170
4171 iRet = pthread_cond_init(&m_tnwdNativeData.cond, attrsPtr);
4172
4173 if (0 != iRet)
4174 {
4175 ERROR("Failed creating thread synchronization condition "
4176 "[error=%d (%s)]\n", iRet, strerror(iRet));
4177 if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
4178 {
4179 poll(NULL, 0, std::min(100,10*iEagains));
4180 goto Cond_retry;
4181 }
4182 else if (ENOMEM == iRet)
4183 {
4184 palErr = ERROR_NOT_ENOUGH_MEMORY;
4185 }
4186 else
4187 {
4188 palErr = ERROR_INTERNAL_ERROR;
4189 }
4190 pthread_mutex_destroy(&m_tnwdNativeData.mutex);
4191 goto IPrC_exit;
4192 }
4193
4194 m_tnwdNativeData.fInitialized = true;
4195
4196 IPrC_exit:
4197 if (attrsPtr != nullptr)
4198 {
4199 pthread_condattr_destroy(attrsPtr);
4200 }
4201 if (NO_ERROR != palErr)
4202 {
4203 m_tsThreadState = TS_FAILED;
4204 }
4205 return palErr;
4206 }
4207
4208 /*++
4209 Method:
4210 CThreadSynchronizationInfo::InitializePostCreate
4211
4212 Part of CThreadSynchronizationInfo's initialization to be carried out
4213 after actual thread creation
4214 --*/
4215 PAL_ERROR CThreadSynchronizationInfo::InitializePostCreate(
4216 CPalThread *pthrCurrent,
4217 SIZE_T threadId,
4218 DWORD dwLwpId)
4219 {
4220 PAL_ERROR palErr = NO_ERROR;
4221
4222 if (TS_FAILED == m_tsThreadState)
4223 {
4224 palErr = ERROR_INTERNAL_ERROR;
4225 }
4226
4227 m_twiWaitInfo.pthrOwner = pthrCurrent;
4228
4229 return palErr;
4230 }
4231
4232
4233 /*++
4234 Method:
4235 CThreadSynchronizationInfo::AddObjectToOwnedList
4236
4237 Adds an object to the list of currently owned objects.
4238 --*/
4239 void CThreadSynchronizationInfo::AddObjectToOwnedList(POwnedObjectsListNode pooln)
4240 {
4241 InsertTailList(&m_leOwnedObjsList, &pooln->Link);
4242 }
4243
4244 /*++
4245 Method:
4246 CThreadSynchronizationInfo::RemoveObjectFromOwnedList
4247
4248 Removes an object from the list of currently owned objects.
4249 --*/
4250 void CThreadSynchronizationInfo::RemoveObjectFromOwnedList(POwnedObjectsListNode pooln)
4251 {
4252 RemoveEntryList(&pooln->Link);
4253 }
4254
4255 /*++
4256 Method:
4257 CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList
4258
4259 Removes the first object from the list of currently owned objects.
4260 --*/
4261 POwnedObjectsListNode CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList()
4262 {
4263 OwnedObjectsListNode * poolnItem;
4264
4265 if (IsListEmpty(&m_leOwnedObjsList))
4266 {
4267 poolnItem = NULL;
4268 }
4269 else
4270 {
4271 PLIST_ENTRY pLink = RemoveHeadList(&m_leOwnedObjsList);
4272 poolnItem = CONTAINING_RECORD(pLink, OwnedObjectsListNode, Link);
4273 }
4274
4275 return poolnItem;
4276 }
4277
4278 void CThreadSynchronizationInfo::AddOwnedNamedMutex(NamedMutexProcessData *processData)
4279 {
4280 _ASSERTE(processData != nullptr);
4281 _ASSERTE(processData->GetNextInThreadOwnedNamedMutexList() == nullptr);
4282
4283 EnterCriticalSection(&m_ownedNamedMutexListLock);
4284 processData->SetNextInThreadOwnedNamedMutexList(m_ownedNamedMutexListHead);
4285 m_ownedNamedMutexListHead = processData;
4286 LeaveCriticalSection(&m_ownedNamedMutexListLock);
4287 }
4288
4289 void CThreadSynchronizationInfo::RemoveOwnedNamedMutex(NamedMutexProcessData *processData)
4290 {
4291 _ASSERTE(processData != nullptr);
4292
4293 EnterCriticalSection(&m_ownedNamedMutexListLock);
4294 if (m_ownedNamedMutexListHead == processData)
4295 {
4296 m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
4297 processData->SetNextInThreadOwnedNamedMutexList(nullptr);
4298 }
4299 else
4300 {
4301 bool found = false;
4302 for (NamedMutexProcessData
4303 *previous = m_ownedNamedMutexListHead,
4304 *current = previous->GetNextInThreadOwnedNamedMutexList();
4305 current != nullptr;
4306 previous = current, current = current->GetNextInThreadOwnedNamedMutexList())
4307 {
4308 if (current == processData)
4309 {
4310 found = true;
4311 previous->SetNextInThreadOwnedNamedMutexList(current->GetNextInThreadOwnedNamedMutexList());
4312 current->SetNextInThreadOwnedNamedMutexList(nullptr);
4313 break;
4314 }
4315 }
4316 _ASSERTE(found);
4317 }
4318 LeaveCriticalSection(&m_ownedNamedMutexListLock);
4319 }
4320
4321 NamedMutexProcessData *CThreadSynchronizationInfo::RemoveFirstOwnedNamedMutex()
4322 {
4323 EnterCriticalSection(&m_ownedNamedMutexListLock);
4324 NamedMutexProcessData *processData = m_ownedNamedMutexListHead;
4325 if (processData != nullptr)
4326 {
4327 m_ownedNamedMutexListHead = processData->GetNextInThreadOwnedNamedMutexList();
4328 processData->SetNextInThreadOwnedNamedMutexList(nullptr);
4329 }
4330 LeaveCriticalSection(&m_ownedNamedMutexListLock);
4331 return processData;
4332 }
4333
4334 bool CThreadSynchronizationInfo::OwnsNamedMutex(NamedMutexProcessData *processData)
4335 {
4336 EnterCriticalSection(&m_ownedNamedMutexListLock);
4337 bool found = false;
4338 for (NamedMutexProcessData *current = m_ownedNamedMutexListHead;
4339 current != nullptr;
4340 current = current->GetNextInThreadOwnedNamedMutexList())
4341 {
4342 if (current == processData)
4343 {
4344 found = true;
4345 break;
4346 }
4347 }
4348 LeaveCriticalSection(&m_ownedNamedMutexListLock);
4349 return found;
4350 }
4351
4352#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4353
4354 /*++
4355 Method:
4356 CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings
4357
4358 Carries out all the pending condition signalings for the current thread.
4359 --*/
4360 PAL_ERROR CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings()
4361 {
4362 PAL_ERROR palErr = NO_ERROR;
4363
4364 _ASSERTE(0 <= m_lPendingSignalingCount);
4365
4366 if (0 < m_lPendingSignalingCount)
4367 {
4368 LONG lArrayPendingSignalingCount = std::min(PendingSignalingsArraySize, m_lPendingSignalingCount);
4369 LONG lIdx = 0;
4370 PAL_ERROR palTempErr;
4371
4372 // Signal all the pending signalings from the array
4373 for (lIdx = 0; lIdx < lArrayPendingSignalingCount; lIdx++)
4374 {
4375 // Do the actual signaling
4376 palTempErr = CPalSynchronizationManager::SignalThreadCondition(
4377 m_rgpthrPendingSignalings[lIdx]->synchronizationInfo.GetNativeData());
4378 if (NO_ERROR != palTempErr)
4379 {
4380 palErr = palTempErr;
4381 }
4382
4383 // Release the thread reference
4384 m_rgpthrPendingSignalings[lIdx]->ReleaseThreadReference();
4385 }
4386
4387 // Signal any pending signalings from the array overflow list
4388 if (m_lPendingSignalingCount > PendingSignalingsArraySize)
4389 {
4390 PLIST_ENTRY pLink;
4391 DeferredSignalingListNode * pdsln;
4392
4393 while (!IsListEmpty(&m_lePendingSignalingsOverflowList))
4394 {
4395 // Remove a node from the head of the queue
4396 // Note: no need to synchronize the access to this list since
4397 // it is meant to be accessed only by the owner thread.
4398 pLink = RemoveHeadList(&m_lePendingSignalingsOverflowList);
4399 pdsln = CONTAINING_RECORD(pLink,
4400 DeferredSignalingListNode,
4401 Link);
4402
4403 // Do the actual signaling
4404 palTempErr = CPalSynchronizationManager::SignalThreadCondition(
4405 pdsln->pthrTarget->synchronizationInfo.GetNativeData());
4406 if (NO_ERROR != palTempErr)
4407 {
4408 palErr = palTempErr;
4409 }
4410
4411 // Release the thread reference
4412 pdsln->pthrTarget->ReleaseThreadReference();
4413
4414 // Delete the node
4415 InternalDelete(pdsln);
4416
4417 lIdx += 1;
4418 }
4419
4420 _ASSERTE(lIdx == m_lPendingSignalingCount);
4421 }
4422
4423 // Reset the counter of pending signalings for this thread
4424 m_lPendingSignalingCount = 0;
4425 }
4426
4427 return palErr;
4428 }
4429
4430#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
4431
4432 /*++
4433 Method:
4434 CPalSynchronizationManager::HasProcessExited
4435
4436 Tests whether or not a process has exited
4437 --*/
4438 bool CPalSynchronizationManager::HasProcessExited(
4439 DWORD dwPid,
4440 DWORD * pdwExitCode,
4441 bool * pfIsActualExitCode)
4442 {
4443 pid_t pidWaitRetval;
4444 int iStatus;
4445 bool fRet = false;
4446
4447 TRACE("Looking for status of process; trying wait()\n");
4448
4449 while(1)
4450 {
4451 /* try to get state of process, using non-blocking call */
4452 pidWaitRetval = waitpid(dwPid, &iStatus, WNOHANG);
4453
4454 if ((DWORD)pidWaitRetval == dwPid)
4455 {
4456 /* success; get the exit code */
4457 if (WIFEXITED(iStatus))
4458 {
4459 *pdwExitCode = WEXITSTATUS(iStatus);
4460 *pfIsActualExitCode = true;
4461 TRACE("Exit code was %d\n", *pdwExitCode);
4462 }
4463 else
4464 {
4465 WARN("Process terminated without exiting; can't get exit "
4466 "code. Assuming EXIT_FAILURE.\n");
4467 *pfIsActualExitCode = true;
4468 *pdwExitCode = EXIT_FAILURE;
4469 }
4470
4471 fRet = true;
4472 }
4473 else if (0 == pidWaitRetval)
4474 {
4475 // The process is still running.
4476 TRACE("Process %#x is still active.\n", dwPid);
4477 }
4478 else
4479 {
4480 // A legitimate cause of failure is EINTR; if this happens we
4481 // have to try again. A second legitimate cause is ECHILD, which
4482 // happens if we're trying to retrieve the status of a currently-
4483 // running process that isn't a child of this process.
4484 if(EINTR == errno)
4485 {
4486 TRACE("waitpid() failed with EINTR; re-waiting\n");
4487 continue;
4488 }
4489 else if (ECHILD == errno)
4490 {
4491 TRACE("waitpid() failed with ECHILD; calling kill instead\n");
4492 if (kill(dwPid, 0) != 0)
4493 {
4494 if (ESRCH == errno)
4495 {
4496 WARN("kill() failed with ESRCH, i.e. target "
4497 "process exited and it wasn't a child, "
4498 "so can't get the exit code, assuming "
4499 "it was 0.\n");
4500 *pfIsActualExitCode = false;
4501 *pdwExitCode = 0;
4502 }
4503 else
4504 {
4505 ERROR("kill(pid, 0) failed; errno is %d (%s)\n",
4506 errno, strerror(errno));
4507 *pfIsActualExitCode = false;
4508 *pdwExitCode = EXIT_FAILURE;
4509 }
4510
4511 fRet = true;
4512 }
4513 }
4514 else
4515 {
4516 // Ignoring unexpected waitpid errno and assuming that
4517 // the process is still running
4518 ERROR("waitpid(pid=%u) failed with errno=%d (%s)\n",
4519 dwPid, errno, strerror(errno));
4520 }
4521 }
4522
4523 // Break out of the loop in all cases except EINTR.
4524 break;
4525 }
4526
4527 return fRet;
4528 }
4529
4530 /*++
4531 Method:
4532 CPalSynchronizationManager::InterlockedAwaken
4533
4534 Tries to change the target wait status to 'active' in an interlocked fashion
4535 --*/
4536 bool CPalSynchronizationManager::InterlockedAwaken(
4537 DWORD *pWaitState,
4538 bool fAlertOnly)
4539 {
4540 DWORD dwPrevState;
4541
4542 dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_ALERTABLE);
4543 if (TWS_ALERTABLE != dwPrevState)
4544 {
4545 if (fAlertOnly)
4546 {
4547 return false;
4548 }
4549
4550 dwPrevState = InterlockedCompareExchange((LONG *)pWaitState, TWS_ACTIVE, TWS_WAITING);
4551 if (TWS_WAITING == dwPrevState)
4552 {
4553 return true;
4554 }
4555 }
4556 else
4557 {
4558 return true;
4559 }
4560
4561 return false;
4562 }
4563
4564 /*++
4565 Method:
4566 CPalSynchronizationManager::GetAbsoluteTimeout
4567
4568 Converts a relative timeout to an absolute one.
4569 --*/
4570 PAL_ERROR CPalSynchronizationManager::GetAbsoluteTimeout(DWORD dwTimeout, struct timespec * ptsAbsTmo, BOOL fPreferMonotonicClock)
4571 {
4572 PAL_ERROR palErr = NO_ERROR;
4573 int iRet;
4574
4575#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
4576 if (fPreferMonotonicClock)
4577 {
4578 iRet = clock_gettime(CLOCK_MONOTONIC, ptsAbsTmo);
4579 }
4580 else
4581 {
4582#endif
4583#if HAVE_WORKING_CLOCK_GETTIME
4584 // Not every platform implements a (working) clock_gettime
4585 iRet = clock_gettime(CLOCK_REALTIME, ptsAbsTmo);
4586#elif HAVE_WORKING_GETTIMEOFDAY
4587 // Not every platform implements a (working) gettimeofday
4588 struct timeval tv;
4589 iRet = gettimeofday(&tv, NULL);
4590 if (0 == iRet)
4591 {
4592 ptsAbsTmo->tv_sec = tv.tv_sec;
4593 ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds;
4594 }
4595#else
4596 #error "Don't know how to get hi-res current time on this platform"
4597#endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY
4598#if HAVE_CLOCK_MONOTONIC && HAVE_PTHREAD_CONDATTR_SETCLOCK
4599 }
4600#endif
4601 if (0 == iRet)
4602 {
4603 ptsAbsTmo->tv_sec += dwTimeout / tccSecondsToMillieSeconds;
4604 ptsAbsTmo->tv_nsec += (dwTimeout % tccSecondsToMillieSeconds) * tccMillieSecondsToNanoSeconds;
4605 while (ptsAbsTmo->tv_nsec >= tccSecondsToNanoSeconds)
4606 {
4607 ptsAbsTmo->tv_sec += 1;
4608 ptsAbsTmo->tv_nsec -= tccSecondsToNanoSeconds;
4609 }
4610 }
4611 else
4612 {
4613 palErr = ERROR_INTERNAL_ERROR;
4614 }
4615
4616 return palErr;
4617 }
4618}
4619