123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- #include "il2cpp-config.h"
- #if !IL2CPP_THREADS_STD && IL2CPP_THREADS_WIN32 && !RUNTIME_TINY
- #include "ThreadImpl.h"
- #include "os/ThreadLocalValue.h"
- #include "os/Time.h"
- #include "utils/StringUtils.h"
- #include "os/Debug.h"
- #include "WindowsHelpers.h"
- namespace il2cpp
- {
- namespace os
- {
- static Event s_ThreadSleepObject;
- struct ThreadImplStartData
- {
- Thread::StartFunc m_StartFunc;
- void* m_StartArg;
- volatile DWORD* m_ThreadId;
- };
- static DWORD WINAPI ThreadStartWrapper(LPVOID arg)
- {
- ThreadImplStartData startData = *(ThreadImplStartData*)arg;
- free(arg);
- *startData.m_ThreadId = GetCurrentThreadId();
- startData.m_StartFunc(startData.m_StartArg);
- return 0;
- }
- ThreadImpl::ThreadImpl()
- : m_ThreadHandle(0), m_ThreadId(0), m_StackSize(IL2CPP_DEFAULT_STACK_SIZE), m_ApartmentState(kApartmentStateUnknown), m_Priority(kThreadPriorityNormal)
- , m_ConditionSemaphore(1)
- {
- }
- ThreadImpl::~ThreadImpl()
- {
- if (m_ThreadHandle != NULL)
- CloseHandle(m_ThreadHandle);
- }
- size_t ThreadImpl::Id()
- {
- return m_ThreadId;
- }
- void ThreadImpl::SetNameForDebugger(const char* name)
- {
- // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
- const DWORD MS_VC_EXCEPTION = 0x406D1388;
- #pragma pack(push,8)
- typedef struct tagTHREADNAME_INFO
- {
- DWORD dwType; // Must be 0x1000.
- LPCSTR szName; // Pointer to name (in user addr space).
- DWORD dwThreadID; // Thread ID (-1=caller thread).
- DWORD dwFlags; // Reserved for future use, must be zero.
- } THREADNAME_INFO;
- #pragma pack(pop)
- THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = name;
- info.dwThreadID = static_cast<DWORD>(Id());
- info.dwFlags = 0;
- __try
- {
- RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- }
- }
- typedef HRESULT (__stdcall *SETTHREADPROC) (HANDLE, PCWSTR);
- void ThreadImpl::SetName(const char* name)
- {
- #if !IL2CPP_TARGET_WINRT
- SETTHREADPROC ProcSetThreadDescription;
- ProcSetThreadDescription = (SETTHREADPROC)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "SetThreadDescription");
- if (ProcSetThreadDescription != NULL)
- {
- const UTF16String varName = utils::StringUtils::Utf8ToUtf16(name);
- (ProcSetThreadDescription)(m_ThreadHandle, varName.c_str());
- }
- #endif
- if (Debug::IsDebuggerPresent())
- SetNameForDebugger(name);
- }
- void ThreadImpl::SetPriority(ThreadPriority priority)
- {
- if (m_ThreadHandle == NULL)
- m_Priority = priority;
- else
- {
- int ret = ::SetThreadPriority(m_ThreadHandle, priority - 2);
- IL2CPP_ASSERT(ret);
- }
- }
- ThreadPriority ThreadImpl::GetPriority()
- {
- if (m_ThreadHandle == NULL)
- return m_Priority;
- int ret = ::GetThreadPriority(m_ThreadHandle) + 2;
- IL2CPP_ASSERT(ret != THREAD_PRIORITY_ERROR_RETURN);
- return (ThreadPriority)ret;
- }
- ErrorCode ThreadImpl::Run(Thread::StartFunc func, void* arg, int64_t affinityMask)
- {
- // It might happen that func will start executing and will try to access m_ThreadId before CreateThread gets a chance to assign it.
- // Therefore m_ThreadId is assigned both by this thread and from the newly created thread (race condition could go the other way too).
- ThreadImplStartData* startData = (ThreadImplStartData*)malloc(sizeof(ThreadImplStartData));
- startData->m_StartFunc = func;
- startData->m_StartArg = arg;
- startData->m_ThreadId = &m_ThreadId;
- // Create thread.
- DWORD threadId;
- HANDLE threadHandle = ::CreateThread(NULL, m_StackSize, &ThreadStartWrapper, startData, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId);
- if (!threadHandle)
- return kErrorCodeGenFailure;
- #if IL2CPP_TARGET_WINDOWS_GAMES || IL2CPP_TARGET_XBOXONE
- if (affinityMask != Thread::kThreadAffinityAll)
- SetThreadAffinityMask(threadHandle, static_cast<DWORD_PTR>(affinityMask));
- #endif
- m_ThreadHandle = threadHandle;
- m_ThreadId = threadId;
- SetPriority(m_Priority);
- return kErrorCodeSuccess;
- }
- void ThreadImpl::Sleep(uint32_t ms, bool interruptible)
- {
- s_ThreadSleepObject.Wait(ms, interruptible);
- }
- void ThreadImpl::CheckForUserAPCAndHandle()
- {
- m_PendingAPCsMutex.Acquire();
- while (!m_PendingAPCs.empty())
- {
- APCRequest apcRequest = m_PendingAPCs.front();
- // Remove from list. Do before calling the function to make sure the list
- // is up to date in case the function throws.
- m_PendingAPCs.erase(m_PendingAPCs.begin());
- // Release mutex while we call the function so that we don't deadlock
- // if the function starts waiting on a thread that tries queuing an APC
- // on us.
- m_PendingAPCsMutex.Release();
- // Call function.
- apcRequest.callback(apcRequest.context);
- // Re-acquire mutex.
- m_PendingAPCsMutex.Acquire();
- }
- m_PendingAPCsMutex.Release();
- }
- void ThreadImpl::SetWaitObject(WaitObject* waitObject)
- {
- // This is an unprotected write as write acccess is restricted to the
- // current thread.
- m_CurrentWaitObject = waitObject;
- }
- void ThreadImpl::QueueUserAPC(Thread::APCFunc func, void* context)
- {
- IL2CPP_ASSERT(func != NULL);
- // Put on queue.
- {
- m_PendingAPCsMutex.Acquire();
- m_PendingAPCs.push_back(APCRequest(func, context));
- m_PendingAPCsMutex.Release();
- }
- // Interrupt an ongoing wait, only interrupt if we have an object waiting
- if (m_CurrentWaitObject.load())
- {
- m_ConditionSemaphore.Release(1);
- }
- }
- int ThreadImpl::GetMaxStackSize()
- {
- return INT_MAX;
- }
- ThreadImpl* ThreadImpl::GetCurrentThread()
- {
- return Thread::GetCurrentThread()->m_Thread;
- }
- namespace
- {
- // It would be nice to always use CoGetApartmentType but it's only available on Windows 7 and later.
- // That's why we check for function at runtime and do a fallback on Windows XP.
- // CoGetApartmentType is always available in Windows Store Apps.
- typedef HRESULT (STDAPICALLTYPE * CoGetApartmentTypeFunc)(APTTYPE* type, APTTYPEQUALIFIER* qualifier);
- ApartmentState GetApartmentWindows7(CoGetApartmentTypeFunc coGetApartmentType, bool* implicit)
- {
- *implicit = false;
- APTTYPE type;
- APTTYPEQUALIFIER qualifier;
- const HRESULT hr = coGetApartmentType(&type, &qualifier);
- if (FAILED(hr))
- {
- IL2CPP_ASSERT(CO_E_NOTINITIALIZED == hr);
- return kApartmentStateUnknown;
- }
- switch (type)
- {
- case APTTYPE_STA:
- case APTTYPE_MAINSTA:
- return kApartmentStateInSTA;
- case APTTYPE_MTA:
- *implicit = (APTTYPEQUALIFIER_IMPLICIT_MTA == qualifier);
- return kApartmentStateInMTA;
- case APTTYPE_NA:
- switch (qualifier)
- {
- case APTTYPEQUALIFIER_NA_ON_STA:
- case APTTYPEQUALIFIER_NA_ON_MAINSTA:
- return kApartmentStateInSTA;
- case APTTYPEQUALIFIER_NA_ON_MTA:
- return kApartmentStateInMTA;
- case APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA:
- *implicit = true;
- return kApartmentStateInMTA;
- }
- break;
- }
- IL2CPP_ASSERT(0 && "CoGetApartmentType returned unexpected value.");
- return kApartmentStateUnknown;
- }
- #if IL2CPP_TARGET_WINDOWS_DESKTOP
- ApartmentState GetApartmentWindowsXp(bool* implicit)
- {
- *implicit = false;
- IUnknown* context = nullptr;
- HRESULT hr = CoGetContextToken(reinterpret_cast<ULONG_PTR*>(&context));
- if (SUCCEEDED(hr))
- {
- IComThreadingInfo* info;
- hr = context->QueryInterface(&info);
- if (SUCCEEDED(hr))
- {
- THDTYPE type;
- hr = info->GetCurrentThreadType(&type);
- if (SUCCEEDED(hr))
- {
- // THDTYPE_PROCESSMESSAGES means that we are in STA thread.
- // Otherwise it's an MTA thread. We are not sure at this moment if CoInitializeEx has been called explicitly on this thread
- // or if it has been implicitly made MTA by a CoInitialize call on another thread.
- if (THDTYPE_PROCESSMESSAGES == type)
- return kApartmentStateInSTA;
- // Assume implicit. Even if it's explicit, we'll handle the case correctly by checking CoInitializeEx return value.
- *implicit = true;
- return kApartmentStateInMTA;
- }
- info->Release();
- }
- // No need to release context.
- }
- return kApartmentStateUnknown;
- }
- class CoGetApartmentTypeHelper
- {
- private:
- HMODULE _library;
- CoGetApartmentTypeFunc _func;
- public:
- inline CoGetApartmentTypeHelper()
- {
- _library = LoadLibraryW(L"ole32.dll");
- Assert(_library);
- _func = reinterpret_cast<CoGetApartmentTypeFunc>(GetProcAddress(_library, "CoGetApartmentType"));
- }
- inline ~CoGetApartmentTypeHelper()
- {
- FreeLibrary(_library);
- }
- inline CoGetApartmentTypeFunc GetFunc() const { return _func; }
- };
- inline ApartmentState GetApartmentImpl(bool* implicit)
- {
- static CoGetApartmentTypeHelper coGetApartmentTypeHelper;
- const CoGetApartmentTypeFunc func = coGetApartmentTypeHelper.GetFunc();
- return func ? GetApartmentWindows7(func, implicit) : GetApartmentWindowsXp(implicit);
- }
- #else
- inline ApartmentState GetApartmentImpl(bool* implicit)
- {
- return GetApartmentWindows7(CoGetApartmentType, implicit);
- }
- #endif
- }
- ApartmentState ThreadImpl::GetApartment()
- {
- Assert(GetCurrentThreadId() == m_ThreadId);
- ApartmentState state = static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
- if (kApartmentStateUnknown == state)
- {
- bool implicit;
- state = GetApartmentImpl(&implicit);
- if (!implicit)
- m_ApartmentState = state;
- }
- return state;
- }
- ApartmentState ThreadImpl::GetExplicitApartment()
- {
- return static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
- }
- ApartmentState ThreadImpl::SetApartment(ApartmentState state)
- {
- Assert(GetCurrentThreadId() == m_ThreadId);
- // Unknown state uninitializes COM.
- if (kApartmentStateUnknown == state)
- {
- if (m_ApartmentState & kApartmentStateCoInitialized)
- {
- CoUninitialize();
- m_ApartmentState = kApartmentStateUnknown;
- }
- return GetApartment();
- }
- // Initialize apartment state. Ignore result of this function because it will return MTA value for both implicit and explicit apartment.
- // On the other hand m_ApartmentState will only be set to MTA if it was initialized explicitly with CoInitializeEx.
- GetApartment();
- ApartmentState currentState = static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
- if (kApartmentStateUnknown != currentState)
- {
- Assert(state == currentState);
- return currentState;
- }
- #if IL2CPP_TARGET_XBOXONE
- if (state == kApartmentStateInSTA)
- {
- // Only assert in debug.. we wouldn't want to bring down the application in Release config
- IL2CPP_ASSERT(false && "STA apartment state is not supported on Xbox One");
- state = kApartmentStateInMTA;
- }
- #endif
- HRESULT hr = CoInitializeEx(nullptr, (kApartmentStateInSTA == state) ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED);
- if (SUCCEEDED(hr))
- {
- m_ApartmentState = state;
- if (S_OK == hr)
- m_ApartmentState = static_cast<ApartmentState>(m_ApartmentState | kApartmentStateCoInitialized);
- else
- CoUninitialize(); // Someone has already called correct CoInitialize. Don't leave incorrect reference count.
- }
- else if (RPC_E_CHANGED_MODE == hr)
- {
- // CoInitialize has already been called with a different apartment state.
- m_ApartmentState = (kApartmentStateInSTA == state) ? kApartmentStateInMTA : kApartmentStateInSTA;
- }
- else
- {
- // Based on where this function is called (Init and Shutdown) we can't really recover from this, so
- // just abort.
- abort();
- }
- return GetApartment();
- }
- void ThreadImpl::SetExplicitApartment(ApartmentState state)
- {
- Assert(!(m_ApartmentState & kApartmentStateCoInitialized));
- m_ApartmentState = state;
- }
- size_t ThreadImpl::CurrentThreadId()
- {
- return GetCurrentThreadId();
- }
- ThreadImpl* ThreadImpl::CreateForCurrentThread()
- {
- ThreadImpl* thread = new ThreadImpl();
- BOOL duplicateResult = DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(), &thread->m_ThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
- Assert(duplicateResult && "DuplicateHandle failed.");
- thread->m_ThreadId = ::GetCurrentThreadId();
- return thread;
- }
- bool ThreadImpl::YieldInternal()
- {
- return SwitchToThread();
- }
- #if IL2CPP_HAS_NATIVE_THREAD_CLEANUP
- static Thread::ThreadCleanupFunc s_ThreadCleanupFunction;
- static ThreadLocalValue s_ThreadCleanupArguments;
- void ThreadImpl::SetNativeThreadCleanup(Thread::ThreadCleanupFunc cleanupFunction)
- {
- s_ThreadCleanupFunction = cleanupFunction;
- }
- void ThreadImpl::RegisterCurrentThreadForCleanup(void* arg)
- {
- s_ThreadCleanupArguments.SetValue(arg);
- }
- void ThreadImpl::UnregisterCurrentThreadForCleanup()
- {
- s_ThreadCleanupArguments.SetValue(NULL);
- }
- void ThreadImpl::OnCurrentThreadExiting()
- {
- Thread::ThreadCleanupFunc cleanupFunction = s_ThreadCleanupFunction;
- if (cleanupFunction == NULL)
- return;
- void* threadCleanupArgument = NULL;
- s_ThreadCleanupArguments.GetValue(&threadCleanupArgument);
- if (threadCleanupArgument != NULL)
- cleanupFunction(threadCleanupArgument);
- }
- #endif
- }
- }
- #endif
|