1 /* $Id: ncbi_core.c,v 6.37 2016/07/21 21:29:14 fukanchi Exp $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Denis Vakatov, Anton Lavrentiev
27  *
28  * File Description:
29  *   Types and code shared by all "ncbi_*.[ch]" modules.
30  *
31  */
32 
33 #include "ncbi_ansi_ext.h"
34 #include "ncbi_priv.h"
35 #include <stdlib.h>
36 
37 #ifdef NCBI_OS_UNIX
38 #  include <unistd.h>
39 #endif /*NCBI_OS_UNIX*/
40 
41 #if defined(NCBI_CXX_TOOLKIT)  &&  defined(_MT)  &&  !defined(NCBI_WITHOUT_MT)
42 #  if defined(NCBI_OS_MSWIN)
43 #    define WIN32_LEAN_AND_MEAN
44 #    include <windows.h>
45 #    define NCBI_WIN32_THREADS
46 #  elif defined(NCBI_OS_UNIX)
47 #    include <pthread.h>
48 #    define NCBI_POSIX_THREADS
49 #  else
50 #    define NCBI_NO_THREADS
51 #  endif /*NCBI_OS*/
52 #else
53 #  define   NCBI_NO_THREADS
54 #endif /*NCBI_CXX_TOOLKT && _MT && !NCBI_WITHOUT_MT*/
55 
56 
57 
58 /******************************************************************************
59  *  IO status
60  */
61 
IO_StatusStr(EIO_Status status)62 extern const char* IO_StatusStr(EIO_Status status)
63 {
64     static const char* kStatusStr[eIO_Unknown + 1] = {
65         "Success",
66         "Timeout",
67         "Closed",
68         "Interrupt",
69         "Invalid argument",
70         "Not supported",
71         "Unknown"
72     };
73 
74     assert(eIO_Success <= status  &&  status <= eIO_Unknown);
75     return eIO_Success <= status  &&  status <= eIO_Unknown
76         ? kStatusStr[status]
77         : 0;
78 }
79 
80 
81 
82 /******************************************************************************
83  *  MT locking
84  */
85 
86 /* Check the validity of the MT locker */
87 #define MT_LOCK_VALID  \
88     assert(lk->ref_count  &&  lk->magic_number == kMT_LOCK_magic_number)
89 
90 
91 /* MT locker data and callbacks */
92 struct MT_LOCK_tag {
93   unsigned int     ref_count;    /* reference counter */
94   void*            user_data;    /* for "handler()" and "cleanup()" */
95   FMT_LOCK_Handler handler;      /* locking function */
96   FMT_LOCK_Cleanup cleanup;      /* cleanup function */
97   unsigned int     magic_number; /* used internally to make sure it's init'd */
98 };
99 #define kMT_LOCK_magic_number  0x7A96283F
100 
101 
102 #ifndef NCBI_NO_THREADS
103 /*ARGSUSED*/
s_CORE_MT_Lock_default_handler(void * unused,EMT_Lock action)104 static int/*bool*/ s_CORE_MT_Lock_default_handler(void*    unused,
105                                                   EMT_Lock action)
106 {
107 #  if   defined(NCBI_POSIX_THREADS)  &&  \
108         defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
109 
110     static pthread_mutex_t sx_Mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
111 
112     switch (action) {
113     case eMT_Lock:
114     case eMT_LockRead:
115         return pthread_mutex_lock(&sx_Mutex)    == 0 ? 1/*ok*/ : 0/*fail*/;
116     case eMT_Unlock:
117         return pthread_mutex_unlock(&sx_Mutex)  == 0 ? 1/*ok*/ : 0/*fail*/;
118     case eMT_TryLock:
119     case eMT_TryLockRead:
120         return pthread_mutex_trylock(&sx_Mutex) == 0 ? 1/*ok*/ : 0/*fail*/;
121     }
122     return 0/*failure*/;
123 
124 #  elif defined(NCBI_WIN32_THREADS)
125 
126     static CRITICAL_SECTION sx_Crit;
127     static LONG             sx_Init   = 0;
128     static int/*bool*/      sx_Inited = 0/*false*/;
129 
130     LONG init = InterlockedCompareExchange(&sx_Init, 1, 0);
131     if (!init) {
132         InitializeCriticalSection(&sx_Crit);
133         sx_Inited = 1; /*go*/
134     } else while (!sx_Inited)
135         Sleep(1/*ms*/); /*spin*/
136 
137     switch (action) {
138     case eMT_Lock:
139     case eMT_LockRead:
140         EnterCriticalSection(&sx_Crit);
141         return 1/*success*/;
142     case eMT_Unlock:
143         LeaveCriticalSection(&sx_Crit);
144         return 1/*success*/;
145     case eMT_TryLock:
146     case eMT_TryLockRead:
147         return TryEnterCriticalSection(&sx_Crit) ? 1/*ok*/ : 0/*fail*/;
148     }
149     return 0/*failure*/;
150 
151 #  else
152 
153     return -1/*not implemented*/;
154 
155 #  endif /*NCBI_..._THREADS*/
156 }
157 #endif /*!NCBI_NO_THREADS*/
158 
159 
160 struct MT_LOCK_tag g_CORE_MT_Lock_default = {
161     1/* ref count */,
162     0/* user data */,
163 #ifndef NCBI_NO_THREADS
164     s_CORE_MT_Lock_default_handler,
165 #else
166     0/* noop handler */,
167 #endif /*NCBI_NO_THREADS*/
168     0/* cleanup */,
169     kMT_LOCK_magic_number
170 };
171 
172 
MT_LOCK_Create(void * user_data,FMT_LOCK_Handler handler,FMT_LOCK_Cleanup cleanup)173 extern MT_LOCK MT_LOCK_Create
174 (void*            user_data,
175  FMT_LOCK_Handler handler,
176  FMT_LOCK_Cleanup cleanup)
177 {
178     MT_LOCK lk = (struct MT_LOCK_tag*) malloc(sizeof(struct MT_LOCK_tag));
179 
180     if (lk) {
181         lk->ref_count    = 1;
182         lk->user_data    = user_data;
183         lk->handler      = handler;
184         lk->cleanup      = cleanup;
185         lk->magic_number = kMT_LOCK_magic_number;
186     }
187     return lk;
188 }
189 
190 
MT_LOCK_AddRef(MT_LOCK lk)191 extern MT_LOCK MT_LOCK_AddRef(MT_LOCK lk)
192 {
193     MT_LOCK_VALID;
194     if (lk != &g_CORE_MT_Lock_default)
195         lk->ref_count++;
196     return lk;
197 }
198 
199 
MT_LOCK_Delete(MT_LOCK lk)200 extern MT_LOCK MT_LOCK_Delete(MT_LOCK lk)
201 {
202     if (lk  &&  lk != &g_CORE_MT_Lock_default) {
203         MT_LOCK_VALID;
204 
205         if (!--lk->ref_count) {
206             if (lk->handler) {  /* weak extra protection */
207                 verify(lk->handler(lk->user_data, eMT_Lock));
208                 verify(lk->handler(lk->user_data, eMT_Unlock));
209             }
210 
211             if (lk->cleanup)
212                 lk->cleanup(lk->user_data);
213 
214             lk->magic_number++;
215             free(lk);
216             lk = 0;
217         }
218     }
219     return lk;
220 }
221 
222 
MT_LOCK_DoInternal(MT_LOCK lk,EMT_Lock how)223 extern int/*bool*/ MT_LOCK_DoInternal(MT_LOCK lk, EMT_Lock how)
224 {
225     MT_LOCK_VALID;
226 
227     return lk->handler
228         ? lk->handler(lk->user_data, how)
229         : -1/* rightful non-doing */;
230 }
231 
232 
233 
234 /******************************************************************************
235  *  ERROR HANDLING and LOGGING
236  */
237 
238 /* Lock/unlock the logger */
239 #define LOG_LOCK_WRITE  verify(MT_LOCK_Do(lg->mt_lock, eMT_Lock))
240 #define LOG_LOCK_READ   verify(MT_LOCK_Do(lg->mt_lock, eMT_LockRead))
241 #define LOG_UNLOCK      verify(MT_LOCK_Do(lg->mt_lock, eMT_Unlock))
242 
243 
244 /* Check the validity of the logger */
245 #define LOG_VALID  \
246     assert(lg->ref_count  &&  lg->magic_number == kLOG_magic_number)
247 
248 
249 /* Logger data and callbacks */
250 struct LOG_tag {
251     unsigned int ref_count;
252     void*        user_data;
253     FLOG_Handler handler;
254     FLOG_Cleanup cleanup;
255     MT_LOCK      mt_lock;
256     unsigned int magic_number;  /* used internally, to make sure it's init'd */
257 };
258 #define kLOG_magic_number  0x3FB97156
259 
260 
LOG_LevelStr(ELOG_Level level)261 extern const char* LOG_LevelStr(ELOG_Level level)
262 {
263     static const char* kPostSeverityStr[eLOG_Fatal + 1] = {
264         "TRACE",
265         "NOTE",
266         "WARNING",
267         "ERROR",
268         "CRITICAL",
269         "FATAL"
270     };
271 
272     assert(eLOG_Trace <= level  &&  level <= eLOG_Fatal);
273     return eLOG_Trace <= level  &&  level <= eLOG_Fatal
274         ? kPostSeverityStr[level]
275         : 0;
276 }
277 
278 
LOG_Create(void * user_data,FLOG_Handler handler,FLOG_Cleanup cleanup,MT_LOCK mt_lock)279 extern LOG LOG_Create
280 (void*        user_data,
281  FLOG_Handler handler,
282  FLOG_Cleanup cleanup,
283  MT_LOCK      mt_lock)
284 {
285     LOG lg = (struct LOG_tag*) malloc(sizeof(struct LOG_tag));
286 
287     if (lg) {
288         lg->ref_count    = 1;
289         lg->user_data    = user_data;
290         lg->handler      = handler;
291         lg->cleanup      = cleanup;
292         lg->mt_lock      = mt_lock;
293         lg->magic_number = kLOG_magic_number;
294     }
295     return lg;
296 }
297 
298 
LOG_Reset(LOG lg,void * user_data,FLOG_Handler handler,FLOG_Cleanup cleanup)299 extern LOG LOG_Reset
300 (LOG          lg,
301  void*        user_data,
302  FLOG_Handler handler,
303  FLOG_Cleanup cleanup)
304 {
305     LOG_LOCK_WRITE;
306     LOG_VALID;
307 
308     if (lg->cleanup)
309         lg->cleanup(lg->user_data);
310 
311     lg->user_data = user_data;
312     lg->handler   = handler;
313     lg->cleanup   = cleanup;
314 
315     LOG_UNLOCK;
316     return lg;
317 }
318 
319 
LOG_AddRef(LOG lg)320 extern LOG LOG_AddRef(LOG lg)
321 {
322     LOG_LOCK_WRITE;
323     LOG_VALID;
324 
325     lg->ref_count++;
326 
327     LOG_UNLOCK;
328     return lg;
329 }
330 
331 
LOG_Delete(LOG lg)332 extern LOG LOG_Delete(LOG lg)
333 {
334     if (lg) {
335         LOG_LOCK_WRITE;
336         LOG_VALID;
337 
338         if (lg->ref_count > 1) {
339             lg->ref_count--;
340             LOG_UNLOCK;
341             return lg;
342         }
343 
344         LOG_UNLOCK;
345 
346         LOG_Reset(lg, 0, 0, 0);
347         lg->ref_count--;
348         lg->magic_number++;
349 
350         if (lg->mt_lock)
351             MT_LOCK_Delete(lg->mt_lock);
352         free(lg);
353     }
354     return 0;
355 }
356 
357 
LOG_WriteInternal(LOG lg,SLOG_Handler * call_data)358 extern void LOG_WriteInternal
359 (LOG           lg,
360  SLOG_Handler* call_data
361  )
362 {
363     assert(!call_data->raw_size  ||  call_data->raw_data);
364 
365     if (lg) {
366         LOG_LOCK_READ;
367         LOG_VALID;
368 
369         if (lg->handler)
370             lg->handler(lg->user_data, call_data);
371 
372         LOG_UNLOCK;
373 
374         if (call_data->dynamic  &&  call_data->message)
375             free((void*) call_data->message);
376     }
377 
378     /* unconditional exit/abort on fatal error */
379     if (call_data->level == eLOG_Fatal) {
380 #ifdef NDEBUG
381         fflush(0);
382         _exit(255);
383 #else
384         abort();
385 #endif /*NDEBUG*/
386     }
387 }
388 
389 
LOG_Write(LOG lg,int code,int subcode,ELOG_Level level,const char * module,const char * func,const char * file,int line,const char * message,const void * raw_data,size_t raw_size)390 extern void LOG_Write
391 (LOG         lg,
392  int         code,
393  int         subcode,
394  ELOG_Level  level,
395  const char* module,
396  const char* func,
397  const char* file,
398  int         line,
399  const char* message,
400  const void* raw_data,
401  size_t      raw_size
402  )
403 {
404     SLOG_Handler call_data;
405 
406     call_data.dynamic     = 0;
407     call_data.message     = message;
408     call_data.level       = level;
409     call_data.module      = module;
410     call_data.func        = func;
411     call_data.file        = file;
412     call_data.line        = line;
413     call_data.raw_data    = raw_data;
414     call_data.raw_size    = raw_size;
415     call_data.err_code    = code;
416     call_data.err_subcode = subcode;
417 
418     LOG_WriteInternal(lg, &call_data);
419 }
420 
421 
422 
423 /******************************************************************************
424  *  REGISTRY
425  */
426 
427 /* Lock/unlock the registry  */
428 #define REG_LOCK_WRITE  verify(MT_LOCK_Do(rg->mt_lock, eMT_Lock))
429 #define REG_LOCK_READ   verify(MT_LOCK_Do(rg->mt_lock, eMT_LockRead))
430 #define REG_UNLOCK      verify(MT_LOCK_Do(rg->mt_lock, eMT_Unlock))
431 
432 
433 /* Check the validity of the registry */
434 #define REG_VALID  \
435     assert(rg->ref_count  &&  rg->magic_number == kREG_magic_number)
436 
437 
438 /* Logger data and callbacks */
439 struct REG_tag {
440     unsigned int ref_count;
441     void*        user_data;
442     FREG_Get     get;
443     FREG_Set     set;
444     FREG_Cleanup cleanup;
445     MT_LOCK      mt_lock;
446     unsigned int magic_number;  /* used internally, to make sure it's init'd */
447 };
448 #define kREG_magic_number  0xA921BC08
449 
450 
REG_Create(void * user_data,FREG_Get get,FREG_Set set,FREG_Cleanup cleanup,MT_LOCK mt_lock)451 extern REG REG_Create
452 (void*        user_data,
453  FREG_Get     get,
454  FREG_Set     set,
455  FREG_Cleanup cleanup,
456  MT_LOCK      mt_lock)
457 {
458     REG rg = (struct REG_tag*) malloc(sizeof(struct REG_tag));
459 
460     if (rg) {
461         rg->ref_count    = 1;
462         rg->user_data    = user_data;
463         rg->get          = get;
464         rg->set          = set;
465         rg->cleanup      = cleanup;
466         rg->mt_lock      = mt_lock;
467         rg->magic_number = kREG_magic_number;
468     }
469     return rg;
470 }
471 
472 
REG_Reset(REG rg,void * user_data,FREG_Get get,FREG_Set set,FREG_Cleanup cleanup,int do_cleanup)473 extern void REG_Reset
474 (REG          rg,
475  void*        user_data,
476  FREG_Get     get,
477  FREG_Set     set,
478  FREG_Cleanup cleanup,
479  int/*bool*/  do_cleanup)
480 {
481     REG_LOCK_WRITE;
482     REG_VALID;
483 
484     if (rg->cleanup  &&  do_cleanup)
485         rg->cleanup(rg->user_data);
486 
487     rg->user_data = user_data;
488     rg->get       = get;
489     rg->set       = set;
490     rg->cleanup   = cleanup;
491 
492     REG_UNLOCK;
493 }
494 
495 
REG_AddRef(REG rg)496 extern REG REG_AddRef(REG rg)
497 {
498     REG_LOCK_WRITE;
499     REG_VALID;
500 
501     rg->ref_count++;
502 
503     REG_UNLOCK;
504     return rg;
505 }
506 
507 
REG_Delete(REG rg)508 extern REG REG_Delete(REG rg)
509 {
510     if (rg) {
511         REG_LOCK_WRITE;
512         REG_VALID;
513 
514         if (rg->ref_count > 1) {
515             rg->ref_count--;
516             REG_UNLOCK;
517             return rg;
518         }
519 
520         REG_UNLOCK;
521 
522         REG_Reset(rg, 0, 0, 0, 0, 1/*true*/);
523         rg->ref_count--;
524         rg->magic_number++;
525 
526         if (rg->mt_lock)
527             MT_LOCK_Delete(rg->mt_lock);
528         free(rg);
529     }
530     return 0;
531 }
532 
533 
REG_Get(REG rg,const char * section,const char * name,char * value,size_t value_size,const char * def_value)534 extern const char* REG_Get
535 (REG         rg,
536  const char* section,
537  const char* name,
538  char*       value,
539  size_t      value_size,
540  const char* def_value)
541 {
542     if (!value  ||  value_size <= 0)
543         return 0/*failed*/;
544 
545     if (def_value)
546         strncpy0(value, def_value, value_size - 1);
547     else
548         *value = '\0';
549 
550     if (rg) {
551         REG_LOCK_READ;
552         REG_VALID;
553 
554         if (rg->get)
555             rg->get(rg->user_data, section, name, value, value_size);
556 
557         REG_UNLOCK;
558     }
559 
560     return value;
561 }
562 
563 
REG_Set(REG rg,const char * section,const char * name,const char * value,EREG_Storage storage)564 extern int/*bool*/ REG_Set
565 (REG          rg,
566  const char*  section,
567  const char*  name,
568  const char*  value,
569  EREG_Storage storage)
570 {
571     int/*bool*/ result;
572 
573     if (rg) {
574         REG_LOCK_READ;
575         REG_VALID;
576 
577         result = (rg->set
578                   ? rg->set(rg->user_data, section, name, value, storage)
579                   : 0/*failed*/);
580 
581         REG_UNLOCK;
582     } else
583         result = 0/*failed*/;
584 
585     return result;
586 }
587