//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

/*++



Module Name:

    synchmanager.cpp

Abstract:
    Implementation of Synchronization Manager and related objects



--*/

#include "synchmanager.hpp"
#include "pal/file.hpp"

#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <errno.h>
#if HAVE_POLL
#include <poll.h>
#else
#include "pal/fakepoll.h"
#endif // HAVE_POLL

namespace CorUnix
{
    /////////////////////////////////
    //                             //
    //   WaitingThreadsListNode    //
    //                             //
    /////////////////////////////////
#ifdef SYNCH_OBJECT_VALIDATION
    _WaitingThreadsListNode::_WaitingThreadsListNode()
    {
        ValidateEmptyObject();
        dwDebugHeadSignature = HeadSignature;
        dwDebugTailSignature = TailSignature;
    }
    _WaitingThreadsListNode::~_WaitingThreadsListNode()
    {
        ValidateObject();
        InvalidateObject();
    }
    void _WaitingThreadsListNode::ValidateObject()
    {
        TRACE("Verifying WaitingThreadsListNode @ %p\n", this);
        _ASSERT_MSG(HeadSignature == dwDebugHeadSignature,
                    "WaitingThreadsListNode header signature corruption [p=%p]", 
                    this);
        _ASSERT_MSG(TailSignature == dwDebugTailSignature,
                    "WaitingThreadsListNode trailer signature corruption [p=%p]", 
                    this);
    }
    void _WaitingThreadsListNode::ValidateEmptyObject()
    {
        _ASSERT_MSG(HeadSignature != dwDebugHeadSignature,
                    "WaitingThreadsListNode header previously signed [p=%p]", 
                    this);
        _ASSERT_MSG(TailSignature != dwDebugTailSignature,
                    "WaitingThreadsListNode trailer previously signed [p=%p]", 
                    this);
    }
    void _WaitingThreadsListNode::InvalidateObject()
    {
        TRACE("Invalidating WaitingThreadsListNode @ %p\n", this);
        dwDebugHeadSignature = EmptySignature;
        dwDebugTailSignature = EmptySignature;
    }
#endif // SYNCH_OBJECT_VALIDATION

    //////////////////////////////
    //                          //
    //  CPalSynchMgrController  //
    //                          //
    //////////////////////////////

    /*++
    Method:
      CPalSynchMgrController::CreatePalSynchronizationManager

    Creates the Synchronization Manager. It must be called once per process.
    --*/
    IPalSynchronizationManager * CPalSynchMgrController::CreatePalSynchronizationManager()
    { 
        return CPalSynchronizationManager::CreatePalSynchronizationManager();
    };

    /*++
    Method:
      CPalSynchMgrController::StartWorker

    Starts the Synchronization Manager's Worker Thread
    --*/
    PAL_ERROR CPalSynchMgrController::StartWorker(
        CPalThread * pthrCurrent)
    {
        return CPalSynchronizationManager::StartWorker(pthrCurrent);
    }

    /*++
    Method:
      CPalSynchMgrController::PrepareForShutdown

    This method performs the part of Synchronization Manager's shutdown that 
    needs to be carried out when core PAL subsystems are still active
    --*/
    PAL_ERROR CPalSynchMgrController::PrepareForShutdown()
    {
        return CPalSynchronizationManager::PrepareForShutdown();
    }

    //////////////////////////////////
    //                              //
    //  CPalSynchronizationManager  //
    //                              //
    //////////////////////////////////

    IPalSynchronizationManager * g_pSynchronizationManager = NULL;
    
    CPalSynchronizationManager * CPalSynchronizationManager::s_pObjSynchMgr = NULL;
    Volatile<LONG> CPalSynchronizationManager::s_lInitStatus PAL_GLOBAL = SynchMgrStatusIdle;
    CRITICAL_SECTION CPalSynchronizationManager::s_csSynchProcessLock PAL_GLOBAL;
    CRITICAL_SECTION CPalSynchronizationManager::s_csMonitoredProcessesLock PAL_GLOBAL;

    CPalSynchronizationManager::CPalSynchronizationManager()
        : m_dwWorkerThreadTid(0),
          m_pipoThread(NULL),
          m_pthrWorker(NULL),
          m_iProcessPipeRead(-1),
          m_iProcessPipeWrite(-1),
          m_pmplnMonitoredProcesses(NULL),
          m_lMonitoredProcessesCount(0),
          m_pmplnExitedNodes(NULL),
          m_cacheWaitCtrlrs(CtrlrsCacheMaxSize),
          m_cacheStateCtrlrs(CtrlrsCacheMaxSize),
          m_cacheSynchData(SynchDataCacheMaxSize),
          m_cacheSHRSynchData(SynchDataCacheMaxSize),
          m_cacheWTListNodes(WTListNodeCacheMaxSize),
          m_cacheSHRWTListNodes(WTListNodeCacheMaxSize),
          m_cacheThreadApcInfoNodes(ApcInfoNodeCacheMaxSize),
          m_cacheOwnedObjectsListNodes(OwnedObjectsListCacheMaxSize)
    {
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
        m_iKQueue = -1;
        // Initialize data to 0 and flags to EV_EOF
        EV_SET(&m_keProcessPipeEvent, 0, 0, EV_EOF, 0, 0, 0);        
#endif // HAVE_KQUEUE            
    }

    CPalSynchronizationManager::~CPalSynchronizationManager()
    {
    }        

    /*++
    Method:
      CPalSynchronizationManager::BlockThread

    Call by a thread to go to sleep for a wait or a sleep

    NOTE: This methot must must be called without holding any 
          synchronization lock (as well as other locks) 
    --*/
    PAL_ERROR CPalSynchronizationManager::BlockThread(
        CPalThread *pthrCurrent,
        DWORD dwTimeout,
        bool fAlertable,
        bool fIsSleep,
        ThreadWakeupReason *ptwrWakeupReason,
        DWORD * pdwSignaledObject)
    {
        PAL_ERROR palErr = NO_ERROR;
        ThreadWakeupReason twrWakeupReason = WaitFailed;
        DWORD * pdwWaitState;
        DWORD dwWaitState = 0;
        DWORD dwSigObjIdx = 0;
        bool fRaceAlerted = false;
        bool fEarlyDeath = false;
            
        pdwWaitState = SharedIDToTypePointer(DWORD,
                pthrCurrent->synchronizationInfo.m_shridWaitAwakened);

        _ASSERT_MSG(NULL != pdwWaitState,
                    "Got NULL pdwWaitState from m_shridWaitAwakened=%p\n",
                    (VOID *)pthrCurrent->synchronizationInfo.m_shridWaitAwakened);
        
        if (fIsSleep)
        {
            // If fIsSleep is true we are being called by Sleep/SleepEx
            // and we need to switch the wait state to TWS_WAITING or 
            // TWS_ALERTABLE (according to fAlertable)

            if (fAlertable) 
            {
                // If we are in alertable mode we need to grab the lock to 
                // make sure that no APC is queued right before the 
                // InterlockedCompareExchange. 
                // If there are APCs queued at this time, no native wakeup
                // will be posted, so we need to skip the native wait
                
                // Lock
                AcquireLocalSynchLock(pthrCurrent);
                AcquireSharedSynchLock(pthrCurrent);

                if (AreAPCsPending(pthrCurrent))
                {
                    // APCs have been queued when the thread wait status was 
                    // still TWS_ACTIVE, therefore the queueing thread will not
                    // post any native wakeup: we need to skip the actual 
                    // native wait
                    fRaceAlerted = true;
                }
            }                

            if (!fRaceAlerted)
            {
                // Setting the thread in wait state 
                dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);

                TRACE("Switching my wait state [%p] from TWS_ACTIVE to %u "
                      "[current *pdwWaitState=%u]\n", 
                      pdwWaitState, dwWaitState, *pdwWaitState);

                dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState, 
                                                         dwWaitState, 
                                                         TWS_ACTIVE);
                
                if((DWORD)TWS_ACTIVE != dwWaitState)
                {
                    if (fAlertable) 
                    {
                        // Unlock
                        ReleaseSharedSynchLock(pthrCurrent);
                        ReleaseLocalSynchLock(pthrCurrent);
                    }
                    if((DWORD)TWS_EARLYDEATH == dwWaitState)
                    {
                        // Process is terminating, this thread will soon be 
                        // suspended (by SuspendOtherThreads).
                        WARN("Thread is about to get suspended by "
                             "TerminateProcess\n");

                        fEarlyDeath = true;
                        palErr = WAIT_FAILED;
                    }
                    else
                    {
                        ASSERT("Unexpected thread wait state %u\n", 
                               dwWaitState);
                        palErr = ERROR_INTERNAL_ERROR;
                    }
                    goto BT_exit;
                }
            }
            
            if (fAlertable) 
            {
                // Unlock
                ReleaseSharedSynchLock(pthrCurrent);
                ReleaseLocalSynchLock(pthrCurrent);
            }
        }
        
        if (fRaceAlerted)
        {
            twrWakeupReason = Alerted;
        }
        else
        {
            TRACE("Current thread is about to block for waiting\n");

            palErr = ThreadNativeWait(
                &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
                dwTimeout,
                &twrWakeupReason,
                &dwSigObjIdx);

            if (NO_ERROR != palErr)
            {
                ERROR("ThreadNativeWait() failed [palErr=%d]\n", palErr);
                twrWakeupReason = WaitFailed;
                goto BT_exit;
            }

            TRACE("ThreadNativeWait returned {WakeupReason=%u "
                  "dwSigObjIdx=%u}\n", twrWakeupReason, dwSigObjIdx);
        }
                
        if (WaitTimeout == twrWakeupReason)
        {
            // timeout reached. set wait state back to 'active'
            dwWaitState = (DWORD)(fAlertable ? TWS_ALERTABLE : TWS_WAITING);

            TRACE("Current thread awakened for timeout: switching wait "
                  "state [%p] from %u to TWS_ACTIVE [current *pdwWaitState=%u]\n",
                   pdwWaitState, dwWaitState, *pdwWaitState);
            
            DWORD dwOldWaitState = InterlockedCompareExchange(
                                        (LONG *)pdwWaitState,
                                        TWS_ACTIVE, (LONG)dwWaitState);
            
            switch (dwOldWaitState)
            {
                case TWS_ACTIVE:                        
                    // We were already ACTIVE; someone decided to wake up this
                    // thread sometime between the moment the native wait
                    // timed out and here. Since the signaling side succeeded
                    // its InterlockedCompareExchange, it will signal the
                    // condition/predicate pair (we just raced overtaking it);
                    // therefore we need to clear the condition/predicate
                    // by waiting on it one more time.
                    // That will also cause this method to report a signal
                    // rather than a timeout.
                    // In the remote signaling scenario, this second wait
                    // also makes sure that the shared id passed over the 
                    // process pipe is valid for the entire duration of time 
                    // in which the worker thread deals with it
                    TRACE("Current thread already ACTIVE: a signaling raced "
                          "with the timeout: re-waiting natively to clear the "
                          "predicate\n");

                    palErr = ThreadNativeWait(
                        &pthrCurrent->synchronizationInfo.m_tnwdNativeData,
                        SecondNativeWaitTimeout,
                        &twrWakeupReason,
                        &dwSigObjIdx);

                    if (NO_ERROR != palErr)
                    {
                        ERROR("ThreadNativeWait() failed [palErr=%d]\n", 
                              palErr);
                        twrWakeupReason = WaitFailed;
                    }
                    if (WaitTimeout == twrWakeupReason)
                    {
                        ERROR("Second native wait timed out\n");
                    }
                    break;
                case TWS_EARLYDEATH:
                    // Thread is about to be suspended by TerminateProcess.
                    // Anyway, if the wait timed out, we still want to 
                    // (try to) unregister the wait (especially if it 
                    // involves shared objects)
                    WARN("Thread is about to be suspended by "
                         "TerminateProcess\n");
                    fEarlyDeath = true;
                    palErr = WAIT_FAILED;
                    break;
                case TWS_WAITING:
                case TWS_ALERTABLE:
                default:
                    _ASSERT_MSG(dwOldWaitState == dwWaitState,
                                "Unexpected wait status: actual=%u, "
                                "expected=%u\n",
                               dwOldWaitState, dwWaitState);
                    break;
            }
        }

        switch (twrWakeupReason)
        {
            case WaitTimeout:
            {
                // Awakened for timeout: we need to unregister the wait
                ThreadWaitInfo * ptwiWaitInfo;

                TRACE("Current thread awakened for timeout: "
                            "unregistering the wait\n");
                
                // Local lock
                AcquireLocalSynchLock(pthrCurrent);

                ptwiWaitInfo = GetThreadWaitInfo(pthrCurrent);

                // Unregister the wait
                // Note: UnRegisterWait will take care of grabbing the shared
                //       synch lock, if needed.
                UnRegisterWait(pthrCurrent, ptwiWaitInfo, false);

                // Unlock
                ReleaseLocalSynchLock(pthrCurrent);
                
                break;
            }
            case WaitSucceeded:
            case MutexAbondoned:
                *pdwSignaledObject = dwSigObjIdx;
                break;
            default:
                // 'Alerted' and 'WaitFailed' go through this case
                break;                
        }

        // Set the returned wakeup reason
        *ptwrWakeupReason = twrWakeupReason;
                
        TRACE("Current thread is now active [WakeupReason=%u SigObjIdx=%u]\n",
              twrWakeupReason, dwSigObjIdx);

        _ASSERT_MSG(TWS_ACTIVE == VolatileLoad(pdwWaitState) || 
                    TWS_EARLYDEATH == VolatileLoad(pdwWaitState),
                    "Unexpected thread wait state %u\n", VolatileLoad(pdwWaitState));

    BT_exit:
        if (fEarlyDeath)
        {
            ThreadPrepareForShutdown();
        }
        return palErr;        
    }

#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
        ThreadNativeWaitData * ptnwdNativeWaitData,
        DWORD dwTimeout,
        ThreadWakeupReason * ptwrWakeupReason,
        DWORD * pdwSignaledObject)
    {
        PAL_ERROR palErr = NO_ERROR;
        int iRet, iWaitRet = 0;
        struct timespec tsAbsTmo;      

        TRACE("ThreadNativeWait(ptnwdNativeWaitData=%p, dwTimeout=%u, ...)\n",
              ptnwdNativeWaitData, dwTimeout);
        
        if (dwTimeout != INFINITE)
        {
            // Calculate absolute timeout
            palErr = GetAbsoluteTimeout(dwTimeout, &tsAbsTmo);
            if (NO_ERROR != palErr)
            {
                ERROR("Failed to convert timeout to absolute timeout\n");
                goto TNW_exit;
            }            
        }

        // Lock the mutex
        iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
        if (0 != iRet)
        {
            ERROR("Internal Error: cannot lock mutex\n");
            palErr = ERROR_INTERNAL_ERROR;
            *ptwrWakeupReason = WaitFailed;
            goto TNW_exit;
        }
        
        while (FALSE == ptnwdNativeWaitData->iPred) 
        {            
            if (INFINITE == dwTimeout)
            {
                iWaitRet = pthread_cond_wait(&ptnwdNativeWaitData->cond,
                                             &ptnwdNativeWaitData->mutex);
            }
            else
            {
                iWaitRet = pthread_cond_timedwait(&ptnwdNativeWaitData->cond, 
                                                  &ptnwdNativeWaitData->mutex, 
                                                  &tsAbsTmo);
            }

            if (ETIMEDOUT == iWaitRet)
            {
                _ASSERT_MSG(INFINITE != dwTimeout,
                            "Got a ETIMEDOUT despite timeout was INFINITE\n");                    
                break;
            }
            else if (0 != iWaitRet)
            {
                ERROR("pthread_cond_%swait returned %d [errno=%d (%s)]\n", 
                       (INFINITE == dwTimeout) ? "" : "timed", 
                       iWaitRet, errno, strerror(errno));
                palErr = ERROR_INTERNAL_ERROR;
                break;
            }            
        } 

        // Reset the predicate
        if (0 == iWaitRet)
        {
            // We don't want to reset the predicate if pthread_cond_timedwait
            // timed out racing with a pthread_cond_signal. When 
            // pthread_cond_timedwait times out, it needs to grab the mutex
            // before returning. At timeout time, it may happen that the 
            // signaling thread just grabbed the mutex, but it hasn't called
            // pthread_cond_signal yet. In this scenario pthread_cond_timedwait
            // will have to wait for the signaling side to release the mutex.
            // As result it will return with error timeout, but the predicate 
            // will be set. Since pthread_cond_timedwait timed out, the 
            // predicate value is intended for the next signal. In case of a
            // object signaling racing with a wait timeout this predicate value
            // will be picked up by the 'second native wait' (see comments in
            // BlockThread).
            
            ptnwdNativeWaitData->iPred = FALSE;
        }

        // Unlock the mutex
        iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);
        if (0 != iRet)
        {
            ERROR("Cannot unlock mutex [err=%d]\n", iRet);
            palErr = ERROR_INTERNAL_ERROR;
            goto TNW_exit;
        }

        _ASSERT_MSG(ETIMEDOUT != iRet || INFINITE != dwTimeout,
                    "Got time out return code with INFINITE timeout\n");

        if (0 == iWaitRet)
        {
            *ptwrWakeupReason  = ptnwdNativeWaitData->twrWakeupReason;
            *pdwSignaledObject = ptnwdNativeWaitData->dwObjectIndex;
        }
        else if (ETIMEDOUT == iWaitRet)
        {
            *ptwrWakeupReason = WaitTimeout;
        }

    TNW_exit:
        
        TRACE("ThreadNativeWait: returning %u [WakeupReason=%u]\n", 
              palErr, *ptwrWakeupReason);
        
        return palErr;
    }

