eathread_thread_kettle.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. #include <EABase/eabase.h>
  5. #include <eathread/eathread_thread.h>
  6. #include <eathread/eathread.h>
  7. #include <eathread/eathread_sync.h>
  8. #include <eathread/eathread_callstack.h>
  9. #include <new>
  10. #include <kernel.h>
  11. #include <time.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <errno.h>
  15. #include <sceerror.h>
  16. #define EA_ALLOW_POSIX_THREADS_PRIORITIES 1
  17. namespace
  18. {
  19. // We convert a an EAThread priority (higher value implies higher priority) to a native priority
  20. // value, as some implementations of pthread_disableds use lower values to indicate higher priority.
  21. void ConvertToNativePriority(int eathreadPriority, sched_param& param, int& policy)
  22. {
  23. using namespace EA::Thread;
  24. policy = SCE_KERNEL_SCHED_RR;
  25. const int nMin = SCE_KERNEL_PRIO_FIFO_HIGHEST;
  26. const int nMax = SCE_KERNEL_PRIO_FIFO_LOWEST;
  27. // Kettle pthread_disableds uses a reversed interpretation of sched_get_priority_min and sched_get_priority_max.
  28. param.sched_priority = (SCE_KERNEL_PRIO_FIFO_DEFAULT + (-1 * eathreadPriority));
  29. if(param.sched_priority < nMin)
  30. param.sched_priority = nMin;
  31. else if(param.sched_priority > nMax)
  32. param.sched_priority = nMax;
  33. }
  34. // We convert a native priority value to an EAThread priority (higher value implies higher
  35. // priority), as some implementations of pthread_disableds use lower values to indicate higher priority.
  36. int ConvertFromNativePriority(const sched_param& param, int policy)
  37. {
  38. using namespace EA::Thread;
  39. // Some implementations of pthreads associate higher priorities with smaller
  40. // integer values. We hide this. To the user, a higher value must always
  41. // indicate higher priority.
  42. // Kettle pthread_disableds uses a reversed interpretation of sched_get_priority_min and sched_get_priority_max.
  43. return -1 * (param.sched_priority - SCE_KERNEL_PRIO_FIFO_DEFAULT);
  44. }
  45. // Setup stack and/or priority of a new thread
  46. void SetupThreadAttributes(ScePthreadAttr& creationAttribs, const EA::Thread::ThreadParameters* pTP)
  47. {
  48. int result = 0;
  49. EA_UNUSED( result ); //only used for assertions
  50. // We create the thread as attached, and we'll call either pthread_disabled_join or pthread_disabled_detach,
  51. // depending on whether WaitForEnd (pthread_disabled_join) is called or not (pthread_disabled_detach).
  52. if(pTP)
  53. {
  54. // Set thread stack address and/or size
  55. if(pTP->mpStack)
  56. {
  57. EAT_ASSERT(pTP->mnStackSize != 0);
  58. result = scePthreadAttrSetstack(&creationAttribs, (void*)pTP->mpStack, pTP->mnStackSize);
  59. EAT_ASSERT(result == 0);
  60. }
  61. else if(pTP->mnStackSize)
  62. {
  63. result = scePthreadAttrSetstacksize(&creationAttribs, pTP->mnStackSize);
  64. EAT_ASSERT(result == 0);
  65. }
  66. // Set initial non-zero priority
  67. // Even if pTP->mnPriority == kThreadPriorityDefault, we need to run this on some platforms, as the thread priority for new threads on them isn't the same as the thread priority for the main thread.
  68. int policy = SCHED_OTHER;
  69. sched_param param;
  70. ConvertToNativePriority(pTP->mnPriority, param, policy);
  71. result = scePthreadAttrSetschedpolicy(&creationAttribs, policy);
  72. EAT_ASSERT(result == 0);
  73. result = scePthreadAttrSetschedparam(&creationAttribs, &param);
  74. EAT_ASSERT(result == 0);
  75. // Unix doesn't let you specify thread CPU affinity via pthread_disabled attributes.
  76. // Instead you need to call sched_setaffinity or pthread_setaffinity_np.
  77. }
  78. else
  79. {
  80. result = scePthreadAttrSetschedpolicy(&creationAttribs, SCE_KERNEL_SCHED_RR);
  81. EAT_ASSERT(result == 0);
  82. }
  83. }
  84. // This function is not currently used if the thread name can be set from any other thread
  85. #if !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED
  86. void SetCurrentThreadName(const char8_t* pName)
  87. {
  88. EAT_COMPILETIME_ASSERT(EATHREAD_NAME_SIZE == 32); // New name (up to 32 bytes including the NULL terminator), or NULL
  89. scePthreadRename(scePthreadSelf(), pName);
  90. }
  91. #endif
  92. static void SetPlatformThreadAffinity(EAThreadDynamicData* pTDD)
  93. {
  94. if(pTDD->mThreadId != EA::Thread::kThreadIdInvalid) // If the thread has been created...
  95. {
  96. SceKernelCpumask mask;
  97. mask = (1 << pTDD->mStartupProcessor) & 0xFF;
  98. int nResult = scePthreadSetaffinity(pTDD->mSysThreadId, mask);
  99. EAT_ASSERT(nResult == SCE_OK); EA_UNUSED(nResult);
  100. }
  101. // Else the thread hasn't started yet, or has already exited. Let the thread set its own
  102. // affinity when it starts.
  103. }
  104. } // namespace
  105. namespace EA
  106. {
  107. namespace Thread
  108. {
  109. extern Allocator* gpAllocator;
  110. const size_t kMaxThreadDynamicDataCount = 128;
  111. struct EAThreadGlobalVars
  112. {
  113. EA_PREFIX_ALIGN(8)
  114. char gThreadDynamicData[kMaxThreadDynamicDataCount][sizeof(EAThreadDynamicData)] EA_POSTFIX_ALIGN(8);
  115. AtomicInt32 gThreadDynamicDataAllocated[kMaxThreadDynamicDataCount];
  116. Mutex gThreadDynamicMutex;
  117. };
  118. EATHREAD_GLOBALVARS_CREATE_INSTANCE;
  119. EAThreadDynamicData* AllocateThreadDynamicData()
  120. {
  121. for(size_t i(0); i < kMaxThreadDynamicDataCount; i++)
  122. {
  123. if(EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[i].SetValueConditional(1, 0))
  124. return (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  125. }
  126. // This is a safety fallback mechanism. In practice it won't be used in almost all situations.
  127. if(gpAllocator)
  128. return (EAThreadDynamicData*)gpAllocator->Alloc(sizeof(EAThreadDynamicData));
  129. else
  130. return (EAThreadDynamicData*)new char[sizeof(EAThreadDynamicData)]; // We assume the returned alignment is sufficient.
  131. }
  132. void FreeThreadDynamicData(EAThreadDynamicData* pEAThreadDynamicData)
  133. {
  134. if((pEAThreadDynamicData >= (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData) && (pEAThreadDynamicData < ((EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData + kMaxThreadDynamicDataCount)))
  135. {
  136. pEAThreadDynamicData->~EAThreadDynamicData();
  137. EATHREAD_GLOBALVARS.gThreadDynamicDataAllocated[pEAThreadDynamicData - (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData].SetValue(0);
  138. }
  139. else
  140. {
  141. // Assume the data was allocated via the fallback mechanism.
  142. pEAThreadDynamicData->~EAThreadDynamicData();
  143. if(gpAllocator)
  144. gpAllocator->Free(pEAThreadDynamicData);
  145. else
  146. delete[] (char*)pEAThreadDynamicData;
  147. }
  148. }
  149. // This is a public function.
  150. EAThreadDynamicData* FindThreadDynamicData(ThreadId threadId)
  151. {
  152. for(size_t i(0); i < kMaxThreadDynamicDataCount; i++)
  153. {
  154. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  155. if(pTDD->mThreadId == threadId)
  156. return pTDD;
  157. }
  158. return NULL; // This is no practical way we can find the data unless thread-specific storage was involved.
  159. }
  160. EAThreadDynamicData* FindThreadDynamicData(SysThreadId sysThreadId)
  161. {
  162. for(size_t i(0); i < kMaxThreadDynamicDataCount; i++)
  163. {
  164. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)(void*)EATHREAD_GLOBALVARS.gThreadDynamicData[i];
  165. if(pTDD->mSysThreadId == sysThreadId)
  166. return pTDD;
  167. }
  168. return NULL; // This is no practical way we can find the data unless thread-specific storage was involved.
  169. }
  170. }
  171. }
  172. EAThreadDynamicData::EAThreadDynamicData()
  173. : mThreadId(EA::Thread::kThreadIdInvalid),
  174. mSysThreadId(0),
  175. mThreadPid(0),
  176. mnStatus(EA::Thread::Thread::kStatusNone),
  177. mnReturnValue(0),
  178. //mpStartContext[],
  179. mpBeginThreadUserWrapper(NULL),
  180. mnRefCount(0),
  181. //mName[],
  182. mStartupProcessor(EA::Thread::kProcessorDefault),
  183. mRunMutex(),
  184. mStartedSemaphore(),
  185. mnThreadAffinityMask(EA::Thread::kThreadAffinityMaskAny)
  186. {
  187. memset(mpStartContext, 0, sizeof(mpStartContext));
  188. memset(mName, 0, sizeof(mName));
  189. }
  190. EAThreadDynamicData::~EAThreadDynamicData()
  191. {
  192. if(mThreadId != EA::Thread::kThreadIdInvalid)
  193. scePthreadDetach(mSysThreadId);
  194. mThreadId = EA::Thread::kThreadIdInvalid;
  195. mThreadPid = 0;
  196. mSysThreadId = 0;
  197. }
  198. void EAThreadDynamicData::AddRef()
  199. {
  200. mnRefCount.Increment(); // Note that mnRefCount is an AtomicInt32.
  201. }
  202. void EAThreadDynamicData::Release()
  203. {
  204. if(mnRefCount.Decrement() == 0) // Note that mnRefCount is an AtomicInt32.
  205. EA::Thread::FreeThreadDynamicData(this);
  206. }
  207. EA::Thread::ThreadParameters::ThreadParameters()
  208. : mpStack(NULL),
  209. mnStackSize(0),
  210. mnPriority(kThreadPriorityDefault),
  211. mnProcessor(kProcessorDefault),
  212. mpName(""),
  213. mnAffinityMask(kThreadAffinityMaskAny),
  214. mbDisablePriorityBoost(false)
  215. {
  216. // Empty
  217. }
  218. EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::sGlobalRunnableFunctionUserWrapper = NULL;
  219. EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::sGlobalRunnableClassUserWrapper = NULL;
  220. EA::Thread::AtomicInt32 EA::Thread::Thread::sDefaultProcessor = kProcessorAny;
  221. EA::Thread::AtomicUint64 EA::Thread::Thread::sDefaultProcessorMask = UINT64_C(0xffffffffffffffff);
  222. EA::Thread::RunnableFunctionUserWrapper EA::Thread::Thread::GetGlobalRunnableFunctionUserWrapper()
  223. {
  224. return sGlobalRunnableFunctionUserWrapper;
  225. }
  226. void EA::Thread::Thread::SetGlobalRunnableFunctionUserWrapper(EA::Thread::RunnableFunctionUserWrapper pUserWrapper)
  227. {
  228. if(sGlobalRunnableFunctionUserWrapper)
  229. EAT_FAIL_MSG("Thread::SetGlobalRunnableFunctionUserWrapper already set."); // Can only be set once for the application.
  230. else
  231. sGlobalRunnableFunctionUserWrapper = pUserWrapper;
  232. }
  233. EA::Thread::RunnableClassUserWrapper EA::Thread::Thread::GetGlobalRunnableClassUserWrapper()
  234. {
  235. return sGlobalRunnableClassUserWrapper;
  236. }
  237. void EA::Thread::Thread::SetGlobalRunnableClassUserWrapper(EA::Thread::RunnableClassUserWrapper pUserWrapper)
  238. {
  239. if(sGlobalRunnableClassUserWrapper)
  240. EAT_FAIL_MSG("EAThread::SetGlobalRunnableClassUserWrapper already set."); // Can only be set once for the application.
  241. else
  242. sGlobalRunnableClassUserWrapper = pUserWrapper;
  243. }
  244. EA::Thread::Thread::Thread()
  245. {
  246. mThreadData.mpData = NULL;
  247. }
  248. EA::Thread::Thread::Thread(const Thread& t)
  249. : mThreadData(t.mThreadData)
  250. {
  251. if(mThreadData.mpData)
  252. mThreadData.mpData->AddRef();
  253. }
  254. EA::Thread::Thread& EA::Thread::Thread::operator=(const Thread& t)
  255. {
  256. // We don't synchronize access to mpData; we assume that the user
  257. // synchronizes it or this Thread instances is used from a single thread.
  258. if(t.mThreadData.mpData)
  259. t.mThreadData.mpData->AddRef();
  260. if(mThreadData.mpData)
  261. mThreadData.mpData->Release();
  262. mThreadData = t.mThreadData;
  263. return *this;
  264. }
  265. EA::Thread::Thread::~Thread()
  266. {
  267. // We don't synchronize access to mpData; we assume that the user
  268. // synchronizes it or this Thread instances is used from a single thread.
  269. if(mThreadData.mpData)
  270. mThreadData.mpData->Release();
  271. }
  272. static void* RunnableFunctionInternal(void* pContext)
  273. {
  274. // The parent thread is sharing memory with us and we need to
  275. // make sure our view of it is synchronized with the parent.
  276. EAReadWriteBarrier();
  277. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext;
  278. EA::Thread::RunnableFunction pFunction = (EA::Thread::RunnableFunction)pTDD->mpStartContext[0];
  279. void* pCallContext = pTDD->mpStartContext[1];
  280. pTDD->mThreadPid = 0;
  281. // Lock the runtime mutex which is used to allow other threads to wait on this thread with a timeout.
  282. pTDD->mRunMutex.Lock(); // Important that this be before the semaphore post.
  283. pTDD->mStartedSemaphore.Post(); // Announce that the thread has started.
  284. pTDD->mnStatus = EA::Thread::Thread::kStatusRunning;
  285. pTDD->mpStackBase = EA::Thread::GetStackBase();
  286. #if !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED
  287. // Under Unix we need to set the thread name from the thread that is being named and not from an outside thread.
  288. if(pTDD->mName[0])
  289. SetCurrentThreadName(pTDD->mName);
  290. #endif
  291. #ifdef EA_PLATFORM_ANDROID
  292. AttachJavaThread();
  293. #endif
  294. if(pTDD->mpBeginThreadUserWrapper)
  295. {
  296. // If user wrapper is specified, call user wrapper and pass the pFunction and pContext.
  297. EA::Thread::RunnableFunctionUserWrapper pWrapperFunction = (EA::Thread::RunnableFunctionUserWrapper)pTDD->mpBeginThreadUserWrapper;
  298. pTDD->mnReturnValue = pWrapperFunction(pFunction, pCallContext);
  299. }
  300. else
  301. pTDD->mnReturnValue = pFunction(pCallContext);
  302. #ifdef EA_PLATFORM_ANDROID
  303. DetachJavaThread();
  304. #endif
  305. void* pReturnValue = (void*)pTDD->mnReturnValue;
  306. pTDD->mnStatus = EA::Thread::Thread::kStatusEnded;
  307. pTDD->mRunMutex.Unlock();
  308. pTDD->Release();
  309. return pReturnValue;
  310. }
  311. static void* RunnableObjectInternal(void* pContext)
  312. {
  313. EAThreadDynamicData* const pTDD = (EAThreadDynamicData*)pContext;
  314. EA::Thread::IRunnable* pRunnable = (EA::Thread::IRunnable*)pTDD->mpStartContext[0];
  315. void* pCallContext = pTDD->mpStartContext[1];
  316. pTDD->mThreadPid = 0;
  317. pTDD->mRunMutex.Lock(); // Important that this be before the semaphore post.
  318. pTDD->mStartedSemaphore.Post();
  319. pTDD->mnStatus = EA::Thread::Thread::kStatusRunning;
  320. #if !EATHREAD_OTHER_THREAD_NAMING_SUPPORTED
  321. // Under Unix we need to set the thread name from the thread that is being named and not from an outside thread.
  322. if(pTDD->mName[0])
  323. SetCurrentThreadName(pTDD->mName);
  324. #endif
  325. #ifdef EA_PLATFORM_ANDROID
  326. AttachJavaThread();
  327. #endif
  328. if(pTDD->mpBeginThreadUserWrapper)
  329. {
  330. // If user wrapper is specified, call user wrapper and pass the pFunction and pContext.
  331. EA::Thread::RunnableClassUserWrapper pWrapperClass = (EA::Thread::RunnableClassUserWrapper)pTDD->mpBeginThreadUserWrapper;
  332. pTDD->mnReturnValue = pWrapperClass(pRunnable, pCallContext);
  333. }
  334. else
  335. pTDD->mnReturnValue = pRunnable->Run(pCallContext);
  336. #ifdef EA_PLATFORM_ANDROID
  337. DetachJavaThread();
  338. #endif
  339. void* const pReturnValue = (void*)pTDD->mnReturnValue;
  340. pTDD->mnStatus = EA::Thread::Thread::kStatusEnded;
  341. pTDD->mRunMutex.Unlock();
  342. pTDD->Release();
  343. return pReturnValue;
  344. }
  345. void EA::Thread::Thread::SetAffinityMask(EA::Thread::ThreadAffinityMask nAffinityMask)
  346. {
  347. if(mThreadData.mpData && mThreadData.mpData->mThreadId)
  348. {
  349. EA::Thread::SetThreadAffinityMask(mThreadData.mpData->mThreadId, nAffinityMask);
  350. }
  351. }
  352. EA::Thread::ThreadAffinityMask EA::Thread::Thread::GetAffinityMask()
  353. {
  354. if(mThreadData.mpData->mThreadId)
  355. {
  356. return mThreadData.mpData->mnThreadAffinityMask;
  357. }
  358. return kThreadAffinityMaskAny;
  359. }
  360. /// BeginThreadInternal
  361. /// Extraction of both RunnableFunction and RunnableObject EA::Thread::Begin in order to have thread initialization
  362. /// in one place
  363. static EA::Thread::ThreadId BeginThreadInternal(EAThreadData& mThreadData, void* pRunnableOrFunction, void* pContext, const EA::Thread::ThreadParameters* pTP,
  364. void* pUserWrapper, void* (*InternalThreadFunction)(void*))
  365. {
  366. using namespace EA::Thread;
  367. // The parent thread is sharing memory with us and we need to
  368. // make sure our view of it is synchronized with the parent.
  369. EAReadWriteBarrier();
  370. // Check there is an entry for the current thread context in our ThreadDynamicData array.
  371. EA::Thread::ThreadId thisThreadId = EA::Thread::GetThreadId();
  372. if(!FindThreadDynamicData(thisThreadId))
  373. {
  374. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData;
  375. if(pData)
  376. {
  377. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  378. // Do no AddRef for thread execution because this is not an EAThread managed thread.
  379. pData->AddRef(); // AddRef for this function, to be released upon this function's exit.
  380. pData->mThreadId = thisThreadId;
  381. pData->mSysThreadId = GetSysThreadId();
  382. strncpy(pData->mName, "external", EATHREAD_NAME_SIZE);
  383. pData->mName[EATHREAD_NAME_SIZE - 1] = 0;
  384. pData->mpStackBase = EA::Thread::GetStackBase();
  385. }
  386. }
  387. if(mThreadData.mpData)
  388. mThreadData.mpData->Release(); // Matches the "AddRef for ourselves" below.
  389. // We use the pData temporary throughout this function because it's possible that mThreadData.mpData could be
  390. // modified as we are executing, in particular in the case that mThreadData.mpData is destroyed and changed
  391. // during execution.
  392. EAThreadDynamicData* pData = new(AllocateThreadDynamicData()) EAThreadDynamicData; // Note that we use a special new here which doesn't use the heap.
  393. EAT_ASSERT(pData);
  394. if(pData)
  395. {
  396. mThreadData.mpData = pData;
  397. pData->AddRef(); // AddRef for ourselves, to be released upon this Thread class being deleted or upon Begin being called again for a new thread.
  398. pData->AddRef(); // AddRef for the thread, to be released upon the thread exiting.
  399. pData->AddRef(); // AddRef for this function, to be released upon this function's exit.
  400. pData->mThreadId = kThreadIdInvalid;
  401. pData->mSysThreadId = kSysThreadIdInvalid;
  402. pData->mThreadPid = 0;
  403. pData->mnStatus = Thread::kStatusNone;
  404. pData->mpStartContext[0] = pRunnableOrFunction;
  405. pData->mpStartContext[1] = pContext;
  406. pData->mpBeginThreadUserWrapper = pUserWrapper;
  407. pData->mStartupProcessor = pTP ? pTP->mnProcessor % EA::Thread::GetProcessorCount() : kProcessorDefault;
  408. pData->mnThreadAffinityMask = pTP ? pTP->mnAffinityMask : kThreadAffinityMaskAny;
  409. strncpy(pData->mName, (pTP && pTP->mpName) ? pTP->mpName : "", EATHREAD_NAME_SIZE);
  410. pData->mName[EATHREAD_NAME_SIZE - 1] = 0;
  411. // Pass NULL attribute pointer if there are no special setup steps
  412. ScePthreadAttr* pCreationAttribs = NULL;
  413. int result(0);
  414. ScePthreadAttr creationAttribs;
  415. scePthreadAttrInit(&creationAttribs);
  416. // Sony has stated that we should call scePthreadAttrSetinheritsched, otherwise the
  417. // thread priority set up in pthread_attr_t gets ignored by the newly created thread.
  418. scePthreadAttrSetinheritsched(&creationAttribs, SCE_PTHREAD_EXPLICIT_SCHED);
  419. if(pData->mStartupProcessor == EA::Thread::kProcessorAny)
  420. {
  421. if(pData->mnThreadAffinityMask == kThreadAffinityMaskAny)
  422. // Unless you specifically set the thread affinity to SCE_KERNEL_CPUMASK_USER_ALL,
  423. // Sony apparently assigns your thread to a single CPU.
  424. scePthreadAttrSetaffinity(&creationAttribs, SCE_KERNEL_CPUMASK_USER_ALL);
  425. else
  426. scePthreadAttrSetaffinity(&creationAttribs, pData->mnThreadAffinityMask);
  427. }
  428. else if(pData->mStartupProcessor != kProcessorDefault)
  429. {
  430. SceKernelCpumask mask = (1 << pData->mStartupProcessor) & 0xFF;
  431. scePthreadAttrSetaffinity(&creationAttribs, mask);
  432. }
  433. SetupThreadAttributes(creationAttribs, pTP);
  434. pCreationAttribs = &creationAttribs;
  435. result = scePthreadCreate(&pData->mSysThreadId, pCreationAttribs, InternalThreadFunction, pData, mThreadData.mpData->mName);
  436. if(result == 0) // If success...
  437. {
  438. // NOTE: This cast must match the caset that is done in EA::Thread::GetThreadId.
  439. pData->mThreadId = *reinterpret_cast<EA::Thread::ThreadId*>(pData->mSysThreadId);
  440. ThreadId threadIdTemp = pData->mThreadId; // Temp value because Release below might delete pData.
  441. // If additional attributes were used, free initialization data.
  442. if(pCreationAttribs)
  443. {
  444. result = scePthreadAttrDestroy(pCreationAttribs);
  445. EAT_ASSERT(result == 0);
  446. }
  447. pData->Release(); // Matches AddRef for this function.
  448. return threadIdTemp;
  449. }
  450. // If additional attributes were used, free initialization data
  451. if(pCreationAttribs)
  452. {
  453. result = scePthreadAttrDestroy(pCreationAttribs);
  454. EAT_ASSERT(result == 0);
  455. }
  456. pData->Release(); // Matches AddRef for "cleanup" above.
  457. pData->Release(); // Matches AddRef for this Thread class above.
  458. pData->Release(); // Matches AddRef for thread above.
  459. mThreadData.mpData = NULL; // mThreadData.mpData == pData
  460. }
  461. return (ThreadId)kThreadIdInvalid;
  462. }
  463. EA::Thread::ThreadId EA::Thread::Thread::Begin(RunnableFunction pFunction, void* pContext,
  464. const ThreadParameters* pTP, RunnableFunctionUserWrapper pUserWrapper)
  465. {
  466. ThreadId threadId = BeginThreadInternal(mThreadData, reinterpret_cast<void*>((uintptr_t)pFunction), pContext, pTP,
  467. reinterpret_cast<void*>((uintptr_t)pUserWrapper), RunnableFunctionInternal);
  468. return threadId;
  469. }
  470. EA::Thread::ThreadId EA::Thread::Thread::Begin(IRunnable* pRunnable, void* pContext,
  471. const ThreadParameters* pTP, RunnableClassUserWrapper pUserWrapper)
  472. {
  473. ThreadId threadId = BeginThreadInternal(mThreadData, reinterpret_cast<void*>((uintptr_t)pRunnable), pContext, pTP,
  474. reinterpret_cast<void*>((uintptr_t)pUserWrapper), RunnableObjectInternal);
  475. return threadId;
  476. }
  477. EA::Thread::Thread::Status EA::Thread::Thread::WaitForEnd(const ThreadTime& timeoutAbsolute, intptr_t* pThreadReturnValue)
  478. {
  479. // In order to support timeoutAbsolute, we don't just call pthread_disabled_join, as that's an infinitely blocking call.
  480. // Instead we wait on a Mutex (with a timeout) which the running thread locked, and will unlock as it is exiting.
  481. // Only after the successful Mutex lock do we call pthread_disabled_join, as we know that it won't block for an indeterminate
  482. // amount of time (barring a thread priority inversion problem). If the user never calls WaitForEnd, then we
  483. // will eventually call pthread_disabled_detach in the EAThreadDynamicData destructor.
  484. // The mThreadData memory is shared between threads and when
  485. // reading it we must be synchronized.
  486. EAReadWriteBarrier();
  487. // A mutex lock around mpData is not needed below because mpData is never allowed to go from non-NULL to NULL.
  488. // However, there is an argument that can be made for placing a memory read barrier before reading it.
  489. if(mThreadData.mpData) // If this is non-zero then we must have created the thread.
  490. {
  491. // We must not call WaitForEnd from the thread we are waiting to end.
  492. // That would result in a deadlock, at least if the timeout was infinite.
  493. EAT_ASSERT(mThreadData.mpData->mThreadId != EA::Thread::GetThreadId());
  494. Status currentStatus = GetStatus();
  495. if(currentStatus == kStatusNone) // If the thread hasn't started yet...
  496. {
  497. // The thread has not been started yet. Wait on the semaphore (which is posted when the thread actually starts executing).
  498. Semaphore::Result result = (Semaphore::Result)mThreadData.mpData->mStartedSemaphore.Wait(timeoutAbsolute);
  499. EAT_ASSERT(result != Semaphore::kResultError);
  500. if(result >= 0) // If the Wait succeeded, as opposed to timing out...
  501. {
  502. // We know for sure that the thread status is running now.
  503. currentStatus = kStatusRunning;
  504. mThreadData.mpData->mStartedSemaphore.Post(); // Re-post the semaphore so that any other callers of WaitForEnd don't block on the Wait above.
  505. }
  506. } // fall through.
  507. if(currentStatus == kStatusRunning) // If the thread has started but not yet exited...
  508. {
  509. // Lock on the mutex (which is available when the thread is exiting)
  510. Mutex::Result result = (Mutex::Result)mThreadData.mpData->mRunMutex.Lock(timeoutAbsolute);
  511. EAT_ASSERT(result != Mutex::kResultError);
  512. if(result > 0) // If the Lock succeeded, as opposed to timing out... then the thread has exited or is in the process of exiting.
  513. {
  514. // Do a pthread_disabled join. This is a blocking call, but we know that it will end very soon,
  515. // as the mutex unlock the thread did is done right before the thread returns to the OS.
  516. // The return value of pthread_disabled_join has information that isn't currently useful to us.
  517. scePthreadJoin(mThreadData.mpData->mSysThreadId, NULL);
  518. mThreadData.mpData->mThreadId = kThreadIdInvalid;
  519. // We know for sure that the thread status is ended now.
  520. currentStatus = kStatusEnded;
  521. mThreadData.mpData->mRunMutex.Unlock();
  522. }
  523. // Else the Lock timed out, which means that the thread didn't exit before we ran out of time.
  524. // In this case we need to return to the user that the status is kStatusRunning.
  525. }
  526. else
  527. {
  528. // Else currentStatus == kStatusEnded.
  529. scePthreadJoin(mThreadData.mpData->mSysThreadId, NULL);
  530. mThreadData.mpData->mThreadId = kThreadIdInvalid;
  531. }
  532. if(currentStatus == kStatusEnded)
  533. {
  534. // Call GetStatus again to get the thread return value.
  535. currentStatus = GetStatus(pThreadReturnValue);
  536. }
  537. return currentStatus;
  538. }
  539. else
  540. {
  541. // Else the user hasn't started the thread yet, so we wait until the user starts it.
  542. // Ideally, what we really want to do here is wait for some kind of signal.
  543. // Instead for the time being we do a polling loop.
  544. while((!mThreadData.mpData || (mThreadData.mpData->mThreadId == kThreadIdInvalid)) && (GetThreadTime() < timeoutAbsolute))
  545. {
  546. ThreadSleep(1);
  547. EAReadWriteBarrier();
  548. EACompilerMemoryBarrier();
  549. }
  550. if(mThreadData.mpData)
  551. return WaitForEnd(timeoutAbsolute);
  552. }
  553. return kStatusNone;
  554. }
  555. EA::Thread::Thread::Status EA::Thread::Thread::GetStatus(intptr_t* pThreadReturnValue) const
  556. {
  557. if(mThreadData.mpData)
  558. {
  559. EAReadBarrier();
  560. Status status = (Status)mThreadData.mpData->mnStatus;
  561. if(pThreadReturnValue && (status == kStatusEnded))
  562. *pThreadReturnValue = mThreadData.mpData->mnReturnValue;
  563. return status;
  564. }
  565. return kStatusNone;
  566. }
  567. EA::Thread::ThreadId EA::Thread::Thread::GetId() const
  568. {
  569. // A mutex lock around mpData is not needed below because
  570. // mpData is never allowed to go from non-NULL to NULL.
  571. if(mThreadData.mpData)
  572. return mThreadData.mpData->mThreadId;
  573. return kThreadIdInvalid;
  574. }
  575. int EA::Thread::Thread::GetPriority() const
  576. {
  577. // A mutex lock around mpData is not needed below because
  578. // mpData is never allowed to go from non-NULL to NULL.
  579. if(mThreadData.mpData)
  580. {
  581. int policy;
  582. sched_param param;
  583. int result = scePthreadGetschedparam(mThreadData.mpData->mSysThreadId, &policy, &param);
  584. if(result == 0)
  585. return ConvertFromNativePriority(param, policy);
  586. return kThreadPriorityDefault;
  587. }
  588. return kThreadPriorityUnknown;
  589. }
  590. bool EA::Thread::Thread::SetPriority(int nPriority)
  591. {
  592. // A mutex lock around mpData is not needed below because
  593. // mpData is never allowed to go from non-NULL to NULL.
  594. EAT_ASSERT(nPriority != kThreadPriorityUnknown);
  595. if(mThreadData.mpData)
  596. {
  597. int policy;
  598. sched_param param;
  599. int result = scePthreadGetschedparam(mThreadData.mpData->mSysThreadId, &policy, &param);
  600. if(result == 0) // If success...
  601. {
  602. ConvertToNativePriority(nPriority, param, policy);
  603. result = scePthreadSetschedparam(mThreadData.mpData->mSysThreadId, policy, &param);
  604. }
  605. return (result == 0);
  606. }
  607. return false;
  608. }
  609. // To consider: Make it so we return a value.
  610. void EA::Thread::Thread::SetProcessor(int nProcessor)
  611. {
  612. if(mThreadData.mpData)
  613. {
  614. mThreadData.mpData->mStartupProcessor = nProcessor; // Assign this in case the thread hasn't started yet and thus we are leaving it a message to set it when it has started.
  615. SetPlatformThreadAffinity(mThreadData.mpData);
  616. }
  617. }
  618. void EA::Thread::Thread::Wake()
  619. {
  620. // Todo: implement this. The solution is to use a signal to wake the sleeping thread via an EINTR.
  621. // Possibly use the SIGCONT signal. Have to look into this to tell what the best approach is.
  622. }
  623. const char* EA::Thread::Thread::GetName() const
  624. {
  625. return mThreadData.mpData ? mThreadData.mpData->mName : "";
  626. }
  627. void EA::Thread::Thread::SetName(const char* pName)
  628. {
  629. if(mThreadData.mpData && pName)
  630. SetThreadName(mThreadData.mpData->mThreadId, pName);
  631. }