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