ThreadImpl.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. #include "il2cpp-config.h"
  2. #if !IL2CPP_THREADS_STD && IL2CPP_THREADS_WIN32 && !RUNTIME_TINY
  3. #include "ThreadImpl.h"
  4. #include "os/ThreadLocalValue.h"
  5. #include "os/Time.h"
  6. #include "utils/StringUtils.h"
  7. #include "os/Debug.h"
  8. #include "WindowsHelpers.h"
  9. namespace il2cpp
  10. {
  11. namespace os
  12. {
  13. static Event s_ThreadSleepObject;
  14. struct ThreadImplStartData
  15. {
  16. Thread::StartFunc m_StartFunc;
  17. void* m_StartArg;
  18. volatile DWORD* m_ThreadId;
  19. };
  20. static DWORD WINAPI ThreadStartWrapper(LPVOID arg)
  21. {
  22. ThreadImplStartData startData = *(ThreadImplStartData*)arg;
  23. free(arg);
  24. *startData.m_ThreadId = GetCurrentThreadId();
  25. startData.m_StartFunc(startData.m_StartArg);
  26. return 0;
  27. }
  28. ThreadImpl::ThreadImpl()
  29. : m_ThreadHandle(0), m_ThreadId(0), m_StackSize(IL2CPP_DEFAULT_STACK_SIZE), m_ApartmentState(kApartmentStateUnknown), m_Priority(kThreadPriorityNormal)
  30. , m_ConditionSemaphore(1)
  31. {
  32. }
  33. ThreadImpl::~ThreadImpl()
  34. {
  35. if (m_ThreadHandle != NULL)
  36. CloseHandle(m_ThreadHandle);
  37. }
  38. size_t ThreadImpl::Id()
  39. {
  40. return m_ThreadId;
  41. }
  42. void ThreadImpl::SetNameForDebugger(const char* name)
  43. {
  44. // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
  45. const DWORD MS_VC_EXCEPTION = 0x406D1388;
  46. #pragma pack(push,8)
  47. typedef struct tagTHREADNAME_INFO
  48. {
  49. DWORD dwType; // Must be 0x1000.
  50. LPCSTR szName; // Pointer to name (in user addr space).
  51. DWORD dwThreadID; // Thread ID (-1=caller thread).
  52. DWORD dwFlags; // Reserved for future use, must be zero.
  53. } THREADNAME_INFO;
  54. #pragma pack(pop)
  55. THREADNAME_INFO info;
  56. info.dwType = 0x1000;
  57. info.szName = name;
  58. info.dwThreadID = static_cast<DWORD>(Id());
  59. info.dwFlags = 0;
  60. __try
  61. {
  62. RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR*)&info);
  63. }
  64. __except (EXCEPTION_EXECUTE_HANDLER)
  65. {
  66. }
  67. }
  68. typedef HRESULT (__stdcall *SETTHREADPROC) (HANDLE, PCWSTR);
  69. void ThreadImpl::SetName(const char* name)
  70. {
  71. #if !IL2CPP_TARGET_WINRT
  72. SETTHREADPROC ProcSetThreadDescription;
  73. ProcSetThreadDescription = (SETTHREADPROC)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "SetThreadDescription");
  74. if (ProcSetThreadDescription != NULL)
  75. {
  76. const UTF16String varName = utils::StringUtils::Utf8ToUtf16(name);
  77. (ProcSetThreadDescription)(m_ThreadHandle, varName.c_str());
  78. }
  79. #endif
  80. if (Debug::IsDebuggerPresent())
  81. SetNameForDebugger(name);
  82. }
  83. void ThreadImpl::SetPriority(ThreadPriority priority)
  84. {
  85. if (m_ThreadHandle == NULL)
  86. m_Priority = priority;
  87. else
  88. {
  89. int ret = ::SetThreadPriority(m_ThreadHandle, priority - 2);
  90. IL2CPP_ASSERT(ret);
  91. }
  92. }
  93. ThreadPriority ThreadImpl::GetPriority()
  94. {
  95. if (m_ThreadHandle == NULL)
  96. return m_Priority;
  97. int ret = ::GetThreadPriority(m_ThreadHandle) + 2;
  98. IL2CPP_ASSERT(ret != THREAD_PRIORITY_ERROR_RETURN);
  99. return (ThreadPriority)ret;
  100. }
  101. ErrorCode ThreadImpl::Run(Thread::StartFunc func, void* arg, int64_t affinityMask)
  102. {
  103. // It might happen that func will start executing and will try to access m_ThreadId before CreateThread gets a chance to assign it.
  104. // Therefore m_ThreadId is assigned both by this thread and from the newly created thread (race condition could go the other way too).
  105. ThreadImplStartData* startData = (ThreadImplStartData*)malloc(sizeof(ThreadImplStartData));
  106. startData->m_StartFunc = func;
  107. startData->m_StartArg = arg;
  108. startData->m_ThreadId = &m_ThreadId;
  109. // Create thread.
  110. DWORD threadId;
  111. HANDLE threadHandle = ::CreateThread(NULL, m_StackSize, &ThreadStartWrapper, startData, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId);
  112. if (!threadHandle)
  113. return kErrorCodeGenFailure;
  114. #if IL2CPP_TARGET_WINDOWS_GAMES || IL2CPP_TARGET_XBOXONE
  115. if (affinityMask != Thread::kThreadAffinityAll)
  116. SetThreadAffinityMask(threadHandle, static_cast<DWORD_PTR>(affinityMask));
  117. #endif
  118. m_ThreadHandle = threadHandle;
  119. m_ThreadId = threadId;
  120. SetPriority(m_Priority);
  121. return kErrorCodeSuccess;
  122. }
  123. void ThreadImpl::Sleep(uint32_t ms, bool interruptible)
  124. {
  125. s_ThreadSleepObject.Wait(ms, interruptible);
  126. }
  127. void ThreadImpl::CheckForUserAPCAndHandle()
  128. {
  129. m_PendingAPCsMutex.Acquire();
  130. while (!m_PendingAPCs.empty())
  131. {
  132. APCRequest apcRequest = m_PendingAPCs.front();
  133. // Remove from list. Do before calling the function to make sure the list
  134. // is up to date in case the function throws.
  135. m_PendingAPCs.erase(m_PendingAPCs.begin());
  136. // Release mutex while we call the function so that we don't deadlock
  137. // if the function starts waiting on a thread that tries queuing an APC
  138. // on us.
  139. m_PendingAPCsMutex.Release();
  140. // Call function.
  141. apcRequest.callback(apcRequest.context);
  142. // Re-acquire mutex.
  143. m_PendingAPCsMutex.Acquire();
  144. }
  145. m_PendingAPCsMutex.Release();
  146. }
  147. void ThreadImpl::SetWaitObject(WaitObject* waitObject)
  148. {
  149. // This is an unprotected write as write acccess is restricted to the
  150. // current thread.
  151. m_CurrentWaitObject = waitObject;
  152. }
  153. void ThreadImpl::QueueUserAPC(Thread::APCFunc func, void* context)
  154. {
  155. IL2CPP_ASSERT(func != NULL);
  156. // Put on queue.
  157. {
  158. m_PendingAPCsMutex.Acquire();
  159. m_PendingAPCs.push_back(APCRequest(func, context));
  160. m_PendingAPCsMutex.Release();
  161. }
  162. // Interrupt an ongoing wait, only interrupt if we have an object waiting
  163. if (m_CurrentWaitObject.load())
  164. {
  165. m_ConditionSemaphore.Release(1);
  166. }
  167. }
  168. int ThreadImpl::GetMaxStackSize()
  169. {
  170. return INT_MAX;
  171. }
  172. ThreadImpl* ThreadImpl::GetCurrentThread()
  173. {
  174. return Thread::GetCurrentThread()->m_Thread;
  175. }
  176. namespace
  177. {
  178. // It would be nice to always use CoGetApartmentType but it's only available on Windows 7 and later.
  179. // That's why we check for function at runtime and do a fallback on Windows XP.
  180. // CoGetApartmentType is always available in Windows Store Apps.
  181. typedef HRESULT (STDAPICALLTYPE * CoGetApartmentTypeFunc)(APTTYPE* type, APTTYPEQUALIFIER* qualifier);
  182. ApartmentState GetApartmentWindows7(CoGetApartmentTypeFunc coGetApartmentType, bool* implicit)
  183. {
  184. *implicit = false;
  185. APTTYPE type;
  186. APTTYPEQUALIFIER qualifier;
  187. const HRESULT hr = coGetApartmentType(&type, &qualifier);
  188. if (FAILED(hr))
  189. {
  190. IL2CPP_ASSERT(CO_E_NOTINITIALIZED == hr);
  191. return kApartmentStateUnknown;
  192. }
  193. switch (type)
  194. {
  195. case APTTYPE_STA:
  196. case APTTYPE_MAINSTA:
  197. return kApartmentStateInSTA;
  198. case APTTYPE_MTA:
  199. *implicit = (APTTYPEQUALIFIER_IMPLICIT_MTA == qualifier);
  200. return kApartmentStateInMTA;
  201. case APTTYPE_NA:
  202. switch (qualifier)
  203. {
  204. case APTTYPEQUALIFIER_NA_ON_STA:
  205. case APTTYPEQUALIFIER_NA_ON_MAINSTA:
  206. return kApartmentStateInSTA;
  207. case APTTYPEQUALIFIER_NA_ON_MTA:
  208. return kApartmentStateInMTA;
  209. case APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA:
  210. *implicit = true;
  211. return kApartmentStateInMTA;
  212. }
  213. break;
  214. }
  215. IL2CPP_ASSERT(0 && "CoGetApartmentType returned unexpected value.");
  216. return kApartmentStateUnknown;
  217. }
  218. #if IL2CPP_TARGET_WINDOWS_DESKTOP
  219. ApartmentState GetApartmentWindowsXp(bool* implicit)
  220. {
  221. *implicit = false;
  222. IUnknown* context = nullptr;
  223. HRESULT hr = CoGetContextToken(reinterpret_cast<ULONG_PTR*>(&context));
  224. if (SUCCEEDED(hr))
  225. {
  226. IComThreadingInfo* info;
  227. hr = context->QueryInterface(&info);
  228. if (SUCCEEDED(hr))
  229. {
  230. THDTYPE type;
  231. hr = info->GetCurrentThreadType(&type);
  232. if (SUCCEEDED(hr))
  233. {
  234. // THDTYPE_PROCESSMESSAGES means that we are in STA thread.
  235. // Otherwise it's an MTA thread. We are not sure at this moment if CoInitializeEx has been called explicitly on this thread
  236. // or if it has been implicitly made MTA by a CoInitialize call on another thread.
  237. if (THDTYPE_PROCESSMESSAGES == type)
  238. return kApartmentStateInSTA;
  239. // Assume implicit. Even if it's explicit, we'll handle the case correctly by checking CoInitializeEx return value.
  240. *implicit = true;
  241. return kApartmentStateInMTA;
  242. }
  243. info->Release();
  244. }
  245. // No need to release context.
  246. }
  247. return kApartmentStateUnknown;
  248. }
  249. class CoGetApartmentTypeHelper
  250. {
  251. private:
  252. HMODULE _library;
  253. CoGetApartmentTypeFunc _func;
  254. public:
  255. inline CoGetApartmentTypeHelper()
  256. {
  257. _library = LoadLibraryW(L"ole32.dll");
  258. Assert(_library);
  259. _func = reinterpret_cast<CoGetApartmentTypeFunc>(GetProcAddress(_library, "CoGetApartmentType"));
  260. }
  261. inline ~CoGetApartmentTypeHelper()
  262. {
  263. FreeLibrary(_library);
  264. }
  265. inline CoGetApartmentTypeFunc GetFunc() const { return _func; }
  266. };
  267. inline ApartmentState GetApartmentImpl(bool* implicit)
  268. {
  269. static CoGetApartmentTypeHelper coGetApartmentTypeHelper;
  270. const CoGetApartmentTypeFunc func = coGetApartmentTypeHelper.GetFunc();
  271. return func ? GetApartmentWindows7(func, implicit) : GetApartmentWindowsXp(implicit);
  272. }
  273. #else
  274. inline ApartmentState GetApartmentImpl(bool* implicit)
  275. {
  276. return GetApartmentWindows7(CoGetApartmentType, implicit);
  277. }
  278. #endif
  279. }
  280. ApartmentState ThreadImpl::GetApartment()
  281. {
  282. Assert(GetCurrentThreadId() == m_ThreadId);
  283. ApartmentState state = static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
  284. if (kApartmentStateUnknown == state)
  285. {
  286. bool implicit;
  287. state = GetApartmentImpl(&implicit);
  288. if (!implicit)
  289. m_ApartmentState = state;
  290. }
  291. return state;
  292. }
  293. ApartmentState ThreadImpl::GetExplicitApartment()
  294. {
  295. return static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
  296. }
  297. ApartmentState ThreadImpl::SetApartment(ApartmentState state)
  298. {
  299. Assert(GetCurrentThreadId() == m_ThreadId);
  300. // Unknown state uninitializes COM.
  301. if (kApartmentStateUnknown == state)
  302. {
  303. if (m_ApartmentState & kApartmentStateCoInitialized)
  304. {
  305. CoUninitialize();
  306. m_ApartmentState = kApartmentStateUnknown;
  307. }
  308. return GetApartment();
  309. }
  310. // Initialize apartment state. Ignore result of this function because it will return MTA value for both implicit and explicit apartment.
  311. // On the other hand m_ApartmentState will only be set to MTA if it was initialized explicitly with CoInitializeEx.
  312. GetApartment();
  313. ApartmentState currentState = static_cast<ApartmentState>(m_ApartmentState & ~kApartmentStateCoInitialized);
  314. if (kApartmentStateUnknown != currentState)
  315. {
  316. Assert(state == currentState);
  317. return currentState;
  318. }
  319. #if IL2CPP_TARGET_XBOXONE
  320. if (state == kApartmentStateInSTA)
  321. {
  322. // Only assert in debug.. we wouldn't want to bring down the application in Release config
  323. IL2CPP_ASSERT(false && "STA apartment state is not supported on Xbox One");
  324. state = kApartmentStateInMTA;
  325. }
  326. #endif
  327. HRESULT hr = CoInitializeEx(nullptr, (kApartmentStateInSTA == state) ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED);
  328. if (SUCCEEDED(hr))
  329. {
  330. m_ApartmentState = state;
  331. if (S_OK == hr)
  332. m_ApartmentState = static_cast<ApartmentState>(m_ApartmentState | kApartmentStateCoInitialized);
  333. else
  334. CoUninitialize(); // Someone has already called correct CoInitialize. Don't leave incorrect reference count.
  335. }
  336. else if (RPC_E_CHANGED_MODE == hr)
  337. {
  338. // CoInitialize has already been called with a different apartment state.
  339. m_ApartmentState = (kApartmentStateInSTA == state) ? kApartmentStateInMTA : kApartmentStateInSTA;
  340. }
  341. else
  342. {
  343. // Based on where this function is called (Init and Shutdown) we can't really recover from this, so
  344. // just abort.
  345. abort();
  346. }
  347. return GetApartment();
  348. }
  349. void ThreadImpl::SetExplicitApartment(ApartmentState state)
  350. {
  351. Assert(!(m_ApartmentState & kApartmentStateCoInitialized));
  352. m_ApartmentState = state;
  353. }
  354. size_t ThreadImpl::CurrentThreadId()
  355. {
  356. return GetCurrentThreadId();
  357. }
  358. ThreadImpl* ThreadImpl::CreateForCurrentThread()
  359. {
  360. ThreadImpl* thread = new ThreadImpl();
  361. BOOL duplicateResult = DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(), &thread->m_ThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
  362. Assert(duplicateResult && "DuplicateHandle failed.");
  363. thread->m_ThreadId = ::GetCurrentThreadId();
  364. return thread;
  365. }
  366. bool ThreadImpl::YieldInternal()
  367. {
  368. return SwitchToThread();
  369. }
  370. #if IL2CPP_HAS_NATIVE_THREAD_CLEANUP
  371. static Thread::ThreadCleanupFunc s_ThreadCleanupFunction;
  372. static ThreadLocalValue s_ThreadCleanupArguments;
  373. void ThreadImpl::SetNativeThreadCleanup(Thread::ThreadCleanupFunc cleanupFunction)
  374. {
  375. s_ThreadCleanupFunction = cleanupFunction;
  376. }
  377. void ThreadImpl::RegisterCurrentThreadForCleanup(void* arg)
  378. {
  379. s_ThreadCleanupArguments.SetValue(arg);
  380. }
  381. void ThreadImpl::UnregisterCurrentThreadForCleanup()
  382. {
  383. s_ThreadCleanupArguments.SetValue(NULL);
  384. }
  385. void ThreadImpl::OnCurrentThreadExiting()
  386. {
  387. Thread::ThreadCleanupFunc cleanupFunction = s_ThreadCleanupFunction;
  388. if (cleanupFunction == NULL)
  389. return;
  390. void* threadCleanupArgument = NULL;
  391. s_ThreadCleanupArguments.GetValue(&threadCleanupArgument);
  392. if (threadCleanupArgument != NULL)
  393. cleanupFunction(threadCleanupArgument);
  394. }
  395. #endif
  396. }
  397. }
  398. #endif