blob: b45557cb81c925053872054b58d838b738174f0f [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
class ServerContextManager
{
public:
static void RegisterThreadContext(ServerThreadContext* threadContext);
static void UnRegisterThreadContext(ServerThreadContext* threadContext);
static void RegisterScriptContext(ServerScriptContext* scriptContext);
static void UnRegisterScriptContext(ServerScriptContext* scriptContext);
static bool CheckLivenessAndAddref(ServerScriptContext* context);
static bool CheckLivenessAndAddref(ServerThreadContext* context);
private:
static JsUtil::BaseHashSet<ServerThreadContext*, HeapAllocator> threadContexts;
static JsUtil::BaseHashSet<ServerScriptContext*, HeapAllocator> scriptContexts;
static CriticalSection cs;
#ifdef STACK_BACK_TRACE
public:
template<class T>
struct ClosedContextEntry
{
__declspec(noinline)
ClosedContextEntry(T* context)
:context(context)
{
stack = StackBackTrace::Capture(&NoThrowHeapAllocator::Instance, 2);
}
~ClosedContextEntry()
{
if (stack)
{
stack->Delete(&NoThrowHeapAllocator::Instance);
}
}
T* context;
union {
DWORD runtimeProcId;
ServerThreadContext* threadCtx;
};
StackBackTrace* stack;
};
static void RecordCloseContext(ServerThreadContext* context)
{
auto record = HeapNewNoThrow(ClosedContextEntry<ServerThreadContext>, context);
if (record)
{
record->runtimeProcId = context->GetRuntimePid();
}
ClosedThreadContextList.PrependNoThrow(&NoThrowHeapAllocator::Instance, record);
}
static void RecordCloseContext(ServerScriptContext* context)
{
auto record = HeapNewNoThrow(ClosedContextEntry<ServerScriptContext>, context);
if (record)
{
record->threadCtx = context->GetThreadContext();
}
ClosedScriptContextList.PrependNoThrow(&NoThrowHeapAllocator::Instance, record);
}
static SList<ClosedContextEntry<ServerThreadContext>*, NoThrowHeapAllocator> ClosedThreadContextList;
static SList<ClosedContextEntry<ServerScriptContext>*, NoThrowHeapAllocator> ClosedScriptContextList;
#endif
static void Shutdown()
{
#ifdef STACK_BACK_TRACE
while (!ClosedThreadContextList.Empty())
{
auto record = ClosedThreadContextList.Pop();
if (record)
{
HeapDelete(record);
}
}
while (!ClosedScriptContextList.Empty())
{
auto record = ClosedScriptContextList.Pop();
if (record)
{
HeapDelete(record);
}
}
#endif
}
};
struct ContextClosedException {};
struct AutoReleaseThreadContext
{
AutoReleaseThreadContext(ServerThreadContext* threadContext)
:threadContext(threadContext)
{
if (!ServerContextManager::CheckLivenessAndAddref(threadContext))
{
// Don't assert here because ThreadContext can be closed before scriptContext closing call
// and ThreadContext closing causes all related scriptContext be closed
threadContext = nullptr;
throw ContextClosedException();
}
}
~AutoReleaseThreadContext()
{
if (threadContext)
{
threadContext->Release();
}
}
ServerThreadContext* threadContext;
};
struct AutoReleaseScriptContext
{
AutoReleaseScriptContext(ServerScriptContext* scriptContext)
:scriptContext(scriptContext)
{
if (!ServerContextManager::CheckLivenessAndAddref(scriptContext))
{
// Don't assert here because ThreadContext can be closed before scriptContext closing call
// and ThreadContext closing causes all related scriptContext be closed
scriptContext = nullptr;
threadContext = nullptr;
throw ContextClosedException();
}
threadContext = scriptContext->GetThreadContext();
}
~AutoReleaseScriptContext()
{
if (scriptContext)
{
scriptContext->Release();
}
if (threadContext)
{
threadContext->Release();
}
}
ServerScriptContext* scriptContext;
ServerThreadContext* threadContext;
};
template<typename Fn>
HRESULT ServerCallWrapper(ServerThreadContext* threadContextInfo, Fn fn);
template<typename Fn>
HRESULT ServerCallWrapper(ServerScriptContext* scriptContextInfo, Fn fn);