#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    PAL_ERROR CPalSynchronizationManager::ThreadNativeWait(
        ThreadNativeWaitData * ptnwdNativeWaitData,
        DWORD dwTimeout,
        ThreadWakeupReason * ptwrWakeupReason,
        DWORD * pdwSignaledObject)
    {
        PAL_ERROR palErr = NO_ERROR;
        DWORD dwTmo = dwTimeout;
        DWORD dwOldTime = GetTickCount();
        int iRet;
        int iPollTmo;
        int iWaitRet = 0;  
        struct pollfd pllfd;
        bool fAgain;

        TRACE("ThreadNativeWait(ptnwdNativeWaitData=%p, dwTimeout=%u, ...)\n",
              ptnwdNativeWaitData, dwTimeout);
        
        do
        {
            fAgain = false;
            
            pllfd.fd = ptnwdNativeWaitData->iPipeRd;
            pllfd.events = POLLIN;
            pllfd.revents = 0;
            iPollTmo = (INFINITE == dwTmo) ? INFTIM : (int)min(INT_MAX,dwTmo);            

            iRet = poll(&pllfd, 1, iPollTmo);

            if (1 == iRet &&
                ((POLLERR | POLLHUP | POLLNVAL) & pllfd.revents))
            {
                ERROR("Unexpected revents=%x while polling pipe %d\n", 
                      pllfd.revents, ptnwdNativeWaitData->iPipeRd);
                palErr = ERROR_INTERNAL_ERROR;
                break;
            }

            switch(iRet)
            {
                case -1:
                    // poll failed
                    if(EINTR != errno)
                    {
                        // Error
                        ERROR("Unexpected errno=%d (%s) while polling pipe %d\n", 
                              errno, strerror(errno), ptnwdNativeWaitData->iPipeRd);
                        palErr = ERROR_INTERNAL_ERROR;
                        break;
                    }
                    // fall through
                case 0:
                    // Timeout
                    if (INFINITE != dwTmo)
                    {
                        UpdateTimeout(&dwOldTime, &dwTmo);
                        TRACE("Timeout updated: %u\n", dwTmo);
                    }

                    if (0 != dwTmo)
                    {
                        fAgain = true;
                    }
                    else
                    {
                        _ASSERTE(INFINITE != dwTimeout);
                        *ptwrWakeupReason = WaitTimeout;                        
                    }
                    break;
                case 1:
                {
                    // Signaled
                    char c;
                    int iRt;
                    iRt = read(ptnwdNativeWaitData->iPipeRd, &c, sizeof(c));

                    _ASSERTE(sizeof(c) == iRt);
                    _ASSERTE(c == (char)ptnwdNativeWaitData->twrWakeupReason);
                    
                    *ptwrWakeupReason  = ptnwdNativeWaitData->twrWakeupReason;
                    *pdwSignaledObject = ptnwdNativeWaitData->dwObjectIndex;
                    break;
                }
                default:
                    // Error
                    ERROR("Unexpected return code %d while polling pipe %d\n", 
                          iRet, ptnwdNativeWaitData->iPipeRd);

                    palErr = ERROR_INTERNAL_ERROR;
                    break;
            }
                    
        } while (fAgain);

    TNW_exit:
        
        TRACE("ThreadNativeWait: returning %u [WakeupReason=%u]\n", 
              palErr, *ptwrWakeupReason);
        
        return palErr;
    }

