eathread_thread_cpp11.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include "eathread/eathread_thread.h"
  5. #include "eathread/eathread.h"
  6. #include "eathread/eathread_sync.h"
  7. #include "eathread/eathread_callstack.h"
  8. #include "eathread/internal/eathread_global.h"
  9. namespace EA
  10. {
  11. namespace Thread
  12. {
  13. extern Allocator* gpAllocator;
  14. static AtomicInt32 nLastProcessor = 0;
  15. const size_t kMaxThreadDynamicDataCount = 128;
  16. struct EAThreadGlobalVars
  17. {
  18. char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)];
  19. AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount];
  20. Mutex gThreadDynamicMutex;
  21. };
  22. EATHREAD_GLOBALVARS_CREATE_INSTANCE;
  23. EAThreadDynamicData* AllocateThreadDynamicData()
  24. {
  25. for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i)
  26. {
  27. if (EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0))
  28. return (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  29. }
  30. // This is a safety fallback mechanism. In practice it won't be used in almost all situations.
  31. if (gpAllocator)
  32. return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData));
  33. return nullptr;
  34. }
  35. void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData)
  36. {
  37. pEAThreadDynamicData->~EAThreadDynamicData();
  38. if ((pEAThreadDynamicData >= (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount)))
  39. {
  40. EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0);
  41. }
  42. else
  43. {
  44. // Assume the data was allocated via the fallback mechanism.
  45. if (gpAllocator)
  46. {
  47. gpAllocator->Free(pEAThreadDynamicData);
  48. }
  49. }
  50. }
  51. EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId)
  52. {
  53. for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i)
  54. {
  55. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  56. if (pTDD->mpComp && pTDD->mpComp->mThread.get_id() == threadId)
  57. return pTDD;
  58. }
  59. return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved.
  60. }
  61. EAThreadDynamicData* FindThreadDynamicData(EA::Thread::ThreadUniqueId threadId)
  62. {
  63. for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i)
  64. {
  65. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  66. if (pTDD->mUniqueThreadId == threadId)
  67. return pTDD;
  68. }
  69. return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved.
  70. }
  71. EAThreadDynamicData* FindThreadDynamicData(EA::Thread::SysThreadId sysThreadId)
  72. {
  73. for (size_t i(0); i < kMaxThreadDynamicDataCount; ++i)
  74. {
  75. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  76. if (pTDD->mpComp && pTDD->mpComp->mThread.native_handle() == sysThreadId)
  77. return pTDD;
  78. }
  79. // NOTE: This function does not support finding externally created threads due to limitations in the CPP11 std::thread API.
  80. // At the time of writing, it is not possible to retrieve the thread object of a thread not created by the CPP11 API.
  81. return nullptr; // This is no practical way we can find the data unless thread-specific storage was involved.
  82. }
  83. }
  84. }
  85. EA_DISABLE_VC_WARNING(4355) // this used in base member initializer list - should be safe in this context
  86. EAThreadDynamicData::EAThreadDynamicData(void* userFunc, void* userContext, void* userWrapperFunc, ThreadFunc threadFunc) :
  87. mnRefCount(2), // Init ref count to 2, one corresponding release happens on threadFunc exit and the other when Thread class is destroyed or Begin is called again
  88. mStatus(EA::Thread::Thread::kStatusNone),
  89. mpComp(nullptr)
  90. {
  91. mpComp = new EAThreadComposite();
  92. if(mpComp)
  93. mpComp->mThread = std::thread(threadFunc, this, userFunc, userContext, userWrapperFunc); // This doesn't spawn CPP11 threads when created within the EAThreadComposite constructor.
  94. }
  95. EAThreadDynamicData::EAThreadDynamicData(EA::Thread::ThreadUniqueId uniqueThreadId, const char* pThreadName) :
  96. mnRefCount(2), // Init ref count to 2, one corresponding release happens on threadFunc exit and the other when Thread class is destroyed or Begin is called again
  97. mStatus(EA::Thread::Thread::kStatusNone),
  98. mpComp(nullptr),
  99. mUniqueThreadId(uniqueThreadId)
  100. {
  101. strncpy(mName, pThreadName, EATHREAD_NAME_SIZE);
  102. mName[EATHREAD_NAME_SIZE - 1] = 0;
  103. }
  104. EA_RESTORE_VC_WARNING()
  105. EAThreadDynamicData::~EAThreadDynamicData()
  106. {
  107. if (mpComp->mThread.joinable())
  108. mpComp->mThread.detach();
  109. if(mpComp)
  110. delete mpComp;
  111. mpComp = nullptr;
  112. // the threads, promises, and futures in this class will
  113. // allocate memory with the Concurrency runtime new/delete operators.
  114. // If you're crashing in here with access violations on process exit,
  115. // then you likely have a static instance of EA::Thread::Thread somewhere
  116. // that's being destructed after your memory system is uninitialized
  117. // leaving dangling pointers to bad memory. Attempt to change
  118. // these static instances to be constructed/destructed with the scope
  119. // of normal app operation.
  120. }
  121. void EAThreadDynamicData::AddRef()
  122. {
  123. mnRefCount.Increment();
  124. }
  125. void EAThreadDynamicData::Release()
  126. {
  127. if(mnRefCount.Decrement() == 0)
  128. EA::Thread::FreeThreadDynamicData(this);
  129. }
  130. namespace EA
  131. {
  132. namespace Thread
  133. {
  134. ThreadParameters::ThreadParameters() :
  135. mpStack(NULL),
  136. mnStackSize(0),
  137. mnPriority(kThreadPriorityDefault),
  138. mnProcessor(kProcessorDefault),
  139. mpName(""),
  140. mbDisablePriorityBoost(false)
  141. {
  142. }
  143. RunnableFunctionUserWrapper Thread::sGlobalRunnableFunctionUserWrapper = NULL;
  144. RunnableClassUserWrapper Thread::sGlobalRunnableClassUserWrapper = NULL;
  145. AtomicInt32 Thread::sDefaultProcessor = kProcessorAny;
  146. RunnableFunctionUserWrapper Thread::GetGlobalRunnableFunctionUserWrapper()
  147. {
  148. return sGlobalRunnableFunctionUserWrapper;
  149. }
  150. void Thread::SetGlobalRunnableFunctionUserWrapper(RunnableFunctionUserWrapper pUserWrapper)
  151. {
  152. if (sGlobalRunnableFunctionUserWrapper != NULL)
  153. {
  154. // Can only be set once in entire game.
  155. EAT_ASSERT(false);
  156. }
  157. else
  158. {
  159. sGlobalRunnableFunctionUserWrapper = pUserWrapper;
  160. }
  161. }
  162. RunnableClassUserWrapper Thread::GetGlobalRunnableClassUserWrapper()
  163. {
  164. return sGlobalRunnableClassUserWrapper;
  165. }
  166. void Thread::SetGlobalRunnableClassUserWrapper(RunnableClassUserWrapper pUserWrapper)
  167. {
  168. if (sGlobalRunnableClassUserWrapper != NULL)
  169. {
  170. // Can only be set once in entire game.
  171. EAT_ASSERT(false);
  172. }
  173. else
  174. {
  175. sGlobalRunnableClassUserWrapper = pUserWrapper;
  176. }
  177. }
  178. Thread::Thread()
  179. {
  180. mThreadData.mpData = NULL;
  181. }
  182. Thread::Thread(const Thread& t) :
  183. mThreadData(t.mThreadData)
  184. {
  185. if (mThreadData.mpData)
  186. mThreadData.mpData->AddRef();
  187. }
  188. Thread& Thread::operator=(const Thread& t)
  189. {
  190. // We don't synchronize access to mpData; we assume that the user
  191. // synchronizes it or this Thread instances is used from a single thread.
  192. if (t.mThreadData.mpData)
  193. t.mThreadData.mpData->AddRef();
  194. if (mThreadData.mpData)
  195. mThreadData.mpData->Release();
  196. mThreadData = t.mThreadData;
  197. return *this;
  198. }
  199. Thread::~Thread()
  200. {
  201. // We don't synchronize access to mpData; we assume that the user
  202. // synchronizes it or this Thread instances is used from a single thread.
  203. if (mThreadData.mpData)
  204. mThreadData.mpData->Release();
  205. }
  206. static void RunnableFunctionInternal(EAThreadDynamicData* tdd, void* userFunc, void* userContext, void* userWrapperFunc)
  207. {
  208. tdd->mStatus = Thread::kStatusRunning;
  209. tdd->mpStackBase = EA::Thread::GetStackBase();
  210. RunnableFunction pFunction = (RunnableFunction)userFunc;
  211. if (userWrapperFunc)
  212. {
  213. RunnableFunctionUserWrapper pWrapperFunction = (RunnableFunctionUserWrapper)userWrapperFunc;
  214. // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext
  215. tdd->mpComp->mReturnPromise.set_value(pWrapperFunction(pFunction, userContext));
  216. }
  217. else
  218. {
  219. tdd->mpComp->mReturnPromise.set_value(pFunction(userContext));
  220. }
  221. tdd->mStatus = Thread::kStatusEnded;
  222. tdd->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor
  223. }
  224. ThreadId Thread::Begin(RunnableFunction pFunction, void* pContext, const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper)
  225. {
  226. // Check there is an entry for the current thread context in our ThreadDynamicData array.
  227. ThreadUniqueId threadUniqueId;
  228. EAThreadGetUniqueId(threadUniqueId);
  229. if(!FindThreadDynamicData(threadUniqueId))
  230. {
  231. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData(threadUniqueId, "external");
  232. if(pData)
  233. {
  234. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  235. // Do no AddRef for thread execution because this is not an EAThread managed thread.
  236. }
  237. }
  238. if (mThreadData.mpData)
  239. mThreadData.mpData->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor
  240. // C++11 Threads don't support user-supplied stacks. A user-supplied stack pointer
  241. // here would be a waste of user memory, and so we assert that mpStack == NULL.
  242. EAT_ASSERT(!pTP || (pTP->mpStack == NULL));
  243. // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be
  244. // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed
  245. // during execution.
  246. EAThreadDynamicData* pDataAddr = AllocateThreadDynamicData();
  247. EAT_ASSERT(pDataAddr != nullptr);
  248. EAThreadDynamicData* pData = new(pDataAddr) EAThreadDynamicData(pFunction, pContext, pUserWrapper, RunnableFunctionInternal); // Note that we use a special new here which doesn't use the heap.
  249. EAT_ASSERT(pData != nullptr);
  250. mThreadData.mpData = pData;
  251. if (pTP)
  252. SetName(pTP->mpName);
  253. return pData->mpComp->mThread.get_id();
  254. }
  255. static void RunnableObjectInternal(EAThreadDynamicData* tdd, void* userFunc, void* userContext, void* userWrapperFunc)
  256. {
  257. tdd->mStatus = Thread::kStatusRunning;
  258. IRunnable* pRunnable = (IRunnable*)userFunc;
  259. if (userWrapperFunc)
  260. {
  261. RunnableClassUserWrapper pWrapperFunction = (RunnableClassUserWrapper)userWrapperFunc;
  262. // if user wrapper is specified, call user wrapper and pass down the pFunction and pContext
  263. tdd->mpComp->mReturnPromise.set_value(pWrapperFunction(pRunnable, userContext));
  264. }
  265. else
  266. {
  267. tdd->mpComp->mReturnPromise.set_value(pRunnable->Run(userContext));
  268. }
  269. tdd->mStatus = Thread::kStatusEnded;
  270. tdd->Release(); // Matches implicit AddRef in EAThreadDynamicData constructor
  271. }
  272. ThreadId Thread::Begin(IRunnable* pRunnable, void* pContext, const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper)
  273. {
  274. if (mThreadData.mpData)
  275. mThreadData.mpData->Release(); // Matches an implicit AddRef in EAThreadDynamicData constructor
  276. // C++11 Threads don't support user-supplied stacks. A user-supplied stack pointer
  277. // here would be a waste of user memory, and so we assert that mpStack == NULL.
  278. EAT_ASSERT(!pTP || (pTP->mpStack == NULL));
  279. // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be
  280. // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed
  281. // during execution.
  282. EAThreadDynamicData* pDataAddr = AllocateThreadDynamicData();
  283. EAT_ASSERT(pDataAddr != nullptr);
  284. EAThreadDynamicData* pData = new(pDataAddr) EAThreadDynamicData(pRunnable, pContext, pUserWrapper, RunnableObjectInternal); // Note that we use a special new here which doesn't use the heap.
  285. EAT_ASSERT(pData != nullptr);
  286. mThreadData.mpData = pData;
  287. if (pTP)
  288. SetName(pTP->mpName);
  289. EAT_ASSERT(pData && pData->mpComp);
  290. return pData->mpComp->mThread.get_id();
  291. }
  292. Thread::Status Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue)
  293. {
  294. // The mThreadData memory is shared between threads and when
  295. // reading it we must be synchronized.
  296. EAReadWriteBarrier();
  297. // A mutex lock around mpData is not needed below because
  298. // mpData is never allowed to go from non-NULL to NULL.
  299. // Todo: Consider that there may be a subtle race condition here if
  300. // the user immediately calls WaitForEnd right after calling Begin.
  301. if (mThreadData.mpData && mThreadData.mpData->mpComp)
  302. {
  303. // We must not call WaitForEnd from the thread we are waiting to end. That would result in a deadlock.
  304. EAT_ASSERT(mThreadData.mpData->mpComp->mThread.get_id() != GetThreadId());
  305. std::chrono::milliseconds timeoutAbsoluteMs(timeoutAbsolute);
  306. std::chrono::time_point<std::chrono::system_clock> timeoutTime(timeoutAbsoluteMs);
  307. if (mThreadData.mpData->mpComp->mReturnFuture.wait_until(timeoutTime) == std::future_status::timeout)
  308. {
  309. return kStatusRunning;
  310. }
  311. if (pThreadReturnValue)
  312. {
  313. mThreadData.mpData->mReturnValue = mThreadData.mpData->mpComp->mReturnFuture.get();
  314. *pThreadReturnValue = mThreadData.mpData->mReturnValue;
  315. }
  316. mThreadData.mpData->mpComp->mThread.join();
  317. return kStatusEnded; // A thread was created, so it must have ended.
  318. }
  319. else
  320. {
  321. // Else the user hasn't started the thread yet, so we wait until the user starts it.
  322. // Ideally, what we really want to do here is wait for some kind of signal.
  323. // Instead for the time being we do a polling loop.
  324. while ((!mThreadData.mpData) && (GetThreadTime() < timeoutAbsolute))
  325. {
  326. ThreadSleep(1);
  327. }
  328. if (mThreadData.mpData)
  329. return WaitForEnd(timeoutAbsolute);
  330. }
  331. return kStatusNone; // No thread has been started.
  332. }
  333. Thread::Status Thread::GetStatus(intptr_t* pThreadReturnValue) const
  334. {
  335. if (mThreadData.mpData && mThreadData.mpData->mpComp)
  336. {
  337. auto status = static_cast<Thread::Status>(mThreadData.mpData->mStatus.GetValue());
  338. if (pThreadReturnValue && status == kStatusEnded)
  339. {
  340. if (mThreadData.mpData->mpComp->mGetStatusFuture.valid())
  341. mThreadData.mpData->mReturnValue = mThreadData.mpData->mpComp->mGetStatusFuture.get();
  342. *pThreadReturnValue = mThreadData.mpData->mReturnValue;
  343. }
  344. return status;
  345. }
  346. return kStatusNone;
  347. }
  348. int Thread::GetPriority() const
  349. {
  350. // No way to query or set thread priority through standard C++11 thread library.
  351. // On some platforms this could be implemented through platform specific APIs using native_handle()
  352. return kThreadPriorityDefault;
  353. }
  354. bool Thread::SetPriority(int nPriority)
  355. {
  356. // No way to query or set thread priority through standard C++11 thread library.
  357. // On some platforms this could be implemented through platform specific APIs using native_handle()
  358. return false;
  359. }
  360. void Thread::SetProcessor(int nProcessor)
  361. {
  362. // No way to query or set thread priority through standard C++11 thread library.
  363. // On some platforms this could be implemented through platform specific APIs using native_handle()
  364. }
  365. void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask)
  366. {
  367. if(mThreadData.mpData)
  368. {
  369. EA::Thread::SetThreadAffinityMask(nAffinityMask);
  370. }
  371. }
  372. EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask()
  373. {
  374. if(mThreadData.mpData)
  375. {
  376. return mThreadData.mpData->mnThreadAffinityMask;
  377. }
  378. return kThreadAffinityMaskAny;
  379. }
  380. void Thread::Wake()
  381. {
  382. // No way to wake a thread through standard C++11 thread library.
  383. // On some platforms this could be implemented through platform specific APIs using native_handle()
  384. }
  385. const char* Thread::GetName() const
  386. {
  387. if (mThreadData.mpData)
  388. return mThreadData.mpData->mName;
  389. return "";
  390. }
  391. void Thread::SetName(const char* pName)
  392. {
  393. if (mThreadData.mpData && pName)
  394. {
  395. strncpy(mThreadData.mpData->mName, pName, EATHREAD_NAME_SIZE);
  396. mThreadData.mpData->mName[EATHREAD_NAME_SIZE - 1] = 0;
  397. }
  398. }
  399. ThreadId Thread::GetId() const
  400. {
  401. if (mThreadData.mpData && mThreadData.mpData->mpComp)
  402. return mThreadData.mpData->mpComp->mThread.get_id();
  403. return kThreadIdInvalid;
  404. }
  405. }
  406. }