// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ThreadProxy.cpp
//
// Proxy for an OS context.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "concrtinternal.h"
namespace Concurrency
{
namespace details
{
///
/// Constructs a thread proxy.
///
ThreadProxy::ThreadProxy(IThreadProxyFactory * pFactory, unsigned int stackSize)
: m_pFactory(pFactory)
, m_stackSize(stackSize)
, m_threadPriority(THREAD_PRIORITY_NORMAL)
, m_fSuspended(false)
, m_fBlocked(TRUE)
, m_fCanceled(FALSE)
{
// Thread proxy factories for Win32 threads need to be reference counted by the individual thread proxies, unlike
// UMS based thread proxy factories. This is because thread proxies that were loaned out to threads based schedulers
// could still be executing their dispatch loop and about to put themselves onto the idle pool on the factory at
// the time that the schedulers and corresponding scheduler proxies are actually destroyed (and have removed their
// references on the RM). If no references exist on the RM, the RM goes aheads and destroys the factories. However,
// it is dangerous to do this while thread proxies are possibly in the process of returning to the factory. Therefore,
// the outstanding thread proxies (alive but not in the idle pool), need to keep the factory alive until they have all
// returned.
//
// UMS thread proxies on the other hand, need the existence of a UMS virtual processor root in order to execute, and the
// UMS virtual processor roots are responsible for adding them to the idle pool. It is safe to say that all UMS thread
// proxies loaned out to a UMS scheduler are back in the idle pool of the factory at the time the UMS scheduler/scheduler
// proxy (virtual processors roots and all) are destroyed, and the factory can safely be shutdown without worrying about
// stragglers.
m_pFactory->Reference();
m_id = ResourceManager::GetThreadProxyId();
// Auto-reset event that is not signalled initially
m_hBlock = platform::__CreateAutoResetEvent(); // VSO#459907
m_hPhysicalContext = LoadLibraryAndCreateThread(NULL,
m_stackSize*KB,
ThreadProxyMain,
this,
STACK_SIZE_PARAM_IS_A_RESERVATION,
&m_threadId);
if (m_hPhysicalContext == NULL)
{
// Cleanup everything we've allocated because this exception may be caught by a higher
// layer to provide resiliency against thread creation failures during thread proxy construction.
CloseHandle(m_hBlock);
m_pFactory->Release();
throw scheduler_worker_creation_error(HRESULT_FROM_WIN32(GetLastError()));
}
}
///
/// Destroys a thread proxy.
///
ThreadProxy::~ThreadProxy()
{
CloseHandle(m_hBlock);
platform::__CloseThreadHandle(m_hPhysicalContext);
m_pFactory->Release();
}
///
/// Returns a process unique identifier for the thread proxy.
///
unsigned int ThreadProxy::GetId() const
{
return m_id;
}
#pragma warning (push)
#pragma warning (disable : 4702) // unreachable code
///
/// Sets the priority of the underlying thread.
///
///
/// The new priority value for the thread.
///
void ThreadProxy::SetPriority(int priority)
{
m_threadPriority = priority;
platform::__SetThreadPriority(m_hPhysicalContext, m_threadPriority);
}
#pragma warning (pop)
///
/// Blocks the thread proxy until is is resumed via ResumeExecution or a different thread proxy switching to it.
///
void ThreadProxy::SuspendExecution()
{
ASSERT(m_fBlocked == FALSE);
InterlockedExchange(&m_fBlocked, TRUE);
WaitForSingleObjectEx(m_hBlock, INFINITE, FALSE);
ASSERT(m_fBlocked == TRUE);
InterlockedExchange(&m_fBlocked, FALSE);
}
///
/// Resumes execution of a thread proxy.
///
void ThreadProxy::ResumeExecution()
{
SetEvent(m_hBlock);
}
///
/// Cancels the thread proxy causing the underlying thread to exit.
///
void ThreadProxy::Cancel()
{
ASSERT(m_fCanceled == false);
m_fCanceled = true;
ResumeExecution();
}
///
/// Spins until the 'this' thread proxy is in a firmly blocked state.
///
///
/// This implements a sort of barrier. At certain points during execution, it is essential to wait until a thread proxy
/// has set the flag indicating it is blocked, in order to preserve correct behavior. One example is if there is a race
/// between block and unblock for the same proxy, i.e. if a thread proxy is trying to block at the same time a different
/// context is trying to unblock it.
///
void ThreadProxy::SpinUntilBlocked()
{
if (m_fBlocked == FALSE)
{
_SpinWaitBackoffNone spinWait(_Sleep0);
do
{
spinWait._SpinOnce();
} while (m_fBlocked == FALSE);
}
ASSERT(m_fBlocked == TRUE);
}
///
/// Thread start routine for proxies.
///
DWORD CALLBACK ThreadProxy::ThreadProxyMain(LPVOID lpParameter)
{
ThreadProxy* pThreadProxy = reinterpret_cast (lpParameter);
// To start the dispatch loop cleanly, the context must block until it is switched to, or resumed..
WaitForSingleObjectEx(pThreadProxy->m_hBlock, INFINITE, FALSE);
InterlockedExchange(&pThreadProxy->m_fBlocked, FALSE);
pThreadProxy->Dispatch();
ASSERT(pThreadProxy->m_fCanceled);
// Thread proxy needs to be deleted after it is canceled and it returns from the dispatch loop.
delete pThreadProxy;
FreeLibraryAndDestroyThread(0);
return 0;
}
} // namespace details
} // namespace Concurrency