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