posix_threads.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <errno.h>
  23. #include <stdio.h>
  24. #include <dirent.h>
  25. #include <unistd.h>
  26. #include <sys/mman.h>
  27. #include <sys/time.h>
  28. #include <pwd.h>
  29. #include <pthread.h>
  30. #include "../../idlib/precompiled.h"
  31. #include "posix_public.h"
  32. #if defined(_DEBUG)
  33. // #define ID_VERBOSE_PTHREADS
  34. #endif
  35. /*
  36. ======================================================
  37. locks
  38. ======================================================
  39. */
  40. // we use an extra lock for the local stuff
  41. const int MAX_LOCAL_CRITICAL_SECTIONS = MAX_CRITICAL_SECTIONS + 1;
  42. static pthread_mutex_t global_lock[ MAX_LOCAL_CRITICAL_SECTIONS ];
  43. /*
  44. ==================
  45. Sys_EnterCriticalSection
  46. ==================
  47. */
  48. void Sys_EnterCriticalSection( int index ) {
  49. assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS );
  50. #ifdef ID_VERBOSE_PTHREADS
  51. if ( pthread_mutex_trylock( &global_lock[index] ) == EBUSY ) {
  52. Sys_Printf( "busy lock %d in thread '%s'\n", index, Sys_GetThreadName() );
  53. if ( pthread_mutex_lock( &global_lock[index] ) == EDEADLK ) {
  54. Sys_Printf( "FATAL: DEADLOCK %d, in thread '%s'\n", index, Sys_GetThreadName() );
  55. }
  56. }
  57. #else
  58. pthread_mutex_lock( &global_lock[index] );
  59. #endif
  60. }
  61. /*
  62. ==================
  63. Sys_LeaveCriticalSection
  64. ==================
  65. */
  66. void Sys_LeaveCriticalSection( int index ) {
  67. assert( index >= 0 && index < MAX_LOCAL_CRITICAL_SECTIONS );
  68. #ifdef ID_VERBOSE_PTHREADS
  69. if ( pthread_mutex_unlock( &global_lock[index] ) == EPERM ) {
  70. Sys_Printf( "FATAL: NOT LOCKED %d, in thread '%s'\n", index, Sys_GetThreadName() );
  71. }
  72. #else
  73. pthread_mutex_unlock( &global_lock[index] );
  74. #endif
  75. }
  76. /*
  77. ======================================================
  78. wait and trigger events
  79. we use a single lock to manipulate the conditions, MAX_LOCAL_CRITICAL_SECTIONS-1
  80. the semantics match the win32 version. signals raised while no one is waiting stay raised until a wait happens (which then does a simple pass-through)
  81. NOTE: we use the same mutex for all the events. I don't think this would become much of a problem
  82. cond_wait unlocks atomically with setting the wait condition, and locks it back before exiting the function
  83. the potential for time wasting lock waits is very low
  84. ======================================================
  85. */
  86. pthread_cond_t event_cond[ MAX_TRIGGER_EVENTS ];
  87. bool signaled[ MAX_TRIGGER_EVENTS ];
  88. bool waiting[ MAX_TRIGGER_EVENTS ];
  89. /*
  90. ==================
  91. Sys_WaitForEvent
  92. ==================
  93. */
  94. void Sys_WaitForEvent( int index ) {
  95. assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
  96. Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
  97. assert( !waiting[ index ] ); // WaitForEvent from multiple threads? that wouldn't be good
  98. if ( signaled[ index ] ) {
  99. // emulate windows behaviour: signal has been raised already. clear and keep going
  100. signaled[ index ] = false;
  101. } else {
  102. waiting[ index ] = true;
  103. pthread_cond_wait( &event_cond[ index ], &global_lock[ MAX_LOCAL_CRITICAL_SECTIONS - 1 ] );
  104. waiting[ index ] = false;
  105. }
  106. Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
  107. }
  108. /*
  109. ==================
  110. Sys_TriggerEvent
  111. ==================
  112. */
  113. void Sys_TriggerEvent( int index ) {
  114. assert( index >= 0 && index < MAX_TRIGGER_EVENTS );
  115. Sys_EnterCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
  116. if ( waiting[ index ] ) {
  117. pthread_cond_signal( &event_cond[ index ] );
  118. } else {
  119. // emulate windows behaviour: if no thread is waiting, leave the signal on so next wait keeps going
  120. signaled[ index ] = true;
  121. }
  122. Sys_LeaveCriticalSection( MAX_LOCAL_CRITICAL_SECTIONS - 1 );
  123. }
  124. /*
  125. ======================================================
  126. thread create and destroy
  127. ======================================================
  128. */
  129. // not a hard limit, just what we keep track of for debugging
  130. #define MAX_THREADS 10
  131. xthreadInfo *g_threads[MAX_THREADS];
  132. int g_thread_count = 0;
  133. typedef void *(*pthread_function_t) (void *);
  134. /*
  135. ==================
  136. Sys_CreateThread
  137. ==================
  138. */
  139. void Sys_CreateThread( xthread_t function, void *parms, xthreadPriority priority, xthreadInfo& info, const char *name, xthreadInfo **threads, int *thread_count ) {
  140. Sys_EnterCriticalSection( );
  141. pthread_attr_t attr;
  142. pthread_attr_init( &attr );
  143. if ( pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ) != 0 ) {
  144. common->Error( "ERROR: pthread_attr_setdetachstate %s failed\n", name );
  145. }
  146. if ( pthread_create( ( pthread_t* )&info.threadHandle, &attr, ( pthread_function_t )function, parms ) != 0 ) {
  147. common->Error( "ERROR: pthread_create %s failed\n", name );
  148. }
  149. pthread_attr_destroy( &attr );
  150. info.name = name;
  151. if ( *thread_count < MAX_THREADS ) {
  152. threads[ ( *thread_count )++ ] = &info;
  153. } else {
  154. common->DPrintf( "WARNING: MAX_THREADS reached\n" );
  155. }
  156. Sys_LeaveCriticalSection( );
  157. }
  158. /*
  159. ==================
  160. Sys_DestroyThread
  161. ==================
  162. */
  163. void Sys_DestroyThread( xthreadInfo& info ) {
  164. // the target thread must have a cancelation point, otherwise pthread_cancel is useless
  165. assert( info.threadHandle );
  166. if ( pthread_cancel( ( pthread_t )info.threadHandle ) != 0 ) {
  167. common->Error( "ERROR: pthread_cancel %s failed\n", info.name );
  168. }
  169. if ( pthread_join( ( pthread_t )info.threadHandle, NULL ) != 0 ) {
  170. common->Error( "ERROR: pthread_join %s failed\n", info.name );
  171. }
  172. info.threadHandle = 0;
  173. Sys_EnterCriticalSection( );
  174. for( int i = 0 ; i < g_thread_count ; i++ ) {
  175. if ( &info == g_threads[ i ] ) {
  176. g_threads[ i ] = NULL;
  177. int j;
  178. for( j = i+1 ; j < g_thread_count ; j++ ) {
  179. g_threads[ j-1 ] = g_threads[ j ];
  180. }
  181. g_threads[ j-1 ] = NULL;
  182. g_thread_count--;
  183. Sys_LeaveCriticalSection( );
  184. return;
  185. }
  186. }
  187. Sys_LeaveCriticalSection( );
  188. }
  189. /*
  190. ==================
  191. Sys_GetThreadName
  192. find the name of the calling thread
  193. ==================
  194. */
  195. const char* Sys_GetThreadName( int *index ) {
  196. Sys_EnterCriticalSection( );
  197. pthread_t thread = pthread_self();
  198. for( int i = 0 ; i < g_thread_count ; i++ ) {
  199. if ( thread == (pthread_t)g_threads[ i ]->threadHandle ) {
  200. if ( index ) {
  201. *index = i;
  202. }
  203. Sys_LeaveCriticalSection( );
  204. return g_threads[ i ]->name;
  205. }
  206. }
  207. if ( index ) {
  208. *index = -1;
  209. }
  210. Sys_LeaveCriticalSection( );
  211. return "main";
  212. }
  213. /*
  214. =========================================================
  215. Async Thread
  216. =========================================================
  217. */
  218. xthreadInfo asyncThread;
  219. /*
  220. =================
  221. Posix_StartAsyncThread
  222. =================
  223. */
  224. void Posix_StartAsyncThread() {
  225. if ( asyncThread.threadHandle == 0 ) {
  226. Sys_CreateThread( (xthread_t)Sys_AsyncThread, NULL, THREAD_NORMAL, asyncThread, "Async", g_threads, &g_thread_count );
  227. } else {
  228. common->Printf( "Async thread already running\n" );
  229. }
  230. common->Printf( "Async thread started\n" );
  231. }
  232. /*
  233. ==================
  234. Posix_InitPThreads
  235. ==================
  236. */
  237. void Posix_InitPThreads( ) {
  238. int i;
  239. pthread_mutexattr_t attr;
  240. // init critical sections
  241. for ( i = 0; i < MAX_LOCAL_CRITICAL_SECTIONS; i++ ) {
  242. pthread_mutexattr_init( &attr );
  243. pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK );
  244. pthread_mutex_init( &global_lock[i], &attr );
  245. pthread_mutexattr_destroy( &attr );
  246. }
  247. // init event sleep/triggers
  248. for ( i = 0; i < MAX_TRIGGER_EVENTS; i++ ) {
  249. pthread_cond_init( &event_cond[ i ], NULL );
  250. signaled[i] = false;
  251. waiting[i] = false;
  252. }
  253. // init threads table
  254. for ( i = 0; i < MAX_THREADS; i++ ) {
  255. g_threads[ i ] = NULL;
  256. }
  257. }