1 /* $Id: ncbithr.c,v 6.38 2003/12/04 18:02:43 rsmith Exp $ */
2 /*****************************************************************************
3
4 Name: ncbithr.c
5
6 Description: Internal functions for Multi-thread API library
7
8 Authors: Denis Vakatov, Sergei Shavirin
9
10 ***************************************************************************
11
12 PUBLIC DOMAIN NOTICE
13 National Center for Biotechnology Information
14
15 This software/database is a "United States Government Work" under the
16 terms of the United States Copyright Act. It was written as part of
17 the author's official duties as a United States Government employee
18 and thus cannot be copyrighted. This software/database is freely
19 available to the public for use. The National Library of Medicine and
20 the U.S. Government have not placed any restriction on its use or
21 reproduction.
22
23 Although all reasonable efforts have been taken to ensure the accuracy
24 and reliability of the software and data, the NLM and the U.S.
25 Government do not and cannot warrant the performance or results that
26 may be obtained by using this software or data. The NLM and the U.S.
27 Government disclaim all warranties, express or implied, including
28 warranties of performance, merchantability or fitness for any
29 particular purpose.
30
31 Please cite the author in any work or product based on this material.
32
33 ***************************************************************************
34
35 Modification History:
36 -----------------------------------------------------------------------------
37 * $Log: ncbithr.c,v $
38 * Revision 6.38 2003/12/04 18:02:43 rsmith
39 * Move includes out of definition of NlmCPUNumber.
40 *
41 * Revision 6.37 2003/09/19 22:58:38 coulouri
42 * NetBSD does not (yet?) have pthread_attr_setschedpolicy()
43 *
44 * Revision 6.36 2003/05/05 11:57:34 rsmith
45 * Codewarrior and MSC compilers use the same declaration for thread_exit_arr and thread_self.
46 *
47 * Revision 6.35 2003/03/06 15:12:27 coulouri
48 * add mach/mach.h header for codewarrior
49 *
50 * Revision 6.34 2003/03/06 14:15:04 coulouri
51 * add mach support for NlmCPUNumber
52 *
53 * Revision 6.33 2002/03/12 15:50:00 ivanov
54 * Changed a name created mutex from "Nlm_InitLock32" to NULL
55 * in the NlmMutexInit() under WIN32
56 *
57 * Revision 6.32 2001/12/14 21:09:20 ivanov
58 * Enable threads under WIN32 in MT-configurations
59 *
60 * Revision 6.31 2001/11/09 14:18:06 ivanov
61 * Fixed error in the preprocessor command
62 *
63 * Revision 6.30 2001/11/08 21:57:19 ivanov
64 * Changed NlmCPUNumber() (under UNIX)
65 *
66 * Revision 6.29 2001/01/19 20:23:34 kans
67 * support for OS_UNIX_DARWIN (contributed by William Van Etten)
68 *
69 * Revision 6.28 2001/01/08 19:52:26 vakatov
70 * NlmThreadCreateEx() -- by default, to use BOUND scope scheduling (if
71 * found) or PROCESS scope scheduling (the last default).
72 * Use "-DPOSIX_BOUND_THREADS_AVAIL" to enforce SYSTEM scope scheduling.
73 * {By Haruna N. Cofer; Applications - Chem/Bio, SGI; haruna@sgi.com}
74 *
75 * Revision 6.27 2000/11/06 17:09:20 vakatov
76 * RW_HISTORY, RW_TRACE -- To gather and printout info on the RW-lock history
77 *
78 * Revision 6.26 2000/05/16 20:26:01 vakatov
79 * [WIN32_THREADS_AVAIL] Changed the locking policy for nested RW-locks.
80 * // Fully tested on Solaris(native and POSIX), Win-NT, IRIX, and Linux
81 *
82 * Revision 6.25 2000/03/01 19:44:44 vakatov
83 * NlmMutexUnlock() -- fixed by Haruna N. Cofer (Applications - Chem/Bio,
84 * SGI; haruna@sgi.com) for an intermittent deadlock happening on SGI/IRIX
85 * and caused by the swapping of two adjacent operators in s_MutexLock()
86 * by the C compiler optimizer (when compiled with "-O" flag). Thanks, Haruna!
87 *
88 * Revision 6.24 1999/11/08 17:03:05 vakatov
89 * [POSIX] NlmThreadCreateEx() -- fixed a bug leading to the thread
90 * handle corruption on { BIG_ENDIAN, 64-bit pointer, 32-bit pthread_t}
91 * platforms (like 64-bit IRIX/SGI/MIPS)
92 *
93 * Revision 6.23 1999/10/14 19:43:24 kans
94 * new headers for mac
95 *
96 * Revision 6.22 1999/10/14 19:08:09 kans
97 * header changes for Mac
98 *
99 * Revision 6.21 1999/09/20 17:48:32 vakatov
100 * [POSIX_THREADS_AVAIL] Bug fix for R6.20 -- for the case of "R1, R2, U1, W1"
101 *
102 * Revision 6.20 1999/08/20 19:55:18 vakatov
103 * s_ThreadCounter***: use {counter + semaphore} instead of just
104 * {RWlock} to count and join all threads.
105 * [POSIX_THREADS_AVAIL] Changed the locking policy for nested RW-locks.
106 * // Fully tested on Solaris(native and POSIX), Win-NT, IRIX, OSF1 and Linux
107 *
108 * Revision 6.19 1998/12/10 17:04:08 vakatov
109 * Fixed to compile under LINUX(Red Hat 2.XX, gcc, with POSIX threads)
110 *
111 * Revision 6.18 1998/09/22 14:50:58 vakatov
112 * Use "s_TlsMutex" to protect the TLS key creation in "init_exit_arr()"
113 * -- instead of former "s_Key_mutex"
114 *
115 * Revision 6.17 1998/09/01 16:35:58 vakatov
116 * [OS_UNUX_IRIX] By default, create regular unbound(PTHREAD_SCOPE_PROCESS)
117 * threads as there may be no "capability" to create bound threads.
118 * Hint: compile with "-DPOSIX_BOUND_THREADS_AVAIL" if you wanna allow
119 * bound threads for IRIX(see also CAP_SCHED_MGT in "man capabilities").
120 *
121 * Revision 6.16 1998/07/28 20:31:37 vakatov
122 * Moved(slightly modified) contents of former "ncbithri.h" to "ncbithr.c"
123 *
124 * Revision 6.15 1998/07/16 17:43:35 vakatov
125 * [OS_UNIX_AIX, POSIX] Dont set thread priority on this platform as
126 * pthread_attr_setschedparam() returns non-zero(some error?) value here
127 *
128 * Revision 6.14 1998/07/13 18:47:28 vakatov
129 * [POSIX] NlmThreadCreateEx(): call "getschedparam()" before
130 * "setschedparam()" to make sure that "param" is properly initialized
131 *
132 * Revision 6.13 1998/06/11 19:00:01 shavirin
133 * Fixed some compiler warnings.
134 *
135 * Revision 6.12 1998/02/27 17:13:53 vakatov
136 * [WIN32 DLL] Declared some functions as NLM_EXTERN(DLL-exportable)
137 *
138 * Revision 6.11 1998/02/24 22:38:45 vakatov
139 * NlmThreadDestroyAll(): check internal RW-locks & mutexes for NULL...
140 *
141 * Revision 6.10 1998/02/20 18:56:33 vakatov
142 * [NCBI_NOTHREADS_AVAIL] Fixed minor mem.leak in NlmTlsSetValue()
143 *
144 * Revision 6.9 1998/02/17 20:13:51 vakatov
145 * NlmTlsInit() removed from the public API; instead, the
146 * NlmTlsSetValue() now initializes the TLS if it is not initialized yet
147 *
148 * Revision 6.8 1998/02/13 15:15:32 vakatov
149 * Added "cleanup" parameter to NlmTlsSetValue() to provide TLS cleanup.
150 * Removed NlmTlsDestroy(); now destroy all TLSs in NlmThreadDestroyAll()
151 *
152 * Revision 6.7 1998/01/08 17:27:53 vakatov
153 * Added NlmThreadJoinAll() and NlmThreadCleanupAll()
154 *
155 * Revision 6.6 1997/12/24 17:17:59 vakatov
156 * Added TLS(Thread Local Storage) functionality for all platforms(incl.
157 * non-"thread-capable" ones -- interpreted as a single-thread application)
158 *
159 * Revision 6.5 1997/12/23 19:13:29 vakatov
160 * Thread handling -- revised, fixed:
161 * removed NlmThreadKill(), NlmThread[Set|Get]Concurrency() and
162 * NlmThread[Set|Get]Priority() functions;
163 * NlmThreadCreate() doesn't accept parameter "flags" anymore;
164 * added "priority" parameter to NlmThreadCreateEx()
165 *
166 * Revision 6.4 1997/12/17 19:11:24 vakatov
167 * Use all-platform THREAD_{RUN|BOUND|DETACHED} instead of the platform
168 * specific THR_{...} thread creation/running attributes
169 *
170 * Revision 6.3 1997/12/16 23:12:35 vakatov
171 * Mutexes and RW-locks: revised, rewritten, fixed; added built-in
172 * strict run-time checks -- (use -D_DEBUG_HARD)
173 *
174 * Revision 6.2 1997/12/05 22:16:21 vakatov
175 * [SOLARIS_THREADS_AVAIL] Made RWlock functions always return "-1" on error
176 * [POSIX_THREADS_AVAIL] Fixed minor mem.leak
177 * [TEST_MODULE_NCBITHR] Allow arbitrary N_SPAWN(was restricted to max.6)
178 *
179 * Revision 6.1 1997/10/08 14:54:22 vakatov
180 * [WIN32] Implemented RW-lock functionality
181 * [POSIX] Fixed bug in RW[try[rw|rd]lock() functions
182 * [DO_RWLOCK] Added test code for RW-lock functionality
183 *
184 * Revision 5.23 1997/07/16 21:12:11 vakatov
185 * #define NULL_thread -- rather than global variable "NULL_thread"
186 *
187 * Revision 5.22 1997/05/13 17:29:42 vakatov
188 * Fixed typo in NlmMutexTryLock()
189 * [POSIX_THREADS_AVAIL] Initialize Nlm_lock with PTHREAD_MUTEX_INITIALIZER
190 *
191 * Revision 5.21 1997/03/31 18:59:25 vakatov
192 * Minor fixes(type casts) to pass through the C++ compiler
193 *
194 * Revision 5.20 1997/03/17 21:49:04 vakatov
195 * NlmThreadWrapper(): do not print banner to the logfile when starting
196 * a new thread.
197 * [POSIX_THREADS_AVAIL] NlmRWtrywrlock(): fixed "=" -> "==" in IF statement.
198 *
199 * Revision 5.19 1997/02/20 16:05:31 shavirin
200 * Moved to standard 1003.1c for DEC Threads
201 *
202 * Revision 5.18 1997/02/19 20:39:16 shavirin
203 * Changed function pthread_attr_create to pthread_attr_init() for IRIX 6.2
204 *
205 * Revision 5.17 1997/02/18 19:02:43 shavirin
206 * Fixed DETACHED state in POSIX threads
207 *
208 * Revision 5.16 1997/01/24 20:41:00 shavirin
209 * Added detach handling for OSF1
210 *
211 * Revision 5.15 1997/01/09 15:14:21 shavirin
212 * Added global NULL_thread variable
213 *
214 * Revision 5.14 1997/01/08 23:07:47 shavirin
215 * Added support for DEC ALPHA OSF1
216 *
217 * Revision 5.13 1996/12/03 21:48:33 vakatov
218 * Adopted for 32-bit MS-Windows DLLs
219 *
220 * Revision 5.12 1996/11/25 19:02:27 vakatov
221 * Inherit error posting flags and log-file from the parent thread.
222 * NlmMutexInit() now accepts pointer to mutex as an argument -- to
223 * guarantee no MT-mess during the mutex initialization.
224 * Added function NlmMutexLockEx() to lock yet initialized mutex;
225 * it initialize the mutex before locking, if necessary.
226 * Semaphor functions, NlmThreadKill(), NlmMutexUnlock(), NlmMutexDestroy():
227 * got rid of the return value discrepancy between different platforms;
228 * now return 0 on success, -1 on error on all platforms.
229 * NlmMutexLock<Ex>(): protected from "self-deadlock" under Solaris/POSIX.
230 *
231 * Revision 5.11 1996/10/16 00:54:53 epstein
232 * add NCBI_NOTHREADS_AVAIL flag to disable threading
233 *
234 * Revision 5.10 1996/09/04 19:37:32 epstein
235 * workaround for SGI4
236 *
237 * Revision 5.9 1996/09/04 14:34:44 kans
238 * trivial fixes to allow compilation under Symantec C++
239 *
240 * Revision 5.8 1996/08/22 14:07:53 shavirin
241 * Finaly NlmThreadsAvailable return TRUE for WIN32
242 *
243 * Revision 5.7 1996/08/07 17:07:58 shavirin
244 * Added function NlmCPUNumber(void), that returns number of CPU
245 * on calling host
246 *
247 * Revision 5.6 1996/08/02 19:52:38 vakatov
248 * Most of the thread-handling code has been protected from being
249 * compiled on the non-thread platforms
250 *
251 * Revision 5.5 1996/08/02 15:59:38 shavirin
252 * Added support for POSIX threads. Implemented semaphores and
253 * Read-Write lock objects for POSIX threads through POSIX mutexes
254 *
255 * Revision 5.4 1996/07/16 19:58:50 vakatov
256 * [WIN32] NlmThreadSelf() now returns handler rather than pseudo-handler.
257 * [SOLARIS...] [POSIX...] init_exit_arr() protected by "mutex" from.
258 * [POSIX] "mutex"-related stuff implemented.
259 * NlmThreadMemFree() auxiliary function added.
260 * The "thread destructors" calling order has been changed from FIFO to LIFO.
261 * Added NlmThreadCreateEx() function to specify the thread destructor
262 * from outside the thread.
263 * The program name property is to be automatially inherited by the threads.
264 *
265 * Revision 5.3 1996/07/09 18:13:37 vakatov
266 * Added a basic MT-support for POSIX and WIN32-SDK(Windows-95 and Windows-NT)
267 * API -- the thread creation, joining and terminating. The thread joining
268 * (waiting for the terminating) interface and behaviour has been slightly
269 * changed.
270 * Tested the lately introduced "OnExit"-features for the new platforms.
271 *
272 * Revision 5.2 1996/07/05 21:09:22 vakatov
273 * Added NlmThreadAddOnExit() and NlmThreadRemoveOnExit() functions allowing
274 * the user to specify a set of functions to be automatically executed on the
275 * thread exiting stage. In addition, the execution of NlmThreadExit() function
276 * is guaranteed in the case when the thread's main function exits with
277 * the "return" operator, without calling the NlmThreadExit() explicitly
278 *
279 * The concerning stuff is also implemented(however, not tested yet) for
280 * POSIX and WIN32-SDK(MSVC++ and Borland C++) threads
281 *
282 * Tests are added at the end of the file(preprocessor variable TEST_MODULE)
283 * to provide easy testing of the new(and some of the old) NCBI thread features
284 *
285 * 30 Apr 1996 - Shavirin Sergei - original written
286 ****************************************************************************/
287
288
289 #include <ncbistd.h>
290 #include <ncbimem.h>
291 #include <ncbierr.h>
292 #include <ncbistr.h>
293 #include <ncbiprop.h>
294
295 #ifdef WIN32
296 #include <windows.h>
297 #if defined(_MT)
298 # undef NCBI_NOTHREADS_AVAIL
299 #endif
300 #endif
301
302
303 #ifdef NCBI_NOTHREADS_AVAIL
304
305 #ifdef SOLARIS_THREADS_AVAIL
306 #undef SOLARIS_THREADS_AVAIL
307 #endif
308 #ifdef POSIX_THREADS_AVAIL
309 #undef POSIX_THREADS_AVAIL
310 #endif
311 #ifdef WIN32_THREADS_AVAIL
312 #undef WIN32_THREADS_AVAIL
313 #endif
314
315 #else /* def NCBI_NOTHREADS_AVAIL */
316
317 #if !defined(WIN32) && !defined(WIN16) && !defined(WIN_MAC)
318 #include <unistd.h>
319 #endif /* ndef WIN32, WIN16, WIN_MAC */
320
321 #if defined(OS_UNIX_IRIX)
322 #define PTHREAD_SCOPE_BOUND_NP 2
323 #endif
324
325 #if defined(SOLARIS_THREADS_AVAIL)
326 #define NCBI_THREADS_AVAIL
327 #include <thread.h>
328 #include <signal.h>
329 #include <synch.h>
330
331 #elif defined(POSIX_THREADS_AVAIL)
332 #define NCBI_THREADS_AVAIL
333 #ifdef _PTHREAD_USE_D4
334 #undef _PTHREAD_USE_D4
335 #endif
336 #if !defined(POSIX_BOUND_THREADS_AVAIL) && !defined(OS_UNIX_IRIX)
337 #define POSIX_BOUND_THREADS_AVAIL
338 #endif
339 #include <pthread.h>
340 #include <sched.h>
341 #include <signal.h>
342
343 #elif defined(WIN32_THREADS_AVAIL)
344 #define NCBI_THREADS_AVAIL
345 #endif /* def SOLARIS_THREADS_AVAIL elif POSIX_... elif WIN32_... */
346
347 #endif /* def else NCBI_NOTHREADS_AVAIL */
348
349
350 #include <ncbithr.h>
351
352
353 #ifdef _DEBUG_HARD
354 #define X_ASSERT(expr) ((expr) ? \
355 (void)0 : Nlm_AssertionFailed(#expr,THIS_MODULE,THIS_FILE,__LINE__))
356 #define X_VERIFY X_ASSERT
357 #else
358 #define X_ASSERT(expr) ((void)0)
359 #define X_VERIFY(expr) ((void)(expr))
360 #endif /* _DEBUG_HARD -- for the internal strict debugging */
361
362
363 TNlmMutex corelibMutex; /* exported in "corepriv.h" */
364
365
366 /***********************************************************************
367 * INTERNAL
368 ***********************************************************************/
369
370 /* this platform's native mutex type & default initialization */
371 #if defined (SOLARIS_THREADS_AVAIL)
372 typedef mutex_t MUTEX_T;
373 #define MUTEX_T_INIT
374 #elif defined (POSIX_THREADS_AVAIL)
375 typedef pthread_mutex_t MUTEX_T ;
376 #define MUTEX_T_INIT = PTHREAD_MUTEX_INITIALIZER
377 #elif defined (WIN32_THREADS_AVAIL)
378 typedef HANDLE MUTEX_T;
379 #define MUTEX_T_INIT
380 #endif
381
382
383 /* POSIX implementation suppose that semaphore and RW objects
384 * must be implemented by the user himself
385 */
386 #if defined (POSIX_THREADS_AVAIL)
387 typedef struct psema_t {
388 Int4 count;
389 pthread_mutex_t mutex;
390 pthread_cond_t cond;
391 } psema_t;
392 #endif
393
394
395 /* To allow main NCBI thread to wait until all other NCBI threads are exited
396 */
397 #ifdef NCBI_THREADS_AVAIL
398 /* Counter for the running NCBI threads (excluding the main thread): >= 0
399 */
400 static Uint4 s_ThreadCounter = 0;
401
402 /* Semaphore to wait (at the very end of main thread) for the
403 * "s_ThreadCounter" to become zero -- to make sure that all registered
404 * NCBI threads are terminated
405 */
406 static TNlmSemaphore s_ThreadCounter_sema = 0;
407
408 /* Protective mutex for the counter and the semaphore
409 */
410 static TNlmMutex s_ThreadCounter_mutex = 0;
411 #endif
412
413
414 /*
415 * Functions and data structures providing storage and execution of
416 * the "thread destructor functions"
417 */
418 typedef struct
419 {
420 TNlmThreadOnExit func;
421 VoidPtr arg;
422 } TOnExitData;
423
424 typedef struct
425 {
426 size_t size;
427 TOnExitData user[1];
428 } TOnExitArray, PNTR TOnExitArrayPtr;
429
430
431 #if defined (SOLARIS_THREADS_AVAIL)
432 static thread_key_t thread_exit_key;
433 #elif defined (POSIX_THREADS_AVAIL)
434 static pthread_key_t thread_exit_key;
435 #elif defined (WIN32_THREADS_AVAIL)
436 #if defined(_MSC_VER) || defined(COMP_METRO)
437 static __declspec( thread ) TOnExitArrayPtr thread_exit_arr = NULL;
438 static __declspec( thread ) HANDLE thread_self = NULL;
439 #else
440 static TOnExitArrayPtr __thread thread_exit_arr = NULL;
441 static HANDLE __thread thread_self = NULL;
442 #endif
443 #else
444 static TOnExitArrayPtr thread_exit_arr = NULL;
445 #endif
446
447
448 /* protective mutex for all TLS-related objects */
449 static TNlmMutex s_TlsMutex;
450
451
init_exit_arr(void)452 static Boolean init_exit_arr(void)
453 {
454 static Boolean initialized = FALSE;
455
456 if ( initialized )
457 return TRUE;
458
459 X_VERIFY( !NlmMutexLockEx(&s_TlsMutex) );
460 #if defined (SOLARIS_THREADS_AVAIL)
461 if (!initialized &&
462 thr_keycreate(&thread_exit_key, NULL) == 0)
463 initialized = TRUE;
464 #elif defined (POSIX_THREADS_AVAIL)
465 if (!initialized &&
466 pthread_key_create(&thread_exit_key, NULL) == 0)
467 initialized = TRUE;
468 #elif defined (WIN32_THREADS_AVAIL)
469 initialized = TRUE;
470 #else
471 initialized = TRUE;
472 #endif
473 X_VERIFY( !NlmMutexUnlock(s_TlsMutex) );
474
475 return initialized;
476 }
477
478
get_exit_arr(void)479 static TOnExitArrayPtr get_exit_arr(void)
480 {
481 if ( !init_exit_arr() )
482 return NULL;
483
484 {{
485 #if defined (SOLARIS_THREADS_AVAIL)
486 VoidPtr arr;
487 if (thr_getspecific(thread_exit_key, &arr) != 0)
488 return NULL;
489 return (TOnExitArrayPtr)arr;
490 #elif defined (POSIX_THREADS_AVAIL)
491 return (TOnExitArrayPtr)pthread_getspecific( thread_exit_key );
492 #elif defined (WIN32_THREADS_AVAIL)
493 return thread_exit_arr;
494 #else
495 return thread_exit_arr;
496 #endif
497 }}
498 }
499
500
set_exit_arr(TOnExitArrayPtr arr_ptr)501 static void set_exit_arr(TOnExitArrayPtr arr_ptr)
502 {
503 #if defined (SOLARIS_THREADS_AVAIL)
504 if ( thr_setspecific(thread_exit_key, (VoidPtr)arr_ptr) != 0)
505 abort();
506 #elif defined (POSIX_THREADS_AVAIL)
507 if (pthread_setspecific(thread_exit_key, (VoidPtr)arr_ptr) != 0)
508 abort();
509 #elif defined (WIN32_THREADS_AVAIL)
510 thread_exit_arr = arr_ptr;
511 #else
512 thread_exit_arr = arr_ptr;
513 #endif
514 }
515
516 /* Internal prototypes
517 */
518 static Boolean s_TlsCleanupAll(void);
519
520
521 /***********************************************************************
522 * EXTERNAL
523 ***********************************************************************/
524
NlmThreadAddOnExit(TNlmThreadOnExit func,VoidPtr arg)525 NLM_EXTERN Boolean NlmThreadAddOnExit(TNlmThreadOnExit func, VoidPtr arg)
526 {
527 TOnExitArrayPtr arr_ptr = get_exit_arr();
528 size_t size = (arr_ptr == NULL) ? 0 : arr_ptr->size;
529
530 size_t i = 0;
531 while (i < size && arr_ptr->user[i].func != NULL)
532 i++;
533
534 if (i == size)
535 {
536 TOnExitArrayPtr new_arr = (TOnExitArrayPtr)
537 Nlm_MemNew(sizeof(TOnExitArray) + size * sizeof(TOnExitData));
538 if (new_arr == NULL)
539 return FALSE;
540
541 new_arr->size = size + 1;
542 if (size != 0)
543 {
544 Nlm_MemCpy(new_arr->user, arr_ptr->user,
545 size * sizeof(TOnExitData));
546 Nlm_MemFree( arr_ptr );
547 }
548 arr_ptr = new_arr;
549 }
550
551 arr_ptr->user[i].func = func;
552 arr_ptr->user[i].arg = arg;
553 set_exit_arr( arr_ptr );
554 return TRUE;
555 }
556
557
NlmThreadRemoveOnExit(TNlmThreadOnExit func,VoidPtr arg)558 NLM_EXTERN void NlmThreadRemoveOnExit(TNlmThreadOnExit func, VoidPtr arg)
559 {
560 TOnExitArrayPtr arr_ptr = get_exit_arr();
561 size_t size = (arr_ptr == NULL) ? 0 : arr_ptr->size;
562
563 size_t i;
564 for (i = 0; i < size; i++)
565 {
566 if (arr_ptr->user[i].func == func &&
567 arr_ptr->user[i].arg == arg)
568 {
569 Nlm_MemMove(arr_ptr->user + i, arr_ptr->user + i + 1,
570 (size - i - 1) * sizeof(TOnExitData));
571 arr_ptr->user[size-1].func = NULL;
572 }
573 }
574 }
575
576
NlmThreadMemFree(VoidPtr ptr)577 NLM_EXTERN void NlmThreadMemFree(VoidPtr ptr)
578 {
579 Nlm_MemFree(ptr);
580 }
581
582
583 /* -------------------- NlmThreadWrapper -------------------------
584 Purpose: To wrap the starting thread main function
585 Parameters: The user's main thread function with args and flags
586 Returns: New thread handle. -- to be used to identify the thread.
587 Description: This function creates new thread - independent process
588 inside current process, that share most resources of the
589 parent thread.
590 NOTE: This function must be used in NlmThreadCreate() only!
591 ------------------------------------------------------------------*/
592
593 #ifdef NCBI_THREADS_AVAIL
594 typedef struct
595 {
596 TNlmThreadStart func;
597 VoidPtr arg;
598 TNlmThreadOnExit exit_func;
599 VoidPtr exit_arg;
600 CharPtr progName;
601 CharPtr logName;
602 ErrOpts errOpts;
603 } TNlmThreadWrapperArg, PNTR TNlmThreadWrapperArgPtr;
604
605
606 static
607 #if defined(SOLARIS_THREADS_AVAIL) || defined(POSIX_THREADS_AVAIL)
608 VoidPtr
609 #elif defined(WIN32_THREADS_AVAIL)
610 DWORD WINAPI
611 #endif
NlmThreadWrapper(VoidPtr wrapper_arg)612 NlmThreadWrapper(VoidPtr wrapper_arg)
613 {
614 VoidPtr ret_value;
615 TNlmThreadWrapperArgPtr user = (TNlmThreadWrapperArgPtr)wrapper_arg;
616 TNlmThreadStart startFunc = user->func;
617 VoidPtr startFuncArg = user->arg;
618
619 #ifdef WIN32_THREADS_AVAIL
620 /* duplicate and store an actual(not a pseudo-) handle to the thread */
621 if ( !DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
622 GetCurrentProcess(), &thread_self,
623 0, FALSE, DUPLICATE_SAME_ACCESS) )
624 abort();
625 #endif
626
627 /* register the exiting function specified by the thread parent, if any */
628 if (user->exit_func != NULL &&
629 !NlmThreadAddOnExit(user->exit_func, user->exit_arg))
630 abort();
631
632 /* store program name */
633 if (user->progName != NULL)
634 Nlm_SetAppProperty("ProgramName", (void *)user->progName);
635
636 /* set the error posting environment */
637 if (user->logName != NULL)
638 {
639 Nlm_ErrSetLogfile(user->logName, ELOG_APPEND);
640 user->logName = (CharPtr)Nlm_MemFree( user->logName );
641 }
642 ErrRestoreOptions( &user->errOpts );
643
644 Nlm_Free( wrapper_arg );
645
646 /* run the user's thread function */
647 ret_value = (*startFunc)( startFuncArg );
648
649 /* the regular exit from the thread */
650 NlmThreadExit( ret_value );
651
652 /* should never reach here... */
653 abort();
654 return 0;
655 }
656 #endif /* NCBI_THREADS_AVAIL */
657
658
659
NlmThreadCreate(TNlmThreadStart theStartFunction,VoidPtr arg)660 NLM_EXTERN TNlmThread NlmThreadCreate(TNlmThreadStart theStartFunction,
661 VoidPtr arg)
662 {
663 return NlmThreadCreateEx(theStartFunction, arg,
664 THREAD_RUN, eTP_Default, NULL, NULL);
665 }
666
667
668 #if defined(SOLARIS_THREADS_AVAIL) || defined(POSIX_THREADS_AVAIL)
669
670 #if defined(OS_UNIX_SOL) && defined(POSIX_THREADS_AVAIL)
671 #define prio_value(x) (-1)
672 #else
prio_value(EThreadPriority priority)673 static int prio_value(EThreadPriority priority)
674 {
675 #if defined(SOLARIS_THREADS_AVAIL)
676 const int min = 0;
677 const int max = 127;
678 #elif defined(POSIX_THREADS_AVAIL)
679 #ifdef OS_UNIX_OSF1
680 const int min = PRI_OTHER_MIN;
681 const int max = PRI_OTHER_MAX;
682 #elif defined(OS_UNIX_DARWIN)
683 const int min = -20;
684 const int max = 20;
685 #else
686 int min = sched_get_priority_min(SCHED_OTHER);
687 int max = sched_get_priority_max(SCHED_OTHER);
688 #endif
689 #endif
690
691 switch ( priority )
692 {
693 case eTP_Default:
694 case eTP_Normal:
695 return (max + min) / 2;
696 case eTP_Idle: return min;
697 case eTP_Lowest: return min + (max - min) / 5;
698 case eTP_Low: return min + (max - min) / 3;
699 case eTP_High: return min + (max - min) * 3/4;
700 case eTP_Highest: return max;
701 }
702 X_ASSERT ( FALSE );
703 return (max + min) / 2;
704 }
705 #endif
706
707 #elif defined(WIN32_THREADS_AVAIL)
708 static const int prio_value[eTP_Default] = {
709 THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST,
710 THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL,
711 THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST
712 };
713 #endif
714
715
NlmThreadCreateEx(TNlmThreadStart theStartFunction,VoidPtr arg,Int4 flags,EThreadPriority priority,TNlmThreadOnExit exit_func,VoidPtr exit_arg)716 NLM_EXTERN TNlmThread NlmThreadCreateEx(TNlmThreadStart theStartFunction,
717 VoidPtr arg,
718 Int4 flags,
719 EThreadPriority priority,
720 TNlmThreadOnExit exit_func,
721 VoidPtr exit_arg)
722 {
723 TNlmThread thread_handle = NULL_thread;
724
725 #ifdef NCBI_THREADS_AVAIL
726
727 /* initialize thread wrapper func */
728 TNlmThreadWrapperArgPtr wrapper_data =
729 (TNlmThreadWrapperArgPtr) Calloc(1, sizeof(TNlmThreadWrapperArg));
730 if (wrapper_data == NULL)
731 return NULL_thread;
732 wrapper_data->func = theStartFunction;
733 wrapper_data->arg = arg;
734 wrapper_data->exit_func = exit_func;
735 wrapper_data->exit_arg = exit_arg;
736 wrapper_data->progName =
737 Nlm_StringSave( (char *)Nlm_GetAppProperty("ProgramName") );
738 wrapper_data->logName = Nlm_StringSave( Nlm_ErrGetLogfile() );
739 ErrSaveOptions(&wrapper_data->errOpts);
740
741 /* indicate that one more NCBI thread is running */
742 NlmMutexLockEx(&s_ThreadCounter_mutex);
743 if ( !s_ThreadCounter_sema ) {
744 s_ThreadCounter_sema = NlmSemaInit(0);
745 }
746 s_ThreadCounter++;
747 NlmMutexUnlock(s_ThreadCounter_mutex);
748
749 /* create new thread */
750 #if defined(SOLARIS_THREADS_AVAIL)
751 {{
752 thread_t tid;
753 long x_flags = 0;
754 if (flags & THREAD_DETACHED) /* default - attached */
755 x_flags |= THR_DETACHED;
756
757 if (flags & THREAD_BOUND) /* default - unbound */
758 x_flags |= THR_BOUND;
759 else if (priority != eTP_Default)
760 x_flags |= THR_SUSPENDED; /* create suspended to adjust the priority */
761
762 if (thr_create(NULL, 0, NlmThreadWrapper, (void *)wrapper_data,
763 x_flags, &tid) != 0) {
764 tid = 0;
765 }
766 else if (priority != eTP_Default) {/* adjust priority and resume */
767 if (thr_setprio(tid, prio_value(priority)) != 0 ||
768 thr_continue(tid) != 0) {
769 X_VERIFY ( !thr_kill(tid, SIGKILL) );
770 tid = 0;
771 }
772 }
773 thread_handle = (TNlmThread)tid;
774 }}
775
776 #elif defined(POSIX_THREADS_AVAIL)
777 {{
778 #if !defined(PTHREAD_CREATE_JOINABLE) && defined(PTHREAD_CREATE_UNDETACHED)
779 #define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
780 #endif
781 pthread_attr_t attr;
782 pthread_t thread_id;
783 X_VERIFY ( !pthread_attr_init(&attr) );
784 X_VERIFY ( !pthread_attr_setdetachstate(&attr, (flags & THREAD_DETACHED) ?
785 PTHREAD_CREATE_DETACHED :
786 PTHREAD_CREATE_JOINABLE) );
787 #ifdef POSIX_BOUND_THREADS_AVAIL
788 if (flags & THREAD_BOUND)
789 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
790 else
791 #elif defined(OS_UNIX_IRIX)
792 if (flags & THREAD_BOUND)
793 /* SGI-HNC: Check for PTHREAD_SCOPE_BOUND (IRIX 6.5.8)
794 or PTHREAD_SCOPE_BOUND_NP (IRIX 6.5.9 and higher) */
795 if (pthread_attr_setscope(&attr, PTHREAD_SCOPE_BOUND_NP) != 0)
796 /* SGI-HNC: Otherwise default to PTHREAD_SCOPE_PROCESS */
797 #endif
798 #ifndef OS_UNIX_NETBSD
799 if (!pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS) &&
800 !pthread_attr_setschedpolicy(&attr, SCHED_OTHER) &&
801 priority != eTP_Default) { /* adjust the thread priority */
802 #ifndef OS_UNIX_AIX
803 int new_priority = prio_value(priority);
804 if (new_priority != -1) {
805 struct sched_param param;
806 X_VERIFY ( !pthread_attr_getschedparam(&attr, ¶m) );
807 param.sched_priority = new_priority;
808 X_VERIFY ( !pthread_attr_setschedparam(&attr, ¶m) );
809 }
810 #endif
811 }
812 #endif
813
814 if (pthread_create(&thread_id, &attr,
815 NlmThreadWrapper, (void *)wrapper_data) == 0) {
816 thread_handle = (TNlmThread) thread_id;
817 } else {
818 X_ASSERT( 0 );
819 thread_handle = NULL_thread;
820 }
821 X_VERIFY ( !pthread_attr_destroy(&attr) );
822 }}
823
824 #elif defined(WIN32_THREADS_AVAIL)
825 {{
826 DWORD thread_id;
827 thread_handle = (TNlmThread)CreateThread(NULL, 0,
828 NlmThreadWrapper, (LPVOID)wrapper_data, 0, &thread_id);
829
830 if (priority != eTP_Default) { /* adjust the priority */
831 X_VERIFY ( SetThreadPriority(thread_handle, prio_value[priority]) );
832 }
833 }}
834 #endif
835
836 /* is OK? */
837 if (NlmThreadCompare(thread_handle, NULL_thread)) {
838 X_ASSERT ( FALSE );
839 wrapper_data->logName = (CharPtr)Nlm_MemFree(wrapper_data->logName);
840 wrapper_data->progName = (CharPtr)Nlm_MemFree(wrapper_data->progName);
841 Nlm_Free(wrapper_data);
842 NlmMutexLock(s_ThreadCounter_mutex);
843 ASSERT(s_ThreadCounter > 0);
844 s_ThreadCounter--;
845 NlmMutexUnlock(s_ThreadCounter_mutex);
846 }
847 #endif /* NCBI_THREADS_AVAIL */
848
849 return thread_handle;
850 }
851
852
NlmThreadSelf(void)853 NLM_EXTERN TNlmThread NlmThreadSelf(void)
854 {
855 #if defined(SOLARIS_THREADS_AVAIL)
856 return (TNlmThread) thr_self();
857 #elif defined(POSIX_THREADS_AVAIL)
858 return (TNlmThread) pthread_self();
859 #elif defined(WIN32_THREADS_AVAIL)
860 return (TNlmThread) thread_self;
861 #else /* default */
862 return NULL_thread;
863 #endif
864 }
865
866
NlmThreadCompare(TNlmThread thread1,TNlmThread thread2)867 NLM_EXTERN Boolean NlmThreadCompare(TNlmThread thread1, TNlmThread thread2)
868 {
869 return (Boolean)(thread1 == thread2);
870 }
871
872
NlmThreadJoin(TNlmThread wait_for,VoidPtr * status)873 NLM_EXTERN Int4 NlmThreadJoin(TNlmThread wait_for, VoidPtr *status)
874 {
875 int err_code;
876 #if defined(SOLARIS_THREADS_AVAIL)
877 err_code = thr_join((thread_t)wait_for, NULL, status);
878 #elif defined(POSIX_THREADS_AVAIL)
879 err_code = pthread_join((pthread_t)wait_for, status);
880 #elif defined(WIN32_THREADS_AVAIL)
881 HANDLE thread = (HANDLE)wait_for;
882 WaitForSingleObject(thread, INFINITE);
883 if (GetExitCodeThread(thread, (LPDWORD)status) &&
884 (DWORD)*status != (DWORD)STILL_ACTIVE) {
885 X_VERIFY ( CloseHandle(thread) );
886 err_code = 0;
887 }
888 else {
889 err_code = (int)GetLastError();
890 if (err_code == 0)
891 err_code = -1;
892 }
893 #else /* default */
894 err_code = -1;
895 #endif
896
897 X_ASSERT ( err_code == 0 );
898 return err_code ? -1 : 0;
899 }
900
901
NlmThreadJoinAll(void)902 NLM_EXTERN Int4 NlmThreadJoinAll(void)
903 {
904 #ifdef NCBI_THREADS_AVAIL
905 /* wait for all threads to exit */
906 while (s_ThreadCounter > 0)
907 NlmSemaWait(s_ThreadCounter_sema);
908
909 /* make sure the s_ThreadCounter_mutex become unlocked */
910 if ( s_ThreadCounter_mutex ) {
911 NlmMutexLock(s_ThreadCounter_mutex);
912 NlmMutexUnlock(s_ThreadCounter_mutex);
913 }
914
915 ASSERT(s_ThreadCounter == 0);
916 return (s_ThreadCounter == 0) ? 0 : -1;
917 #else
918 return 0;
919 #endif
920 }
921
922
NlmThreadExit(VoidPtr status)923 NLM_EXTERN void NlmThreadExit(VoidPtr status)
924 {
925 /* execute all terminating functions registered by user */
926 TOnExitArrayPtr arr_ptr = get_exit_arr();
927 size_t size = (arr_ptr == NULL) ? 0 : arr_ptr->size;
928 while ( size-- ) {
929 if (arr_ptr->user[size].func != NULL)
930 (*arr_ptr->user[size].func)( arr_ptr->user[size].arg );
931 }
932
933 if (arr_ptr != NULL) {
934 Free(arr_ptr);
935 set_exit_arr( (TOnExitArrayPtr)~0 );
936 }
937
938 #ifdef NCBI_THREADS_AVAIL
939 /* exiting from thread must clear "context" structure */
940 {{
941 const char *progName = (const char *)Nlm_GetAppProperty("ProgramName");
942 Nlm_ReleaseAppContext();
943 Nlm_MemFree((void *)progName);
944 }}
945
946 /* zeroing(and cleaning up) TLS data */
947 s_TlsCleanupAll();
948
949 /* indicate that one more thread has exited */
950 NlmMutexLock(s_ThreadCounter_mutex);
951 ASSERT(s_ThreadCounter > 0);
952 s_ThreadCounter--;
953 if (s_ThreadCounter == 0) {
954 NlmSemaPost(s_ThreadCounter_sema);
955 }
956 NlmMutexUnlock(s_ThreadCounter_mutex);
957 #endif /* NCBI_THREADS_AVAIL */
958
959 /* exit the thread */
960 #if defined(SOLARIS_THREADS_AVAIL)
961 thr_exit(status);
962 #elif defined(POSIX_THREADS_AVAIL)
963 pthread_exit(status);
964 #elif defined(WIN32_THREADS_AVAIL)
965 X_VERIFY ( CloseHandle(thread_self) );
966 ExitThread((DWORD)status);
967 #endif
968 }
969
970
971 /********************************************************************/
972 /* */
973 /* =========== Semaphore support for the thread API library ======= */
974 /* */
975 /********************************************************************/
976
NlmSemaInit(Uint4 count)977 NLM_EXTERN TNlmSemaphore NlmSemaInit(Uint4 count)
978 {
979 #if defined(SOLARIS_THREADS_AVAIL)
980 sema_t *theSemaphore = (sema_t *)Malloc(sizeof(sema_t));
981 if (theSemaphore == NULL ||
982 sema_init(theSemaphore, count, USYNC_THREAD, NULL) != 0)
983 return NULL;
984
985 return (TNlmSemaphore)theSemaphore;
986
987 #elif defined(POSIX_THREADS_AVAIL)
988 psema_t *theSemaphore;
989 if ( !(theSemaphore = (psema_t *)Malloc(sizeof(psema_t))) )
990 return NULL;
991
992 theSemaphore->count = count;
993
994 if (pthread_mutex_init(&theSemaphore->mutex, NULL) != 0 ||
995 pthread_cond_init (&theSemaphore->cond, NULL) != 0)
996 return NULL;
997
998 return (TNlmSemaphore)theSemaphore;
999
1000 #elif defined(WIN32_THREADS_AVAIL)
1001 #define MAX_SEMAPHORE_COUNT 32000
1002 HANDLE theSemaphore;
1003 if((theSemaphore =
1004 CreateSemaphore(NULL, count, MAX_SEMAPHORE_COUNT, NULL)) != NULL)
1005 return (TNlmSemaphore)theSemaphore;
1006 else
1007 return NULL;
1008
1009 #else
1010 return NULL;
1011 #endif
1012 }
1013
1014
NlmSemaDestroy(TNlmSemaphore theSemaphore)1015 NLM_EXTERN Int4 NlmSemaDestroy(TNlmSemaphore theSemaphore)
1016 {
1017 #if defined(SOLARIS_THREADS_AVAIL)
1018 Int4 error = sema_destroy( (sema_t *)theSemaphore );
1019 Free( theSemaphore );
1020 return error;
1021
1022 #elif defined(POSIX_THREADS_AVAIL)
1023 psema_t *PSemaphore = (psema_t *)theSemaphore;
1024
1025 if (pthread_mutex_destroy(&PSemaphore->mutex) != 0 ||
1026 pthread_cond_destroy (&PSemaphore->cond) != 0)
1027 {
1028 Free( theSemaphore );
1029 return -1;
1030 }
1031 Free( theSemaphore );
1032 return 0;
1033
1034 #elif defined(WIN32_THREADS_AVAIL)
1035 return CloseHandle( (HANDLE)theSemaphore ) ? 0 : -1;
1036
1037 #else
1038 return 0;
1039 #endif
1040 }
1041
1042
NlmSemaWait(TNlmSemaphore theSemaphore)1043 NLM_EXTERN Int4 NlmSemaWait(TNlmSemaphore theSemaphore)
1044 {
1045 #if defined(SOLARIS_THREADS_AVAIL)
1046 return sema_wait((sema_t *)theSemaphore);
1047
1048 #elif defined(POSIX_THREADS_AVAIL)
1049 psema_t *PSemaphore = (psema_t *)theSemaphore;
1050 if (pthread_mutex_lock(&PSemaphore->mutex) != 0)
1051 return -1;
1052
1053 if (PSemaphore->count > 0)
1054 PSemaphore->count--;
1055 else {
1056 do {
1057 pthread_cond_wait(&PSemaphore->cond, &PSemaphore->mutex);
1058 } while (PSemaphore->count == 0);
1059 PSemaphore->count--;
1060 }
1061 return pthread_mutex_unlock(&PSemaphore->mutex) ? -1 : 0;
1062
1063 #elif defined(WIN32_THREADS_AVAIL)
1064 return
1065 (WaitForSingleObject((HANDLE)theSemaphore, INFINITE) == WAIT_OBJECT_0) ?
1066 0 : -1;
1067
1068 #else
1069 return 0;
1070 #endif
1071 }
1072
1073
NlmSemaTryWait(TNlmSemaphore theSemaphore)1074 NLM_EXTERN Int4 NlmSemaTryWait(TNlmSemaphore theSemaphore)
1075 {
1076 #if defined(SOLARIS_THREADS_AVAIL)
1077 return sema_trywait((sema_t *)theSemaphore);
1078
1079 #elif defined (POSIX_THREADS_AVAIL)
1080 Int4 retval;
1081 psema_t *PSemaphore = (psema_t *)theSemaphore;
1082 if (pthread_mutex_lock(&PSemaphore->mutex) != 0)
1083 return -1;
1084
1085 if (PSemaphore->count > 0) {
1086 PSemaphore->count--;
1087 retval = 0;
1088 }
1089 else {
1090 retval = -1;
1091 }
1092 return pthread_mutex_unlock(&PSemaphore->mutex) ? -1 : retval;
1093
1094 #elif defined(WIN32_THREADS_AVAIL)
1095 return
1096 (WaitForSingleObject((HANDLE)theSemaphore, 0) == WAIT_OBJECT_0) ?
1097 0 : -1;
1098
1099 #else
1100 return 0;
1101 #endif
1102 }
1103
1104
NlmSemaPost(TNlmSemaphore theSemaphore)1105 NLM_EXTERN Int4 NlmSemaPost(TNlmSemaphore theSemaphore)
1106 {
1107 #if defined(SOLARIS_THREADS_AVAIL)
1108 return sema_post((sema_t *)theSemaphore);
1109
1110 #elif defined (POSIX_THREADS_AVAIL)
1111 psema_t *PSemaphore = (psema_t *)theSemaphore;
1112 if (pthread_mutex_lock(&PSemaphore->mutex) != 0)
1113 return -1;
1114
1115 PSemaphore->count++;
1116 if (pthread_cond_signal(&PSemaphore->cond) != 0) {
1117 PSemaphore->count--;
1118 return -1;
1119 }
1120 return pthread_mutex_unlock(&PSemaphore->mutex) ? -1 : 0;
1121
1122 #elif defined(WIN32_THREADS_AVAIL)
1123 return ReleaseSemaphore((HANDLE)theSemaphore, 1, NULL) ? 0 : -1;
1124
1125 #else
1126 return 0;
1127 #endif
1128 }
1129
1130
1131
1132 /********************************************************************/
1133 /* */
1134 /* === Semaphore-like object to keep read/write syncronization ==== */
1135 /* */
1136 /********************************************************************/
1137
1138
1139 /* [POSIX and WIN32] Nested locking policy:
1140 * W after R -- never allowed;
1141 * W after W -- allowed if the W-lock is owned by the same thread;
1142 * R after W -- allowed if the W-lock is owned by the same thread (and,
1143 * then this R is treated as if it was W);
1144 * R after R -- always allowed (unless there already was a "R after W"
1145 * performed in another thread)
1146 * U after W -- only if the W-lock is owned by the same thread
1147 */
1148
1149
1150 #define RW_UNKNOWN_OWNER ((TNlmThread)(~0))
1151
1152
1153 /******************************************************************
1154 * DEBUG-only feature -- keep track of the RW-locks
1155 */
1156
1157 #if defined(POSIX_THREADS_AVAIL) || defined(WIN32_THREADS_AVAIL)
1158 # if defined(_DEBUG_HARD)
1159 # define RW_HISTORY
1160 # endif
1161 #endif
1162
1163 #if defined(RW_HISTORY)
1164
1165 # define RW_HISTORY_SIZE 256
1166 # define RW_HISTORY_DECL SRWHistoryArray rw_history;
1167 # define RW_HISTORY_UPDATE(RW,thread,file,line) \
1168 s_RWHistoryUpdate(RW,thread,file,line)
1169
1170 typedef struct {
1171 const char* file;
1172 int line;
1173 int readers; /* "readers" value *after* the change */
1174 TNlmThread thread; /* ID of the thread caused this change */
1175 } SRWHistory;
1176
1177 typedef struct {
1178 size_t size;
1179 SRWHistory elem[RW_HISTORY_SIZE];
1180 } SRWHistoryArray;
1181
1182 #else /* def RW_HISTORY */
1183
1184 # define RW_HISTORY_DECL
1185 # define RW_HISTORY_UPDATE(RW,thread,file,line) ((void)0)
1186
1187 #endif /* else RW_HISTORY */
1188
1189
1190
1191 /******************************************************************
1192 * Internal structure of RW-locks
1193 */
1194
1195 #if defined(SOLARIS_THREADS_AVAIL)
1196
1197 typedef struct TNlmRWlockTag {
1198 rwlock_t rwlock; /* native Solaris-style RW-lock */
1199 } structRWlock;
1200
1201 #elif defined(POSIX_THREADS_AVAIL)
1202
1203 typedef struct TNlmRWlockTag {
1204 /* if W-locked -- keeps the lock owner;
1205 * also, exclusively for the reason of being easier to debug:
1206 * if R-locked -- may(but may not!) keep the owner of one of the R-locks,
1207 * if Unlocked -- keeps what it kept just before the last unlock
1208 */
1209 TNlmThread owner;
1210
1211 /* < 0 -- # of locks by writers or nested writers/readers
1212 * = 0 -- not locked
1213 * > 0 -- # of locks by readers
1214 */
1215 Int4 readers;
1216
1217 /* protects members of this RW from being modified by more than one thread
1218 * at once
1219 */
1220 pthread_mutex_t mutex;
1221
1222 pthread_cond_t cond_r; /* condition variable for readers */
1223 pthread_cond_t cond_w; /* condition variable for writers */
1224
1225 RW_HISTORY_DECL /* for DEBUG purposes only! */
1226 } structRWlock;
1227
1228 #elif defined(WIN32_THREADS_AVAIL)
1229
1230 typedef struct TNlmRWlockTag {
1231 TNlmThread owner; /* see comment above (for POSIX_THREADS_AVAIL) */
1232 Int4 readers; /* see comment above (for POSIX_THREADS_AVAIL) */
1233 HANDLE mutex; /* mutex to protect "readers" variable */
1234 HANDLE sema_rw; /* semaphore for readers and writers */
1235 HANDLE sema_w; /* semaphore for writers */
1236 RW_HISTORY_DECL /* for DEBUG purposes only! */
1237 } structRWlock;
1238
1239 #else
1240 # define NO_RWLOCK
1241 #endif
1242
1243
1244 static char* s_RWprintout(TNlmRWlock RW, int/*bool*/ do_mutex);
1245
1246
1247 #if defined(RW_HISTORY)
s_RWHistoryUpdate(TNlmRWlock RW,TNlmThread thread,const char * file,int line)1248 static void s_RWHistoryUpdate
1249 (TNlmRWlock RW,
1250 TNlmThread thread,
1251 const char* file,
1252 int line)
1253 {
1254 SRWHistoryArray* arr = &RW->rw_history;
1255 SRWHistory* rec = arr->elem + (arr->size % RW_HISTORY_SIZE);
1256
1257 rec->file = file;
1258 rec->line = line;
1259 rec->readers = RW->readers;
1260 rec->thread = thread;
1261 arr->size++;
1262 #if defined(RW_TRACE)
1263 if (RW->readers == 0 || (arr->size % RW_HISTORY_SIZE) == 0) {
1264 char* str = s_RWprintout(RW, 0/*no_mutex*/);
1265 fprintf(stderr, "%s", str);
1266 free(str);
1267 }
1268 #endif
1269 if (RW->readers == 0) {
1270 arr->size = 0;
1271 }
1272 }
1273 #endif
1274
1275
1276 /******************************************************************
1277 * RW-lock functions
1278 */
1279
NlmRWinit(void)1280 NLM_EXTERN TNlmRWlock NlmRWinit(void)
1281 {
1282 #ifdef NO_RWLOCK
1283 return NULL;
1284 #else
1285
1286 int err_code = 0;
1287 TNlmRWlock RW;
1288 if ((RW = (TNlmRWlock) Calloc(1, sizeof(structRWlock))) == NULL)
1289 return NULL;
1290
1291 #if defined(SOLARIS_THREADS_AVAIL)
1292 err_code = rwlock_init(&RW->rwlock, USYNC_THREAD, NULL);
1293
1294 #elif defined(POSIX_THREADS_AVAIL)
1295 RW->readers = 0;
1296 err_code = pthread_mutex_init(&RW->mutex, NULL);
1297 if (err_code == 0) {
1298 err_code = pthread_cond_init(&RW->cond_r, NULL);
1299 if (err_code == 0) {
1300 err_code = pthread_cond_init(&RW->cond_w, NULL);
1301 if (err_code != 0) { /* error initializing "cond_w" -- clean */
1302 X_VERIFY ( !pthread_cond_destroy (&RW->cond_r) );
1303 X_VERIFY ( !pthread_mutex_destroy(&RW->mutex ) );
1304 }
1305 } else { /* error initializing "cond_r" -- clean */
1306 X_VERIFY ( !pthread_mutex_destroy(&RW->mutex) );
1307 }
1308 }
1309
1310 #elif defined (WIN32_THREADS_AVAIL)
1311 RW->readers = 0;
1312 if ( !(RW->mutex = CreateMutex(NULL, FALSE, NULL)) ) {
1313 err_code = 1;
1314 } else {
1315 if ( !(RW->sema_rw = CreateSemaphore(NULL, 1, 1, NULL)) ) {
1316 CloseHandle(RW->mutex);
1317 err_code = 2;
1318 } else {
1319 if ( !(RW->sema_w = CreateSemaphore(NULL, 1, 1, NULL)) ) {
1320 CloseHandle(RW->sema_rw);
1321 CloseHandle(RW->mutex);
1322 err_code = 3;
1323 }
1324 }
1325 }
1326 #endif
1327
1328 if (err_code != 0) {
1329 X_ASSERT ( FALSE );
1330 Free(RW);
1331 return NULL;
1332 }
1333
1334 return RW;
1335 #endif /* else!NO_RWLOCK */
1336 }
1337
1338
NlmRWdestroy(TNlmRWlock RW)1339 NLM_EXTERN Int4 NlmRWdestroy(TNlmRWlock RW)
1340 {
1341 #ifdef NO_RWLOCK
1342 return 0;
1343 #else
1344
1345 int err_code = 0;
1346
1347 #if defined(SOLARIS_THREADS_AVAIL)
1348 err_code = rwlock_destroy(&RW->rwlock);
1349
1350 #elif defined (POSIX_THREADS_AVAIL)
1351 ASSERT(RW->readers == 0);
1352 err_code = pthread_mutex_destroy(&RW->mutex);
1353 if (err_code == 0) {
1354 err_code = pthread_cond_destroy(&RW->cond_r);
1355 if (err_code == 0) {
1356 err_code = pthread_cond_destroy(&RW->cond_w);
1357 }
1358 }
1359
1360 #elif defined(WIN32_THREADS_AVAIL)
1361 ASSERT(RW->readers == 0);
1362 if ( !CloseHandle(RW->mutex) )
1363 err_code |= 0x1;
1364 if ( !CloseHandle(RW->sema_rw) )
1365 err_code |= 0x2;
1366 if ( !CloseHandle(RW->sema_w) )
1367 err_code |= 0x4;
1368 #endif
1369
1370 X_ASSERT(err_code == 0);
1371 Free(RW);
1372 return err_code ? -1 : 0;
1373 #endif /* else!NO_RWLOCK */
1374 }
1375
1376
NlmRWrdlockEx(TNlmRWlock RW,const char * file,int line)1377 NLM_EXTERN Int4 NlmRWrdlockEx(TNlmRWlock RW,
1378 const char* file, int line)
1379 {
1380 #if defined(NO_RWLOCK)
1381 return 0;
1382
1383 #elif defined(SOLARIS_THREADS_AVAIL)
1384 int err_code = rw_rdlock(&RW->rwlock);
1385 X_ASSERT(err_code == 0);
1386 return err_code ? -1 : 0;
1387
1388 #elif defined(POSIX_THREADS_AVAIL)
1389 TNlmThread this_thread = NlmThreadSelf();
1390
1391 /* protect members of "*RW" from being changed by other threads */
1392 int err_code = pthread_mutex_lock(&RW->mutex);
1393 if (err_code != 0) {
1394 X_ASSERT(0);
1395 return -1;
1396 }
1397
1398 /* obtain R-lock */
1399 if (RW->readers < 0 && RW->owner == this_thread) {
1400 /* W-locked by this the same thread already */
1401 RW->readers--; /* treate it as a W-lock then */
1402 } else if (RW->readers <= 0) {
1403 /* Unlocked or W-locked by another thread */
1404 /* if W-locked by another thread -- wait here until it's Unlocked */
1405 while (RW->readers < 0) {
1406 pthread_cond_wait(&RW->cond_r, &RW->mutex);
1407 }
1408 /* ...not locked, or it can be R-locked by other thread by now */
1409 ASSERT(RW->readers >= 0);
1410 RW->readers++;
1411 RW->owner = this_thread;
1412 } else {
1413 /* R-locked already -- increment # of R-locks; may reset the owner */
1414 RW->readers++;
1415 if (RW->owner == RW_UNKNOWN_OWNER) {
1416 RW->owner = this_thread;
1417 }
1418 }
1419
1420 /* release the "*RW" protective mutex */
1421 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1422 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1423 return 0;
1424
1425 #elif defined(WIN32_THREADS_AVAIL)
1426 TNlmThread this_thread = NlmThreadSelf();
1427
1428 /* protect members of "*RW" from being changed by other threads */
1429 if (WaitForSingleObject(RW->mutex, INFINITE) != WAIT_OBJECT_0) {
1430 X_ASSERT(0);
1431 return -1;
1432 }
1433
1434 /* treate R-lock as W-lock if W-locked by this the same thread already */
1435 if (RW->readers < 0 && RW->owner == this_thread) {
1436 RW->readers--;
1437 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1438 X_VERIFY(ReleaseMutex(RW->mutex));
1439 return 0;
1440 }
1441
1442 /* wait if W-locked by another thread */
1443 if (RW->readers < 0) {
1444 HANDLE obj[2];
1445 DWORD wait_res;
1446 obj[0] = RW->mutex;
1447 obj[1] = RW->sema_rw;
1448 X_VERIFY(ReleaseMutex(RW->mutex));/* allow other threads to do stuff */
1449 wait_res = WaitForMultipleObjects(2, obj, TRUE, INFINITE);
1450 if (WAIT_OBJECT_0 <= wait_res && wait_res < WAIT_OBJECT_0 + 2) {
1451 LONG prev_sema;
1452 X_VERIFY(ReleaseSemaphore(RW->sema_rw, 1, &prev_sema));
1453 X_ASSERT(prev_sema == 0);
1454 } else {
1455 X_ASSERT(0);
1456 return -1;
1457 }
1458 }
1459 X_ASSERT(RW->readers >= 0);
1460
1461 /* first reader: lock against writers and assign the owner */
1462 if (RW->readers == 0) {
1463 if (WaitForSingleObject(RW->sema_w, 0) != WAIT_OBJECT_0) {
1464 X_VERIFY(ReleaseMutex(RW->mutex));
1465 X_ASSERT(0);
1466 return -1;
1467 }
1468 RW->owner = this_thread;
1469 }
1470
1471 /* increment the "readers" counter */
1472 RW->readers++;
1473
1474 /* release the "*RW" protective mutex */
1475 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1476 X_VERIFY(ReleaseMutex(RW->mutex));
1477 return 0;
1478 #endif
1479 }
1480
1481
NlmRWwrlockEx(TNlmRWlock RW,const char * file,int line)1482 NLM_EXTERN Int4 NlmRWwrlockEx(TNlmRWlock RW,
1483 const char* file, int line)
1484 {
1485 #if defined(NO_RWLOCK)
1486 return 0;
1487
1488 #elif defined(SOLARIS_THREADS_AVAIL)
1489 int err_code = rw_wrlock(&RW->rwlock);
1490 X_ASSERT(err_code == 0);
1491 return err_code ? -1 : 0;
1492
1493 #elif defined(POSIX_THREADS_AVAIL)
1494 TNlmThread this_thread = NlmThreadSelf();
1495
1496 /* protect members of "*RW" from being changed by other threads */
1497 int err_code = pthread_mutex_lock(&RW->mutex);
1498 if (err_code != 0) {
1499 X_ASSERT(0);
1500 return -1;
1501 }
1502
1503 /* obtain W-lock */
1504 if (RW->readers < 0 && RW->owner == this_thread) {
1505 /* W-locked by this the same thread already */
1506 RW->readers--;
1507 } else if (RW->readers == 0 || RW->owner != this_thread) {
1508 /* Unlocked or RW-locked by another thread(s) */
1509 /* RW-locked by another thread(s) -- wait here until it is Unlocked */
1510 while (RW->readers != 0) {
1511 pthread_cond_wait(&RW->cond_w, &RW->mutex);
1512 }
1513 /* ...not locked now */
1514 RW->readers = -1;
1515 RW->owner = this_thread;
1516 } else {
1517 /* already R-locked by this thread (not always detectable) */
1518 ASSERT(0);
1519 err_code = -1;
1520 }
1521
1522 /* release the "*RW" protective mutex */
1523 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1524 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1525 return 0;
1526
1527 #elif defined(WIN32_THREADS_AVAIL)
1528 TNlmThread this_thread = NlmThreadSelf();
1529 HANDLE obj[3];
1530 DWORD wait_res;
1531
1532 /* protect members of "*RW" from being changed by other threads */
1533 if (WaitForSingleObject(RW->mutex, INFINITE) != WAIT_OBJECT_0) {
1534 X_ASSERT(0);
1535 return -1;
1536 }
1537
1538 /* already W-locked by this the same thread already */
1539 if (RW->readers < 0 && RW->owner == this_thread) {
1540 RW->readers--;
1541 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1542 X_VERIFY(ReleaseMutex(RW->mutex));
1543 return 0;
1544 }
1545
1546 /* array of sync-objects to wait for */
1547 /* (on success, both "sema_rw" and "sema_w" will be locked, so that */
1548 /* no other thread can acquire either read or write lock) */
1549 obj[0] = RW->sema_rw;
1550 obj[1] = RW->sema_w;
1551 obj[2] = RW->mutex;
1552
1553 /* not locked by anybody: sure lock 'n' go */
1554 if (RW->readers == 0) {
1555 wait_res = WaitForMultipleObjects(2, obj, TRUE,0);
1556 if (wait_res < WAIT_OBJECT_0 || WAIT_OBJECT_0 + 2 <= wait_res) {
1557 X_VERIFY(ReleaseMutex(RW->mutex));
1558 X_ASSERT(0);
1559 return -1;
1560 }
1561 RW->readers = -1;
1562 RW->owner = this_thread;
1563 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1564 X_VERIFY(ReleaseMutex(RW->mutex));
1565 return 0;
1566 }
1567
1568 /* RW-locked by other thread(s): wait until all locks are free */
1569 X_VERIFY(ReleaseMutex(RW->mutex)); /* allow other threads to do stuff */
1570 wait_res = WaitForMultipleObjects(3, obj, TRUE, INFINITE);
1571 if (wait_res < WAIT_OBJECT_0 || WAIT_OBJECT_0 + 3 <= wait_res) {
1572 X_ASSERT(0);
1573 return -1;
1574 }
1575 X_ASSERT(RW->readers == 0);
1576 RW->readers = -1;
1577 RW->owner = this_thread;
1578 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1579 X_VERIFY(ReleaseMutex(RW->mutex));
1580 return 0;
1581 #endif
1582 }
1583
1584
NlmRWunlockEx(TNlmRWlock RW,const char * file,int line)1585 NLM_EXTERN Int4 NlmRWunlockEx(TNlmRWlock RW,
1586 const char* file, int line)
1587 {
1588 #if !defined (NO_RWLOCK)
1589 # ifdef _TRACE_HARD
1590 int dummy = fprintf(stderr, "%ld\n", (long) RW->readers);
1591 # endif
1592 #endif
1593
1594
1595 #if defined(NO_RWLOCK)
1596 return 0;
1597
1598 #elif defined(SOLARIS_THREADS_AVAIL)
1599 int err_code = rw_unlock(&RW->rwlock);
1600 X_ASSERT(err_code == 0);
1601 return err_code ? -1 : 0;
1602
1603 #elif defined (POSIX_THREADS_AVAIL)
1604 TNlmThread this_thread = NlmThreadSelf();
1605
1606 /* protect members of "*RW" from being changed by other threads */
1607 int err_code = pthread_mutex_lock(&RW->mutex);
1608 if (err_code != 0) {
1609 X_ASSERT(0);
1610 return -1;
1611 }
1612
1613 /* catch the illegal uses of RW-unlock */
1614 ASSERT(RW->readers != 0);
1615 if (RW->readers < 0 && RW->owner != this_thread) {
1616 /* attempted to Unlock a W-lock held by another thread -- trouble! */
1617 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1618 ASSERT(0);
1619 return -1;
1620 }
1621
1622 /* do unlock (R or W) */
1623 if (RW->readers == -1) {
1624 /* only one W-lock left -- allow for both R- and W-locks */
1625 if ((err_code = pthread_cond_broadcast(&RW->cond_r)) == 0)
1626 err_code = pthread_cond_signal(&RW->cond_w);
1627 if (err_code == 0)
1628 RW->readers = 0;
1629 } else if (RW->readers < 1) {
1630 /* nested W-lock -- just decrement the # of W-locks left */
1631 RW->readers++;
1632 } else if (RW->readers == 1) {
1633 /* one R-lock left - allow for W-locks (R-locks are allowed already) */
1634 if ((err_code = pthread_cond_signal(&RW->cond_w)) == 0)
1635 RW->readers = 0;
1636 } else {
1637 /* nested R-lock -- decrement the # of R-locks left; loose the owner */
1638 RW->owner = RW_UNKNOWN_OWNER;
1639 RW->readers--;
1640 }
1641
1642 /* release the "*RW" protective mutex */
1643 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1644 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1645 ASSERT(err_code == 0);
1646 return err_code ? -1 : 0;
1647
1648 #elif defined(WIN32_THREADS_AVAIL)
1649 TNlmThread this_thread = NlmThreadSelf();
1650
1651 /* protect members of "*RW" from being changed by other threads */
1652 if (WaitForSingleObject(RW->mutex, INFINITE) != WAIT_OBJECT_0) {
1653 X_ASSERT(0);
1654 return -1;
1655 }
1656
1657 /* catch the illegal uses of RW-unlock */
1658 ASSERT(RW->readers != 0);
1659 if (RW->readers < 0 && RW->owner != this_thread) {
1660 /* attempted to Unlock a W-lock held by another thread -- trouble! */
1661 X_VERIFY(ReleaseMutex(RW->mutex));
1662 ASSERT(0);
1663 return -1;
1664 }
1665
1666 /* do unlock (R or W) */
1667 if (RW->readers == -1 || RW->readers == 1) {
1668 /* only one R or W lock left -- unlock everything */
1669 LONG prev_sema;
1670 if (RW->readers == -1) {
1671 X_VERIFY(ReleaseSemaphore(RW->sema_rw, 1, &prev_sema));
1672 X_ASSERT(prev_sema == 0);
1673 }
1674 X_VERIFY(ReleaseSemaphore(RW->sema_w, 1, &prev_sema));
1675 X_ASSERT(prev_sema == 0);
1676 RW->readers = 0;
1677 } else {
1678 /* more than one reader or writer left -- just update the counter */
1679 X_ASSERT(RW->readers < -1 || 1 < RW->readers);
1680 if (RW->readers > 0)
1681 RW->readers--;
1682 else
1683 RW->readers++;
1684 }
1685
1686 /* release the "*RW" protective mutex */
1687 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1688 X_VERIFY(ReleaseMutex(RW->mutex));
1689 return 0;
1690 #endif
1691 }
1692
1693
NlmRWtryrdlockEx(TNlmRWlock RW,const char * file,int line)1694 NLM_EXTERN Int4 NlmRWtryrdlockEx(TNlmRWlock RW,
1695 const char* file, int line)
1696 {
1697 #if defined(NO_RWLOCK)
1698 return 0;
1699
1700 #elif defined(SOLARIS_THREADS_AVAIL)
1701 int err_code = rw_tryrdlock(&RW->rwlock);
1702 if (err_code != 0 && err_code != EBUSY) {
1703 ASSERT(0);
1704 return -1;
1705 }
1706 return err_code ? -1 : 0;
1707
1708 #elif defined(POSIX_THREADS_AVAIL)
1709 int err_code;
1710 TNlmThread this_thread = NlmThreadSelf();
1711
1712 /* quick check -- dont care to lock here */
1713 if (RW->readers < 0 && RW->owner != this_thread)
1714 return -1;
1715
1716 /* protect members of "*RW" from being changed by other threads */
1717 err_code = pthread_mutex_lock(&RW->mutex);
1718 if (err_code != 0) {
1719 X_ASSERT(0);
1720 return -1;
1721 }
1722
1723 /* trying to obtain R-lock */
1724 if (RW->readers == 0) {
1725 /* Unlocked -- do R-lock, store ownership to help catch "W after R" */
1726 RW->readers = 1;
1727 RW->owner = this_thread;
1728 } else if (RW->readers > 0) {
1729 /* R-locked already -- increment # of R-locks; may reset the owner */
1730 RW->readers++;
1731 if (RW->owner == RW_UNKNOWN_OWNER)
1732 RW->owner = this_thread;
1733 } else {
1734 /* W-locked already -- check if locked by the same thread (nested W) */
1735 if (RW->owner == this_thread)
1736 RW->readers--; /* nested locking (interpret R as W here) */
1737 else
1738 err_code = -1; /* cannot R-lock instantaneously */
1739 }
1740
1741 /* release the "*RW" protective mutex */
1742 if (err_code == 0) {
1743 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1744 }
1745 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1746 return err_code;
1747
1748 #elif defined(WIN32_THREADS_AVAIL)
1749 int err_code;
1750 TNlmThread this_thread = NlmThreadSelf();
1751
1752 /* quick check -- dont care to lock here */
1753 if (RW->readers < 0 && RW->owner != this_thread)
1754 return -1;
1755
1756 /* protect members of "*RW" from being changed by other threads */
1757 if (WaitForSingleObject(RW->mutex, INFINITE) != WAIT_OBJECT_0) {
1758 X_ASSERT(0);
1759 return -1;
1760 }
1761
1762 /* trying to obtain R-lock */
1763 err_code = 0;
1764
1765 if (RW->readers < 0) {
1766 /* W-locked... */
1767 if (RW->owner == this_thread)
1768 RW->readers--; /* ...by this thread -- nested; interpret R as W */
1769 else
1770 err_code = -1; /* ...by another thread -- cannot obtain lock now */
1771 } else if (RW->readers > 0) {
1772 /* R-locked */
1773 RW->readers++;
1774 } else {
1775 /* not locked -- lock the writer semaphore */
1776 if (WaitForSingleObject(RW->sema_w, 0) == WAIT_OBJECT_0) {
1777 RW->readers = 1;
1778 RW->owner = this_thread;
1779 } else {
1780 ASSERT(0);
1781 err_code = -1;
1782 }
1783 }
1784
1785 /* release the "*RW" protective mutex */
1786 if (err_code == 0) {
1787 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1788 }
1789 X_VERIFY(ReleaseMutex(RW->mutex));
1790 return err_code;
1791 #endif
1792 }
1793
1794
NlmRWtrywrlockEx(TNlmRWlock RW,const char * file,int line)1795 NLM_EXTERN Int4 NlmRWtrywrlockEx(TNlmRWlock RW,
1796 const char* file, int line)
1797 {
1798 #if defined(NO_RWLOCK)
1799 return 0;
1800
1801 #elif defined(SOLARIS_THREADS_AVAIL)
1802 int err_code = rw_trywrlock(&RW->rwlock);
1803 if (err_code != 0 && err_code != EBUSY) {
1804 ASSERT(0);
1805 return -1;
1806 }
1807 return err_code ? -1 : 0;
1808
1809 #elif defined(POSIX_THREADS_AVAIL)
1810 int err_code;
1811 TNlmThread this_thread = NlmThreadSelf();
1812
1813 /* quick check -- dont care to lock here */
1814 if (RW->readers > 0 ||
1815 (RW->readers < 0 && RW->owner != this_thread))
1816 return -1;
1817
1818 /* protect members of "*RW" from being changed by other threads */
1819 err_code = pthread_mutex_lock(&RW->mutex);
1820 if (err_code != 0) {
1821 X_ASSERT(0);
1822 return err_code;
1823 }
1824
1825 /* trying to obtain W-lock */
1826 if (RW->readers == 0) {
1827 /* Unlocked -- do W-lock, store ownership */
1828 RW->readers = -1;
1829 RW->owner = this_thread;
1830 } else if (RW->readers > 0) {
1831 /* R-locked already -- cannot W-lock right away */
1832 err_code = -1;
1833 } else {
1834 /* W-locked already -- check if locked by the same thread (nested W) */
1835 if (RW->owner == this_thread)
1836 RW->readers--; /* nested locking (interpret R as W here) */
1837 else
1838 err_code = -1; /* cannot W-lock instantaneously */
1839 }
1840
1841 /* release the "*RW" protective mutex */
1842 if (err_code == 0) {
1843 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1844 }
1845 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1846 return err_code;
1847
1848 #elif defined(WIN32_THREADS_AVAIL)
1849 int err_code;
1850 TNlmThread this_thread = NlmThreadSelf();
1851
1852 /* quick check -- dont care to lock here */
1853 if (RW->readers > 0 ||
1854 (RW->readers < 0 && RW->owner != this_thread))
1855 return -1;
1856
1857 /* protect members of "*RW" from being changed by other threads */
1858 if (WaitForSingleObject(RW->mutex, INFINITE) != WAIT_OBJECT_0) {
1859 X_ASSERT(0);
1860 return -1;
1861 }
1862
1863 /* trying to obtain W-lock */
1864 err_code = 0;
1865
1866 if (RW->readers < 0) {
1867 /* W-locked... */
1868 if (RW->owner == this_thread)
1869 RW->readers--; /* ...by this thread -- nested */
1870 else
1871 err_code = -1; /* ...by another thread -- cannot obtain lock now */
1872 } else if (RW->readers > 0) {
1873 /* R-locked -- cannot obtain lock now */
1874 err_code = -1;
1875 } else {
1876 /* not locked -- lock writer and reader semaphores */
1877 HANDLE obj[2];
1878 DWORD wait_res;
1879 obj[0] = RW->sema_rw;
1880 obj[1] = RW->sema_w;
1881 wait_res = WaitForMultipleObjects(2, obj, TRUE, 0);
1882 if (WAIT_OBJECT_0 <= wait_res && wait_res < WAIT_OBJECT_0 + 2) {
1883 RW->readers = -1;
1884 RW->owner = this_thread;
1885 } else {
1886 X_ASSERT(0);
1887 err_code = -1;
1888 }
1889 }
1890
1891 /* release the "*RW" protective mutex */
1892 if (err_code == 0) {
1893 RW_HISTORY_UPDATE(RW, this_thread, file, line); /* DEBUG */
1894 }
1895 X_VERIFY(ReleaseMutex(RW->mutex));
1896 return err_code;
1897 #endif
1898 }
1899
1900
NlmRWprintout(TNlmRWlock RW)1901 NLM_EXTERN char* NlmRWprintout(TNlmRWlock RW)
1902 {
1903 return s_RWprintout(RW, 1/*true*/);
1904 }
1905
1906
s_RWprintout(TNlmRWlock RW,int do_mutex)1907 static char* s_RWprintout(TNlmRWlock RW, int/*bool*/ do_mutex)
1908 {
1909 char* str;
1910
1911 /* Lock the protective mutex */
1912 if ( do_mutex ) {
1913 #if defined(POSIX_THREADS_AVAIL)
1914 X_VERIFY(pthread_mutex_lock(&RW->mutex) == 0);
1915 #elif defined(WIN32_THREADS_AVAIL)
1916 X_VERIFY(WaitForSingleObject(RW->mutex, INFINITE) == WAIT_OBJECT_0);
1917 #endif
1918 }
1919
1920 /* Allocate string and print to it */
1921 #if defined(RW_HISTORY)
1922 if (RW->rw_history.size == 0) {
1923 ASSERT(RW->readers == 0);
1924 str = (char*) malloc(64);
1925 sprintf(str, "#%p UNLOCKED\n", (void*) RW);
1926 } else {
1927 /* <file>:<line>: #<RW-lock> <readers> @<thread> */
1928 SRWHistoryArray* arr = &RW->rw_history;
1929 size_t len;
1930 size_t i, i_start, i_stop;
1931 char* s;
1932
1933 if (arr->size <= RW_HISTORY_SIZE) {
1934 i_start = 0;
1935 i_stop = arr->size - 1;
1936 } else {
1937 i_start = arr->size;
1938 i_stop = i_start + RW_HISTORY_SIZE - 1;
1939 }
1940
1941 /* roughly count the message length */
1942 len = 0;
1943 for (i = i_start; i <= i_stop; i++) {
1944 SRWHistory* rec = arr->elem + (i % RW_HISTORY_SIZE);
1945 len += 110;
1946 len += rec->file ? strlen(rec->file) : 0;
1947 }
1948 /* allocate and print */
1949 str = (char*) malloc(len);
1950 s = str;
1951 for (i = i_start; i <= i_stop; i++) {
1952 SRWHistory* rec = arr->elem + (i % RW_HISTORY_SIZE);
1953 if ( rec->file ) {
1954 sprintf(s, "%s:%5d: (%3lu) #%p %-+4d @%p\n",
1955 rec->file, rec->line, (unsigned long) i,
1956 (void*) RW, (int) rec->readers, (void*) rec->thread);
1957 } else {
1958 sprintf(s, "line %5d: (%3lu) #%p %-+4d @%p\n",
1959 rec->line, (unsigned long) i,
1960 (void*) RW, (int) rec->readers, (void*) rec->thread);
1961 }
1962 s += strlen(s);
1963 }
1964 *s = '\n'; s++; *s = '\0';
1965 ASSERT(strlen(str) < len);
1966 }
1967 #elif defined(POSIX_THREADS_AVAIL) || defined(WIN32_THREADS_AVAIL)
1968 str = (char*) malloc(128);
1969 sprintf(str, "#%p: readers =%4d, owner = %p\n",
1970 (void*) RW, (int) RW->readers, (void*) RW->owner);
1971 #else
1972 str = Nlm_StringSave("NlmRWprintout(): "
1973 "NOT IMPLEMENTED for Solaris native threads!\n");
1974 #endif
1975
1976 /* Unlock the protective mutex */
1977 if ( do_mutex ) {
1978 #if defined(POSIX_THREADS_AVAIL)
1979 X_VERIFY(!pthread_mutex_unlock(&RW->mutex));
1980 #elif defined(WIN32_THREADS_AVAIL)
1981 X_VERIFY(ReleaseMutex(RW->mutex));
1982 #endif
1983 }
1984
1985 /* Done */
1986 return str;
1987 }
1988
1989
1990
1991 /********************************************************************
1992 * === MUTEXes ====================================================
1993 ********************************************************************/
1994
1995 #if defined(SOLARIS_THREADS_AVAIL) || defined(POSIX_THREADS_AVAIL) || defined(WIN32_THREADS_AVAIL)
1996
1997 struct TNlmMutexTag {
1998 MUTEX_T mutex;
1999 TNlmThread owner;
2000 long counter;
2001 } structMutex;
2002
2003 static MUTEX_T s_Init_mutex MUTEX_T_INIT; /* mutex init protector */
2004
2005 #else
2006
2007 #define NO_MUTEX
2008 #endif
2009
2010
NlmMutexInit(TNlmMutexPtr theMutexPtr)2011 NLM_EXTERN TNlmMutex NlmMutexInit(TNlmMutexPtr theMutexPtr)
2012 {
2013 if (theMutexPtr == NULL) {
2014 X_ASSERT ( FALSE );
2015 return NULL;
2016 }
2017
2018 if (*theMutexPtr != NULL)
2019 return *theMutexPtr;
2020
2021 #if defined(SOLARIS_THREADS_AVAIL)
2022 {{
2023 X_VERIFY ( !mutex_lock(&s_Init_mutex) );
2024 if (*theMutexPtr == NULL) {
2025 TNlmMutex theMutex = (TNlmMutex) Calloc(1, sizeof(structMutex));
2026 if (theMutex && mutex_init(&theMutex->mutex, USYNC_THREAD, NULL) != 0) {
2027 Free( theMutex );
2028 theMutex = NULL;
2029 }
2030 if ( theMutex ) {
2031 theMutex->owner = NlmThreadSelf();
2032 theMutex->counter = 0;
2033 }
2034 *theMutexPtr = theMutex;
2035 }
2036 X_VERIFY ( !mutex_unlock(&s_Init_mutex) );
2037 }}
2038
2039 #elif defined(POSIX_THREADS_AVAIL)
2040 {{
2041 X_VERIFY ( !pthread_mutex_lock(&s_Init_mutex) );
2042 if (*theMutexPtr == NULL) {
2043 TNlmMutex theMutex = (TNlmMutex) Calloc(1, sizeof(structMutex));
2044 if (theMutex && pthread_mutex_init(&theMutex->mutex, NULL) != 0) {
2045 Free( theMutex );
2046 theMutex = NULL;
2047 }
2048 if ( theMutex ) {
2049 theMutex->owner = NlmThreadSelf();
2050 theMutex->counter = 0;
2051 }
2052 *theMutexPtr = theMutex;
2053 }
2054 X_VERIFY ( !pthread_mutex_unlock(&s_Init_mutex) );
2055 }}
2056
2057 #elif defined(WIN32_THREADS_AVAIL)
2058 {{
2059 if (s_Init_mutex == NULL) {
2060 HANDLE x_Init_mutex = CreateMutex(NULL, FALSE, NULL);
2061 if ( x_Init_mutex )
2062 s_Init_mutex = x_Init_mutex;
2063 else
2064 X_ASSERT ( s_Init_mutex && GetLastError() == ERROR_ALREADY_EXISTS );
2065 }
2066 if (WaitForSingleObject(s_Init_mutex, INFINITE) != WAIT_OBJECT_0)
2067 return NULL;
2068
2069 if (*theMutexPtr == NULL) {
2070 TNlmMutex theMutex = (TNlmMutex) Calloc(1, sizeof(structMutex));
2071 if (theMutex && !(theMutex->mutex = CreateMutex(NULL,FALSE,NULL))) {
2072 Free(theMutex);
2073 theMutex = NULL;
2074 }
2075 *theMutexPtr = theMutex;
2076 }
2077
2078 ReleaseMutex(s_Init_mutex);
2079 }}
2080
2081 #else
2082 *theMutexPtr = (TNlmMutex)(~0);
2083 #endif
2084
2085 return *theMutexPtr;
2086 }
2087
2088
s_MutexLock(TNlmMutex theMutex,Nlm_Boolean no_block)2089 static Int4 s_MutexLock(TNlmMutex theMutex, Nlm_Boolean no_block)
2090 {
2091 Int4 err_code = 0;
2092
2093 #ifndef NO_MUTEX
2094 TNlmThread owner = NlmThreadSelf();
2095
2096 if (theMutex == NULL) {
2097 X_ASSERT ( FALSE );
2098 return -1;
2099 }
2100
2101 if (theMutex->counter > 0) {
2102 if ( NlmThreadCompare(theMutex->owner, owner) )
2103 { /* protect from deadlock under Solaris || POSIX */
2104 theMutex->counter++;
2105 return 0;
2106 }
2107 else if ( no_block )
2108 return -1;
2109 }
2110
2111 #ifdef _TRACE_HARD
2112 fprintf(stderr, "Mutex lock , IN = [%2ld :%8ld]\n",
2113 (long)owner, (long)theMutex);
2114 #endif
2115
2116 if ( no_block )
2117 {
2118 #if defined(SOLARIS_THREADS_AVAIL)
2119 err_code = mutex_trylock(&theMutex->mutex);
2120 #elif defined(POSIX_THREADS_AVAIL)
2121 err_code = pthread_mutex_trylock(&theMutex->mutex);
2122 #elif defined(WIN32_THREADS_AVAIL)
2123 {{
2124 DWORD x_err_code = WaitForSingleObject(theMutex->mutex, 1);
2125 X_ASSERT ( x_err_code != WAIT_ABANDONED );
2126 if (x_err_code == WAIT_TIMEOUT)
2127 err_code = -1;
2128 }}
2129 #endif
2130 }
2131 else
2132 {
2133 #if defined(SOLARIS_THREADS_AVAIL)
2134 err_code = mutex_lock(&theMutex->mutex);
2135 #elif defined(POSIX_THREADS_AVAIL)
2136 err_code = pthread_mutex_lock(&theMutex->mutex);
2137 #elif defined(WIN32_THREADS_AVAIL)
2138 {{
2139 DWORD x_err_code = WaitForSingleObject(theMutex->mutex, INFINITE);
2140 X_ASSERT ( x_err_code == WAIT_OBJECT_0 );
2141 if (x_err_code == WAIT_TIMEOUT)
2142 err_code = -1;
2143 }}
2144 #endif
2145 }
2146
2147 #ifdef _TRACE_HARD
2148 fprintf(stderr, "Mutex lock ,OUT = [%2ld :%8ld]\n",
2149 (long)owner, (long)theMutex);
2150 #endif
2151
2152 if (err_code == 0) {
2153 /* surprise! -- these two can be swapped by optimizer (see R6.25) */
2154 theMutex->owner = owner;
2155 theMutex->counter = 1;
2156 }
2157 #endif /* ndef NO_MUTEX */
2158
2159 X_ASSERT ( no_block || err_code == 0 );
2160 return err_code ? -1 : 0;
2161 }
2162
2163
NlmMutexLock(TNlmMutex theMutex)2164 NLM_EXTERN Int4 NlmMutexLock(TNlmMutex theMutex)
2165 {
2166 return s_MutexLock(theMutex, FALSE);
2167 }
2168
2169
NlmMutexTryLock(TNlmMutex theMutex)2170 NLM_EXTERN Int4 NlmMutexTryLock(TNlmMutex theMutex)
2171 {
2172 return s_MutexLock(theMutex, TRUE);
2173 }
2174
2175
NlmMutexLockEx(TNlmMutexPtr theMutexPtr)2176 NLM_EXTERN Int4 NlmMutexLockEx(TNlmMutexPtr theMutexPtr)
2177 {
2178 Int4 err_code = 0;
2179 #ifndef NO_MUTEX
2180 if (!theMutexPtr || (!*theMutexPtr && !NlmMutexInit(theMutexPtr))) {
2181 X_ASSERT ( FALSE );
2182 err_code = -1;
2183 }
2184 else
2185 err_code = s_MutexLock(*theMutexPtr, FALSE);
2186 #endif
2187 return err_code;
2188 }
2189
2190
NlmMutexTryLockEx(TNlmMutexPtr theMutexPtr)2191 NLM_EXTERN Int4 NlmMutexTryLockEx(TNlmMutexPtr theMutexPtr)
2192 {
2193 Int4 err_code = 0;
2194 #ifndef NO_MUTEX
2195 if (!theMutexPtr || (!*theMutexPtr && !NlmMutexInit(theMutexPtr))) {
2196 X_ASSERT ( FALSE );
2197 err_code = -1;
2198 }
2199 else
2200 err_code = s_MutexLock(*theMutexPtr, TRUE);
2201 #endif
2202 return err_code;
2203 }
2204
2205
NlmMutexUnlock(TNlmMutex theMutex)2206 NLM_EXTERN Int4 NlmMutexUnlock(TNlmMutex theMutex)
2207 {
2208 Int4 err_code = 0;
2209
2210 #ifndef NO_MUTEX
2211 TNlmThread this_thread = NlmThreadSelf();
2212
2213 if ( !theMutex ) {
2214 X_ASSERT ( FALSE );
2215 return 0;
2216 }
2217
2218 if (!NlmThreadCompare(theMutex->owner, this_thread) ||
2219 theMutex->counter == 0) {
2220 X_ASSERT ( FALSE );
2221 return -1;
2222 }
2223
2224 if (--theMutex->counter != 0)
2225 return 0;
2226
2227 theMutex->owner = 0;
2228 #if defined(SOLARIS_THREADS_AVAIL)
2229 err_code = mutex_unlock(&theMutex->mutex);
2230 #elif defined(POSIX_THREADS_AVAIL)
2231 err_code = pthread_mutex_unlock(&theMutex->mutex);
2232 #elif defined(WIN32_THREADS_AVAIL)
2233 if ( !ReleaseMutex(theMutex->mutex) )
2234 err_code = GetLastError();
2235 #endif
2236
2237 #ifdef _TRACE_HARD
2238 fprintf(stderr, "Mutex unlock , = [%2ld :%8ld / %3ld] :: %10ld\n",
2239 (long) this_thread, (long) theMutex, (long) theMutex->counter,
2240 (long) err_code);
2241 #endif
2242
2243 if (err_code != 0) {
2244 theMutex->owner = this_thread;
2245 theMutex->counter++;
2246 }
2247 #endif /* ndef NO_MUTEX */
2248
2249 X_ASSERT ( err_code == 0 );
2250 return err_code ? -1 : 0;
2251 }
2252
2253
2254 #ifndef NO_MUTEX
s_MutexDestroy(MUTEX_T * mp)2255 static Int4 s_MutexDestroy(MUTEX_T *mp)
2256 {
2257 Int4 err_code = -1;
2258 #if defined(SOLARIS_THREADS_AVAIL)
2259 err_code = mutex_destroy(mp);
2260 #elif defined(POSIX_THREADS_AVAIL)
2261 err_code = pthread_mutex_destroy(mp);
2262 #elif defined(WIN32_THREADS_AVAIL)
2263 err_code = CloseHandle(*mp) ? 0 : GetLastError();
2264 #endif
2265 X_ASSERT ( err_code == 0 );
2266 return err_code;
2267 }
2268 #endif
2269
2270
NlmMutexDestroy(TNlmMutex theMutex)2271 NLM_EXTERN Int4 NlmMutexDestroy(TNlmMutex theMutex)
2272 {
2273 Int4 err_code = 0;
2274
2275 #ifndef NO_MUTEX
2276 if (theMutex == NULL) {
2277 X_ASSERT ( FALSE );
2278 return 0;
2279 }
2280 X_ASSERT ( theMutex->counter == 0 );
2281
2282 err_code = s_MutexDestroy(&theMutex->mutex);
2283 if (err_code == 0)
2284 Free(theMutex);
2285 #endif /* ndef NO_MUTEX */
2286
2287 return err_code ? -1 : 0;
2288 }
2289
2290
2291 /********************************************************************
2292 * === TLS (Thread Local Storage) ==================================
2293 ********************************************************************/
2294
2295 #define TLS_DATA_FREE (TNlmTlsCleanup)~0
2296
2297 typedef struct {
2298 VoidPtr value;
2299 TNlmTlsCleanup cleanup;
2300 } STlsData;
2301
2302 struct TNlmTlsTag {
2303 #if defined(SOLARIS_THREADS_AVAIL)
2304 thread_key_t key;
2305 #elif defined(POSIX_THREADS_AVAIL)
2306 pthread_key_t key;
2307 #elif defined(WIN32_THREADS_AVAIL)
2308 DWORD index;
2309 #else
2310 STlsData data;
2311 #endif
2312 TNlmTls next;
2313 } structTls;
2314
2315
2316 /* list of TLS allocated be the program */
2317 static TNlmTls s_TlsList = NULL;
2318
2319
2320 /* Initialize system-dependent TLS object; allocate and fill out internals;
2321 * include the new TLS to the global TLS list
2322 */
s_TlsInit(TNlmTls * pTLS)2323 static Nlm_Boolean s_TlsInit(TNlmTls *pTLS)
2324 {
2325 int err_code = 0;
2326 TNlmTls TLS = NULL;
2327
2328 if (NlmMutexLockEx(&s_TlsMutex) != 0)
2329 return FALSE;
2330
2331 while ( !TLS ) {{ /* TRY */
2332 if ( *pTLS ) { /* just init'd by another thread */
2333 TLS = *pTLS;
2334 break;
2335 }
2336
2337 if ((TLS = (TNlmTls)Calloc(1, sizeof(structTls))) == NULL)
2338 break;
2339
2340 #if defined(SOLARIS_THREADS_AVAIL)
2341 err_code = thr_keycreate(&TLS->key, NULL);
2342 #elif defined(POSIX_THREADS_AVAIL)
2343 err_code = pthread_key_create(&TLS->key, NULL);
2344 #elif defined (WIN32_THREADS_AVAIL)
2345 TLS->index = TlsAlloc();
2346 if (TLS->index == (DWORD)(-1))
2347 err_code = GetLastError();
2348 #else
2349 TLS->data.value = NULL;
2350 TLS->data.cleanup = NULL;
2351 #endif
2352
2353 if (err_code != 0) {
2354 X_ASSERT ( FALSE );
2355 Free(TLS);
2356 TLS = NULL;
2357 break;
2358 }
2359
2360 TLS->next = s_TlsList;
2361 s_TlsList = TLS;
2362 }}
2363
2364 if ( TLS )
2365 *pTLS = TLS;
2366 NlmMutexUnlock(s_TlsMutex);
2367 return (Nlm_Boolean)(TLS ? TRUE : FALSE);
2368 }
2369
2370
2371 /* Retrieve the TLS's internal data(i.e. value+cleanup structure)
2372 */
s_TlsGetData(TNlmTls TLS,STlsData ** ppData)2373 static Boolean s_TlsGetData(TNlmTls TLS, STlsData **ppData)
2374 {
2375 int err_code = 0;
2376 #if defined (SOLARIS_THREADS_AVAIL)
2377 err_code = thr_getspecific(TLS->key, (void **)ppData);
2378 #elif defined (POSIX_THREADS_AVAIL)
2379 *ppData = (STlsData *)pthread_getspecific(TLS->key);
2380 #elif defined (WIN32_THREADS_AVAIL)
2381 if ( !(*ppData = (STlsData *)TlsGetValue(TLS->index)) )
2382 err_code = GetLastError(); /* (NOTE: may be 0 i.e. okay) */
2383 #else
2384 *ppData = &TLS->data;
2385 #endif
2386
2387 X_ASSERT ( err_code == 0 );
2388 return (Boolean)(err_code == 0);
2389 }
2390
2391
2392 /* Setup the TLS's internal data(== value + cleanup) structure
2393 */
s_TlsSetData(TNlmTls TLS,STlsData * pData)2394 static Boolean s_TlsSetData(TNlmTls TLS, STlsData *pData)
2395 {
2396 int err_code = 0;
2397 #if defined (SOLARIS_THREADS_AVAIL)
2398 err_code = thr_setspecific(TLS->key, (void *)pData);
2399 #elif defined (POSIX_THREADS_AVAIL)
2400 err_code = pthread_setspecific(TLS->key, (void *)pData);
2401 #elif defined (WIN32_THREADS_AVAIL)
2402 if ( !TlsSetValue(TLS->index, (void *)pData) )
2403 err_code = GetLastError();
2404 #else
2405 /* do nothing; it's always "set" */
2406 #endif
2407
2408 X_ASSERT ( err_code == 0 );
2409 return (Boolean)(err_code == 0);
2410 }
2411
2412
NlmTlsSetValue(TNlmTls * pTLS,VoidPtr value,TNlmTlsCleanup cleanup)2413 NLM_EXTERN Boolean NlmTlsSetValue(TNlmTls *pTLS,
2414 VoidPtr value, TNlmTlsCleanup cleanup)
2415 {
2416 STlsData *pData;
2417 if ( *pTLS ) {
2418 if ( !s_TlsGetData(*pTLS, &pData) )
2419 return FALSE;
2420 }
2421 else { /* not init'd yet -- initialize it here */
2422 if ( !s_TlsInit(pTLS) )
2423 return FALSE;
2424 pData = NULL;
2425 }
2426
2427 /* special case -- cleanup and free the internal data structure */
2428 if (cleanup == TLS_DATA_FREE) {
2429 X_ASSERT ( value == NULL );
2430 if ( !pData )
2431 return TRUE;
2432
2433 if ( !s_TlsSetData(*pTLS, NULL) )
2434 return FALSE;
2435
2436 if ( pData->cleanup )
2437 pData->cleanup(*pTLS, pData->value);
2438 #ifdef NCBI_THREADS_AVAIL
2439 Free(pData);
2440 #else
2441 pData->value = NULL;
2442 pData->cleanup = NULL;
2443 #endif
2444 return TRUE;
2445 }
2446
2447 if ( !pData ) {
2448 /* allocate and setup new local data */
2449 #ifdef NCBI_THREADS_AVAIL
2450 if ((pData = (STlsData *)Calloc(1, sizeof(STlsData))) == NULL) {
2451 ASSERT ( FALSE );
2452 return FALSE;
2453 }
2454 if ( !s_TlsSetData(*pTLS, pData) ) {
2455 Free(pData);
2456 return FALSE;
2457 }
2458 #else
2459 pData = &(*pTLS)->data;
2460 #endif
2461 }
2462 else if (pData->value == value) {
2463 /* do not call cleanup if TLS value did not change */
2464 pData->cleanup = cleanup;
2465 return TRUE;
2466 }
2467
2468 {{ /* set new value and call cleanup */
2469 STlsData xData;
2470 xData = *pData;
2471 pData->value = value;
2472 pData->cleanup = cleanup;
2473
2474 if ( xData.cleanup )
2475 xData.cleanup(*pTLS, xData.value);
2476 }}
2477
2478 return TRUE;
2479 }
2480
2481
NlmTlsGetValue(TNlmTls TLS,VoidPtr * value_ptr)2482 NLM_EXTERN Boolean NlmTlsGetValue(TNlmTls TLS, VoidPtr *value_ptr)
2483 {
2484 STlsData *tls_data;
2485 *value_ptr = NULL;
2486
2487 if ( !TLS )
2488 return TRUE;
2489
2490 if ( !s_TlsGetData(TLS, &tls_data))
2491 return FALSE;
2492
2493 if ( tls_data )
2494 *value_ptr = tls_data->value;
2495
2496 return TRUE;
2497 }
2498
2499
2500 /* Reset all TLS values to zero(call cleanups if any); for this thread only
2501 */
s_TlsCleanupAll(void)2502 static Boolean s_TlsCleanupAll(void)
2503 {
2504 TNlmTls TLS;
2505
2506 if (NlmMutexLockEx(&s_TlsMutex) != 0)
2507 return FALSE;
2508
2509 for (TLS = s_TlsList; TLS; TLS = TLS->next) {
2510 NlmTlsSetValue(&TLS, NULL, TLS_DATA_FREE);
2511 }
2512
2513 NlmMutexUnlock(s_TlsMutex);
2514 return TRUE;
2515 }
2516
2517 /* Destroy all TLS-related structures(for all threads!)
2518 */
s_TlsDestroyAll(void)2519 static Boolean s_TlsDestroyAll(void)
2520 {
2521 if (NlmMutexLockEx(&s_TlsMutex) != 0)
2522 return FALSE;
2523
2524 while ( s_TlsList ) {
2525 TNlmTls TLS = s_TlsList;
2526 s_TlsList = s_TlsList->next;
2527
2528 {{
2529 int err_code = 0;
2530 #if defined(SOLARIS_THREADS_AVAIL)
2531 /* do nothing -- will be */
2532 #elif defined(POSIX_THREADS_AVAIL)
2533 err_code = pthread_key_delete(TLS->key);
2534 #elif defined (WIN32_THREADS_AVAIL)
2535 if ( !TlsFree(TLS->index) )
2536 err_code = GetLastError();
2537 #else
2538 /* nothing to do */
2539 #endif
2540 X_ASSERT ( err_code == 0 );
2541 }}
2542
2543 Free(TLS);
2544 }
2545
2546 s_TlsList = NULL;
2547 NlmMutexUnlock(s_TlsMutex);
2548 NlmMutexDestroy(s_TlsMutex);
2549 s_TlsMutex = NULL;
2550 return TRUE;
2551 }
2552
2553
2554 /********************************************************************
2555 * === Auxiliaries =================================================
2556 ********************************************************************/
2557
NlmThreadsAvailable(void)2558 NLM_EXTERN Boolean NlmThreadsAvailable(void)
2559 {
2560 #ifdef NCBI_THREADS_AVAIL
2561 return TRUE;
2562 #else
2563 return FALSE;
2564 #endif
2565 }
2566
2567
2568 #if defined(OS_UNIX_DARWIN)
2569 #include <mach/mach.h>
2570 #include <mach/mach_host.h>
2571 #include <mach/host_info.h>
2572 #endif
2573
NlmCPUNumber(void)2574 NLM_EXTERN Int4 NlmCPUNumber(void)
2575 {
2576 #if defined(OS_UNIX_DARWIN)
2577
2578 host_basic_info_data_t hinfo;
2579 mach_msg_type_number_t hinfo_count = HOST_BASIC_INFO_COUNT;
2580 kern_return_t rc;
2581
2582 rc=host_info( mach_host_self(), HOST_BASIC_INFO, (host_info_t) &hinfo, &hinfo_count);
2583
2584 if (rc != KERN_SUCCESS)
2585 return -1;
2586
2587 return hinfo.avail_cpus;
2588
2589 #elif defined(WIN32)
2590 SYSTEM_INFO sysInfo;
2591 GetSystemInfo(&sysInfo);
2592 return sysInfo.dwNumberOfProcessors;
2593 #elif defined(_SC_NPROC_ONLN)
2594 return sysconf(_SC_NPROC_ONLN);
2595 #elif defined(_SC_NPROCESSORS_ONLN)
2596 return sysconf(_SC_NPROCESSORS_ONLN);
2597 #else
2598 return -1;
2599 #endif
2600 }
2601
2602
2603 /********************************************************************
2604 * === Internals
2605 ********************************************************************/
2606
NlmThreadDestroyAll(void)2607 NLM_EXTERN void NlmThreadDestroyAll(void)
2608 {
2609 X_VERIFY ( !NlmThreadJoinAll() );
2610
2611 X_VERIFY ( s_TlsCleanupAll() );
2612 X_VERIFY ( s_TlsDestroyAll() );
2613
2614 #ifdef NCBI_THREADS_AVAIL
2615 if (s_ThreadCounter_mutex && NlmMutexDestroy(s_ThreadCounter_mutex) == 0)
2616 s_ThreadCounter_mutex = 0;
2617 if (s_ThreadCounter_sema && NlmSemaDestroy(s_ThreadCounter_sema) == 0)
2618 s_ThreadCounter_sema = 0;
2619 s_ThreadCounter = 0;
2620 X_VERIFY( !s_MutexDestroy(&s_Init_mutex) );
2621 #endif /* NCBI_THREADS_AVAIL */
2622 }
2623
2624
2625 #ifdef TEST_MODULE_NCBITHR
2626 /***********************************************************************
2627 * TEST
2628 ***********************************************************************/
2629
2630 #ifndef NCBI_THREADS_AVAIL
2631 #error ATTENTION -- BAD PLATFORM TO TEST THREADS or THREADS DISABLED !!!!!
2632 #endif
2633
2634
2635 #include <stdio.h>
2636
2637 #ifdef WIN32
2638 #define STDERR stderr_file
2639 static FILE *STDERR = NULL;
2640 #else
2641 #define STDERR stderr
2642 #endif
2643
2644 #define DO_POSTEX
2645 #define DO_STDERR
2646 #define DO_RWLOCK
2647 #define DO_TLS
2648
TEST__ThreadExit0(VoidPtr user_arg)2649 static void TEST__ThreadExit0(VoidPtr user_arg)
2650 {
2651 #ifdef DO_STDERR
2652 fprintf(STDERR, "ThreadExit0(): Self=%ld, Arg = %p\n",
2653 (long)NlmThreadSelf(), user_arg);
2654 #endif
2655 Nlm_MemFree(user_arg);
2656 }
2657
TEST__ThreadExit1(VoidPtr user_arg)2658 static void TEST__ThreadExit1(VoidPtr user_arg)
2659 {
2660 #ifdef DO_STDERR
2661 fprintf(STDERR, "ThreadExit1(): Self=%ld, Arg = %ld\n",
2662 (long)NlmThreadSelf(), (long)user_arg);
2663 #endif
2664 }
2665
TEST__ThreadExit2(VoidPtr user_arg)2666 static void TEST__ThreadExit2(VoidPtr user_arg)
2667 {
2668 #ifdef DO_STDERR
2669 fprintf(STDERR, "ThreadExit2(): Self=%ld, Arg = %ld\n",
2670 (long)NlmThreadSelf(), (long)user_arg);
2671 #endif
2672 }
2673
2674
2675 #define N_EXIT_FUNC 6
2676 TNlmThreadOnExit exit_func_arr[N_EXIT_FUNC] =
2677 {
2678 TEST__ThreadExit0,
2679 TEST__ThreadExit1,
2680 TEST__ThreadExit2,
2681 TEST__ThreadExit1,
2682 TEST__ThreadExit2,
2683 NULL
2684 };
2685
2686 typedef struct
2687 {
2688 Int4 n_exit_func;
2689 TNlmThreadOnExit PNTR exit_func;
2690 } MyExitSet, PNTR MyExitSetPtr;
2691
2692
2693 static Int2 TEST__threads(Int4 n_threads);
2694
2695 static TNlmMutex s_SpawnCounter_mutex;
2696
2697
2698 #ifdef DO_RWLOCK
2699 static TNlmRWlock RWlock;
2700 #endif
2701
2702 #ifdef DO_TLS
2703 #define N_TLS 32
2704 static TNlmTls s_TLS[N_TLS];
2705
TEST_TlsCleanup(TNlmTls TLS,VoidPtr old_value)2706 static void TEST_TlsCleanup(TNlmTls TLS, VoidPtr old_value) {
2707 VoidPtr new_value;
2708 VERIFY ( NlmTlsGetValue(TLS, &new_value) );
2709 ASSERT ( old_value != 0 );
2710 ASSERT ( new_value == 0 || (CharPtr)new_value == (CharPtr)old_value + 1 );
2711 }
2712 #endif
2713
2714
TEST__MyThread(VoidPtr arg)2715 static VoidPtr TEST__MyThread(VoidPtr arg)
2716 {
2717 MyExitSetPtr exit_func_set = (MyExitSetPtr) arg;
2718 Int4 i;
2719
2720 #ifdef DO_STDERR
2721 fprintf(STDERR, "TEST__MyThread(): Thread #%ld started\n",
2722 (long)NlmThreadSelf());
2723 #endif
2724 #ifdef DO_POSTEX
2725 Nlm_ErrPostEx(SEV_INFO, 2, 3, "TEST__MyThread(): Thread #%ld started\n",
2726 (long)NlmThreadSelf());
2727 #endif
2728 #ifdef DO_TLS
2729 for (i = 0; i < N_TLS; i++) {
2730 VoidPtr tls_value = (VoidPtr)~0;
2731 VERIFY ( NlmTlsGetValue(s_TLS[i], &tls_value) );
2732 ASSERT ( tls_value == 0 );
2733 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+1, TEST_TlsCleanup));
2734 }
2735 #endif
2736
2737 {{ /* if to spawn more threads... */
2738 #if defined(SOLARIS_THREADS_AVAIL) || defined(POSIX_THREADS_AVAIL)
2739 #define NCBI_MAX_THREADS _POSIX_THREAD_THREADS_MAX
2740 #elif defined(WIN32)
2741 #define NCBI_MAX_THREADS 32
2742 #else
2743 #define NCBI_MAX_THREADS INT2_MAX
2744 #endif
2745
2746 #define RESERVE_THREADS 8
2747 #define N_SPAWN 4
2748
2749 static int nnn_threads = RESERVE_THREADS + N_SPAWN;
2750 int more;
2751 VERIFY ( !NlmMutexLockEx(&s_SpawnCounter_mutex) );
2752 more = (nnn_threads < NCBI_MAX_THREADS - N_SPAWN);
2753 if ( more )
2754 nnn_threads += N_SPAWN;
2755 VERIFY ( !NlmMutexUnlock(s_SpawnCounter_mutex) );
2756 if ( more )
2757 TEST__threads(N_SPAWN);
2758 }}
2759
2760 #ifdef DO_TLS
2761 for (i = 0; i < N_TLS; i++) {
2762 VoidPtr tls_value = (VoidPtr)~0;
2763 VERIFY ( NlmTlsGetValue(s_TLS[i], &tls_value) );
2764 ASSERT ( tls_value == (VoidPtr)1 );
2765 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+1, TEST_TlsCleanup));
2766 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+2, TEST_TlsCleanup));
2767 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+3, TEST_TlsCleanup));
2768 }
2769 #endif /* DO_TLS */
2770
2771
2772 #ifdef DO_RWLOCK
2773
2774 # if defined(POSIX_THREADS_AVAIL) || defined(WIN32_THREADS_AVAIL)
2775 # define RW_SMART
2776 # endif
2777
2778 {{ /* test RWlock functionality */
2779 static int iii = 0;
2780 static int var = 0;
2781 if ( ++iii % 5 ) { /* reader */
2782 for (i = 0; i < 500; i++) {
2783 int x_var, j;
2784
2785 if (NlmRWtryrdlock(RWlock) != 0)
2786 VERIFY ( !NlmRWrdlock(RWlock) );
2787 # ifdef RW_SMART
2788 if (i % 6 == 0)
2789 VERIFY ( !NlmRWrdlock(RWlock) );
2790 if (i % 12 == 0) {
2791 VERIFY ( !NlmRWtryrdlock(RWlock) );
2792 VERIFY ( !NlmRWunlock(RWlock) );
2793 }
2794 # endif
2795
2796 x_var = var;
2797 for (j = 0; j < 10; j++)
2798 fprintf(STDERR, "%s", "");
2799 ASSERT ( x_var == var );
2800
2801 # ifdef RW_SMART
2802 if (i % 7 == 0) {
2803 VERIFY ( !NlmRWtryrdlock(RWlock) );
2804 VERIFY ( !NlmRWunlock(RWlock) );
2805 }
2806 if (i % 6 == 0)
2807 VERIFY ( !NlmRWunlock(RWlock) );
2808 # endif
2809 VERIFY ( !NlmRWunlock(RWlock) );
2810
2811 for (j = 0; j < 50; j++)
2812 fprintf(STDERR, "%s", "");
2813 if (x_var != var)
2814 fprintf(STDERR, "RW\n");
2815 }
2816 }
2817 else { /* writer */
2818 for (i = 0; i < 1000; i++) {
2819 int j;
2820 if (NlmRWtrywrlock(RWlock) != 0)
2821 VERIFY ( !NlmRWwrlock(RWlock) );
2822 # ifdef RW_SMART
2823 if (i % 4 == 0)
2824 VERIFY ( !NlmRWwrlock(RWlock) );
2825 if (i % 6 == 0)
2826 VERIFY ( !NlmRWrdlock(RWlock) );
2827 if (i % 8 == 0)
2828 VERIFY ( !NlmRWtrywrlock(RWlock) );
2829 if (i % 10 == 0)
2830 VERIFY ( !NlmRWtryrdlock(RWlock) );
2831 # endif
2832
2833 var++;
2834 for (j = 0; j < 7; j++, var++)
2835 fprintf(STDERR, "%s", "");
2836
2837 # ifdef RW_SMART
2838 if (i % 4 == 0)
2839 VERIFY ( !NlmRWunlock(RWlock) );
2840 if (i % 6 == 0)
2841 VERIFY ( !NlmRWunlock(RWlock) );
2842 if (i % 8 == 0)
2843 VERIFY ( !NlmRWunlock(RWlock) );
2844 if (i % 10 == 0)
2845 VERIFY ( !NlmRWunlock(RWlock) );
2846 # endif
2847 VERIFY ( !NlmRWunlock(RWlock) );
2848
2849 for (j = 0; j < 20; j++)
2850 fprintf(STDERR, "%s", "");
2851 }
2852 }
2853 }}
2854 #endif /* DO_RWLOCK */
2855
2856
2857 #ifdef DO_TLS
2858 for (i = 0; i < N_TLS; i++) {
2859 VoidPtr tls_value = (VoidPtr)~0;
2860 VERIFY ( NlmTlsGetValue(s_TLS[i], &tls_value) );
2861 ASSERT ( tls_value == (VoidPtr)4 );
2862 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+1, TEST_TlsCleanup));
2863 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+2, TEST_TlsCleanup));
2864 VERIFY ( NlmTlsSetValue(&s_TLS[i], (CharPtr)tls_value+3, TEST_TlsCleanup));
2865 }
2866 #endif /* DO_TLS */
2867
2868 /* register dummy(debug-print only) exit functions; ungerister some... */
2869 for (i = exit_func_set->n_exit_func - 1; i > 0; i--)
2870 {
2871 NlmThreadAddOnExit(exit_func_set->exit_func[i],
2872 (VoidPtr)NlmThreadSelf());
2873 if (i%2 == 0)
2874 NlmThreadRemoveOnExit(exit_func_set->exit_func[i],
2875 (VoidPtr)NlmThreadSelf());
2876 }
2877
2878 /* register the exit function to deallocate dynamic memory */
2879 NlmThreadAddOnExit(exit_func_set->exit_func[0], (VoidPtr)exit_func_set);
2880
2881 {{ /* sleep for a while before exiting */
2882 static int do_sleep = 0;
2883 if ((do_sleep = !do_sleep) == TRUE) {
2884 #if defined (OS_UNIX)
2885 sleep( 1 );
2886 #elif defined (WIN32)
2887 Sleep( 1000 );
2888 #endif
2889 }
2890 }}
2891
2892 #ifdef DO_POSTEX
2893 Nlm_ErrPostEx(SEV_INFO, 2, 3, "TEST__MyThread(): Thread #%ld finished\n",
2894 (long)NlmThreadSelf());
2895 #endif
2896
2897 return NULL;
2898 }
2899
2900
TEST__threads(Int4 n_threads)2901 static Int2 TEST__threads(Int4 n_threads)
2902 {
2903 Int4 t;
2904 TNlmThread *threads = (TNlmThread *)
2905 Nlm_MemNew(n_threads * sizeof(TNlmThread));
2906
2907 for (t = 0; t < n_threads; t++)
2908 {
2909 static int xxx_counter = 0;
2910 MyExitSetPtr exit_set = (MyExitSetPtr) Nlm_MemNew( sizeof(MyExitSet) );
2911 exit_set->n_exit_func = t % N_EXIT_FUNC;
2912 exit_set->exit_func = exit_func_arr;
2913 threads[t] =
2914 NlmThreadCreateEx(&TEST__MyThread, (VoidPtr)exit_set,
2915 (t == n_threads-1 || t%3 == 1) ? THREAD_RUN :
2916 (t%3 == 2) ? THREAD_BOUND : THREAD_DETACHED,
2917 (EThreadPriority)(xxx_counter++ % eTP_Default),
2918 NULL, NULL);
2919 #ifdef DO_STDERR
2920 fprintf(STDERR, "TEST__threads(): Starting thread #%ld\n",
2921 (long)threads[t]);
2922 #endif
2923 }
2924
2925 for (t = 0; t < n_threads; t++)
2926 /* (detached threads must be joined by NlmThreadJoinAll) */
2927 if (t == n_threads-1 || t%3 != 0) {
2928 VoidPtr status;
2929 Int4 err_code = NlmThreadJoin(threads[t], &status);
2930 #ifdef DO_STDERR
2931 if (err_code != 0) {
2932 fprintf(STDERR, "TEST__threads(): Cannot join thread #%ld; \
2933 error code = %ld\n",
2934 (long)threads[t], (long)err_code);
2935 ASSERT ( FALSE );
2936 }
2937
2938 fprintf(STDERR, "TEST__threads(): Thread #%ld joined; \
2939 terminated, exit status = %p\n",
2940 (long)threads[t], status);
2941 #endif
2942 }
2943
2944 Nlm_MemFree(threads);
2945
2946 #ifdef DO_STDERR
2947 fprintf(STDERR, "TEST__threads(): FINISHED\n");
2948 #endif
2949 return 0;
2950 }
2951
2952
Nlm_Main(void)2953 Nlm_Int2 Nlm_Main( void )
2954 {
2955 Int2 status;
2956
2957 #if defined (WIN32)
2958 STDERR = fopen("stderr", "w");
2959 #endif
2960
2961 VERIFY ( Nlm_ErrSetLog("ncbithr.log") );
2962 Nlm_ErrSetOpts(ERR_TEE, ERR_LOG_ON);
2963 ErrSetOptFlags(EO_SHOW_FILELINE);
2964 Nlm_ErrPostEx(SEV_INFO, 2, 3, "NCBI_THREAD test started...\n");
2965
2966 #ifdef DO_RWLOCK
2967 RWlock = NlmRWinit();
2968 ASSERT ( RWlock );
2969 #endif
2970
2971 status = TEST__threads(N_SPAWN);
2972
2973 VERIFY ( !NlmThreadJoinAll() );
2974
2975 #ifdef DO_RWLOCK
2976 VERIFY ( !NlmRWdestroy(RWlock) );
2977 #endif
2978 if ( s_SpawnCounter_mutex )
2979 VERIFY ( !NlmMutexDestroy(s_SpawnCounter_mutex) );
2980
2981 #if defined (WIN32)
2982 fclose(STDERR);
2983 #endif
2984 return status;
2985 }
2986
2987 #endif /* TEST_MODULE_NCBITHR */
2988
2989 /* EOF */
2990