some fixes to issues pointed by @tkoeppe
[xonotic/netradiant.git] / tools / quake3 / common / threads.c
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "globaldefs.h"
23 #if !GDEF_OS_WINDOWS
24 // The below define is necessary to use
25 // pthreads extensions like pthread_mutexattr_settype
26 #define _GNU_SOURCE
27 #include <pthread.h>
28 #endif
29
30 #include "cmdlib.h"
31 #include "mathlib.h"
32 #include "inout.h"
33 #include "qthreads.h"
34
35 #define MAX_THREADS 64
36
37 int dispatch;
38 int workcount;
39 int oldf;
40 qboolean pacifier;
41
42 qboolean threaded;
43
44 /*
45    =============
46    GetThreadWork
47
48    =============
49  */
50 int GetThreadWork( void ){
51         int r;
52         int f;
53
54         ThreadLock();
55
56         if ( dispatch == workcount ) {
57                 ThreadUnlock();
58                 return -1;
59         }
60
61         f = 40 * dispatch / workcount;
62         if ( f < oldf ) {
63                 Sys_Printf( "warning: progress went backwards (should never happen)\n" );
64                 oldf = f;
65         }
66         while ( f > oldf )
67         {
68                 ++oldf;
69                 if ( pacifier ) {
70                         if ( oldf % 4 == 0 ) {
71                                 Sys_Printf( "%i", f / 4 );
72                         }
73                         else{
74                                 Sys_Printf( "." );
75                         }
76                         fflush( stdout );   /* ydnar */
77                 }
78         }
79
80         r = dispatch;
81         dispatch++;
82         ThreadUnlock();
83
84         return r;
85 }
86
87
88 void ( *workfunction )( int );
89
90 void ThreadWorkerFunction( int threadnum ){
91         int work;
92
93         while ( 1 )
94         {
95                 work = GetThreadWork();
96                 if ( work == -1 ) {
97                         break;
98                 }
99 //Sys_Printf ("thread %i, work %i\n", threadnum, work);
100                 workfunction( work );
101         }
102 }
103
104 void RunThreadsOnIndividual( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
105         if ( numthreads == -1 ) {
106                 ThreadSetDefault();
107         }
108         workfunction = func;
109         RunThreadsOn( workcnt, showpacifier, ThreadWorkerFunction );
110 }
111
112
113 /*
114    ===================================================================
115
116    WIN32
117
118    ===================================================================
119  */
120 #if GDEF_OS_WINDOWS
121
122 #define USED
123
124 #include <windows.h>
125
126 int numthreads = -1;
127 CRITICAL_SECTION crit;
128 static int enter;
129
130 void ThreadSetDefault( void ){
131         SYSTEM_INFO info;
132
133         if ( numthreads == -1 ) { // not set manually
134                 GetSystemInfo( &info );
135                 numthreads = info.dwNumberOfProcessors;
136                 if ( numthreads < 1 || numthreads > 32 ) {
137                         numthreads = 1;
138                 }
139         }
140
141         Sys_Printf( "%i threads\n", numthreads );
142 }
143
144
145 void ThreadLock( void ){
146         if ( !threaded ) {
147                 return;
148         }
149         EnterCriticalSection( &crit );
150         if ( enter ) {
151                 Error( "Recursive ThreadLock\n" );
152         }
153         enter = 1;
154 }
155
156 void ThreadUnlock( void ){
157         if ( !threaded ) {
158                 return;
159         }
160         if ( !enter ) {
161                 Error( "ThreadUnlock without lock\n" );
162         }
163         enter = 0;
164         LeaveCriticalSection( &crit );
165 }
166
167 /*
168    =============
169    RunThreadsOn
170    =============
171  */
172 void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
173         int threadid[MAX_THREADS];
174         HANDLE threadhandle[MAX_THREADS];
175         int i;
176         int start, end;
177
178         start = I_FloatTime();
179         dispatch = 0;
180         workcount = workcnt;
181         oldf = -1;
182         pacifier = showpacifier;
183         threaded = qtrue;
184
185         //
186         // run threads in parallel
187         //
188         InitializeCriticalSection( &crit );
189
190         if ( numthreads == 1 ) { // use same thread
191                 func( 0 );
192         }
193         else
194         {
195                 for ( i = 0 ; i < numthreads ; i++ )
196                 {
197                         threadhandle[i] = CreateThread(
198                                 NULL,   // LPSECURITY_ATTRIBUTES lpsa,
199                             //0,                // DWORD cbStack,
200
201                             /* ydnar: cranking stack size to eliminate radiosity crash with 1MB stack on win32 */
202                                 ( 4096 * 1024 ),
203
204                                 (LPTHREAD_START_ROUTINE)func,   // LPTHREAD_START_ROUTINE lpStartAddr,
205                                 (LPVOID)i,  // LPVOID lpvThreadParm,
206                                 0,          //   DWORD fdwCreate,
207                                 &threadid[i] );
208                 }
209
210                 for ( i = 0 ; i < numthreads ; i++ )
211                         WaitForSingleObject( threadhandle[i], INFINITE );
212         }
213         DeleteCriticalSection( &crit );
214
215         threaded = qfalse;
216         end = I_FloatTime();
217         if ( pacifier ) {
218                 Sys_Printf( " (%i)\n", end - start );
219         }
220 }
221
222
223 #endif
224
225 /*
226    ===================================================================
227
228    OSF1
229
230    ===================================================================
231  */
232
233 #ifdef __osf__
234 #define USED
235
236 int numthreads = 4;
237
238 void ThreadSetDefault( void ){
239         if ( numthreads == -1 ) { // not set manually
240                 numthreads = 4;
241         }
242 }
243
244
245 #include <pthread.h>
246
247 pthread_mutex_t *my_mutex;
248
249 void ThreadLock( void ){
250         if ( my_mutex ) {
251                 pthread_mutex_lock( my_mutex );
252         }
253 }
254
255 void ThreadUnlock( void ){
256         if ( my_mutex ) {
257                 pthread_mutex_unlock( my_mutex );
258         }
259 }
260
261
262 /*
263    =============
264    RunThreadsOn
265    =============
266  */
267 void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
268         int i;
269         pthread_t work_threads[MAX_THREADS];
270         pthread_addr_t status;
271         pthread_attr_t attrib;
272         pthread_mutexattr_t mattrib;
273         int start, end;
274
275         start = I_FloatTime();
276         dispatch = 0;
277         workcount = workcnt;
278         oldf = -1;
279         pacifier = showpacifier;
280         threaded = qtrue;
281
282         if ( pacifier ) {
283                 setbuf( stdout, NULL );
284         }
285
286         if ( !my_mutex ) {
287                 my_mutex = safe_malloc( sizeof( *my_mutex ) );
288                 if ( pthread_mutexattr_create( &mattrib ) == -1 ) {
289                         Error( "pthread_mutex_attr_create failed" );
290                 }
291                 if ( pthread_mutexattr_setkind_np( &mattrib, MUTEX_FAST_NP ) == -1 ) {
292                         Error( "pthread_mutexattr_setkind_np failed" );
293                 }
294                 if ( pthread_mutex_init( my_mutex, mattrib ) == -1 ) {
295                         Error( "pthread_mutex_init failed" );
296                 }
297         }
298
299         if ( pthread_attr_create( &attrib ) == -1 ) {
300                 Error( "pthread_attr_create failed" );
301         }
302         if ( pthread_attr_setstacksize( &attrib, 0x100000 ) == -1 ) {
303                 Error( "pthread_attr_setstacksize failed" );
304         }
305
306         for ( i = 0 ; i < numthreads ; i++ )
307         {
308                 if ( pthread_create( &work_threads[i], attrib
309                                                          , (pthread_startroutine_t)func, (pthread_addr_t)i ) == -1 ) {
310                         Error( "pthread_create failed" );
311                 }
312         }
313
314         for ( i = 0 ; i < numthreads ; i++ )
315         {
316                 if ( pthread_join( work_threads[i], &status ) == -1 ) {
317                         Error( "pthread_join failed" );
318                 }
319         }
320
321         threaded = qfalse;
322
323         end = I_FloatTime();
324         if ( pacifier ) {
325                 Sys_Printf( " (%i)\n", end - start );
326         }
327 }
328
329
330 #endif
331
332 /*
333    ===================================================================
334
335    IRIX
336
337    ===================================================================
338  */
339
340 #ifdef _MIPS_ISA
341 #define USED
342
343 #include <task.h>
344 #include <abi_mutex.h>
345 #include <sys/types.h>
346 #include <sys/prctl.h>
347
348
349 int numthreads = -1;
350 abilock_t lck;
351
352 void ThreadSetDefault( void ){
353         if ( numthreads == -1 ) {
354                 numthreads = prctl( PR_MAXPPROCS );
355         }
356         Sys_Printf( "%i threads\n", numthreads );
357         usconfig( CONF_INITUSERS, numthreads );
358 }
359
360
361 void ThreadLock( void ){
362         spin_lock( &lck );
363 }
364
365 void ThreadUnlock( void ){
366         release_lock( &lck );
367 }
368
369
370 /*
371    =============
372    RunThreadsOn
373    =============
374  */
375 void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
376         int i;
377         int pid[MAX_THREADS];
378         int start, end;
379
380         start = I_FloatTime();
381         dispatch = 0;
382         workcount = workcnt;
383         oldf = -1;
384         pacifier = showpacifier;
385         threaded = qtrue;
386
387         if ( pacifier ) {
388                 setbuf( stdout, NULL );
389         }
390
391         init_lock( &lck );
392
393         for ( i = 0 ; i < numthreads - 1 ; i++ )
394         {
395                 pid[i] = sprocsp( ( void ( * )( void *, size_t ) )func, PR_SALL, (void *)i
396                                                   , NULL, 0x200000 ); // 2 meg stacks
397                 if ( pid[i] == -1 ) {
398                         perror( "sproc" );
399                         Error( "sproc failed" );
400                 }
401         }
402
403         func( i );
404
405         for ( i = 0 ; i < numthreads - 1 ; i++ )
406                 wait( NULL );
407
408         threaded = qfalse;
409
410         end = I_FloatTime();
411         if ( pacifier ) {
412                 Sys_Printf( " (%i)\n", end - start );
413         }
414 }
415
416
417 #endif
418
419
420 /*
421    =======================================================================
422
423    Linux pthreads
424
425    =======================================================================
426  */
427
428 #if GDEF_OS_LINUX || ( GDEF_OS_MACOS && !MAC_STATIC_HACK )
429 #define USED
430
431 #include <unistd.h>
432
433 int numthreads = -1;
434
435 void ThreadSetDefault( void ){
436         if ( numthreads == -1 ) { // not set manually
437 #ifdef _SC_NPROCESSORS_ONLN
438                 long cpus = sysconf( _SC_NPROCESSORS_ONLN );
439                 if ( cpus > 0 ) {
440                         numthreads = cpus;
441                 }
442                 else
443 #endif
444                 /* can't detect, so default to four threads */
445                 numthreads = 4;
446         }
447
448         if ( numthreads > 1 ) {
449                 Sys_Printf( "threads: %d\n", numthreads );
450         }
451 }
452
453 #include <pthread.h>
454
455 typedef struct pt_mutex_s
456 {
457         pthread_t       *owner;
458         pthread_mutex_t a_mutex;
459         pthread_cond_t cond;
460         unsigned int lock;
461 } pt_mutex_t;
462
463 pt_mutex_t global_lock;
464
465 void ThreadLock( void ){
466         pt_mutex_t *pt_mutex = &global_lock;
467
468         if ( !threaded ) {
469                 return;
470         }
471
472         pthread_mutex_lock( &pt_mutex->a_mutex );
473         if ( pthread_equal( pthread_self(), (pthread_t)&pt_mutex->owner ) ) {
474                 pt_mutex->lock++;
475         }
476         else
477         {
478                 if ( ( !pt_mutex->owner ) && ( pt_mutex->lock == 0 ) ) {
479                         pt_mutex->owner = (pthread_t *)pthread_self();
480                         pt_mutex->lock  = 1;
481                 }
482                 else
483                 {
484                         while ( 1 )
485                         {
486                                 pthread_cond_wait( &pt_mutex->cond, &pt_mutex->a_mutex );
487                                 if ( ( !pt_mutex->owner ) && ( pt_mutex->lock == 0 ) ) {
488                                         pt_mutex->owner = (pthread_t *)pthread_self();
489                                         pt_mutex->lock  = 1;
490                                         break;
491                                 }
492                         }
493                 }
494         }
495         pthread_mutex_unlock( &pt_mutex->a_mutex );
496 }
497
498 void ThreadUnlock( void ){
499         pt_mutex_t *pt_mutex = &global_lock;
500
501         if ( !threaded ) {
502                 return;
503         }
504
505         pthread_mutex_lock( &pt_mutex->a_mutex );
506         pt_mutex->lock--;
507
508         if ( pt_mutex->lock == 0 ) {
509                 pt_mutex->owner = NULL;
510                 pthread_cond_signal( &pt_mutex->cond );
511         }
512
513         pthread_mutex_unlock( &pt_mutex->a_mutex );
514 }
515
516 void recursive_mutex_init( pthread_mutexattr_t attribs ){
517         pt_mutex_t *pt_mutex = &global_lock;
518
519         pt_mutex->owner = NULL;
520         if ( pthread_mutex_init( &pt_mutex->a_mutex, &attribs ) != 0 ) {
521                 Error( "pthread_mutex_init failed\n" );
522         }
523         if ( pthread_cond_init( &pt_mutex->cond, NULL ) != 0 ) {
524                 Error( "pthread_cond_init failed\n" );
525         }
526
527         pt_mutex->lock = 0;
528 }
529
530 /*
531    =============
532    RunThreadsOn
533    =============
534  */
535 void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
536         pthread_mutexattr_t mattrib;
537         pthread_attr_t attr;
538         pthread_t work_threads[MAX_THREADS];
539         size_t stacksize;
540
541         int start, end;
542         int i = 0;
543
544         start     = I_FloatTime();
545         pacifier  = showpacifier;
546
547         dispatch  = 0;
548         oldf      = -1;
549         workcount = workcnt;
550
551         pthread_attr_init( &attr );
552         if ( pthread_attr_setstacksize( &attr, 8388608 ) != 0 ) {
553                 stacksize = 0;
554                 pthread_attr_getstacksize( &attr, &stacksize );
555                 Sys_Printf( "Could not set a per-thread stack size of 8 MB, using only %.2f MB\n", stacksize / 1048576.0 );
556         }
557
558         if ( numthreads == 1 ) {
559                 func( 0 );
560         }
561         else
562         {
563                 threaded  = qtrue;
564
565                 if ( pacifier ) {
566                         setbuf( stdout, NULL );
567                 }
568
569                 if ( pthread_mutexattr_init( &mattrib ) != 0 ) {
570                         Error( "pthread_mutexattr_init failed" );
571                 }
572                 if ( pthread_mutexattr_settype( &mattrib, PTHREAD_MUTEX_ERRORCHECK ) != 0 ) {
573                         Error( "pthread_mutexattr_settype failed" );
574                 }
575                 recursive_mutex_init( mattrib );
576
577                 for ( i = 0 ; i < numthreads ; i++ )
578                 {
579                         /* Default pthread attributes: joinable & non-realtime scheduling */
580                         if ( pthread_create(&work_threads[i], &attr, (void *(*)(void *)) func, (void*)(uintptr_t)i ) != 0 ) {
581                                 Error( "pthread_create failed" );
582                         }
583                 }
584                 for ( i = 0 ; i < numthreads ; i++ )
585                 {
586                         if ( pthread_join( work_threads[i], NULL ) != 0 ) {
587                                 Error( "pthread_join failed" );
588                         }
589                 }
590                 pthread_mutexattr_destroy( &mattrib );
591                 threaded = qfalse;
592         }
593
594         end = I_FloatTime();
595         if ( pacifier ) {
596                 Sys_Printf( " (%i)\n", end - start );
597         }
598 }
599 #endif // ifdef __linux__
600
601
602 /*
603    =======================================================================
604
605    SINGLE THREAD
606
607    =======================================================================
608  */
609
610 #ifndef USED
611
612 int numthreads = 1;
613
614 void ThreadSetDefault( void ){
615         numthreads = 1;
616 }
617
618 void ThreadLock( void ){
619 }
620
621 void ThreadUnlock( void ){
622 }
623
624 /*
625    =============
626    RunThreadsOn
627    =============
628  */
629 void RunThreadsOn( int workcnt, qboolean showpacifier, void ( *func )( int ) ){
630         int start, end;
631
632         dispatch = 0;
633         workcount = workcnt;
634         oldf = -1;
635         pacifier = showpacifier;
636         start = I_FloatTime();
637         func( 0 );
638
639         end = I_FloatTime();
640         if ( pacifier ) {
641                 Sys_Printf( " (%i)\n", end - start );
642         }
643 }
644
645 #endif