#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CPalSynchronizationManager::AbandonObjectsOwnedByThread

    This method is called by a thread at thread-exit time to abandon
    any currently owned waitable object (mutexes). If pthrTarget is 
    different from pthrCurrent, AbandonObjectsOwnedByThread assumes
    to be called whether by TerminateThread or at shutdown time. See
    comments below for more details
    --*/
    PAL_ERROR CPalSynchronizationManager::AbandonObjectsOwnedByThread(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget)
    {
        PAL_ERROR palErr = NO_ERROR;
        OwnedObjectsListNode * poolnItem;
        bool fSharedSynchLock = false;
        CThreadSynchronizationInfo * pSynchInfo = 
                    &pthrTarget->synchronizationInfo;
        CPalSynchronizationManager * pSynchManager = GetInstance();

        // Local lock
        AcquireLocalSynchLock(pthrCurrent);

        // Abandon owned objects
        while (NULL != (poolnItem = pSynchInfo->RemoveFirstObjectFromOwnedList()))
        {
            CSynchData * psdSynchData = poolnItem->pPalObjSynchData;

            _ASSERT_MSG(NULL != psdSynchData,
                        "NULL psdSynchData pointer in ownership list node\n");

            VALIDATEOBJECT(psdSynchData);

            TRACE("Abandoning object with SynchData at %p\n",
                  psdSynchData);
            
            if (!fSharedSynchLock && 
                (SharedObject == psdSynchData->GetObjectDomain()))
            {
                AcquireSharedSynchLock(pthrCurrent);
                fSharedSynchLock = true;
            }

            // Reset ownership data
            psdSynchData->ResetOwnership();

            // Set abandoned status; in case there is a thread to be released:
            //  - if the thread is local, ReleaseFirstWaiter will reset the
            //    abandoned status
            //  - if the thread is remote, the remote worker thread will use 
            //    the value and reset it
            psdSynchData->SetAbandoned(true);

            // Signal the object and trigger thread awakening
            psdSynchData->Signal(pthrCurrent, 1, false);

            // Release reference to to SynchData
            psdSynchData->Release(pthrCurrent);

            // Return node to the cache
            pSynchManager->m_cacheOwnedObjectsListNodes.Add(pthrCurrent, 
                                                            poolnItem);
        }

        if (pthrTarget != pthrCurrent)
        {
            // If the target thead is not the current one, we are being called
            // at shutdown time, right before the target thread is suspended,
            // or anyway the target thread is being terminated.
            // In this case we switch its wait state to TWS_EARLYDEATH so that,
            // if the thread is currently waiting/sleeping and it wakes up 
            // before shutdown code manage to suspend it, it will be rerouted 
            // to ThreadPrepareForShutdown (that will be done without holding
            // any internal lock, in a way to accomodate shutdown time thread
            // suspension). 
            // At this time we also unregister the wait, so no dummy nodes are 
            // left around on waiting objects.
            // The TWS_EARLYDEATH wait-state will also prevent the thread from
            // successfully registering for a possible new wait in the same 
            // time window.
            LONG lTWState;
            DWORD * pdwWaitState;
            
            pdwWaitState = SharedIDToTypePointer(DWORD,
                pthrTarget->synchronizationInfo.m_shridWaitAwakened);

            lTWState = InterlockedExchange((LONG *)pdwWaitState, 
                                           TWS_EARLYDEATH);

            if ( (((LONG)TWS_WAITING == lTWState) || 
                  ((LONG)TWS_ALERTABLE == lTWState)) &&
                 (0 < pSynchInfo->m_twiWaitInfo.lObjCount) )
            {
                // Unregister the wait
                // Note: UnRegisterWait will take care of grabbing the shared
                //       synch lock, if needed.
                UnRegisterWait(pthrCurrent, 
                               &pSynchInfo->m_twiWaitInfo, 
                               fSharedSynchLock);
            }
        }
        // Unlock
        if (fSharedSynchLock)
        {
            ReleaseSharedSynchLock(pthrCurrent);
            fSharedSynchLock = false;
        }
        ReleaseLocalSynchLock(pthrCurrent);

        DiscardAllPendingAPCs(pthrCurrent, pthrTarget);

        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::GetSynchWaitControllersForObjects

    Returns an array of wait controllers, one for each of the objects
    in rgObjects
    --*/
    PAL_ERROR CPalSynchronizationManager::GetSynchWaitControllersForObjects(
        CPalThread *pthrCurrent,
        IPalObject *rgObjects[],
        DWORD dwObjectCount,
        ISynchWaitController * rgControllers[])
    {
        return GetSynchControllersForObjects(pthrCurrent,
                                             rgObjects,
                                             dwObjectCount,
                                             (void **)rgControllers,
                                             CSynchControllerBase::WaitController);                                                     
    }                                
    
    /*++
    Method:
      CPalSynchronizationManager::GetSynchStateControllersForObjects

    Returns an array of state controllers, one for each of the objects
    in rgObjects
    --*/
    PAL_ERROR CPalSynchronizationManager::GetSynchStateControllersForObjects(
        CPalThread *pthrCurrent,
        IPalObject *rgObjects[],
        DWORD dwObjectCount,
        ISynchStateController *rgControllers[])
    {
        return GetSynchControllersForObjects(pthrCurrent,
                                             rgObjects,
                                             dwObjectCount,
                                             (void **)rgControllers,
                                             CSynchControllerBase::StateController);
    }                                
    
    /*++
    Method:
      CPalSynchronizationManager::GetSynchControllersForObjects

    Internal common implementation for GetSynchWaitControllersForObjects and 
    GetSynchStateControllersForObjects
    --*/
    PAL_ERROR CPalSynchronizationManager::GetSynchControllersForObjects(
        CPalThread *pthrCurrent,
        IPalObject *rgObjects[],
        DWORD dwObjectCount,
        void ** ppvControllers,
        CSynchControllerBase::ControllerType ctCtrlrType)
    {
        PAL_ERROR palErr = NO_ERROR;        
        unsigned int uIdx, uCount = 0, uSharedObjectCount = 0;
        WaitDomain wdWaitDomain = LocalWait;
        CObjectType * potObjectType = NULL; 
        unsigned int uErrCleanupIdxFirstNotInitializedCtrlr = 0;
        unsigned int uErrCleanupIdxLastCtrlr = 0;
        bool fLocalSynchLock = false;
        union 
        { 
            CSynchWaitController * pWaitCtrlrs[MAXIMUM_WAIT_OBJECTS];
            CSynchStateController * pStateCtrlrs[MAXIMUM_WAIT_OBJECTS];  
        } Ctrlrs;

        if ((dwObjectCount <= 0) || (dwObjectCount > MAXIMUM_WAIT_OBJECTS))
        {
            palErr = ERROR_INVALID_PARAMETER;
            goto GSCFO_exit;
        }
                
        if (CSynchControllerBase::WaitController == ctCtrlrType)
        {
            uCount = (unsigned int)m_cacheWaitCtrlrs.Get(pthrCurrent,
                                                         dwObjectCount, 
                                                         Ctrlrs.pWaitCtrlrs);
        }
        else
        {
            uCount = (unsigned int)m_cacheStateCtrlrs.Get(pthrCurrent, 
                                                          dwObjectCount,
                                                          Ctrlrs.pStateCtrlrs);
        }
        if (uCount < dwObjectCount)
        {
            // We got less controllers (uCount) than we asked for (dwObjectCount), 
            // probably because of low memory.
            // None of these controllers is initialized, so they must be all
            // returned directly to the cache
            uErrCleanupIdxLastCtrlr = uCount;
            
            palErr = ERROR_NOT_ENOUGH_MEMORY;
            goto GSCFO_error_cleanup;
        }          

        //
        // We need to acquire the local synch lock before evaluating object
        // domains
        //
        AcquireLocalSynchLock(pthrCurrent);
        fLocalSynchLock = true;

        for (uIdx=0; uIdx<dwObjectCount; uIdx++)
        {
            if (SharedObject == rgObjects[uIdx]->GetObjectDomain())
            {
                ++uSharedObjectCount; 
            }
            if (uSharedObjectCount > 0 && uSharedObjectCount <= uIdx)
            {            
                wdWaitDomain = MixedWait;
                break;
            }
        }
        if (dwObjectCount == uSharedObjectCount)
        {
            wdWaitDomain = SharedWait;
        } 
        
        for (uIdx=0;uIdx<dwObjectCount;uIdx++)
        {   
            void * pvSData;
            CSynchData * psdSynchData;
            ObjectDomain odObjectDomain = rgObjects[uIdx]->GetObjectDomain();

            palErr = rgObjects[uIdx]->GetObjectSynchData((void **)&pvSData);
            if (NO_ERROR != palErr)
            {
                break;
            }

            psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
                CSynchData, reinterpret_cast<SharedID>(pvSData)) : 
                static_cast<CSynchData *>(pvSData);

            VALIDATEOBJECT(psdSynchData);

            potObjectType = rgObjects[uIdx]->GetObjectType();
            

            if (CSynchControllerBase::WaitController == ctCtrlrType)
            {
                Ctrlrs.pWaitCtrlrs[uIdx]->Init(pthrCurrent,
                                            ctCtrlrType,
                                            odObjectDomain,
                                            potObjectType,
                                            psdSynchData,
                                            wdWaitDomain);
            }
            else
            {
                Ctrlrs.pStateCtrlrs[uIdx]->Init(pthrCurrent,
                                             ctCtrlrType,
                                             odObjectDomain,
                                             potObjectType,
                                             psdSynchData,
                                             wdWaitDomain);
            }

            if (CSynchControllerBase::WaitController == ctCtrlrType &&
                otiProcess == potObjectType->GetId())
            {
                CProcProcessLocalData * pProcLocData;
                IDataLock * pDataLock;

                palErr = rgObjects[uIdx]->GetProcessLocalData(
                    pthrCurrent,
                    ReadLock,
                    &pDataLock,
                    (void **)&pProcLocData);
                if (NO_ERROR != palErr)
                {
                    // In case of failure here, bail out of the loop, but 
                    // keep track (by incrementing the counter 'uIdx') of the 
                    // fact that this controller has already being initialized
                    // and therefore need to be Release'd rather than just
                    // returned to the cache
                    uIdx++;                    
                    break;
                }

                Ctrlrs.pWaitCtrlrs[uIdx]->SetProcessLocalData(pProcLocData);
                pDataLock->ReleaseLock(pthrCurrent, false);
            }
        }
        if (NO_ERROR != palErr)
        {
            // An error occurred while initializing the (uIdx+1)-th controller,
            // i.e. the one at index uIdx; therefore the first uIdx controllers 
            // must be Release'd, while the remaining uCount-uIdx must be returned 
            // directly to the cache.
            uErrCleanupIdxFirstNotInitializedCtrlr = uIdx;
            uErrCleanupIdxLastCtrlr = dwObjectCount;
            
            goto GSCFO_error_cleanup;
        }

        // Succeeded
        if (CSynchControllerBase::WaitController == ctCtrlrType)
        {
            for (uIdx=0;uIdx<dwObjectCount;uIdx++)
            {
                // The multiple cast is NEEDED, though currently it does not
                // change the value ot the pointer. Anyway, if in the future 
                // a virtual method should be added to the base class 
                // CSynchControllerBase, both derived classes would have two 
                // virtual tables, therefore a static cast from, for instance,
                // a CSynchWaitController* to a ISynchWaitController* would 
                // return the given pointer incremented by the size of a 
                // generic pointer on the specific platform

                ppvControllers[uIdx] = reinterpret_cast<void *>(
                    static_cast<ISynchWaitController *>(Ctrlrs.pWaitCtrlrs[uIdx]));
            }
        }
        else
        {
            for (uIdx=0;uIdx<dwObjectCount;uIdx++)
            {
                // See comment above

                ppvControllers[uIdx] = reinterpret_cast<void *>(
                    static_cast<ISynchStateController *>(Ctrlrs.pStateCtrlrs[uIdx]));
            }
        }
        
        // Succeeded: skip error cleanup        
        goto GSCFO_exit;

    GSCFO_error_cleanup:
        if (CSynchControllerBase::WaitController == ctCtrlrType)
        {
            // Release already initialized wait controllers
            for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
            {
                Ctrlrs.pWaitCtrlrs[uIdx]->Release();
            }
            // Return to the cache not yet initialized wait controllers
            for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
            {
                m_cacheWaitCtrlrs.Add(pthrCurrent, Ctrlrs.pWaitCtrlrs[uIdx]);
            }
        }
        else
        {
            // Release already initialized state controllers
            for (uIdx=0; uIdx<uErrCleanupIdxFirstNotInitializedCtrlr; uIdx++)
            {
                Ctrlrs.pStateCtrlrs[uIdx]->Release();
            }
            // Return to the cache not yet initialized state controllers
            for (uIdx=uErrCleanupIdxFirstNotInitializedCtrlr; uIdx<uErrCleanupIdxLastCtrlr; uIdx++)
            {
                m_cacheStateCtrlrs.Add(pthrCurrent, Ctrlrs.pStateCtrlrs[uIdx]);
            }
        }
               
    GSCFO_exit:
    
        if (fLocalSynchLock)
        {
            ReleaseLocalSynchLock(pthrCurrent);
        }
        return palErr;        
    }                                

    /*++
    Method:
      CPalSynchronizationManager::AllocateObjectSynchData

    Returns a new SynchData for an object of given type and domain
    --*/
    PAL_ERROR CPalSynchronizationManager::AllocateObjectSynchData(
        CObjectType *potObjectType,
        ObjectDomain odObjectDomain,
        VOID **ppvSynchData)
    {    
        PAL_ERROR palErr = NO_ERROR;
        CSynchData * psdSynchData = NULL;
        CPalThread * pthrCurrent = InternalGetCurrentThread();

        if (SharedObject == odObjectDomain)
        {
            SharedID shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent); 
            if (NULLSharedID == shridSynchData)
            {
                ERROR("Unable to allocate shared memory\n");
                palErr = ERROR_NOT_ENOUGH_MEMORY;
                goto AOSD_exit;
            }
            psdSynchData = SharedIDToTypePointer(CSynchData, shridSynchData);

            VALIDATEOBJECT(psdSynchData);

            _ASSERT_MSG(NULL != psdSynchData, "Bad shared memory pointer\n");

            // Initialize waiting list pointers
            psdSynchData->SetWTLHeadShrPtr(NULLSharedID);
            psdSynchData->SetWTLTailShrPtr(NULLSharedID);

            // Store shared pointer to this object
            psdSynchData->SetSharedThis(shridSynchData);
            
            *ppvSynchData = reinterpret_cast<void *>(shridSynchData);
        }
        else
        {
            psdSynchData = m_cacheSynchData.Get(pthrCurrent); 
            if (NULL == psdSynchData)
            {
                ERROR("Unable to allocate memory\n");
                palErr = ERROR_NOT_ENOUGH_MEMORY;
                goto AOSD_exit;
            }
            
            // Initialize waiting list pointers
            psdSynchData->SetWTLHeadPtr(NULL);
            psdSynchData->SetWTLTailPtr(NULL);

            // Set shared this pointer to NULL
            psdSynchData->SetSharedThis(NULLSharedID);
            
            *ppvSynchData = static_cast<void *>(psdSynchData);
        }        

        // Initialize object domain and object type;
        psdSynchData->SetObjectDomain(odObjectDomain);
        psdSynchData->SetObjectType(potObjectType);

    AOSD_exit:
        return palErr;
    }                                
    
    /*++
    Method:
      CPalSynchronizationManager::FreeObjectSynchData

    Called to return a no longer used SynchData to the Synchronization
    Manager. The SynchData may actually survive this call, since it
    is a ref-counted object and at FreeObjectSynchData time it may still
    be used from withing the Synchronization Manager itself (e.g. the 
    Worker Thread)
    --*/
    void CPalSynchronizationManager::FreeObjectSynchData(
        CObjectType *potObjectType,
        ObjectDomain odObjectDomain,
        VOID *pvSynchData)
    {
        CSynchData * psdSynchData;
        CPalThread * pthrCurrent = InternalGetCurrentThread();

        if (odObjectDomain == SharedObject)
        {
            psdSynchData = SharedIDToTypePointer(CSynchData,
                reinterpret_cast<SharedID>(pvSynchData));
            if (NULL == psdSynchData)
            {
                ASSERT("Bad shared memory pointer\n");
                goto FOSD_exit;
            }
        }
        else
        {
            psdSynchData = static_cast<CSynchData *>(pvSynchData);
        }

        psdSynchData->Release(pthrCurrent);

    FOSD_exit:
        return;
    }
    
    /*++
    Method:
      CPalSynchronizationManager::CreateSynchStateController

    Creates a state controller for the given object
    --*/
    PAL_ERROR CPalSynchronizationManager::CreateSynchStateController(
        CPalThread *pthrCurrent,
        CObjectType *potObjectType,
        VOID *pvSynchData,
        ObjectDomain odObjectDomain,
        ISynchStateController **ppStateController)
    {
        PAL_ERROR palErr = NO_ERROR;        
        CSynchStateController * pCtrlr =  NULL;
        WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
        CSynchData * psdSynchData;

        psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
            CSynchData, reinterpret_cast<SharedID>(pvSynchData)) : 
            static_cast<CSynchData *>(pvSynchData);

        VALIDATEOBJECT(psdSynchData);

        pCtrlr = m_cacheStateCtrlrs.Get(pthrCurrent);
        if (NULL == pCtrlr)
        {
            palErr = ERROR_NOT_ENOUGH_MEMORY;
            goto CSSC_exit;
        }                  

        pCtrlr->Init(pthrCurrent,
                     CSynchControllerBase::StateController,
                     odObjectDomain,
                     potObjectType,
                     psdSynchData,
                     wdWaitDomain);

        // Succeeded
        *ppStateController = (ISynchStateController *)pCtrlr;
        
    CSSC_exit:
        if ((NO_ERROR != palErr) && (NULL != pCtrlr))
        {
            m_cacheStateCtrlrs.Add(pthrCurrent, pCtrlr);
        }
        return palErr;     
    }
    
    /*++
    Method:
      CPalSynchronizationManager::CreateSynchWaitController

    Creates a wait controller for the given object
    --*/
    PAL_ERROR CPalSynchronizationManager::CreateSynchWaitController(
        CPalThread *pthrCurrent,
        CObjectType *potObjectType,
        VOID *pvSynchData,
        ObjectDomain odObjectDomain,
        ISynchWaitController **ppWaitController)
    {
        PAL_ERROR palErr = NO_ERROR;        
        CSynchWaitController * pCtrlr =  NULL;
        WaitDomain wdWaitDomain = (SharedObject == odObjectDomain) ? SharedWait : LocalWait;
        CSynchData * psdSynchData;

        psdSynchData = (SharedObject == odObjectDomain) ? SharedIDToTypePointer(
            CSynchData, reinterpret_cast<SharedID>(pvSynchData)) : 
            static_cast<CSynchData *>(pvSynchData);

        VALIDATEOBJECT(psdSynchData);
            
        pCtrlr = m_cacheWaitCtrlrs.Get(pthrCurrent);
        if (NULL == pCtrlr)
        {
            palErr = ERROR_NOT_ENOUGH_MEMORY;
            goto CSWC_exit;
        }                  
       
        pCtrlr->Init(pthrCurrent,
                     CSynchControllerBase::WaitController,
                     odObjectDomain,
                     potObjectType,
                     psdSynchData,
                     wdWaitDomain);

        // Succeeded
        *ppWaitController = (ISynchWaitController *)pCtrlr;
       
    CSWC_exit:
        if ((NO_ERROR != palErr) && (NULL != pCtrlr))
        {
            m_cacheWaitCtrlrs.Add(pthrCurrent, pCtrlr);
        }
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::QueueUserAPC

    Internal implementation of QueueUserAPC
    --*/
    PAL_ERROR CPalSynchronizationManager::QueueUserAPC(CPalThread * pthrCurrent,
        CPalThread * pthrTarget,
        PAPCFUNC pfnAPC,
        ULONG_PTR uptrData)
    {
        PAL_ERROR palErr = NO_ERROR;
        ThreadApcInfoNode * ptainNode = NULL;
        DWORD dwWaitState;
        DWORD * pdwWaitState;
        ThreadWaitInfo * pTargetTWInfo = GetThreadWaitInfo(pthrTarget);
        bool fLocalSynchLock = false; 
        bool fSharedSynchLock = false; 
        bool fThreadLock = false;

        ptainNode = m_cacheThreadApcInfoNodes.Get(pthrCurrent);
        if (NULL == ptainNode)
        {                           
            ERROR("No memory for new APCs linked list entry\n");
            palErr = ERROR_NOT_ENOUGH_MEMORY;
            goto QUAPC_exit;
        }                

        ptainNode->pfnAPC = pfnAPC;
        ptainNode->pAPCData = uptrData;
        ptainNode->pNext = NULL;

        AcquireLocalSynchLock(pthrCurrent);
        fLocalSynchLock = true;
        
        if (LocalWait != pTargetTWInfo->wdWaitDomain)
        {
            AcquireSharedSynchLock(pthrCurrent);
            fSharedSynchLock = true;
        }
        
        pthrTarget->Lock(pthrCurrent);
        fThreadLock = true;

        if(TS_DONE == pthrTarget->synchronizationInfo.GetThreadState())
        {
            ERROR("Thread %#x has terminated; can't queue an APC on it\n",
                  pthrTarget->GetThreadId());
            palErr = ERROR_INVALID_PARAMETER;
            goto QUAPC_exit;
        }
        pdwWaitState = SharedIDToTypePointer(DWORD,
            pthrTarget->synchronizationInfo.m_shridWaitAwakened);
        if(TWS_EARLYDEATH == VolatileLoad(pdwWaitState))
        {
            ERROR("Thread %#x is about to be suspended for process shutdwon, "
                  "can't queue an APC on it\n", pthrTarget->GetThreadId());
            palErr = ERROR_INVALID_PARAMETER;
            goto QUAPC_exit;
        }

        if (NULL == pthrTarget->apcInfo.m_ptainTail)
        {
            _ASSERT_MSG(NULL == pthrTarget->apcInfo.m_ptainHead,
                        "Corrupted APC list\n"); 

            pthrTarget->apcInfo.m_ptainHead = ptainNode;
            pthrTarget->apcInfo.m_ptainTail = ptainNode;
        }
        else
        {
            pthrTarget->apcInfo.m_ptainTail->pNext = ptainNode;
            pthrTarget->apcInfo.m_ptainTail = ptainNode;
        }

        // Set ptainNode to NULL so it won't be readded to the cache
        ptainNode = NULL;
        
        TRACE("APC %p with parameter %p added to APC queue\n", 
             pfnAPC, uptrData);

        dwWaitState = InterlockedCompareExchange((LONG *)pdwWaitState,  
                                                 (LONG)TWS_ACTIVE,
                                                 (LONG)TWS_ALERTABLE);

        // Release thread lock
        pthrTarget->Unlock(pthrCurrent);
        fThreadLock = false;

        if(TWS_ALERTABLE == dwWaitState)
        {
            // Unregister the wait
            UnRegisterWait(pthrCurrent, pTargetTWInfo, fSharedSynchLock);

            // Wake up target thread
            palErr = WakeUpLocalThread(
                pthrCurrent,
                pthrTarget,
                Alerted, 
                0);

            if (NO_ERROR != palErr)
            {
                ERROR("Failed to wakeup local thread %#x for dispatching "
                      "APCs [err=%u]\n", pthrTarget->GetThreadId(),
                      palErr);
            }
        }    

    QUAPC_exit:
        if (fThreadLock)
        {
            pthrTarget->Unlock(pthrCurrent); 
        }
        if (fSharedSynchLock)
        {
            ReleaseSharedSynchLock(pthrCurrent);
        }
        if (fLocalSynchLock)
        {
            ReleaseLocalSynchLock(pthrCurrent);
        }            
        if (ptainNode)
        {
            m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
        }
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::AreAPCsPending

    Returns 'true' if there are APCs currently pending for the target 
    thread (normally the current one)
    --*/
    bool CPalSynchronizationManager::AreAPCsPending(
        CPalThread * pthrTarget)
    {
        // No need to lock here
        return (NULL != pthrTarget->apcInfo.m_ptainHead);
    }

    /*++
    Method:
      CPalSynchronizationManager::DispatchPendingAPCs

    Executes any pending APC for the current thread
    --*/
    PAL_ERROR CPalSynchronizationManager::DispatchPendingAPCs(
        CPalThread * pthrCurrent)
    {
        ThreadApcInfoNode * ptainNode, * ptainLocalHead;
        int iAPCsCalled = 0;

        while (TRUE) 
        {
            // Lock
            pthrCurrent->Lock(pthrCurrent);
            ptainLocalHead = pthrCurrent->apcInfo.m_ptainHead;
            if (ptainLocalHead)
            {
                pthrCurrent->apcInfo.m_ptainHead = NULL;
                pthrCurrent->apcInfo.m_ptainTail = NULL;
            }
            // Unlock
            pthrCurrent->Unlock(pthrCurrent);

            if (NULL == ptainLocalHead)
            {
                break;
            }

            while (ptainLocalHead)
            {
                ptainNode = ptainLocalHead;
                ptainLocalHead = ptainNode->pNext;

#if _ENABLE_DEBUG_MESSAGES_
                // reset ENTRY nesting level back to zero while 
                // inside the callback ... 
                int iOldLevel = DBG_change_entrylevel(0);
#endif /* _ENABLE_DEBUG_MESSAGES_ */

                TRACE("Calling APC %p with parameter %#x\n",
                      ptainNode->pfnAPC, ptainNode->pfnAPC);

                // Actual APC call
                ptainNode->pfnAPC(ptainNode->pAPCData);

#if _ENABLE_DEBUG_MESSAGES_
                // ... and set nesting level back to what it was
                DBG_change_entrylevel(iOldLevel);
#endif /* _ENABLE_DEBUG_MESSAGES_ */

                iAPCsCalled++;
                m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
            }
        }

        return (iAPCsCalled > 0) ? NO_ERROR : ERROR_NOT_FOUND;
    }

    /*++
    Method:
      CPalSynchronizationManager::DiscardAllPendingAPCs

    Discards any pending APC for the target pthrTarget thread
    --*/
    void CPalSynchronizationManager::DiscardAllPendingAPCs(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget)
    {
        ThreadApcInfoNode * ptainNode, * ptainLocalHead;

        // Lock
        pthrTarget->Lock(pthrCurrent);
        ptainLocalHead = pthrTarget->apcInfo.m_ptainHead;
        if (ptainLocalHead)
        {
            pthrTarget->apcInfo.m_ptainHead = NULL;
            pthrTarget->apcInfo.m_ptainTail = NULL;
        }
        // Unlock
        pthrTarget->Unlock(pthrCurrent);

        while (ptainLocalHead)
        {
            ptainNode = ptainLocalHead;
            ptainLocalHead = ptainNode->pNext;

            m_cacheThreadApcInfoNodes.Add(pthrCurrent, ptainNode);
        }
    }

    /*++
    Method:
      CPalSynchronizationManager::CreatePalSynchronizationManager

    Creates the Synchronization Manager.
    Private method, it is called only by CPalSynchMgrController.
    --*/
    IPalSynchronizationManager * CPalSynchronizationManager::CreatePalSynchronizationManager()
    { 
        IPalSynchronizationManager * pRet = NULL;
        if (s_pObjSynchMgr == NULL)
        {
            Initialize();
            pRet = static_cast<IPalSynchronizationManager *>(s_pObjSynchMgr);
        }
        else
        {
            ASSERT("Multiple PAL Synchronization manager initializations\n");
        }

        return pRet;
    };

    /*++
    Method:
      CPalSynchronizationManager::Initialize

    Internal Synchronization Manager initialization
    --*/
    PAL_ERROR CPalSynchronizationManager::Initialize()
    {
        PAL_ERROR palErr = NO_ERROR;
        LONG lInit;
        CPalSynchronizationManager * pSynchManager = NULL;
            
        lInit = InterlockedCompareExchange(&s_lInitStatus, 
                                           (LONG)SynchMgrStatusInitializing, 
                                           (LONG)SynchMgrStatusIdle);
        if ((LONG)SynchMgrStatusIdle != lInit)
        {      
            ASSERT("Synchronization Manager already being initialized");
            palErr = ERROR_INTERNAL_ERROR;
            goto I_exit;
        }

        InternalInitializeCriticalSection(&s_csSynchProcessLock);
        InternalInitializeCriticalSection(&s_csMonitoredProcessesLock);        
        
        pSynchManager = InternalNew<CPalSynchronizationManager>();
        if (NULL == pSynchManager)
        {
            ERROR("Failed to allocate memory for Synchronization Manager");
            palErr = ERROR_NOT_ENOUGH_MEMORY;
            goto I_exit;
        }

        if (!pSynchManager->CreateProcessPipe())
        {
            ERROR("Unable to create process pipe \n");
            palErr = ERROR_OPEN_FAILED;
            goto I_exit;
        }

        s_pObjSynchMgr = pSynchManager;

        // Initialization was successful
        g_pSynchronizationManager =
            static_cast<IPalSynchronizationManager *>(pSynchManager);
        s_lInitStatus = (LONG)SynchMgrStatusRunning;
        
    I_exit:
        if (NO_ERROR != palErr)
        {
            s_lInitStatus = (LONG)SynchMgrStatusError;
            if (NULL != pSynchManager)
            {
                pSynchManager->ShutdownProcessPipe();
            }
            s_pObjSynchMgr = NULL;
            g_pSynchronizationManager = NULL;
            InternalDelete(pSynchManager);
        }

        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::StartWorker

    Starts the Synchronization Manager's Worker Thread.
    Private method, it is called only by CPalSynchMgrController.
    --*/
    PAL_ERROR CPalSynchronizationManager::StartWorker(
        CPalThread * pthrCurrent)
    {
        PAL_ERROR palErr = NO_ERROR;
        CPalSynchronizationManager * pSynchManager = GetInstance();
        HANDLE hWorkerThread = NULL;

        if ((NULL == pSynchManager) || ((LONG)SynchMgrStatusRunning != s_lInitStatus))
        {
            ERROR("Trying to to create worker thread in invalid state\n");
            palErr = ERROR_INTERNAL_ERROR;
            goto SW_exit;
        }
        
        palErr = InternalCreateThread(pthrCurrent,
                                      NULL,
                                      0,
                                      &WorkerThread,
                                      (PVOID)pSynchManager,
                                      0,
                                      PalWorkerThread,
                                      &pSynchManager->m_dwWorkerThreadTid,
                                      &hWorkerThread);

        if (NO_ERROR != palErr)
        {
            ERROR("Unable to create worker thread\n");
            goto SW_exit;
        }
        
        palErr = InternalGetThreadDataFromHandle(pthrCurrent,
                                                 hWorkerThread,
                                                 0,
                                                 &pSynchManager->m_pthrWorker,
                                                 &pSynchManager->m_pipoThread);
        if (NO_ERROR != palErr)
        {
            ERROR("Unable to get worker thread data\n");
            goto SW_exit;
        }       
                
    SW_exit:
        if (NULL != hWorkerThread)
        {
            CloseHandle(hWorkerThread);
        }
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::PrepareForShutdown

    This method performs the part of Synchronization Manager's shutdown that 
    needs to be carried out when core PAL subsystems are still active.
    Private method, it is called only by CPalSynchMgrController.
    --*/
    PAL_ERROR CPalSynchronizationManager::PrepareForShutdown()
    {        
        PAL_ERROR palErr = NO_ERROR;
        LONG lInit;
        CPalSynchronizationManager * pSynchManager = GetInstance();
        CPalThread * pthrCurrent = InternalGetCurrentThread();
        int iRet;
        ThreadNativeWaitData * ptnwdWorkerThreadNativeData;
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        struct timespec tsAbsTmo = { 0, 0 };
#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        DWORD dwOldTime = GetTickCount();
        DWORD dwTmo;
        int iPollTmo;
        int iEagains= 0;
        bool fAgain;
        struct pollfd pllfd;
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        lInit = InterlockedCompareExchange(&s_lInitStatus, 
            (LONG)SynchMgrStatusShuttingDown, (LONG)SynchMgrStatusRunning);

        if ((LONG)SynchMgrStatusRunning != lInit)
        {
            ASSERT("Unexpected initialization status found "
                   "in PrepareForShutdown [expected=%d current=%d]\n", 
                   SynchMgrStatusRunning, lInit);
            // We intentionally not set s_lInitStatus to SynchMgrStatusError
            // cause this could interfere with a previous thread already 
            // executing shutdown
            palErr = ERROR_INTERNAL_ERROR;
            goto PFS_exit;
        }

        // Discard process monitoring for process waits
        pSynchManager->DiscardMonitoredProcesses(pthrCurrent);

        if (NULL == pSynchManager->m_pipoThread)
        {
            // If m_pipoThread is NULL here, that means that StartWorker has
            // never been called. That may happen if PAL_Initialize fails
            // sometime after having called CreatePalSynchronizationManager,
            // but before calling StartWorker. Nothing else to do here.
            goto PFS_exit;
        }

        palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdShutdown);
        if (NO_ERROR != palErr)
        {
            ERROR("Failed stopping worker thread [palErr=%u]\n", palErr);
            s_lInitStatus = SynchMgrStatusError;
            goto PFS_exit;
        }

        ptnwdWorkerThreadNativeData = 
            &pSynchManager->m_pthrWorker->synchronizationInfo.m_tnwdNativeData;

#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        palErr = GetAbsoluteTimeout(WorkerThreadTerminationTimeout, &tsAbsTmo);
        if (NO_ERROR != palErr)
        {
            ERROR("Failed to convert timeout to absolute timeout\n");
            s_lInitStatus = SynchMgrStatusError;
            goto PFS_exit;
        }

        // Using the worker thread's predicate/condition/mutex
        // to wait for worker thread to be done
        iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
        if (0 != iRet)
        {
            // pthread calls might fail if the shutdown is called
            // from a signal handler. In this case just don't wait
            // for the worker thread
            ERROR("Cannot lock mutex [err=%d]\n", iRet);
            palErr = ERROR_INTERNAL_ERROR;
            s_lInitStatus = SynchMgrStatusError;
            goto PFS_exit;
        }

        while (FALSE == ptnwdWorkerThreadNativeData->iPred) 
        {
            iRet = pthread_cond_timedwait(&ptnwdWorkerThreadNativeData->cond,
                                          &ptnwdWorkerThreadNativeData->mutex,
                                          &tsAbsTmo);
            if (0 != iRet)
            {
                if (ETIMEDOUT == iRet)
                {
                    WARN("Timed out waiting for worker thread to exit "
                         "(tmo=%u ms)\n", WorkerThreadTerminationTimeout);
                }
                else
                {
                    ERROR("pthread_cond_timedwait returned %d [errno=%d (%s)]\n",
                          iRet, errno, strerror(errno));
                }
                break;
            }
        }
        if (0 == iRet)
        {
            ptnwdWorkerThreadNativeData->iPred = FALSE;
        }
        iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);
        if (0 != iRet)
        {
            ERROR("Cannot unlock mutex [err=%d]\n", iRet);
            palErr = ERROR_INTERNAL_ERROR;
            s_lInitStatus = SynchMgrStatusError;
            goto PFS_exit;
        }

#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        dwTmo = min(WorkerThreadTerminationTimeout, INT_MAX);

        do
        {            
            fAgain = false;

            pllfd.fd = ptnwdWorkerThreadNativeData->iPipeRd;
            pllfd.events = POLLIN;
            pllfd.revents = 0;
            iPollTmo = dwTmo;        

            iRet = poll(&pllfd, 1, iPollTmo);

            switch(iRet)
            {
                case 0:
                    // Timeout
                    WARN("Timed out waiting for worker thread to exit "
                         "(tmo=%u ms)\n", WorkerThreadTerminationTimeout);
                    break;
                case 1:
                    // Signal
                    break;
                case -1:
                    if(EINTR == errno)
                    {
                        if (MaxWorkerConsecutiveEintrs >= ++iEagains)
                        {
                            fAgain = true;
                            UpdateTimeout(&dwOldTime, &dwTmo);
                        }
                        else
                        {
                            ERROR("Too many (%d) consecutive EAGAINs while polling "
                                  "pipe %d, waiting for worker thread to exit\n", 
                                  iEagains, ptnwdWorkerThreadNativeData->iPipeRd);
                            palErr = ERROR_INTERNAL_ERROR;
                        }
                    }
                    else
                    {
                        ERROR("Unexpected errno=%d (%s) while polling pipe %d\n", 
                              errno, strerror(errno), 
                              ptnwdWorkerThreadNativeData->iPipeRd);
                        palErr = ERROR_INTERNAL_ERROR;
                    }
                    break;
                default:
                    // Error
                    ERROR("Unexpected return code %d while polling pipe %d\n", 
                          iRet, ptnwdWorkerThreadNativeData->iPipeRd);
                    palErr = ERROR_INTERNAL_ERROR;
                    break;
            }
        } while (fAgain);
        
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    PFS_exit:
        if (NO_ERROR == palErr) 
        {
            if (NULL != pSynchManager->m_pipoThread)
            {
                pSynchManager->m_pipoThread->ReleaseReference(pthrCurrent);

                // After this release both m_pipoThread and m_pthrWorker 
                // are no longer valid
                pSynchManager->m_pipoThread = NULL;
                pSynchManager->m_pthrWorker = NULL;
            }

            // Ready for process shutdown
            s_lInitStatus = SynchMgrStatusReadyForProcessShutDown;
        }
            
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::WorkerThread

    Synchronization Manager's Worker Thread
    --*/
    DWORD PALAPI CPalSynchronizationManager::WorkerThread(LPVOID pArg)
    {
        PAL_ERROR palErr;
        bool fShuttingDown = false;        
        bool fWorkerIsDone = false;
        int iPollTimeout = INFTIM;
        SynchWorkerCmd swcCmd;
        ThreadWakeupReason twrWakeUpReason;
        SharedID shridMarshaledData;
        DWORD dwData;
        CPalSynchronizationManager * pSynchManager = 
            reinterpret_cast<CPalSynchronizationManager*>(pArg);
        CPalThread * pthrWorker = InternalGetCurrentThread();

        while (!fWorkerIsDone)
        {
            LONG lProcessCount;

            palErr = pSynchManager->ReadCmdFromProcessPipe(iPollTimeout, 
                                                           &swcCmd,
                                                           &shridMarshaledData,
                                                           &dwData);            
            if (NO_ERROR != palErr)
            {
                ERROR("Received error %x from ReadCmdFromProcessPipe()\n",
                      palErr);                
                continue;
            }
            switch (swcCmd)
            {
                case SynchWorkerCmdNop:                    
                    TRACE("Synch Worker: received SynchWorkerCmdNop\n");
                    if (fShuttingDown)
                    {
                        TRACE("Synch Worker: received a timeout when "
                              "fShuttingDown==true: worker is done, bailing "
                              "out from the loop\n");

                        // Whether WorkerThreadShuttingDownTimeout has elapsed
                        // or the last process with a descriptor opened for
                        // write on our process pipe, has just closed it, 
                        // causing an EOF on the read fd (that can happen only 
                        // at shutdown time since during normal run time we 
                        // hold a fd opened for write within this process).
                        // In both the case it is time to go for the worker 
                        // thread.
                        fWorkerIsDone = true;
                    }
                    else
                    {
                        lProcessCount = pSynchManager->DoMonitorProcesses(pthrWorker);
                        if (lProcessCount > 0)
                        {
                            iPollTimeout = WorkerThreadProcMonitoringTimeout;
                        }
                        else
                        {
                            iPollTimeout = INFTIM;
                        }
                    }
                    break;
                case SynchWorkerCmdRemoteSignal:
                {
                    // Note: this cannot be a wait all
                    WaitingThreadsListNode * pWLNode;
                    ThreadWaitInfo * ptwiWaitInfo;
                    DWORD dwObjIndex;
                    bool fSharedSynchLock = false;

                    // Lock
                    AcquireLocalSynchLock(pthrWorker);
                    AcquireSharedSynchLock(pthrWorker);
                    fSharedSynchLock = true;

                    pWLNode = SharedIDToTypePointer(WaitingThreadsListNode, 
                                                    shridMarshaledData);

                    _ASSERT_MSG(NULL != pWLNode, "Received bad Shared ID %p\n",
                                shridMarshaledData);
                    _ASSERT_MSG(gPID == pWLNode->dwProcessId,
                                "Remote signal apparently sent to the wrong "
                                "process [target pid=%u current pid=%u]\n",
                                pWLNode->dwProcessId, gPID);
                    _ASSERT_MSG(0 == (WTLN_FLAG_WAIT_ALL & pWLNode->dwFlags),
                                "Wait all with remote awakening delegated "
                                "through SynchWorkerCmdRemoteSignal rather than "
                                "SynchWorkerCmdDelegatedObjectSignaling\n");


                    // Get the object index
                    dwObjIndex = pWLNode->dwObjIndex;                    

                    // Get the WaitInfo                    
                    ptwiWaitInfo = pWLNode->ptwiWaitInfo;

                    // Initialize the WakeUpReason to WaitSucceeded
                    twrWakeUpReason = WaitSucceeded;                              
                                        
                    CSynchData * psdSynchData = 
                        SharedIDToTypePointer(CSynchData, 
                                              pWLNode->ptrOwnerObjSynchData.shrid);
                    
                    TRACE("Synch Worker: received REMOTE SIGNAL cmd "
                        "[WInfo=%p {Type=%u Domain=%u ObjCount=%d TgtThread=%x} "
                        "SynchData={shriId=%p p=%p} {SigCount=%d IsAbandoned=%d}\n",
                        ptwiWaitInfo, ptwiWaitInfo->wtWaitType, ptwiWaitInfo->wdWaitDomain, 
                        ptwiWaitInfo->lObjCount, ptwiWaitInfo->pthrOwner->GetThreadId(),
                        (VOID *)pWLNode->ptrOwnerObjSynchData.shrid, psdSynchData,
                        psdSynchData->GetSignalCount(), psdSynchData->IsAbandoned());

                    if (CObjectType::OwnershipTracked ==
                        psdSynchData->GetObjectType()->GetOwnershipSemantics())
                    {
                        // Abandoned status is not propagated through process
                        // pipe: need to get it from the object itself before
                        // resetting the data by acquiring the object ownership
                        if (psdSynchData->IsAbandoned())
                        {
                            twrWakeUpReason = MutexAbondoned;
                        }
                        
                        // Acquire ownership
                        palErr = psdSynchData->AssignOwnershipToThread(
                                    pthrWorker,
                                    ptwiWaitInfo->pthrOwner);
                        if (NO_ERROR != palErr)
                        {
                            ERROR("Synch Worker: AssignOwnershipToThread "
                                  "failed with error %u; ownership data on "
                                  "object with SynchData %p may be "
                                  "corrupted\n", palErr, psdSynchData);
                        }
                    }

                    // Unregister the wait
                    pSynchManager->UnRegisterWait(pthrWorker, 
                                                  ptwiWaitInfo, 
                                                  fSharedSynchLock);

                    // pWLNode is no longer valid after UnRegisterWait
                    pWLNode = NULL;

                    TRACE("Synch Worker: Waking up local thread %x "
                          "{WakeUpReason=%u ObjIndex=%u}\n", 
                          ptwiWaitInfo->pthrOwner->GetThreadId(), 
                          twrWakeUpReason, dwObjIndex);

                    // Wake up the target thread
                    palErr = WakeUpLocalThread(
                        pthrWorker,
                        ptwiWaitInfo->pthrOwner,
                        twrWakeUpReason,
                        dwObjIndex);
                    if (NO_ERROR != palErr)
                    {
                        ERROR("Synch Worker: Failed to wake up local thread "
                              "%#x while propagating remote signaling: "
                              "object signaling may be lost\n",
                              ptwiWaitInfo->pthrOwner->GetThreadId());
                    }

                    // Unlock
                    ReleaseSharedSynchLock(pthrWorker);
                    fSharedSynchLock = false;
                    ReleaseLocalSynchLock(pthrWorker);

                    break;
                }
                case SynchWorkerCmdDelegatedObjectSignaling:
                {
                    CSynchData * psdSynchData;

                    TRACE("Synch Worker: received "
                          "SynchWorkerCmdDelegatedObjectSignaling\n");

                    psdSynchData = SharedIDToTypePointer(CSynchData, 
                                                       shridMarshaledData);                    

                    _ASSERT_MSG(NULL != psdSynchData, "Received bad Shared ID %p\n",
                                shridMarshaledData);
                    _ASSERT_MSG(0 < dwData && (DWORD)INT_MAX > dwData, 
                                "Received remote signaling with invalid signal "
                                "count\n");                    
                    
                    // Lock
                    AcquireLocalSynchLock(pthrWorker);
                    AcquireSharedSynchLock(pthrWorker);  
                    
                    TRACE("Synch Worker: received DELEGATED OBJECT SIGNALING "
                        "cmd [SynchData={shriId=%p p=%p} SigCount=%u] [Current obj SigCount=%d "
                        "IsAbandoned=%d]\n", (VOID *)shridMarshaledData, 
                        psdSynchData, dwData, psdSynchData->GetSignalCount(), 
                        psdSynchData->IsAbandoned());

                    psdSynchData->Signal(pthrWorker, 
                                       psdSynchData->GetSignalCount() + dwData, 
                                       true);

                    // Current SynchData has been AddRef'd by remote process in
                    // order to be marshaled to the current one, therefore at 
                    // this point we need to release it
                    psdSynchData->Release(pthrWorker);

                    // Unlock
                    ReleaseSharedSynchLock(pthrWorker);
                    ReleaseLocalSynchLock(pthrWorker);

                    break;
                }
                case SynchWorkerCmdShutdown:
                    TRACE("Synch Worker: received SynchWorkerCmdShutdown\n");

                    // Shutdown the process pipe: this will cause the process 
                    // pipe to be unlinked and its write-only file descriptor 
                    // to be closed, so that when the last fd opened for write
                    // on the fifo (from another process) will be closed, we 
                    // will receive an EOF on the read end (i.e. poll in 
                    // ReadBytesFromProcessPipe will return 1 with no data to 
                    // be read). That will allow the worker thread to process 
                    // possible commands already successfully written to the 
                    // pipe by some other process, before shutting down.
                    pSynchManager->ShutdownProcessPipe();

                    // Shutting down: this will cause the worker thread to
                    // fetch residual cmds from the process pipe until an
                    // EOF is converted to a SynchWorkerCmdNop or the 
                    // WorkerThreadShuttingDownTimeout has elapsed without
                    // receiving any cmd.
                    fShuttingDown = true;                    

                    // Set the timeout to WorkerThreadShuttingDownTimeout
                    iPollTimeout = WorkerThreadShuttingDownTimeout;
                    break;
                default:
                    ASSERT("Synch Worker: Unknown worker cmd [swcWorkerCmd=%d]\n",
                           swcCmd);
                    break;
            }
        }

        int iRet;
        ThreadNativeWaitData * ptnwdWorkerThreadNativeData = 
            &pthrWorker->synchronizationInfo.m_tnwdNativeData;

#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        // Using the worker thread's predicate/condition/mutex
        // (that normally are never used) to signal the shutting 
        // down thread that the worker thread is done
        iRet = pthread_mutex_lock(&ptnwdWorkerThreadNativeData->mutex);
        _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);

        ptnwdWorkerThreadNativeData->iPred = TRUE;

        iRet = pthread_cond_signal(&ptnwdWorkerThreadNativeData->cond);
        if (0 != iRet)
        {
            ERROR ("pthread_cond_signal returned %d [errno=%d (%s)]\n", 
                   iRet, errno, strerror(errno));
        }            

        iRet = pthread_mutex_unlock(&ptnwdWorkerThreadNativeData->mutex);        
        _ASSERT_MSG(0 == iRet, "Cannot lock mutex [err=%d]\n", iRet);

#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        // Using the worker thread's blocking pipe 
        // (that normally is never used) to signal the shutting 
        // down thread that the worker thread is done
        int iEagains = 0;
        char cCode = (char)SynchWorkerCmdNop;

        do
        {
            iRet = write(ptnwdWorkerThreadNativeData->iPipeWr, 
                         (void *)&cCode, 
                         sizeof(cCode));
        } while (-1 == iRet && 
                 EAGAIN == errno && 
                 MaxConsecutiveEagains >= ++iEagains && 
                 0 == sched_yield());

        _ASSERTE(sizeof(cCode) == iRet);

#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        // Sleep forever
        ThreadPrepareForShutdown();

        return 0;
    }

    /*++
    Method:
      CPalSynchronizationManager::ReadCmdFromProcessPipe

    Reads a worker thread cmd from the process pipe. If there is no data 
    to be read on the pipe, it blocks until there is data available or the 
    timeout expires.
    --*/
    PAL_ERROR CPalSynchronizationManager::ReadCmdFromProcessPipe(
        int iPollTimeout, 
        SynchWorkerCmd * pswcWorkerCmd,
        SharedID * pshridMarshaledData,
        DWORD * pdwData)
    {
        PAL_ERROR palErr = NO_ERROR;
        int iRet;
        BYTE byVal;
        SynchWorkerCmd swcWorkerCmd = SynchWorkerCmdNop;

        _ASSERTE(NULL != pswcWorkerCmd);
        _ASSERTE(NULL != pshridMarshaledData);
        _ASSERTE(NULL != pdwData);

        iRet = ReadBytesFromProcessPipe(iPollTimeout, &byVal, sizeof(BYTE));

        if (0 > iRet)
        {
            ERROR("Failed polling the process pipe [ret=%d errno=%d (%s)]\n",
                  iRet, errno, strerror(errno));
            palErr = ERROR_INTERNAL_ERROR;
            goto RCFPP_exit;
        }

        if (iRet != 0)
        {
            _ASSERT_MSG(sizeof(BYTE) == iRet,
                        "Got %d bytes from process pipe while expecting for %d\n",
                        iRet, sizeof(BYTE));

            swcWorkerCmd = (SynchWorkerCmd)byVal;

            if(SynchWorkerCmdLast <= swcWorkerCmd)
            {
                ERROR("Got unknown worker command code %d from the process "
                       "pipe!\n", swcWorkerCmd);
                palErr = ERROR_INTERNAL_ERROR;
                goto RCFPP_exit;
            }

            _ASSERT_MSG(SynchWorkerCmdNop == swcWorkerCmd || 
                        SynchWorkerCmdRemoteSignal == swcWorkerCmd || 
                        SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd || 
                        SynchWorkerCmdShutdown == swcWorkerCmd,
                        "Unknown WrkrCmd=%u\n", swcWorkerCmd);

            TRACE("Got cmd %u from process pipe\n", swcWorkerCmd);
        }

        if (SynchWorkerCmdRemoteSignal == swcWorkerCmd || 
            SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
        {
            SharedID shridMarshaledId = NULLSharedID;
            
            TRACE("Received %s cmd\n",
                  (swcWorkerCmd == SynchWorkerCmdRemoteSignal) ?
                  "REMOTE SIGNAL" : "DELEGATED OBJECT SIGNALING" );
            
            iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout, 
                                            (BYTE *)&shridMarshaledId, 
                                            sizeof(shridMarshaledId));
            if (sizeof(shridMarshaledId) != iRet)
            {
                ERROR("Unable to read marshaled Shared ID from the "
                      "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
                      m_iProcessPipeRead, iRet, errno, strerror(errno));
                palErr = ERROR_INTERNAL_ERROR;
                goto RCFPP_exit;
            }

            TRACE("Received marshaled shrid=%p\n", (VOID *)shridMarshaledId);
                
            *pshridMarshaledData = shridMarshaledId;
        }

        if (SynchWorkerCmdDelegatedObjectSignaling == swcWorkerCmd)
        {
            DWORD dwData;

            iRet = ReadBytesFromProcessPipe(WorkerCmdCompletionTimeout, 
                                            (BYTE *)&dwData, 
                                            sizeof(dwData));
            if (sizeof(dwData) != iRet)
            {
                ERROR("Unable to read signal count from the "
                      "process pipe [pipe=%d ret=%d errno=%d (%s)]\n",
                      m_iProcessPipeRead, iRet, errno, strerror(errno));
                palErr = ERROR_INTERNAL_ERROR;
                goto RCFPP_exit;
            }

            TRACE("Received signal count %u\n", dwData);            

            *pdwData = dwData;
        }

    RCFPP_exit:
        if (NO_ERROR == palErr)
        {
            *pswcWorkerCmd = swcWorkerCmd;
        }
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::ReadBytesFromProcessPipe

    Reads the specified amount ob bytes from the process pipe. If there is 
    no data to be read on the pipe, it blocks until there is data available 
    or the timeout expires.
    --*/
    int CPalSynchronizationManager::ReadBytesFromProcessPipe(
        int iTimeout,
        BYTE * pRecvBuf,
        LONG iBytes)
    {
#if !HAVE_KQUEUE
        struct pollfd Poll;
#endif // !HAVE_KQUEUE
        int iRet = -1;
        int iConsecutiveEintrs = 0;
        LONG iBytesRead = 0;
        BYTE * pPos = pRecvBuf;
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
        struct kevent keChanges;
        struct timespec ts, *pts;
        int iNChanges;
#endif // HAVE_KQUEUE

        _ASSERTE(0 <= iBytes);

        do 
        {
            while (TRUE)
            {
                int iErrno = 0;
#if HAVE_KQUEUE
#if HAVE_BROKEN_FIFO_KEVENT
#if HAVE_BROKEN_FIFO_SELECT
#error Found no way to wait on a FIFO.
#endif

                timeval *ptv;
                timeval tv;

                if (INFTIM == iTimeout)
                {
                    ptv = NULL;
                }
                else
                {
                    tv.tv_usec = (iTimeout % tccSecondsToMillieSeconds) * 
                        tccMillieSecondsToMicroSeconds;
                    tv.tv_sec = iTimeout / tccSecondsToMillieSeconds;
                    ptv = &tv;
                }

                fd_set readfds;
                FD_ZERO(&readfds);
                FD_SET(m_iProcessPipeRead, &readfds);
                iRet = select(m_iProcessPipeRead + 1, &readfds, NULL, NULL, ptv);

#else // HAVE_BROKEN_FIFO_KEVENT

                // Note: FreeBSD needs to use kqueue/kevent support here, since on this
                // platform the EOF notification on FIFOs is not surfaced through poll, 
                // and process pipe shutdown relies on this feature.
                // If a thread is polling a FIFO or a pipe for POLLIN, when the last 
                // write descriptor for that pipe is closed, poll() is supposed to 
                // return with a POLLIN event but no data to be read on the FIFO/pipe,
                // which means EOF.
                // On FreeBSD such feature works for pipes but it doesn't for FIFOs. 
                // Using kevent the EOF is instead surfaced correctly.
                
                if (iBytes > m_keProcessPipeEvent.data)
                {
                    if (INFTIM == iTimeout)
                    {
                        pts = NULL;
                    }
                    else
                    {
                        ts.tv_nsec = (iTimeout % tccSecondsToMillieSeconds) * 
                            tccMillieSecondsToNanoSeconds;
                        ts.tv_sec = iTimeout / tccSecondsToMillieSeconds;
                        pts = &ts;
                    }

                    if (0 != (EV_EOF & m_keProcessPipeEvent.flags))
                    {
                        TRACE("Refreshing kevent settings\n");
                        EV_SET(&keChanges, m_iProcessPipeRead, EVFILT_READ, 
                               EV_ADD | EV_CLEAR, 0, 0, 0);
                        iNChanges = 1;
                    }
                    else
                    {
                        iNChanges = 0;
                    }

                    iRet = kevent(m_iKQueue, &keChanges, iNChanges, 
                                  &m_keProcessPipeEvent, 1, pts);

                    if (0 < iRet)
                    {
                        _ASSERTE(1 == iRet);                       
                        _ASSERTE(EVFILT_READ == m_keProcessPipeEvent.filter);

                        if (EV_ERROR & m_keProcessPipeEvent.flags)
                        {
                            ERROR("EV_ERROR from kevent [ident=%d filter=%d flags=%x]\n", m_keProcessPipeEvent.ident, m_keProcessPipeEvent.filter, m_keProcessPipeEvent.flags);
                            iRet = -1;
                            iErrno = m_keProcessPipeEvent.data;
                            m_keProcessPipeEvent.data = 0;
                        }                        
                    }
                    else if (0 > iRet)
                    {
                        iErrno = errno;
                    }

                    TRACE("Woken up from kevent() with ret=%d flags=%#x data=%d "
                          "[iTimeout=%d]\n", iRet, m_keProcessPipeEvent.flags, 
                          m_keProcessPipeEvent.data, iTimeout);
                }
                else 
                {
                    // There is enough data already available in the buffer,
                    // just use that.
                    iRet = 1;
                }
                
#endif // HAVE_BROKEN_FIFO_KEVENT
#else // HAVE_KQUEUE

                Poll.fd = m_iProcessPipeRead;
                Poll.events = POLLIN;
                Poll.revents = 0;

                iRet = poll(&Poll, 1, iTimeout);            

                TRACE("Woken up from poll() with ret=%d [iTimeout=%d]\n", 
                       iRet, iTimeout);

                if (1 == iRet &&
                    ((POLLERR | POLLHUP | POLLNVAL) & Poll.revents))
                {
                    // During PAL shutdown the pipe gets closed and Poll.revents is set to POLLHUP
                    // (note: no other flags are set). We will also receive an EOF on from the read call.
                    // Please see the comment for SynchWorkerCmdShutdown in CPalSynchronizationManager::WorkerThread.
                    if (!PALIsShuttingDown() || (Poll.revents != POLLHUP))
                    {
                        ERROR("Unexpected revents=%x while polling pipe %d\n",
                            Poll.revents, Poll.fd);
                        iErrno = EINVAL;
                        iRet = -1;
                    }
                }
                else if (0 > iRet)
                {
                    iErrno = errno;
                }

#endif // HAVE_KQUEUE

                if (0 == iRet || 1 == iRet)
                {
                    // 0 == wait timed out
                    // 1 == FIFO has data available
                    break;
                }
                else
                {
                    if (1 < iRet)
                    {
                        // Unexpected iRet > 1
                        ASSERT("Unexpected return code %d from blocking poll/kevent call\n", 
                                iRet);
                        goto RBFPP_exit;
                    }
                        
                    if (EINTR != iErrno)
                    {
                        // Unexpected error
                        ASSERT("Unexpected error from blocking poll/kevent call: %d (%s)\n", 
                               iErrno, strerror(iErrno));
                        goto RBFPP_exit;
                    }

                    iConsecutiveEintrs++;
                    TRACE("poll() failed with EINTR; re-polling\n");

                    if (iConsecutiveEintrs >= MaxWorkerConsecutiveEintrs)
                    {
                        if (iTimeout != INFTIM)
                        {                    
                            WARN("Receiving too many EINTRs; converting one of them "
                                 "to a timeout");
                            iRet = 0;
                            break;
                        }
                        else if (0 == (iConsecutiveEintrs % MaxWorkerConsecutiveEintrs))
                        {
                            WARN("Receiving too many EINTRs [%d so far]",
                                 iConsecutiveEintrs);
                        }
                    }
                }
            }

            if (0 == iRet)
            {
                // Time out
                break;
            }
            else    
            {
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
                if (0 != (EV_EOF & m_keProcessPipeEvent.flags) && 0 == m_keProcessPipeEvent.data)
                {
                    // EOF
                    TRACE("Received an EOF on process pipe via kevent\n");
                    goto RBFPP_exit;
                }
#endif // HAVE_KQUEUE

                iRet = read(m_iProcessPipeRead, pPos, iBytes - iBytesRead);

                if (0 == iRet)
                {
                    // Poll returned 1 and read returned zero: this is an EOF, 
                    // i.e. no other process has the pipe still open for write
                    TRACE("Received an EOF on process pipe via poll\n");                   
                    goto RBFPP_exit;
                }                    
                else if (0 > iRet)            
                {
                    ERROR("Unable to read %d bytes from the the process pipe "
                          "[pipe=%d ret=%d errno=%d (%s)]\n", iBytes - iBytesRead,
                          m_iProcessPipeRead, iRet, errno, strerror(errno));
                    goto RBFPP_exit;
                }

                TRACE("Read %d bytes from process pipe\n", iRet);

                iBytesRead += iRet;
                pPos += iRet;

#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
                // Update available data count
                m_keProcessPipeEvent.data -= iRet;
                _ASSERTE(0 <= m_keProcessPipeEvent.data);
#endif // HAVE_KQUEUE
            }    
        } while(iBytesRead < iBytes);

    RBFPP_exit:
        return (iRet < 0) ? iRet : iBytesRead;
    }

#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CPalSynchronizationManager::WakeUpLocalThread

    Wakes up a local thead currently sleeping for a wait or a sleep
    --*/
    PAL_ERROR CPalSynchronizationManager::WakeUpLocalThread(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget,
        ThreadWakeupReason twrWakeupReason,
        DWORD dwObjectIndex)
    {
        PAL_ERROR palErr = NO_ERROR;
        ThreadNativeWaitData * ptnwdNativeWaitData = 
            pthrTarget->synchronizationInfo.GetNativeData();

        TRACE("Waking up a local thread [WakeUpReason=%u ObjectIndex=%u "
              "ptnwdNativeWaitData=%p]\n", twrWakeupReason, dwObjectIndex,
              ptnwdNativeWaitData);

        // Set wakeup reason and signaled object index
        ptnwdNativeWaitData->twrWakeupReason = twrWakeupReason;
        ptnwdNativeWaitData->dwObjectIndex   = dwObjectIndex;

#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        if (0 < GetLocalSynchLockCount(pthrCurrent))
        {
            // Defer the actual thread signaling to right after 
            // releasing the synch lock(s), so that signaling 
            // can happen from a thread-suspension safe area
            palErr = DeferThreadConditionSignaling(pthrCurrent, pthrTarget);
        }
        else
        {
            // Signal the target thread's condition
            palErr = SignalThreadCondition(ptnwdNativeWaitData);
        }        
#else // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        // Signal the target thread's condition
        palErr = SignalThreadCondition(ptnwdNativeWaitData);
#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING

        return palErr;
    }

#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
    /*++
    Method:
      CPalSynchronizationManager::DeferThreadConditionSignaling

    Defers thread signaling to the final release of synchronization
    lock(s), so that condition signaling can happen when the signaling
    thread is marked as safe for thread suspension.
    --*/
    PAL_ERROR CPalSynchronizationManager::DeferThreadConditionSignaling(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget)
    {
        PAL_ERROR palErr = NO_ERROR;
        LONG lCount = pthrCurrent->synchronizationInfo.m_lPendingSignalingCount;

        _ASSERTE(pthrTarget != pthrCurrent);
        
        if (CThreadSynchronizationInfo::PendingSignalingsArraySize > lCount)
        {
            // If there is available room, add the target thread object to 
            // the array of pending thread signalings.
            pthrCurrent->synchronizationInfo.m_rgpthrPendingSignalings[lCount] = pthrTarget;
        }
        else
        {
            // If the array is full, add the target thread object at the end 
            // of the overflow list
            DeferredSignalingListNode * pdsln = 
                InternalNew<DeferredSignalingListNode>();

            if (pdsln)
            {
                pdsln->pthrTarget = pthrTarget;

                // Add the note to the end of the list. 
                // Note: no need to synchronize the access to this list since
                // it is meant to be accessed only by the owner thread.
                InsertTailList(&pthrCurrent->synchronizationInfo.m_lePendingSignalingsOverflowList, 
                               &pdsln->Link);
            }
            else
            {
                palErr = ERROR_NOT_ENOUGH_MEMORY;
            }
        }

        if (NO_ERROR == palErr)
        {
            // Increment the count of pending signalings
            pthrCurrent->synchronizationInfo.m_lPendingSignalingCount += 1;

            // Add a reference to the target CPalThread object; this is 
            // needed since deferring signaling after releasing the synch 
            // locks implies accessing the target thread object without 
            // holding the local synch lock. In rare circumstances, the 
            // target thread may have already exited while deferred signaling 
            // takes place, therefore invalidating the thread object. The 
            // reference added here ensures that the thread object is still 
            // good, even if the target thread has exited.
            pthrTarget->AddThreadReference();
        }
        return palErr;
    }
#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING

    /*++
    Method:
      CPalSynchronizationManager::SignalThreadCondition

    Performs the actual condition signaling in to wake up the target thread
    --*/
    PAL_ERROR CPalSynchronizationManager::SignalThreadCondition(
        ThreadNativeWaitData * ptnwdNativeWaitData)
    {
        PAL_ERROR palErr = NO_ERROR;
        int iRet;
        
        // Lock the mutex
        iRet = pthread_mutex_lock(&ptnwdNativeWaitData->mutex);
        if (0 != iRet)
        {
            ERROR("Cannot lock mutex [err=%d]\n", iRet);
            palErr = ERROR_INTERNAL_ERROR;
            goto WUT_exit;
        }

        // Set the predicate
        ptnwdNativeWaitData->iPred = TRUE;

        // Signal the condition
        iRet = pthread_cond_signal(&ptnwdNativeWaitData->cond);
        if (0 != iRet)
        {
            ERROR("Failed to signal condition: pthread_cond_signal "
                  "returned %d [errno=%d (%s)]\n", iRet, errno, 
                  strerror(errno));
            palErr = ERROR_INTERNAL_ERROR;
            // Continue in order to unlock the mutex anyway
        }            

        // Unlock the mutex
        iRet = pthread_mutex_unlock(&ptnwdNativeWaitData->mutex);            
        if (0 != iRet)
        {
            ERROR("Cannot unlock mutex [err=%d]\n", iRet);
            palErr = ERROR_INTERNAL_ERROR;
            goto WUT_exit;
        }

    WUT_exit:
        return palErr;
    }

#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CPalSynchronizationManager::WakeUpLocalThread

    Wakes up a local thead currently sleeping for a wait or a sleep
    --*/
    PAL_ERROR CPalSynchronizationManager::WakeUpLocalThread(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget,
        ThreadWakeupReason twrWakeupReason,
        DWORD dwObjectIndex)
    {
        PAL_ERROR palErr = NO_ERROR;
        int iRet = 0;
        int iEagains = 0;
        bool fAgain;
        char cCode = (char)twrWakeupReason;
        ThreadNativeWaitData * ptnwdNativeWaitData = 
            pthrTarget->synchronizationInfo.GetNativeData();

        TRACE("Waking up a local thread [WakeUpReason=%u ObjectIndex=%u "
              "ptnwdNativeWaitData=%p]\n", twrWakeupReason, dwObjectIndex,
              ptnwdNativeWaitData);

        // Set wakeup reason and signaled object index
        ptnwdNativeWaitData->twrWakeupReason = twrWakeupReason;
        ptnwdNativeWaitData->dwObjectIndex   = dwObjectIndex;

        _ASSERTE(-1 != ptnwdNativeWaitData->iPipeWr);

        do 
        {
            fAgain = false;
            
            iRet = write(ptnwdNativeWaitData->iPipeWr, 
                         (void *)&cCode, 
                         sizeof(cCode));
            switch (iRet)
            {
                case (int)sizeof(cCode):
                    // write succeeded
                    break;
                case -1:
                    // error
                    switch (errno)
                    {
                        case EINTR:
                        case EAGAIN:
                            if (MaxConsecutiveEagains >= ++iEagains)
                            {
                                fAgain = true;
                                sched_yield();
                            }
                            else
                            {
                                ERROR("Too many consecutive EAGAINs/EINTRs (%d) while writing "
                                      "to pipe %d\n", iEagains, ptnwdNativeWaitData->iPipeWr);
                                palErr = ERROR_INTERNAL_ERROR;
                            }
                            break;
                        case EBADF:
                        case EFAULT:
                        case EINVAL:
                        case EPIPE:
                            palErr = ERROR_INVALID_DATA;
                            break;
                        default:
                            palErr = ERROR_INTERNAL_ERROR;
                            break;
                    }
                    break;
                default:
                    // unexpected error condition
                    palErr = ERROR_INTERNAL_ERROR;   
                    break;
            }       
        } while (fAgain);
        
    WUT_exit:
        return palErr;
    }
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CPalSynchronizationManager::ReadBytesFromProcessPipe

    Wakes up a remote thead currently sleeping for a wait or a sleep
    by sending the appropriate cmd to the remote process' worker
    thread, which will take care to convert this command into a
    WakeUpLocalThread in the remote process
    --*/
    PAL_ERROR CPalSynchronizationManager::WakeUpRemoteThread(
        SharedID shridWLNode)
    {
        const int MsgSize = sizeof(BYTE) + sizeof(SharedID);
        int i;
        PAL_ERROR palErr = NO_ERROR;
        BYTE rgSendBuf[MsgSize];
        BYTE * pbySrc, * pbyDst = rgSendBuf;
        WaitingThreadsListNode * pWLNode = 
            SharedIDToTypePointer(WaitingThreadsListNode, shridWLNode);
        
        _ASSERT_MSG(gPID != pWLNode->dwProcessId,
                    "WakeUpRemoteThread called on local thread\n");
        _ASSERT_MSG(NULLSharedID != shridWLNode, "NULL shared identifier\n");
        _ASSERT_MSG(NULL != pWLNode, 
                    "Bad shared wait list node identifier (%p)\n", 
                    (VOID*)shridWLNode);
        _ASSERT_MSG(MsgSize <= PIPE_BUF, 
                    "Message too long [MsgSize=%d PIPE_BUF=%d]\n",
                    MsgSize, (int)PIPE_BUF);

        TRACE("Waking up remote thread {pid=%x, tid=%x} by sending "
              "cmd=%u and shridWLNode=%p over process pipe\n",
              pWLNode->dwProcessId, pWLNode->dwThreadId, 
              SynchWorkerCmdRemoteSignal, (VOID *)shridWLNode);

        // Prepare the message
        // Cmd
        *pbyDst++ = (BYTE)(SynchWorkerCmdRemoteSignal & 0xFF);
        // WaitingThreadsListNode (not aligned, copy byte by byte)
        pbySrc = (BYTE *)&shridWLNode;
        for (i=0; i<(int)sizeof(SharedID); i++)
        {
            *pbyDst++ = *pbySrc++;
        }
        _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");

        // Send the message
        palErr = SendMsgToRemoteWorker(pWLNode->dwProcessId, rgSendBuf, 
                                       MsgSize);
        if (NO_ERROR != palErr)
        {
            ERROR("Failed sending message to remote worker in process %u\n",
                  pWLNode->dwProcessId);
            goto WUT_exit;
        }
        
    WUT_exit:
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::DelegateSignalingToRemoteProcess

    This method transfers an object signaling operation to a remote process, 
    where it will be performed by the worker thread. Such delegation takes 
    place when the currently processed thread (among those waiting on the 
    signald object) lives in a different process as the signaling thread, 
    and it is performing a wait all. In this case generally is not possible 
    to find out whether or not the wait all is satisfied, therefore the 
    signaling operation must be continued in the target process.
    --*/
    PAL_ERROR CPalSynchronizationManager::DelegateSignalingToRemoteProcess(
        CPalThread * pthrCurrent,
        DWORD dwTargetProcessId,
        SharedID shridSynchData)
    {
        const int MsgSize = sizeof(BYTE) + sizeof(SharedID) + sizeof(DWORD);
        int i;
        PAL_ERROR palErr = NO_ERROR;
        BYTE rgSendBuf[MsgSize];
        BYTE * pbySrc, * pbyDst = rgSendBuf;
        DWORD dwSigCount;
        CSynchData * psdSynchData = 
            SharedIDToTypePointer(CSynchData, shridSynchData);
        
        _ASSERT_MSG(gPID != dwTargetProcessId,
                    " called on local thread\n");
        _ASSERT_MSG(NULLSharedID != shridSynchData, "NULL shared identifier\n");
        _ASSERT_MSG(NULL != psdSynchData, 
                    "Bad shared SynchData identifier (%p)\n", 
                    (VOID*)shridSynchData);
        _ASSERT_MSG(MsgSize <= PIPE_BUF, 
                    "Message too long [MsgSize=%d PIPE_BUF=%d]\n",
                    MsgSize, (int)PIPE_BUF);

        TRACE("Transfering wait all signaling to remote process pid=%x "
              "by sending cmd=%u and shridSynchData=%p over process pipe\n",
              dwTargetProcessId, SynchWorkerCmdDelegatedObjectSignaling, 
              (VOID *)shridSynchData);

        dwSigCount = psdSynchData->GetSignalCount();

        // AddRef SynchData to be marshaled to remote process
        psdSynchData->AddRef();

        //
        // Prepare the message
        //
        
        // Cmd
        *pbyDst++ = (BYTE)(SynchWorkerCmdDelegatedObjectSignaling & 0xFF);

        // CSynchData (not aligned, copy byte by byte)
        pbySrc = (BYTE *)&shridSynchData;
        for (i=0; i<(int)sizeof(SharedID); i++)
        {
            *pbyDst++ = *pbySrc++;
        }

        // Signal Count (not aligned, copy byte by byte)
        pbySrc = (BYTE *)&dwSigCount;
        for (i=0; i<(int)sizeof(DWORD); i++)
        {
            *pbyDst++ = *pbySrc++;
        }

        _ASSERT_MSG(pbyDst <= rgSendBuf + MsgSize + 1, "Buffer overrun");

        // Send the message
        palErr = SendMsgToRemoteWorker(dwTargetProcessId, rgSendBuf, 
                                       MsgSize);
        if (NO_ERROR != palErr)
        {
            TRACE("Failed sending message to remote worker in process %u\n",
                  dwTargetProcessId);
            // Undo refcounting
            psdSynchData->Release(pthrCurrent);
            goto WUT_exit;
        }
        
    WUT_exit:
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::SendMsgToRemoteWorker

    Sends a message (command + data) to a remote process' worker thread.
    --*/
    PAL_ERROR CPalSynchronizationManager::SendMsgToRemoteWorker(
        DWORD dwProcessId,
        BYTE * pMsg,
        int iMsgSize)
    {
        ASSERT("There should never be a reason to send a message to a remote worker\n");
        return ERROR_INTERNAL_ERROR;
    }
    
    /*++
    Method:
      CPalSynchronizationManager::WakeUpLocalWorkerThread

    Wakes up the local worker thread by writing a 'nop' cmd to the
    process pipe.
    --*/
    PAL_ERROR CPalSynchronizationManager::WakeUpLocalWorkerThread(
        SynchWorkerCmd swcWorkerCmd)
    {
        PAL_ERROR palErr = NO_ERROR;
        ssize_t sszWritten;
        BYTE byCmd;
        int iRetryCount;

        _ASSERT_MSG((swcWorkerCmd & 0xFF) == swcWorkerCmd,
                    "Value too big for swcWorkerCmd\n");
        _ASSERT_MSG((SynchWorkerCmdNop == swcWorkerCmd) ||
                    (SynchWorkerCmdShutdown == swcWorkerCmd),
                    "WakeUpLocalWorkerThread supports only "
                    "SynchWorkerCmdNop and SynchWorkerCmdShutdown  "
                    "[received cmd=%d]\n", swcWorkerCmd);

        byCmd = (BYTE)(swcWorkerCmd & 0xFF);

        TRACE("Waking up Synch Worker Thread for %u [byCmd=%u]\n", 
                    swcWorkerCmd, (unsigned int)byCmd);

        // As long as we use pipes and we keep the message size 
        // within PIPE_BUF, there's no need to lock here, since the
        // write is guaranteed not to be interleaved with/into other
        // writes of PIPE_BUF bytes or less.
        _ASSERT_MSG(sizeof(BYTE) <= PIPE_BUF, "Message too long\n");
        
        iRetryCount = 0;
        do
        {
            sszWritten = write(m_iProcessPipeWrite, &byCmd, sizeof(BYTE));
        } while (-1 == sszWritten && 
                 EAGAIN == errno && 
                 ++iRetryCount < MaxConsecutiveEagains &&
                 0 == sched_yield());
        
        if (sszWritten != sizeof(BYTE))
        {
            ERROR("Unable to write the the process pipe to wakeup the "
                   "worker thread [errno=%d (%s)]\n", errno, strerror(errno));
            palErr = ERROR_INTERNAL_ERROR;
            goto WUWT_exit;
        }

    WUWT_exit:
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::GetThreadWaitInfo

    Returns a pointer to the WaitInfo structure for the passed CPalThread object
    --*/
    ThreadWaitInfo * CPalSynchronizationManager::GetThreadWaitInfo(
        CPalThread * pthrCurrent)
    {
        return &pthrCurrent->synchronizationInfo.m_twiWaitInfo;
    }

    /*++
    Method:
      CPalSynchronizationManager::UnRegisterWait

    Unregister the wait described by ptwiWaitInfo that in general involves
    a thread other than the current one (most of the times the deregistration
    is performed by the signaling thread)

    Note: this method must be called while holding the local process 
          synchronization lock.
    --*/
    void CPalSynchronizationManager::UnRegisterWait(
        CPalThread * pthrCurrent,
        ThreadWaitInfo * ptwiWaitInfo,
        bool fHaveSharedLock)
    {
        int i = 0;
        CSynchData * psdSynchData = NULL;
        bool fSharedSynchLock = false;

        if (!fHaveSharedLock && LocalWait != ptwiWaitInfo->wdWaitDomain)
        {
            AcquireSharedSynchLock(pthrCurrent);
            fSharedSynchLock = true;
        }   

        TRACE("Unregistering wait for thread=%u [ObjCount=%d WaitType=%u WaitDomain=%u]\n",
              ptwiWaitInfo->pthrOwner->GetThreadId(),
              ptwiWaitInfo->lObjCount, ptwiWaitInfo->wtWaitType,
              ptwiWaitInfo->wdWaitDomain);

        for (i=0; i < ptwiWaitInfo->lObjCount; i++)
        {
            WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];

            VALIDATEOBJECT(pwtlnItem);

            if (pwtlnItem->dwFlags & WTLN_FLAG_OWNER_OBJECT_IS_SHARED)
            {
                // Shared object
                WaitingThreadsListNode * pwtlnItemNext, * pwtlnItemPrev;
                
                psdSynchData = SharedIDToTypePointer(CSynchData, 
                    pwtlnItem->ptrOwnerObjSynchData.shrid);

                VALIDATEOBJECT(psdSynchData);

                pwtlnItemNext = SharedIDToTypePointer(WaitingThreadsListNode, 
                    pwtlnItem->ptrNext.shrid);
                pwtlnItemPrev = SharedIDToTypePointer(WaitingThreadsListNode, 
                    pwtlnItem->ptrPrev.shrid);
                if (pwtlnItemPrev)
                {
                    VALIDATEOBJECT(pwtlnItemPrev);
                    pwtlnItemPrev->ptrNext.shrid = pwtlnItem->ptrNext.shrid;
                }
                else
                {
                    psdSynchData->SetWTLHeadShrPtr(pwtlnItem->ptrNext.shrid);
                }
                if (pwtlnItemNext)
                {
                    VALIDATEOBJECT(pwtlnItemNext);
                    pwtlnItemNext->ptrPrev.shrid = pwtlnItem->ptrPrev.shrid;
                }
                else
                {
                    psdSynchData->SetWTLTailShrPtr(pwtlnItem->ptrPrev.shrid);
                }

                m_cacheSHRWTListNodes.Add(pthrCurrent, pwtlnItem->shridSHRThis);
            }
            else
            {    
                // Local object
                psdSynchData = pwtlnItem->ptrOwnerObjSynchData.ptr;

                VALIDATEOBJECT(psdSynchData);

                if (pwtlnItem->ptrPrev.ptr)
                {
                    VALIDATEOBJECT(pwtlnItem);
                    pwtlnItem->ptrPrev.ptr->ptrNext.ptr = pwtlnItem->ptrNext.ptr;
                }
                else
                {
                    psdSynchData->SetWTLHeadPtr(pwtlnItem->ptrNext.ptr);
                }
                if (pwtlnItem->ptrNext.ptr)
                {
                    VALIDATEOBJECT(pwtlnItem);
                    pwtlnItem->ptrNext.ptr->ptrPrev.ptr = pwtlnItem->ptrPrev.ptr;
                }
                else
                {
                    psdSynchData->SetWTLTailPtr(pwtlnItem->ptrPrev.ptr);
                }

                m_cacheWTListNodes.Add(pthrCurrent, pwtlnItem);                
            }

            // Release the node's refcount on the synch data, and decerement
            // waiting thread count
            psdSynchData->DecrementWaitingThreadCount();
            psdSynchData->Release(pthrCurrent);
        }

        // Reset wait data in ThreadWaitInfo structure: it is enough 
        // to reset lObjCount, lSharedObjCount and wdWaitDomain.
        ptwiWaitInfo->lObjCount       = 0;
        ptwiWaitInfo->lSharedObjCount = 0;
        ptwiWaitInfo->wdWaitDomain    = LocalWait;

        // Done
        if (fSharedSynchLock)
        {
            ReleaseSharedSynchLock(pthrCurrent);
        }
        return;
    }
    
    /*++
    Method:
      CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll

    Unsignals all the objects involved in a wait all, except the target 
    one (i.e. psdTgtObjectSynchData)

    Note: this method must be called while holding the synchronization locks 
          appropriate to all the objects involved in the wait-all. If any 
          of the objects is shared, the caller must own both local and 
          shared synch locks; if no shared object is involved in the wait, 
          only the local synch lock is needed.
    --*/
    void CPalSynchronizationManager::UnsignalRestOfLocalAwakeningWaitAll(
        CPalThread * pthrCurrent,
        CPalThread * pthrTarget,
        WaitingThreadsListNode * pwtlnNode,
        CSynchData * psdTgtObjectSynchData)
    {
        int iObjCount = 0;
        int i;
        PAL_ERROR palErr = NO_ERROR;
        CSynchData * psdSynchDataItem = NULL;
        ThreadWaitInfo * ptwiWaitInfo = NULL;

#ifdef _DEBUG
        bool bOriginatingNodeFound = false;
#endif

        VALIDATEOBJECT(psdTgtObjectSynchData);
        VALIDATEOBJECT(pwtlnNode);

        _ASSERT_MSG(0 != (WTLN_FLAG_WAIT_ALL & pwtlnNode->dwFlags),
                    "UnsignalRestOfLocalAwakeningWaitAll() called on a normal "
                    "(non wait all) wait");
            
        _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
                    "UnsignalRestOfLocalAwakeningWaitAll() called on a wait all "
                    "with remote awakening");

        ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;

        iObjCount = ptwiWaitInfo->lObjCount;
        for (i=0; i < iObjCount; i++)
        {
            WaitingThreadsListNode * pwtlnItem = ptwiWaitInfo->rgpWTLNodes[i];

            VALIDATEOBJECT(pwtlnItem);

            if (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnItem->dwFlags))
            {
                psdSynchDataItem = SharedIDToTypePointer(CSynchData, 
                    pwtlnItem->ptrOwnerObjSynchData.shrid);
            }
            else
            {
                psdSynchDataItem = pwtlnItem->ptrOwnerObjSynchData.ptr;
            }

            VALIDATEOBJECT(psdSynchDataItem);

            // Skip originating node
            if (psdTgtObjectSynchData == psdSynchDataItem)
            {
#ifdef _DEBUG                
                bOriginatingNodeFound = true;
#endif
                continue;
            }

            palErr = psdSynchDataItem->ReleaseWaiterWithoutBlocking(
                pthrCurrent, 
                pthrTarget);

            if (NO_ERROR != palErr)
            {
                ERROR("ReleaseWaiterWithoutBlocking failed on SynchData @ %p "
                      "[palErr = %u]\n", psdSynchDataItem, palErr);
            }
        }

        _ASSERT_MSG(bOriginatingNodeFound,
                    "Couldn't find originating node while unsignaling "
                    "rest of the wait all\n");
    }

    /*++
    Method:
      CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress

    Marks all the thread waiting list nodes involved in the the current wait-all
    for "delegated object signaling in progress", so that this wait cannot be
    involved in another delegated object signaling that may happen while the
    current object singaling is being tranfered to the target process (while 
    transfering it, synchronization locks are released in this process and later 
    grabbed again in the target process; in this time window another thread 
    could signal another object part of the same wait-all. In this case no
    signal delegation must take place.

    Note: this method must be called while holding the synchronization locks 
          appropriate to the target object described by pwtlnNode (i.e. the 
          local process synch lock if the target object is local, both local 
          and shared one if the object is shared).
    --*/
    void CPalSynchronizationManager::MarkWaitForDelegatedObjectSignalingInProgress(
        CPalThread * pthrCurrent,
        WaitingThreadsListNode * pwtlnNode)
    {
        int i, iTgtCount;
        ThreadWaitInfo * ptwiWaitInfo = NULL;
        bool fSharedSynchLock = false;
        bool fTargetObjectIsShared = 
            (0 != (WTLN_FLAG_OWNER_OBJECT_IS_SHARED & pwtlnNode->dwFlags));

        VALIDATEOBJECT(pwtlnNode);

        _ASSERT_MSG(gPID == pwtlnNode->dwProcessId,
                    "MarkWaitForDelegatedObjectSignalingInProgress() called "
                    "from the wrong process");

        ptwiWaitInfo = pwtlnNode->ptwiWaitInfo;

        if (!fSharedSynchLock && !fTargetObjectIsShared && 
            LocalWait != ptwiWaitInfo->wdWaitDomain)
        {
            AcquireSharedSynchLock(pthrCurrent);
            fSharedSynchLock = true;
        }   

        _ASSERT_MSG(MultipleObjectsWaitAll == ptwiWaitInfo->wtWaitType,
                    "MarkWaitForDelegatedObjectSignalingInProgress() called "
                    "on a normal (non wait-all) wait");

        // Unmark all node other than the target one
        iTgtCount = ptwiWaitInfo->lObjCount;
        for (i=0; i < iTgtCount; i++)
        {
            VALIDATEOBJECT(ptwiWaitInfo->rgpWTLNodes[i]);
            
            ptwiWaitInfo->rgpWTLNodes[i]->dwFlags &= 
                ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;
        }

        // Mark the target node
        pwtlnNode->dwFlags |= WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;

        // Done
        if (fSharedSynchLock)
        {
            ReleaseSharedSynchLock(pthrCurrent);
        }
        return;
    }

    /*++
    Method:
      CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress

    Resets the "delegated object signaling in progress" flags in all the 
    nodes of the thread waitin list for the target waitable objects (represented
    by its SynchData)

    Note: this method must be called while holding the appropriate 
          synchronization locks (the local process synch lock if the target 
          object is local, both local and shared one if the object is shared).
    --*/
    void CPalSynchronizationManager::UnmarkTWListForDelegatedObjectSignalingInProgress(
        CSynchData * pTgtObjectSynchData)
    {
        bool fSharedObject = (SharedObject == pTgtObjectSynchData->GetObjectDomain());
        WaitingThreadsListNode * pwtlnNode;

        VALIDATEOBJECT(pTgtObjectSynchData);

        pwtlnNode =  fSharedObject ?
            SharedIDToTypePointer(WaitingThreadsListNode, 
                pTgtObjectSynchData->GetWTLHeadShmPtr()) :
                pTgtObjectSynchData->GetWTLHeadPtr();
        while (pwtlnNode)
        {
            VALIDATEOBJECT(pwtlnNode);
            
            pwtlnNode->dwFlags &= ~WTLN_FLAG_DELEGATED_OBJECT_SIGNALING_IN_PROGRESS;

            pwtlnNode = fSharedObject ? 
                SharedIDToTypePointer(WaitingThreadsListNode, 
                    pwtlnNode->ptrNext.shrid) : 
                    pwtlnNode->ptrNext.ptr;
        }
    }

    /*++
    Method:
      CPalSynchronizationManager::RegisterProcessForMonitoring

    Registers the process object represented by the passed psdSynchData and 
    pProcLocalData. The worker thread will monitor the actual process and,
    upon process termination, it will set the exit code in pProcLocalData, 
    and it will signal the process object, by signaling its psdSynchData.
    --*/
    PAL_ERROR CPalSynchronizationManager::RegisterProcessForMonitoring(
        CPalThread * pthrCurrent,
        CSynchData *psdSynchData,
        CProcProcessLocalData * pProcLocalData)
    {
        PAL_ERROR palErr = NO_ERROR;
        MonitoredProcessesListNode * pmpln;
        bool fWakeUpWorker = false;
        bool fMonitoredProcessesLock = false;

        VALIDATEOBJECT(psdSynchData);

        InternalEnterCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        fMonitoredProcessesLock = true;

        pmpln = m_pmplnMonitoredProcesses;
        while (pmpln)
        {
            if (psdSynchData == pmpln->psdSynchData)
            {
                _ASSERT_MSG(pmpln->dwPid == pProcLocalData->dwProcessId,
                            "Invalid node in Monitored Processes List\n");
                break;
            }

            pmpln = pmpln->pNext;
        }

        if (pmpln)
        {
            pmpln->lRefCount++;
        }
        else
        {
            pmpln = InternalNew<MonitoredProcessesListNode>();
            if (NULL == pmpln)
            {
                ERROR("No memory to allocate MonitoredProcessesListNode structure\n");
                palErr = ERROR_NOT_ENOUGH_MEMORY; 
                goto RPFM_exit;
            }

            pmpln->lRefCount      = 1;
            pmpln->dwPid          = pProcLocalData->dwProcessId;
            pmpln->dwExitCode     = 0;
            pmpln->pProcLocalData = pProcLocalData;

            // Acquire SynchData and AddRef it
            pmpln->psdSynchData = psdSynchData;
            psdSynchData->AddRef();
            
            pmpln->pNext = m_pmplnMonitoredProcesses;
            m_pmplnMonitoredProcesses = pmpln;
            m_lMonitoredProcessesCount++;

            fWakeUpWorker = true;
        }
        // Unlock
        InternalLeaveCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        fMonitoredProcessesLock = false;

        if (fWakeUpWorker)
        {
            CPalSynchronizationManager * pSynchManager = GetInstance();

            palErr = pSynchManager->WakeUpLocalWorkerThread(SynchWorkerCmdNop);
            if (NO_ERROR != palErr)
            {
                ERROR("Failed waking up worker thread for process "
                      "monitoring registration [errno=%d {%s%}]\n", 
                      errno, strerror(errno));
                palErr = ERROR_INTERNAL_ERROR;
            } 
        }
        
    RPFM_exit:
        if (fMonitoredProcessesLock)
        {
            InternalLeaveCriticalSection(pthrCurrent, 
                                         &s_csMonitoredProcessesLock);
        }
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::UnRegisterProcessForMonitoring

    Unregisters a process object currently monitored by the worker thread
    (typically called if the wait timed out before the process exited, or 
    if the wait was a normal (i.e. non wait-all) wait that involved othter 
    objects, and another object has been signaled).
    --*/
    PAL_ERROR CPalSynchronizationManager::UnRegisterProcessForMonitoring(
        CPalThread * pthrCurrent,
        CSynchData *psdSynchData, 
        DWORD dwPid)
    {
        PAL_ERROR palErr = NO_ERROR;
        MonitoredProcessesListNode * pmpln, * pmplnPrev = NULL;

        VALIDATEOBJECT(psdSynchData);
            
        InternalEnterCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);

        pmpln = m_pmplnMonitoredProcesses;
        while (pmpln)
        {
            if (psdSynchData == pmpln->psdSynchData)
            {
                _ASSERT_MSG(dwPid == pmpln->dwPid,
                            "Invalid node in Monitored Processes List\n");
                break;
            }

            pmplnPrev = pmpln;
            pmpln = pmpln->pNext;
        }

        if (pmpln)
        {
            if (0 == --pmpln->lRefCount)
            {
                if (NULL != pmplnPrev)
                {
                    pmplnPrev->pNext = pmpln->pNext;
                }
                else
                {
                    m_pmplnMonitoredProcesses = pmpln->pNext;
                }

                m_lMonitoredProcessesCount--;
                pmpln->psdSynchData->Release(pthrCurrent);                
                InternalDelete(pmpln);
            }
        }
        else
        {
            palErr = ERROR_NOT_FOUND;
        }

        InternalLeaveCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::ThreadPrepareForShutdown

    Used to hijack a thread execution from known spots within the 
    Synchronization Manager, in case a PAL shutdown is initiaded or the 
    thread is being terminated by another thread.
    --*/
    void CPalSynchronizationManager::ThreadPrepareForShutdown()
    {
        TRACE("The Synchronixation Manager hijacked the current thread "
              "for process shutdown or thread termination\n");
        while (true)
        {
            poll(NULL, 0, INFTIM);
            sched_yield();
        }
        ASSERT("This code should never be executed\n");
    }
    
    /*++
    Method:
      CPalSynchronizationManager::DoMonitorProcesses

    This method is called by the worker thread to execute one step of
    monitoring for all the process currently registered for monitoring
    --*/
    LONG CPalSynchronizationManager::DoMonitorProcesses(
        CPalThread * pthrCurrent)
    {
        MonitoredProcessesListNode * pNode, * pPrev = NULL, * pNext;
        LONG lInitialNodeCount;
        LONG lRemovingCount = 0;        
        bool fLocalSynchLock = false;
        bool fSharedSynchLock = false;
        bool fMonitoredProcessesLock = false;

        // Note: we first need to grab the monitored processes lock to walk 
        //       the list of monitored processes, and then, if there is any 
        //       which exited, to grab the synchronization lock(s) to signal
        //       the process object. Anyway we cannot grab the synchronization 
        //       lock(s) while holding the monitored processes lock; that
        //       would cause deadlock, since RegisterProcessForMonitoring and
        //       UnRegisterProcessForMonitoring call stacks grab the locks
        //       in the opposite order. Grabbing the synch lock(s) first (and
        //       therefore all the times) would cause unacceptable contention
        //       (process monitoring is done in polling mode).
        //       Therefore we need to remove list nodes for processes that
        //       exited copying them to the exited array, while holding only 
        //       the monitored processes lock, and then to signal them from that 
        //       array holding synch lock(s) and monitored processes lock, 
        //       acquired in this order. Holding again the monitored processes 
        //       lock is needed in order to support object promotion.

        // Grab the monitored processes lock
        InternalEnterCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        fMonitoredProcessesLock = true;

        lInitialNodeCount = m_lMonitoredProcessesCount;        
        
        pNode = m_pmplnMonitoredProcesses;
        while (pNode)
        {
            pNext = pNode->pNext;
            
            if (HasProcessExited(pNode->dwPid,
                                 &pNode->dwExitCode,
                                 &pNode->fIsActualExitCode))
            {
                TRACE("Process %u exited with return code %u\n",
                      pNode->dwPid, 
                      pNode->fIsActualExitCode ? "actual" : "guessed",
                      pNode->dwExitCode);
                                              
                if (NULL != pPrev)
                {
                    pPrev->pNext = pNext;
                }
                else
                {
                    m_pmplnMonitoredProcesses = pNext;
                }
                m_lMonitoredProcessesCount--;

                // Insert in the list of nodes for exited processes
                pNode->pNext = m_pmplnExitedNodes;
                m_pmplnExitedNodes = pNode;                
                lRemovingCount++;
            }
            else
            {
                pPrev = pNode;
            }
            
            // Go to the next
            pNode = pNext;
        }

        // Release the monitored processes lock
        InternalLeaveCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        fMonitoredProcessesLock = false;

        if (lRemovingCount > 0)
        {
            // First grab the local synch lock
            AcquireLocalSynchLock(pthrCurrent);
            fLocalSynchLock = true;

            // Acquire the monitored processes lock
            InternalEnterCriticalSection(pthrCurrent, 
                                         &s_csMonitoredProcessesLock);
            fMonitoredProcessesLock = true;

            if (!fSharedSynchLock)
            {
                bool fSharedSynchLockIsNeeded = false;
                
                // See if the shared lock is needed 
                pNode = m_pmplnExitedNodes;
                while (pNode)
                {
                    if (SharedObject == pNode->psdSynchData->GetObjectDomain())
                    {
                        fSharedSynchLockIsNeeded = true;
                        break;
                    }

                    pNode = pNode->pNext;
                }

                if (fSharedSynchLockIsNeeded)
                {
                    // Release the monitored processes lock
                    InternalLeaveCriticalSection(pthrCurrent, 
                                                 &s_csMonitoredProcessesLock);                
                    fMonitoredProcessesLock = false;

                    // Acquire the shared synch lock
                    AcquireSharedSynchLock(pthrCurrent);
                    fSharedSynchLock = true;

                    // Acquire again the monitored processes lock
                    InternalEnterCriticalSection(pthrCurrent, 
                                                 &s_csMonitoredProcessesLock);
                    fMonitoredProcessesLock = true;
                }
            }

            // Start from the beginning of the exited processes list
            pNode = m_pmplnExitedNodes;

            // Invalidate the list
            m_pmplnExitedNodes = NULL;
            
            while (pNode)
            {
                pNext = pNode->pNext;

                TRACE("Process pid=%u exited with exitcode=%u\n",
                      pNode->dwPid, pNode->dwExitCode);

                // Store the exit code in the process local data
                if (pNode->fIsActualExitCode)
                {
                    pNode->pProcLocalData->dwExitCode = pNode->dwExitCode; 
                }

                // Set process status to PS_DONE
                pNode->pProcLocalData->ps = PS_DONE;

                // Set signal count
                pNode->psdSynchData->SetSignalCount(1);

                // Releasing all local waiters 
                //
                // We just called directly in CSynchData::SetSignalCount(), so
                // we need to take care of waking up waiting threads according
                // to the Process object semantics (i.e. every thread must be
                // awakend). Anyway if a process object is shared among two or
                // more processes and threads from different processes are 
                // waiting on it, the object will be registered for monitoring
                // in each of the processes. As result its signal count will 
                // be set to one more times (which is not a problem, given the
                // process object semantics) and each worker thread will wake 
                // up waiting threads. Therefore we need to make sure that each
                // worker wakes up only threads in its own process: we do that
                // by calling ReleaseAllLocalWaiters
                pNode->psdSynchData->ReleaseAllLocalWaiters(pthrCurrent);

                // Release the reference to the SynchData
                pNode->psdSynchData->Release(pthrCurrent);

                // Delete the node
                InternalDelete(pNode);

                // Go to the next
                pNode = pNext;
            }
        }

        if (fMonitoredProcessesLock)
        {
            // Release the monitored processes lock
            InternalLeaveCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        }
        if (fSharedSynchLock)
        {
            // Release the shared synch lock
            ReleaseSharedSynchLock(pthrCurrent);
        }
        if (fLocalSynchLock)
        {
            // Release the local synch lock
            ReleaseLocalSynchLock(pthrCurrent);
        }

        return (lInitialNodeCount - lRemovingCount);
    }
  
    /*++
    Method:
      CPalSynchronizationManager::DiscardMonitoredProcesses

    This method is called at shutdown time to discard all the registration 
    for the processes currently monitored by the worker thread.
    This method must be called at shutdown time, otherwise some shared memory
    may be leaked at process shutdown.
    --*/
    void CPalSynchronizationManager::DiscardMonitoredProcesses(
        CPalThread * pthrCurrent)
    {
        MonitoredProcessesListNode * pNode;

        // Grab the monitored processes lock
        InternalEnterCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
        
        while (m_pmplnMonitoredProcesses)
        {
            pNode = m_pmplnMonitoredProcesses;
            m_pmplnMonitoredProcesses = pNode->pNext;
            pNode->psdSynchData->Release(pthrCurrent);
            InternalDelete(pNode);
        }

        // Release the monitored processes lock
        InternalLeaveCriticalSection(pthrCurrent, 
                                     &s_csMonitoredProcessesLock);
    }
    
    /*++
    Method:
      CPalSynchronizationManager::CreateProcessPipe

    Creates the process pipe for the current process
    --*/
    bool CPalSynchronizationManager::CreateProcessPipe()
    {
        bool fRet = true;
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
        int iKq = -1;
#endif // HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT

        int rgiPipe[] = { -1, -1 };
        if (pipe(rgiPipe) == -1)
        {
            ERROR("Unable to create the process pipe\n");
            fRet = false;
            goto CPP_exit;
        }

#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
        iKq = kqueue();
        if (-1 == iKq)
        {
            ERROR("Failed to create kqueue associated to process pipe\n");
            fRet = false;
            goto CPP_exit;
        }       
#endif // HAVE_KQUEUE

    CPP_exit:
        if (fRet)
        {
            // Succeeded
            m_iProcessPipeRead = rgiPipe[0];
            m_iProcessPipeWrite = rgiPipe[1];
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
            m_iKQueue = iKq;
#endif // HAVE_KQUEUE
        }
        else
        {
            if (-1 != rgiPipe[0])
            {
                close(rgiPipe[0]);
                close(rgiPipe[1]);
            }
#if HAVE_KQUEUE && !HAVE_BROKEN_FIFO_KEVENT
            if (-1 != iKq)
            {
                close(iKq);
            }
#endif // HAVE_KQUEUE            
        }              
        
        return fRet;
    }

    /*++
    Method:
      CPalSynchronizationManager::ShutdownProcessPipe

    Shuts down the process pipe and removes the fifo so that other processes
    can no longer open it. It also closes the local write end of the pipe (see
    comment below). From this moment on the worker thread will process any
    possible data already received in the pipe (but not yet consumed) and any
    data written by processes that still have a opened write end of this pipe;
    it will wait (with timeout) until the last remote process which has a write
    end opened closes it, and then it will yield to process shutdown.
    --*/
    PAL_ERROR CPalSynchronizationManager::ShutdownProcessPipe()
    {
        PAL_ERROR palErr = NO_ERROR;

        if (-1 != m_iProcessPipeWrite)
        {
            // Closing the write end of the process pipe. When the last process
            // that still has a open write-fd on this pipe will close it, the
            // worker thread will receive an EOF; the worker thread will wait
            // for this EOF before shutting down, so to ensure to process any 
            // possible data already written to the pipe by other processes 
            // when the shutdown has been initiated in the current process.
            // Note: no need here to worry about platforms where close(pipe)
            // blocks on outstanding syscalls, since we are the only one using
            // this fd.
            TRACE("Closing the write end of process pipe\n");
            if (close(m_iProcessPipeWrite) == -1)
            {
                ERROR("Unable to close the write end of process pipe\n");
                palErr = ERROR_INTERNAL_ERROR;                
            }
            m_iProcessPipeWrite = -1;
        }
        
        return palErr;
    }

    /*++
    Method:
      CPalSynchronizationManager::AcquireProcessLock

    Acquires the local Process Lock (which currently is the same as the
    the local Process Synch Lock)
    --*/
    void CPalSynchronizationManager::AcquireProcessLock(CPalThread * pthrCurrent)
    {
        AcquireLocalSynchLock(pthrCurrent);
    }

    /*++
    Method:
      CPalSynchronizationManager::ReleaseProcessLock

    Releases the local Process Lock (which currently is the same as the
    the local Process Synch Lock)
    --*/
    void CPalSynchronizationManager::ReleaseProcessLock(CPalThread * pthrCurrent)
    {
        ReleaseLocalSynchLock(pthrCurrent);
    }

    /*++
    Method:
      CPalSynchronizationManager::PromoteObjectSynchData

    Promotes an object's synchdata from local to shared
    --*/
    PAL_ERROR CPalSynchronizationManager::PromoteObjectSynchData(
        CPalThread *pthrCurrent,
        VOID *pvLocalSynchData,
        VOID **ppvSharedSynchData)
    {
        PAL_ERROR palError = NO_ERROR;
        CSynchData *psdLocal = reinterpret_cast<CSynchData *>(pvLocalSynchData);
        CSynchData *psdShared = NULL;
        SharedID shridSynchData = NULLSharedID;
        SharedID *rgshridWTLNodes = NULL;
        CObjectType *pot = NULL;
        ULONG ulcWaitingThreads;

        _ASSERTE(NULL != pthrCurrent);
        _ASSERTE(NULL != pvLocalSynchData);
        _ASSERTE(NULL != ppvSharedSynchData);
        _ASSERTE(ProcessLocalObject == psdLocal->GetObjectDomain());     

#if _DEBUG

        //
        // TODO: Verify that the proper locks are held
        //
#endif

        //
        // Allocate shared memory CSynchData and map to local memory
        //

        shridSynchData = m_cacheSHRSynchData.Get(pthrCurrent); 
        if (NULLSharedID == shridSynchData)
        {
            ERROR("Unable to allocate shared memory\n");
            palError = ERROR_NOT_ENOUGH_MEMORY;
            goto POSD_exit;
        }
        
        psdShared = SharedIDToTypePointer(CSynchData, shridSynchData);
        _ASSERTE(NULL != psdShared);

        //
        // Allocate shared memory WaitingThreadListNodes if there are
        // any threads currently waiting on this object
        //

        ulcWaitingThreads = psdLocal->GetWaitingThreadCount();
        if (0 < ulcWaitingThreads)
        {
            int i;

            rgshridWTLNodes = InternalNewArray<SharedID>(ulcWaitingThreads);
            if (NULL == rgshridWTLNodes)
            {
                palError = ERROR_OUTOFMEMORY;
                goto POSD_exit;
            }
            
            i = m_cacheSHRWTListNodes.Get(
                    pthrCurrent,
                    ulcWaitingThreads,
                    rgshridWTLNodes
                    );

            if (static_cast<ULONG>(i) != ulcWaitingThreads)
            {
                for (i -= 1; i >= 0; i -= 1)
                {
                    m_cacheSHRWTListNodes.Add(pthrCurrent, rgshridWTLNodes[i]);
                }
                
                palError = ERROR_OUTOFMEMORY;
                goto POSD_exit;
            }
        }

        //
        // If the synch data is for a process object we need to grab
        // the monitored process list lock here
        //

        pot = psdLocal->GetObjectType();
        _ASSERTE(NULL != pot);

        if (otiProcess == pot->GetId())
        {
            InternalEnterCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
        }

        //
        // Copy pertinent CSynchData info to the shared memory version (and
        // initialize other members)
        //

        psdShared->SetSharedThis(shridSynchData);
        psdShared->SetObjectDomain(SharedObject);
        psdShared->SetObjectType(psdLocal->GetObjectType());
        psdShared->SetSignalCount(psdLocal->GetSignalCount());

#ifdef SYNCH_STATISTICS
        psdShared->SetStatContentionCount(psdLocal->GetStatContentionCount());
        psdShared->SetStatWaitCount(psdLocal->GetStatWaitCount());
#endif

        //
        // Rebuild the waiting thread list, and update the wait domain
        // for the waiting threads
        //

        psdShared->SetWTLHeadShrPtr(NULLSharedID);
        psdShared->SetWTLTailShrPtr(NULLSharedID);

        if (0 < ulcWaitingThreads)
        {
            WaitingThreadsListNode *pwtlnOld;
            WaitingThreadsListNode *pwtlnNew;
            int i = 0;

            for (pwtlnOld = psdLocal->GetWTLHeadPtr();
                 pwtlnOld != NULL;
                 pwtlnOld = pwtlnOld->ptrNext.ptr, i += 1)
            {
                pwtlnNew = SharedIDToTypePointer(
                    WaitingThreadsListNode,
                    rgshridWTLNodes[i]
                    );

                _ASSERTE(NULL != pwtlnNew);

                pwtlnNew->shridSHRThis = rgshridWTLNodes[i];
                pwtlnNew->ptrOwnerObjSynchData.shrid = shridSynchData;
                
                pwtlnNew->dwThreadId = pwtlnOld->dwThreadId;
                pwtlnNew->dwProcessId = pwtlnOld->dwProcessId;
                pwtlnNew->dwObjIndex = pwtlnOld->dwObjIndex;
                pwtlnNew->dwFlags = pwtlnOld->dwFlags | WTLN_FLAG_OWNER_OBJECT_IS_SHARED;
                pwtlnNew->shridWaitingState = pwtlnOld->shridWaitingState;
                pwtlnNew->ptwiWaitInfo = pwtlnOld->ptwiWaitInfo;

                psdShared->SharedWaiterEnqueue(rgshridWTLNodes[i]);
                psdShared->AddRef();
                
                _ASSERTE(pwtlnOld = pwtlnOld->ptwiWaitInfo->rgpWTLNodes[pwtlnOld->dwObjIndex]);
                pwtlnNew->ptwiWaitInfo->rgpWTLNodes[pwtlnNew->dwObjIndex] = pwtlnNew;
                
                pwtlnNew->ptwiWaitInfo->lSharedObjCount += 1;
                if (pwtlnNew->ptwiWaitInfo->lSharedObjCount
                    == pwtlnNew->ptwiWaitInfo->lObjCount)
                {
                    pwtlnNew->ptwiWaitInfo->wdWaitDomain = SharedWait;
                }
                else
                {
                    _ASSERTE(pwtlnNew->ptwiWaitInfo->lSharedObjCount
                        < pwtlnNew->ptwiWaitInfo->lObjCount);
                    
                    pwtlnNew->ptwiWaitInfo->wdWaitDomain = MixedWait;
                }
            }

            _ASSERTE(psdShared->GetWaitingThreadCount() == ulcWaitingThreads);
        }

        //
        // If the object tracks ownership and has a current owner update
        // the OwnedObjectsListNode to point to the shared memory synch
        // data
        //

        if (CObjectType::OwnershipTracked == pot->GetOwnershipSemantics())
        {
            OwnedObjectsListNode *pooln;

            pooln = psdLocal->GetOwnershipListNode();
            if (NULL != pooln)
            {
                pooln->pPalObjSynchData = psdShared;
                psdShared->SetOwnershipListNode(pooln);
                psdShared->AddRef();

                //
                // Copy over other ownership info.
                //
                
                psdShared->SetOwner(psdLocal->GetOwnerThread());
                psdShared->SetOwnershipCount(psdLocal->GetOwnershipCount());
                _ASSERTE(!psdShared->IsAbandoned());
            }
            else
            {
                _ASSERTE(0 == psdLocal->GetOwnershipCount());
                _ASSERTE(0 == psdShared->GetOwnershipCount());
                psdShared->SetAbandoned(psdLocal->IsAbandoned());
            }
        }

        //
        // If the synch data is for a process object update the monitored
        // process list nodes to point to the shared memory object data,
        // and release the monitored process list lock
        //

        if (otiProcess == pot->GetId())
        {
            MonitoredProcessesListNode *pmpn;
            
            pmpn = m_pmplnMonitoredProcesses;
            while (NULL != pmpn)
            {
                if (psdLocal == pmpn->psdSynchData)
                {
                    pmpn->psdSynchData = psdShared;
                    psdShared->AddRef();
                }

                pmpn = pmpn->pNext;
            }

            pmpn = m_pmplnExitedNodes;
            while (NULL != pmpn)
            {
                if (psdLocal == pmpn->psdSynchData)
                {
                    pmpn->psdSynchData = psdShared;
                    psdShared->AddRef();
                }

                pmpn = pmpn->pNext;
            }
            
            InternalLeaveCriticalSection(pthrCurrent, &s_csMonitoredProcessesLock);
        }

        *ppvSharedSynchData = reinterpret_cast<VOID*>(shridSynchData);

        //
        // Free the local memory items to caches
        //
        
        if (0 < ulcWaitingThreads)
        {
            WaitingThreadsListNode *pwtln;

            pwtln = psdLocal->GetWTLHeadPtr();
            while (NULL != pwtln)
            {
                WaitingThreadsListNode *pwtlnTemp;

                pwtlnTemp = pwtln;
                pwtln = pwtln->ptrNext.ptr;
                m_cacheWTListNodes.Add(pthrCurrent, pwtlnTemp);
            }
        }

        m_cacheSynchData.Add(pthrCurrent, psdLocal);

    POSD_exit:

        if (NULL != rgshridWTLNodes)
        {
            InternalDeleteArray(rgshridWTLNodes);
        }

        return palError;
    }           


    /////////////////////////////
    //                         //
    //  _ThreadNativeWaitData  //
    //                         //
    /////////////////////////////

    _ThreadNativeWaitData::~_ThreadNativeWaitData()
    {
        if (fInitialized)
        {
            fInitialized = false;
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
            pthread_cond_destroy(&cond);
            pthread_mutex_destroy(&mutex);
#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
            close(iPipeRd);
            close(iPipeWr);
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        }
    }


    //////////////////////////////////
    //                              //
    //  CThreadSynchronizationInfo  //
    //                              //
    //////////////////////////////////
    
    CThreadSynchronizationInfo::CThreadSynchronizationInfo() :
            m_tsThreadState(TS_IDLE),
            m_shridWaitAwakened(NULLSharedID),
            m_lLocalSynchLockCount(0), 
            m_lSharedSynchLockCount(0)
    {
        InitializeListHead(&m_leOwnedObjsList);

#ifdef SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        m_lPendingSignalingCount = 0;
        InitializeListHead(&m_lePendingSignalingsOverflowList);
#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
    }

    CThreadSynchronizationInfo::~CThreadSynchronizationInfo()
    { 
        if (NULLSharedID != m_shridWaitAwakened)
        {
            RawSharedObjectFree(m_shridWaitAwakened);
        }
    }

    void CThreadSynchronizationInfo::AcquireNativeWaitLock()
    {
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        int iRet;
        iRet = pthread_mutex_lock(&m_tnwdNativeData.mutex);
        _ASSERT_MSG(0 == iRet, "pthread_mutex_lock failed with error=%d\n", 
                    iRet);
#endif // !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
    }

    void CThreadSynchronizationInfo::ReleaseNativeWaitLock()
    {
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        int iRet;
        iRet = pthread_mutex_unlock(&m_tnwdNativeData.mutex);
        _ASSERT_MSG(0 == iRet, "pthread_mutex_unlock failed with error=%d\n", 
                    iRet);
#endif // !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
    }

    bool CThreadSynchronizationInfo::TryAcquireNativeWaitLock()
    {       
        bool fRet = true;
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        int iRet;
        iRet = pthread_mutex_trylock(&m_tnwdNativeData.mutex);
        _ASSERT_MSG(0 == iRet || EBUSY == iRet,
                    "pthread_mutex_trylock failed with error=%d\n", iRet);
        fRet = (0 == iRet);
#endif // !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING && !SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING
        return fRet;
    }        


    /*++
    Method:
      CThreadSynchronizationInfo::InitializePreCreate

    Part of CThreadSynchronizationInfo's initialization to be carried out
    before actual thread creation
    --*/
    PAL_ERROR CThreadSynchronizationInfo::InitializePreCreate(void)
    {
        PAL_ERROR palErr = NO_ERROR;
        DWORD * pdwWaitState = NULL;
        int iRet;        
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        const int MaxUnavailableResourceRetries = 10;
        int iEagains;
#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
        int iPipes[2] = { -1, -1};
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        m_shridWaitAwakened = RawSharedObjectAlloc(sizeof(DWORD), 
                                                   DefaultSharedPool);
        if (NULLSharedID == m_shridWaitAwakened)
        {
            ERROR("Fail allocating thread wait status shared object\n");
            palErr = ERROR_NOT_ENOUGH_MEMORY; 
            goto IPrC_exit;
        }
        
        pdwWaitState = SharedIDToTypePointer(DWORD,
            m_shridWaitAwakened);

        _ASSERT_MSG(NULL != pdwWaitState,
                    "Unable to map shared wait state: bad shrared id"
                    "[shrid=%p]\n", (VOID*)m_shridWaitAwakened);

        VolatileStore<DWORD>(pdwWaitState, TWS_ACTIVE);
        m_tsThreadState = TS_STARTING;
                
#if !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        iEagains = 0;
    Mutex_retry:
        iRet = pthread_mutex_init(&m_tnwdNativeData.mutex, NULL);
        if (0 != iRet)
        {
            ERROR("Failed creating thread synchronization mutex "
                  "[error=%d (%s)]\n", iRet, strerror(iRet));
            if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
            {
                poll(NULL, 0, min(100,10*iEagains));
                goto Mutex_retry;
            }
            else if (ENOMEM == iRet)
            {
                palErr = ERROR_NOT_ENOUGH_MEMORY;
            }
            else
            {
                palErr = ERROR_INTERNAL_ERROR;
            }
            goto IPrC_exit;
        }
        
        iEagains = 0;
    Cond_retry:
        iRet = pthread_cond_init(&m_tnwdNativeData.cond, NULL);
        if (0 != iRet)
        {
            ERROR("Failed creating thread synchronization condition "
                  "[error=%d (%s)]\n", iRet, strerror(iRet));
            if (EAGAIN == iRet && MaxUnavailableResourceRetries >= ++iEagains)
            {
                poll(NULL, 0, min(100,10*iEagains));
                goto Cond_retry;
            }
            else if (ENOMEM == iRet)
            {
                palErr = ERROR_NOT_ENOUGH_MEMORY;
            }
            else
            {
                palErr = ERROR_INTERNAL_ERROR;
            }
            pthread_mutex_destroy(&m_tnwdNativeData.mutex);
            goto IPrC_exit;
        }

#else // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        iRet = pipe(iPipes);
        if (0 != iRet)
        {
            ERROR("Failed to create pipes to support native wait [errno=%d (%s)]\n",
                  errno, strerror(errno));
            switch(errno)
            {
                case EMFILE:
                case ENFILE:                        
                    palErr = ERROR_NO_SYSTEM_RESOURCES;
                    break;
                case EFAULT:
                    palErr = ERROR_INVALID_DATA;
                    break;
                default:
                    palErr = ERROR_INTERNAL_ERROR;
                    break;
            }

            goto IPrC_exit;
        }

        if (0 != fcntl(iPipes[0], F_SETFL, O_NONBLOCK) || 
            0 != fcntl(iPipes[1], F_SETFL, O_NONBLOCK))
        {
            ERROR("Failed to set thread-blocking pipes to non-blocking mode "
                  "[errno=%d (%s)]\n", errno, strerror(errno));
            close(iPipes[0]);
            close(iPipes[1]);
            palErr = ERROR_INTERNAL_ERROR;
            goto IPrC_exit;
        }

        m_tnwdNativeData.iPipeRd = iPipes[0];
        m_tnwdNativeData.iPipeWr = iPipes[1];

#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

        m_tnwdNativeData.fInitialized = true;

    IPrC_exit:
        if (NO_ERROR != palErr)
        {
            m_tsThreadState = TS_FAILED;
        }
        return palErr;
    }

    /*++
    Method:
      CThreadSynchronizationInfo::InitializePostCreate

    Part of CThreadSynchronizationInfo's initialization to be carried out
    after actual thread creation
    --*/
    PAL_ERROR CThreadSynchronizationInfo::InitializePostCreate(
        CPalThread *pthrCurrent,
        SIZE_T threadId,
        DWORD dwLwpId)
    {
        PAL_ERROR palErr = NO_ERROR;

        if (TS_FAILED == m_tsThreadState)
        {
            palErr = ERROR_INTERNAL_ERROR;
        }

        m_twiWaitInfo.pthrOwner = pthrCurrent;

        return palErr;
    }


    /*++
    Method:
      CThreadSynchronizationInfo::AddObjectToOwnedList

    Adds an object to the list of currently owned objects.
    --*/
    void CThreadSynchronizationInfo::AddObjectToOwnedList(POwnedObjectsListNode pooln)
    {
        InsertTailList(&m_leOwnedObjsList, &pooln->Link);
    }

    /*++
    Method:
      CThreadSynchronizationInfo::RemoveObjectFromOwnedList

    Removes an object from the list of currently owned objects.
    --*/
    void CThreadSynchronizationInfo::RemoveObjectFromOwnedList(POwnedObjectsListNode pooln)
    {
        RemoveEntryList(&pooln->Link);       
    }

    /*++
    Method:
      CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList

    Removes the first object from the list of currently owned objects.
    --*/
    POwnedObjectsListNode CThreadSynchronizationInfo::RemoveFirstObjectFromOwnedList()
    {        
        OwnedObjectsListNode * poolnItem;

        if (IsListEmpty(&m_leOwnedObjsList))
        {
            poolnItem = NULL;
        }
        else
        {
            PLIST_ENTRY pLink = RemoveHeadList(&m_leOwnedObjsList);
            poolnItem = CONTAINING_RECORD(pLink,
                                          OwnedObjectsListNode,
                                          Link);
        }
        
        return poolnItem;
    }

#if SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING && !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings

    Carries out all the pending condition signalings for the current thread.
    --*/
    PAL_ERROR CThreadSynchronizationInfo::RunDeferredThreadConditionSignalings()
    {
        PAL_ERROR palErr = NO_ERROR;

        _ASSERTE(0 <= m_lPendingSignalingCount);

        if (0 < m_lPendingSignalingCount)
        {
            LONG lArrayPendingSignalingCount = 
                min(PendingSignalingsArraySize, m_lPendingSignalingCount);
            LONG lIdx = 0;
            PAL_ERROR palTempErr;

            // Signal all the pending signalings from the array
            for (lIdx = 0; lIdx < lArrayPendingSignalingCount; lIdx++)
            {
                // Do the actual signaling
                palTempErr = CPalSynchronizationManager::SignalThreadCondition(
                    m_rgpthrPendingSignalings[lIdx]->synchronizationInfo.GetNativeData());
                if (NO_ERROR != palTempErr)
                {
                    palErr = palTempErr;
                }

                // Release the thread reference
                m_rgpthrPendingSignalings[lIdx]->ReleaseThreadReference();
            }

            // Signal any pending signalings from the array overflow list
            if (m_lPendingSignalingCount > PendingSignalingsArraySize)
            {
                PLIST_ENTRY pLink;
                DeferredSignalingListNode * pdsln;
                
                while (!IsListEmpty(&m_lePendingSignalingsOverflowList))
                {
                    // Remove a node from the head of the queue
                    // Note: no need to synchronize the access to this list since
                    // it is meant to be accessed only by the owner thread.
                    pLink = RemoveHeadList(&m_lePendingSignalingsOverflowList);
                    pdsln = CONTAINING_RECORD(pLink,
                                              DeferredSignalingListNode,
                                              Link);

                    // Do the actual signaling
                    palTempErr = CPalSynchronizationManager::SignalThreadCondition(
                        pdsln->pthrTarget->synchronizationInfo.GetNativeData());
                    if (NO_ERROR != palTempErr)
                    {
                        palErr = palTempErr;
                    }

                    // Release the thread reference
                    pdsln->pthrTarget->ReleaseThreadReference();

                    // Delete the node
                    InternalDelete(pdsln);

                    lIdx += 1;
                }

                _ASSERTE(lIdx == m_lPendingSignalingCount);
            }

            // Reset the counter of pending signalings for this thread
            m_lPendingSignalingCount = 0;
        }

        return palErr;
    }

#endif // SYNCHMGR_SUSPENSION_SAFE_CONDITION_SIGNALING && !SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

    /*++
    Method:
      CPalSynchronizationManager::HasProcessExited

    Tests whether or not a process has exited
    --*/
    bool CPalSynchronizationManager::HasProcessExited(
        DWORD dwPid, 
        DWORD * pdwExitCode,
        bool * pfIsActualExitCode)
    {
        pid_t pidWaitRetval;
        int iStatus;
        bool fRet = false;

        TRACE("Looking for status of process; trying wait()\n");

        while(1)
        {
            /* try to get state of process, using non-blocking call */
            pidWaitRetval = waitpid(dwPid, &iStatus, WNOHANG);
            
            if ((DWORD)pidWaitRetval == dwPid)
            {
                /* success; get the exit code */
                if (WIFEXITED(iStatus))
                {
                    *pdwExitCode = WEXITSTATUS(iStatus);
                    *pfIsActualExitCode = true;
                    TRACE("Exit code was %d\n", *pdwExitCode);
                }
                else
                {
                    WARN("Process terminated without exiting; can't get exit "
                         "code. Assuming EXIT_FAILURE.\n");
                    *pfIsActualExitCode = true;
                    *pdwExitCode = EXIT_FAILURE;
                }

                fRet = true;
            }
            else if (0 == pidWaitRetval)
            {
                // The process is still running.
                TRACE("Process %#x is still active.\n", dwPid);
            }
            else
            {
                // A legitimate cause of failure is EINTR; if this happens we 
                // have to try again. A second legitimate cause is ECHILD, which
                // happens if we're trying to retrieve the status of a currently-
                // running process that isn't a child of this process.
                if(EINTR == errno)
                {
                    TRACE("waitpid() failed with EINTR; re-waiting\n");
                    continue;
                }
                else if (ECHILD == errno)
                {
                    TRACE("waitpid() failed with ECHILD; calling kill instead\n");
                    if (kill(dwPid, 0) != 0)
                    {
                        if(ESRCH == errno)
                        {
                            WARN("kill() failed with ESRCH, i.e. target "
                                 "process exited and it wasn't a child, "
                                 "so can't get the exit code, assuming  "
                                 "it was 0.\n");
                            *pfIsActualExitCode = false;
                            *pdwExitCode = 0;
                        }
                        else
                        {
                            ERROR("kill(pid, 0) failed; errno is %d (%s)\n",
                                  errno, strerror(errno));
                            *pfIsActualExitCode = false;
                            *pdwExitCode = EXIT_FAILURE;
                        }

                        fRet = true;
                    }
                }
                else
                {
                    // Ignoring unexpected waitpid errno and assuming that 
                    // the process is still running
                    ERROR("waitpid(pid=%u) failed with errno=%d (%s)\n",
                          dwPid, errno, strerror(errno));
                }
            }

            // Break out of the loop in all cases except EINTR.
            break;
        }
        
        return fRet;
    }
    
    /*++
    Method:
      CPalSynchronizationManager::InterlockedAwaken

    Tries to change the target wait status to 'active' in an interlocked fashion 
    --*/
    bool CPalSynchronizationManager::InterlockedAwaken(
        DWORD *pWaitState,
        bool fAlertOnly)
    {
        DWORD dwPrevState;

        dwPrevState = InterlockedCompareExchange((LONG *)pWaitState,
                                                 TWS_ACTIVE, TWS_ALERTABLE);
        if(TWS_ALERTABLE != dwPrevState)
        {
            if(fAlertOnly)
            {
                return false;
            }

            dwPrevState = InterlockedCompareExchange((LONG *)pWaitState,
                                                     TWS_ACTIVE, TWS_WAITING);
            if(TWS_WAITING == dwPrevState)
            {
                return true;
            }               
        }
        else
        {
            return true;
        }   
        return false;
    }

    /*++
    Method:
      CPalSynchronizationManager::GetAbsoluteTimeout

    Converts a relative timeout to an absolute one, reelatively to the 
    current time.
    --*/
    PAL_ERROR CPalSynchronizationManager::GetAbsoluteTimeout(DWORD dwTimeout, 
                                                 struct timespec * ptsAbsTmo)
    {
        PAL_ERROR palErr = NO_ERROR;
        int iRet;

#if HAVE_WORKING_CLOCK_GETTIME
        // Not every platform implements a (working) clock_gettime
        iRet = clock_gettime(CLOCK_REALTIME, ptsAbsTmo); 
#elif HAVE_WORKING_GETTIMEOFDAY 
        // Not every platform implements a (working) gettimeofday
        struct timeval tv;
        iRet = gettimeofday(&tv, NULL);
        if (0 == iRet)
        {
            ptsAbsTmo->tv_sec  = tv.tv_sec;
            ptsAbsTmo->tv_nsec = tv.tv_usec * tccMicroSecondsToNanoSeconds; 
        }
#else
        #error "Don't know how to get hi-res current time on this platform"
#endif // HAVE_WORKING_CLOCK_GETTIME, HAVE_WORKING_GETTIMEOFDAY
    
        if (0 == iRet)
        {
            ptsAbsTmo->tv_sec  += dwTimeout / tccSecondsToMillieSeconds;
            ptsAbsTmo->tv_nsec += (dwTimeout % tccSecondsToMillieSeconds) * tccMillieSecondsToNanoSeconds;
            while (ptsAbsTmo->tv_nsec >= tccSecondsToNanoSeconds)
            {
                ptsAbsTmo->tv_sec  += 1;
                ptsAbsTmo->tv_nsec -= tccSecondsToNanoSeconds;
            }
        }
        else
        {
            palErr = ERROR_INTERNAL_ERROR;
        }

        return palErr;
    }

#if SYNCHMGR_PIPE_BASED_THREAD_BLOCKING
    void CPalSynchronizationManager::UpdateTimeout(DWORD * pdwOldTime, DWORD * pdwTimeout)
    {
        DWORD dwNewTime;
        DWORD dwDeltaTime;
        dwNewTime = GetTickCount();
        
        // check for wrap around
        if(dwNewTime < *pdwOldTime)
        {
            dwDeltaTime = dwNewTime + (UINT_MAX - *pdwOldTime) + 1;
        }
        else
        {
            dwDeltaTime = dwNewTime - *pdwOldTime;
        }
        *pdwOldTime = dwNewTime;
        if(*pdwTimeout > dwDeltaTime)
        {
            *pdwTimeout -= dwDeltaTime;
        }
        else
        {
            *pdwTimeout = 0;
        }
    }
#endif // SYNCHMGR_PIPE_BASED_THREAD_BLOCKING

}

