📄 externalcontextbase.cpp
字号:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// ExternalContextBase.cpp
//
// Source file containing the metaphor for an external execution ContextBase/stack/thread.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#include "concrtinternal.h"
namespace Concurrency
{
namespace details
{
/// <summary>
/// Constructs an external context.
/// </summary>
/// <param name="pScheduler">
/// The scheduler the context will belong to.
/// </param>
/// <param name="explicitAttach">
/// Whether or not this is an explicit attach. An explicit attach occurs as a result of calling a scheduler
/// creation API, or the scheduler attach API. The scheduler will not detach implicitly for explicitly
/// attached threads, on thread exit.
/// </param>
ExternalContextBase::ExternalContextBase(SchedulerBase *pScheduler, bool explicitAttach) :
ContextBase(pScheduler, true),
m_hPhysicalContext(NULL),
m_pSubAllocator(NULL)
{
// Create an auto-reset event that is initially not signaled.
m_hBlock = CreateEventW(NULL, FALSE, FALSE, NULL);
if (m_hBlock == NULL)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
// External contexts are all grouped together in the 'anonymous' schedule group.
m_pGroup = m_pScheduler->GetAnonymousScheduleGroup();
// Create external context statistics as a place where this external context we are about to create
// will store all the statistical data.
m_pStats = new ExternalStatistics();
m_pScheduler->AddExternalStatistics(m_pStats);
// Initialize data that is reset each time the external context is reused.
PrepareForUse(explicitAttach);
}
/// <summary>
/// Initializes fields that need re-initialization when an external context is recycled. This is called
/// in the constructor and when an external context is taken off the idle pool for reuse.
/// </summary>
/// <param name="explicitAttach">
/// Whether or not this is an explicit attach. An explicit attach occurs as a result of calling a scheduler
/// creation API, or the scheduler attach API. The scheduler will not detach implicitly for explicitly
/// attached threads, on thread exit.
/// </param>
void ExternalContextBase::PrepareForUse(bool explicitAttach)
{
// Even in the case of a nested external context being initialized, we expect the TLS slot to be clear.
ASSERT(SchedulerBase::FastCurrentContext() == NULL);
m_fExplicitlyAttached = explicitAttach;
m_threadId = GetCurrentThreadId();
if (!explicitAttach)
{
// We only need to capture the current thread's handle for an implicit attach, so that we can register the
// handle for exit tracking, in order that references may be released on thread exit.
if (!DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&m_hPhysicalContext,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
m_pScheduler->RegisterForExitTracking(this);
}
}
/// <summary>
/// Causes the external context to block. Since external contexts do not execute on virtual processors,
/// the context does not switch to another one. Instead, it stops executing until it is unblocked.
/// </summary>
void ExternalContextBase::Block()
{
ASSERT(this == SchedulerBase::FastCurrentContext());
TraceContextEvent(CONCRT_EVENT_BLOCK, TRACE_LEVEL_INFORMATION, m_pScheduler->Id(), m_id);
if (InterlockedIncrement(&m_contextSwitchingFence) == 1)
{
WaitForSingleObject(m_hBlock, INFINITE);
}
else
{
// Skip the block, since an unblock has already been encountered.
}
}
/// <summary>
/// Unblocks the external context causing it to start running.
/// </summary>
void ExternalContextBase::Unblock()
{
if (this != SchedulerBase::FastCurrentContext())
{
TraceContextEvent(CONCRT_EVENT_UNBLOCK, TRACE_LEVEL_INFORMATION, m_pScheduler->Id(), m_id);
LONG newValue = InterlockedDecrement(&m_contextSwitchingFence);
if (newValue == 0)
{
SetEvent(m_hBlock);
}
else
{
if ((newValue < -1) || (newValue > 0))
{
// Should not be able to get m_contextSwitchingFence above 0.
ASSERT(newValue < -1);
throw context_unblock_unbalanced();
}
}
}
else
{
throw context_self_unblock();
}
}
/// <summary>
/// Just a thread yield on the current processor.
/// </summary>
void ExternalContextBase::Yield()
{
TraceContextEvent(CONCRT_EVENT_YIELD, TRACE_LEVEL_INFORMATION, m_pScheduler->Id(), m_id);
SwitchToThread();
}
/// <summary>
/// See comments for Concurrency::Context::Oversubscribe.
/// External contexts do not support oversubscription. However, we keep track of calls and throw exceptions
/// when appropriate.
/// </summary>
void ExternalContextBase::Oversubscribe(bool beginOversubscription)
{
if (beginOversubscription)
{
ASSERT(m_oversubscribeCount >= 0);
++m_oversubscribeCount;
}
else
{
if (m_oversubscribeCount == 0)
{
throw invalid_oversubscribe_operation();
}
--m_oversubscribeCount;
}
}
/// <summary>
/// Allocates a block of memory of the size specified.
/// </summary>
/// <param name="numBytes">
/// Number of bytes to allocate.
/// </param>
/// <returns>
/// A pointer to newly allocated memory.
/// </returns>
void* ExternalContextBase::Alloc(size_t numBytes)
{
void* pAllocation = NULL;
ASSERT(SchedulerBase::FastCurrentContext() == this);
// Find the suballocator for this external context if there is one. Note that if we are unable to get an allocator now,
// we may be able to get one for a later Alloc or Free call (if a different external context released its allocator to
// the free pool).
SubAllocator* pAllocator = GetCurrentSubAllocator();
if (pAllocator != NULL)
{
pAllocation = pAllocator->Alloc(numBytes);
}
else
{
// Allocate from the CRT heap. At the point this allocation is freed, if the context has a suballocator, it will be
// freed to the suballocator of the context.
pAllocation = SubAllocator::StaticAlloc(numBytes);
}
return pAllocation;
}
/// <summary>
/// Frees a block of memory previously allocated by the Alloc API.
/// </summary>
/// <param name="pAllocation">
/// A pointer to an allocation previously allocated by Alloc.
/// </param>
void ExternalContextBase::Free(void* pAllocation)
{
ASSERT(SchedulerBase::FastCurrentContext() == this);
ASSERT(pAllocation != NULL);
// Find the suballocator for this external context if there is one. Note that if we are unable to get an allocator now,
// we may be able to get one for a later Alloc or Free call (if a different external context released its allocator to
// the free pool).
SubAllocator* pAllocator = GetCurrentSubAllocator();
if (pAllocator != NULL)
{
pAllocator->Free(pAllocation);
}
else
{
// Free to the CRT heap.
SubAllocator::StaticFree(pAllocation);
}
}
/// <summary>
/// Prepares an external context for the idle pool by releasing some resources.
/// </summary>
void ExternalContextBase::RemoveFromUse()
{
ReleaseWorkQueue();
CORE_ASSERT(GetCriticalRegionType() == OutsideCriticalRegion);
if (m_hPhysicalContext != NULL)
{
CloseHandle(m_hPhysicalContext);
m_hPhysicalContext = NULL;
}
}
/// <summary>
/// Destroys an external thread based context.
/// </summary>
ExternalContextBase::~ExternalContextBase()
{
// This takes care of calling the cleanup routine for ContextBase.
Cleanup();
}
/// <summary>
/// Performs cleanup of the external context
/// </summary>
void ExternalContextBase::Cleanup()
{
ContextBase::Cleanup();
//
// m_pGroup is an anonymous schedule group that is destroyed at scheduler shutdonw, so don't release here.
//
if (m_hPhysicalContext != NULL)
{
CloseHandle(m_hPhysicalContext);
m_hPhysicalContext = NULL;
}
if (m_hBlock)
{
CloseHandle(m_hBlock);
}
if (m_pSubAllocator != NULL)
{
SchedulerBase::ReturnSubAllocator(m_pSubAllocator);
}
// Mark the scheduler's list of non-internal contexts (external or non-bound to context) for removal. We
// can't remove this item yet because statistics might not have had a chance to aggregate this information yet.
DetachStatistics();
}
} // namespace details
} // namespace Concurrency
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -