1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*++ |
6 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | synchmanager.cpp |
12 | |
13 | Abstract: |
14 | Implementation of Synchronization Manager and related objects |
15 | |
16 | |
17 | |
18 | --*/ |
19 | |
20 | #include "pal/dbgmsg.h" |
21 | |
22 | SET_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 | |
44 | const 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. |
49 | PTERMINATION_REQUEST_HANDLER g_terminationRequestHandler = NULL; |
50 | |
51 | // Set the handler for process termination requests. |
52 | VOID PALAPI PAL_SetTerminationRequestHandler( |
53 | IN PTERMINATION_REQUEST_HANDLER terminationHandler) |
54 | { |
55 | g_terminationRequestHandler = terminationHandler; |
56 | } |
57 | |
58 | namespace 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 | |