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