blob: 371bf81d0a690b74722242cf22cfb0cb569e6380 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeBasePch.h"
#include "Base/ThreadContextTlsEntry.h"
#include "Base/ThreadBoundThreadContextManager.h"
ThreadBoundThreadContextManager::EntryList ThreadBoundThreadContextManager::entries(&HeapAllocator::Instance);
#if ENABLE_BACKGROUND_JOB_PROCESSOR
JsUtil::BackgroundJobProcessor * ThreadBoundThreadContextManager::s_sharedJobProcessor = NULL;
#endif
CriticalSection ThreadBoundThreadContextManager::s_sharedJobProcessorCreationLock;
uint ThreadBoundThreadContextManager::s_maxNumberActiveThreadContexts = 0;
ThreadContext * ThreadBoundThreadContextManager::EnsureContextForCurrentThread()
{
AutoCriticalSection lock(ThreadContext::GetCriticalSection());
ThreadContextTLSEntry * entry = ThreadContextTLSEntry::GetEntryForCurrentThread();
if (entry == NULL)
{
ThreadContextTLSEntry::CreateEntryForCurrentThread();
entry = ThreadContextTLSEntry::GetEntryForCurrentThread();
entries.Prepend(entry);
}
ThreadContext * threadContext = entry->GetThreadContext();
// An existing TLS entry may have a null ThreadContext
// DllCanUnload may have cleaned out all the TLS entry when the module lock count is 0,
// but the library didn't get unloaded because someone is holding onto ref count via LoadLibrary.
// Just reinitialize the thread context.
if (threadContext == nullptr)
{
threadContext = HeapNew(ThreadContext);
threadContext->SetIsThreadBound();
if (!ThreadContextTLSEntry::TrySetThreadContext(threadContext))
{
HeapDelete(threadContext);
return NULL;
}
}
Assert(threadContext != NULL);
s_maxNumberActiveThreadContexts = max(s_maxNumberActiveThreadContexts, GetActiveThreadContextCount());
return threadContext;
}
void ThreadBoundThreadContextManager::DestroyContextAndEntryForCurrentThread()
{
AutoCriticalSection lock(ThreadContext::GetCriticalSection());
ThreadContextTLSEntry * entry = ThreadContextTLSEntry::GetEntryForCurrentThread();
if (entry == NULL)
{
return;
}
ThreadContext * threadContext = static_cast<ThreadContext *>(entry->GetThreadContext());
entries.Remove(entry);
if (threadContext != NULL && threadContext->GetIsThreadBound())
{
ShutdownThreadContext(threadContext);
}
ThreadContextTLSEntry::CleanupThread();
}
void ThreadBoundThreadContextManager::DestroyAllContexts()
{
#if ENABLE_BACKGROUND_JOB_PROCESSOR
JsUtil::BackgroundJobProcessor * jobProcessor = NULL;
#endif
{
AutoCriticalSection lock(ThreadContext::GetCriticalSection());
ThreadContextTLSEntry * currentEntry = ThreadContextTLSEntry::GetEntryForCurrentThread();
if (currentEntry == NULL)
{
// We need a current thread entry so that we can use it to release any thread contexts
// we find below.
try
{
AUTO_NESTED_HANDLED_EXCEPTION_TYPE(ExceptionType_OutOfMemory);
currentEntry = ThreadContextTLSEntry::CreateEntryForCurrentThread();
entries.Prepend(currentEntry);
}
catch (Js::OutOfMemoryException)
{
return;
}
}
else
{
// We need to clear out the current thread entry so that we can use it to release any
// thread contexts we find below.
ThreadContext * threadContext = static_cast<ThreadContext *>(currentEntry->GetThreadContext());
if (threadContext != NULL)
{
if (threadContext->GetIsThreadBound())
{
ShutdownThreadContext(threadContext);
ThreadContextTLSEntry::ClearThreadContext(currentEntry, false);
}
else
{
ThreadContextTLSEntry::ClearThreadContext(currentEntry, true);
}
}
}
EntryList::Iterator iter(&entries);
while (iter.Next())
{
ThreadContextTLSEntry * entry = iter.Data();
ThreadContext * threadContext = static_cast<ThreadContext *>(entry->GetThreadContext());
if (threadContext != nullptr)
{
// Found a thread context. Remove it from the containing entry.
ThreadContextTLSEntry::ClearThreadContext(entry, true);
// Now set it to our thread's entry.
ThreadContextTLSEntry::SetThreadContext(currentEntry, threadContext);
// Clear it out.
ShutdownThreadContext(threadContext);
// Now clear it out of our entry.
ThreadContextTLSEntry::ClearThreadContext(currentEntry, false);
}
}
// We can only clean up our own TLS entry, so we're going to go ahead and do that here.
entries.Remove(currentEntry);
ThreadContextTLSEntry::CleanupThread();
#if ENABLE_BACKGROUND_JOB_PROCESSOR
if (s_sharedJobProcessor != NULL)
{
jobProcessor = s_sharedJobProcessor;
s_sharedJobProcessor = NULL;
jobProcessor->Close();
}
#endif
}
#if ENABLE_BACKGROUND_JOB_PROCESSOR
if (jobProcessor != NULL)
{
HeapDelete(jobProcessor);
}
#endif
}
void ThreadBoundThreadContextManager::DestroyAllContextsAndEntries()
{
AutoCriticalSection lock(ThreadContext::GetCriticalSection());
while (!entries.Empty())
{
ThreadContextTLSEntry * entry = entries.Head();
ThreadContext * threadContext = static_cast<ThreadContext *>(entry->GetThreadContext());
entries.RemoveHead();
if (threadContext != nullptr)
{
#if DBG
PageAllocator* pageAllocator = threadContext->GetPageAllocator();
if (pageAllocator)
{
pageAllocator->SetConcurrentThreadId(::GetCurrentThreadId());
}
#endif
threadContext->ShutdownThreads();
HeapDelete(threadContext);
}
ThreadContextTLSEntry::Delete(entry);
}
#if ENABLE_BACKGROUND_JOB_PROCESSOR
if (s_sharedJobProcessor != NULL)
{
s_sharedJobProcessor->Close();
HeapDelete(s_sharedJobProcessor);
s_sharedJobProcessor = NULL;
}
#endif
}
JsUtil::JobProcessor * ThreadBoundThreadContextManager::GetSharedJobProcessor()
{
#if ENABLE_BACKGROUND_JOB_PROCESSOR
if (s_sharedJobProcessor == NULL)
{
// Don't use ThreadContext::GetCriticalSection() because it's also locked during thread detach while the loader lock is
// held, and that may prevent the background job processor's thread from being started due to contention on the loader
// lock, leading to a deadlock
AutoCriticalSection lock(&s_sharedJobProcessorCreationLock);
if (s_sharedJobProcessor == NULL)
{
// We don't need to have allocation policy manager for web worker.
s_sharedJobProcessor = HeapNew(JsUtil::BackgroundJobProcessor, NULL, NULL, false /*disableParallelThreads*/);
}
}
return s_sharedJobProcessor;
#else
return nullptr;
#endif
}
void RentalThreadContextManager::DestroyThreadContext(ThreadContext* threadContext)
{
ShutdownThreadContext(threadContext);
}
void ThreadContextManagerBase::ShutdownThreadContext(ThreadContext* threadContext)
{
#if DBG
PageAllocator* pageAllocator = threadContext->GetPageAllocator();
if (pageAllocator)
{
pageAllocator->SetConcurrentThreadId(::GetCurrentThreadId());
}
#endif
threadContext->ShutdownThreads();
HeapDelete(threadContext);
}
uint ThreadBoundThreadContextManager::GetActiveThreadContextCount()
{
return entries.Count();
}
void ThreadBoundThreadContextManager::ResetMaxNumberActiveThreadContexts()
{
s_maxNumberActiveThreadContexts = GetActiveThreadContextCount();
}