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, &param) );
807           param.sched_priority = new_priority;
808           X_VERIFY ( !pthread_attr_setschedparam(&attr, &param) );
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