2
* ===========================================================================
5
* National Center for Biotechnology Information
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.
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
22
* Please cite the author in any work or product based on this material.
24
* ===========================================================================
26
* File Name: ncbiprop.c
30
* Version Creation Date: 06-04-93
34
* File Description: Application Property Functions.
36
* Application properties were introduced to allow the NCBI Toolbox
37
* to be implemented as a dynamic link library (DLL). Under Windows,
38
* global variables located in the DLL are not instantiated for each
39
* application instance, but instead shared by all applications having
40
* a run-time linkage. Values that might ordinarily be stored in
41
* global variables can instead be saved as application properties
42
* (unless sharing is desired, of course). An application property
43
* is simply a pointer (or other value cast to a pointer) that is
44
* identified by a string key. Properties are kept in a linked list
45
* (sorted by key) whose head is stored in an application context block.
46
* The application context block is created on-demand and marked
47
* with the process ID of the calling application (or it's creation
48
* can be forced by calling InitAppContext() at the beginning of your
49
* program. The linked list of (smallish) application context
50
* structures is the only thing allocated from the DLL's data space,
51
* all other memory is owned by the application that called the DLL
52
* is automatically freed by the system when the application terminates.
54
* If this code is not actually compiled as a DLL, but bound at link
55
* time to a single application in the normal way, the behavior of all
56
* functions will be the same. The only difference being that the
57
* list of application context structures will contain exactly one item.
60
* --------------------------------------------------------------------------
61
* $Log: ncbiprop.c,v $
62
* Revision 6.3 2001/03/02 19:52:34 vakatov
63
* Do not use "pid" in the app.context anymore.
64
* It was needed for 16-bit MS-Win DLLs, a long time ago, and now it's
65
* just eating resources...
67
* Revision 6.2 1998/08/24 17:42:02 kans
68
* fixed old style function definition warnings
70
* Revision 6.1 1997/10/29 02:44:20 vakatov
71
* Type castings to pass through the C++ compiler
73
* Revision 6.0 1997/08/25 18:16:56 madden
74
* Revision changed to 6.0
76
* Revision 5.6 1997/01/28 21:19:12 kans
77
* <Desk.h>, <OSEvents.h> and <GestaltEqu.h> are obsolete in CodeWarrior
79
* Revision 5.5 1997/01/08 22:55:36 shavirin
80
* Added threads comparison through NlmThreadCompare
82
* Revision 5.4 1996/12/03 21:48:33 vakatov
83
* Adopted for 32-bit MS-Windows DLLs
85
* Revision 5.3 1996/11/25 19:05:30 vakatov
86
* Made Get/ReleaseAppContext() MT-safe
88
* Revision 5.2 1996/07/16 19:58:01 vakatov
89
* Nlm_GetScratchBuffer() now has a "size" parameter and has a variable
90
* size -- thus it works as an fast and smart "ad hoc" memory allocator.
91
* Added "corelib.h" header; thread_id argum. added to new_AppContext(...);
92
* delete_AppContext(): new cleaning funcs ReleaseApp{Err|Msg}Info called.
93
* Added a code to test error posting functions(they use the ScratchBuffer
94
* and Properties intensively) for multiple concurrent threads.
96
* Revision 5.1 1996/06/12 20:18:29 shavirin
97
* Added multy-thread ability of toolkit to handle error posting
98
* ==========================================================================
101
#include "corepriv.h"
105
/* ----------------------------------------------------------------------
106
* new_AppProperty allocates/constructs an AppProperty struct
107
* delete_AppProperty deallocates/destructs an AppProperty struct
112
* 00-00-00 YourName What changes did you make?
113
* ---------------------------------------------------------------------- */
115
typedef struct _AppProperty_
117
struct _AppProperty_ *next;
123
static AppProperty * new_AppProperty PROTO((const char *key, void *data));
124
static void delete_AppProperty PROTO((AppProperty *prop));
126
static AppProperty * new_AppProperty (const char *key, void *data)
128
AppProperty *prop = (AppProperty *)Calloc(1, sizeof(AppProperty));
129
ASSERT_HARD(key != NULL);
130
ASSERT_HARD(*key != '\0');
134
if ( (prop->key = StrSave(key)) == NULL )
147
static void delete_AppProperty (AppProperty *prop)
149
ASSERT_HARD(prop != NULL);
151
MemFill(prop, '\xFF', sizeof(*prop));
157
/* ----------------------------------------------------------------------
158
* new_AppContext allocates/constructs an AppContext struct
159
* delete_AppContext deallocates/destructs an AppContext struct
161
* Notes: (1) If a valid context cannot be created, the application halts.
162
* (2) The memory allocated for the AppContext struct is owned by
163
* the DLL and the scratch buffer is owned by the application.
164
* (3) It is important that this function NOT call TRACE, Message,
165
* or ErrPost (or anything else that could result in any of these
166
* being called) as they use the applicaion context's scratch buffer.
168
* For every non-thread capable platform tid == 0
173
* 00-00-00 YourName What changes did you make?
174
06-12-96 Shavirin Added TNlmThread tid element to
175
distinguish contexes belong to diffrent threads
176
* ---------------------------------------------------------------------- */
179
#define SCRATCH_SIZE_DEFAULT (2*KBYTE)
182
typedef struct _AppContext_
184
struct _AppContext_ *next;
185
struct _AppProperty_ *proplist;
188
unsigned enums :15; /* number of nested enumerations in-progress */
189
unsigned lock :1; /* if TRUE, property list is locked */
195
/* this is the only global variable in this file: */
196
static AppContext * g_appList; /* Application Context List */
198
static AppContext * new_AppContext(TNlmThread thread_id);
199
static void delete_AppContext PROTO((AppContext *prop));
200
INLINE static void AppContext_Lock PROTO((AppContext *context));
201
INLINE static void AppContext_Unlock PROTO((AppContext *context));
202
INLINE static unsigned AppContext_IsLocked PROTO((AppContext *context));
205
static AppContext * new_AppContext(TNlmThread thread_id)
207
AppContext *context = (AppContext *)dll_Malloc(sizeof(AppContext));
212
memset((void*)context,0,sizeof(struct _AppContext_));
213
context->tid = thread_id; /* thread id for thread-capable OS */
214
context->scratch_size = 0;
215
context->scratch = NULL;
219
static void delete_AppContext (AppContext *context)
221
AppProperty *p1, *p2;
223
ASSERT_HARD(context != NULL);
228
for (p1=context->proplist; p1; p1=p2)
231
delete_AppProperty(p1);
234
Nlm_GetScratchBuffer( 0 );
238
static INLINE void AppContext_Lock (AppContext *context)
240
ASSERT(context->lock==0);
244
static INLINE void AppContext_Unlock (AppContext *context)
246
ASSERT(context->lock==1);
250
static INLINE unsigned AppContext_IsLocked (AppContext *context)
252
return context->lock;
257
/* ----------------------------------------------------------------------
258
* InitAppContext -- Initializes a context struct for current application.
261
* If a valid context cannot be created, the application halts.
262
* Although it is not strictly necessary to call InitAppContext() as
263
* contexts are created on-demand, calling it once at the beginning
264
* of the program may reduce heap fragmentation.
269
* 06-12-96 Shavirin Added multy-thread ability of context handling
272
* ---------------------------------------------------------------------- */
274
/* helper functions for internal use only */
276
static AppContext *GetAppContext (void)
280
TNlmThread thread_id = NlmThreadSelf(); /* thread ID */
282
NlmMutexLockEx( &corelibMutex );
285
* First we scan the list of contexts for one with the current
286
* application's process ID.
288
for (p1=g_appList,p2=NULL; p1; p1=p1->next)
290
if ( NlmThreadCompare(p1->tid, thread_id) )
292
NlmMutexUnlock( corelibMutex );
299
* If we reach this point, the context for current does not
300
* exist yet, so we need to create one and link it into the list.
302
app = new_AppContext(thread_id);
310
NlmMutexUnlock( corelibMutex );
316
NLM_EXTERN void LIBCALL Nlm_InitAppContext (void)
322
NLM_EXTERN char * LIBCALL Nlm_GetScratchBuffer(size_t size)
324
AppContext *context = GetAppContext();
328
if ((size == 0 || context->scratch_size < size) &&
329
context->scratch != NULL)
331
/* reset scratch buffer */
332
Free( context->scratch );
333
context->scratch_size = 0;
334
context->scratch = NULL;
337
/* nothing but reset */
341
/* do we have enough allocated memory already? */
342
if (context->scratch_size >= size)
343
return (char *) context->scratch;
345
/* allocate new buffer */
346
size = (size + 15) / 16;
348
if ((context->scratch = (char *) Malloc( size )) == NULL)
351
context->scratch_size = size;
352
return (char *) context->scratch;
357
/* ----------------------------------------------------------------------
358
* ReleaseAppContext -- frees application context struct for current app.
361
* For most platforms, memory will be recovered automatically by the
362
* operating system. However, since we cannot guarantee this for
363
* all systems, it might be wise to call ReleaseAppContext() once
364
* just before the application exits.
369
* 06-12-96 Shavirin Added multi-thread ability of context handling
372
* ---------------------------------------------------------------------- */
374
NLM_EXTERN void LIBCALL Nlm_ReleaseAppContext (void)
377
TNlmThread thread_id = NlmThreadSelf();
379
NlmMutexLockEx( &corelibMutex );
382
* Scan the list for the context of the current app
384
for (p1=g_appList,p2=NULL; p1; p1=p1->next)
386
if ( NlmThreadCompare(p1->tid, thread_id) )
391
* Adjust links and release memory
395
AppContext *next = p1->next;
396
delete_AppContext( p1 );
404
NlmMutexUnlock( corelibMutex );
409
/* ----------------------------------------------------------------------
410
* SetAppProperty -- Sets a data item int the application context's
411
* property list, replacing the existing one or creating a new
412
* one if no property with that key exists.
415
* key: key identifying the property
416
* value: pointer to arbitrary data
419
* Previous value of the property, if any, or NULL otherwise.
424
* 00-00-00 YourName What changes did you make?
425
* ---------------------------------------------------------------------- */
427
NLM_EXTERN void * LIBCALL Nlm_SetAppProperty (const char *key, void *data)
431
AppContext *context = GetAppContext();
432
AppProperty *p1, *p2, *prop;
436
for (p1=context->proplist, p2=NULL; p1; p1=p1->next)
438
d = strcmp(key,p1->key);
444
return prev; /* previous value */
450
* If we reach here, a property with the given key does not exist, so
451
* let's create a new one and link it into the list.
454
if (AppContext_IsLocked(context))
456
TRACE("SetAppProperty: ** property list is locked **\n");
460
AppContext_Lock(context);
461
if ((prop = new_AppProperty(key,data)) != NULL)
464
context->proplist = prop;
469
AppContext_Unlock(context);
472
return NULL; /* no previous value */
477
/* ----------------------------------------------------------------------
478
* GetAppProperty -- Retrieves data value that was set with SetAppProperty.
481
* key: key identifying the property
484
* Value that was set with SetAppProperty or NULL if no property with
490
* 00-00-00 YourName What changes did you make?
492
* ---------------------------------------------------------------------- */
494
NLM_EXTERN void * LIBCALL Nlm_GetAppProperty (const char *key)
498
AppContext *context = GetAppContext();
501
for (prop=context->proplist; prop; prop=prop->next)
503
if (strcmp(prop->key,key) == 0)
512
/* ----------------------------------------------------------------------
513
* RemoveAppProperty -- Removes a property from the application context's
514
* property list (if it exists) and returns the data value that was
515
* set with SetAppParam().
518
* key: key identifying the property
521
* Value that was set with SetAppProperty or NULL if no property with
525
* It is the responsibiliy of the caller to free whatever resources
526
* the property's data (return value) may happen to point to.
531
* 00-00-00 YourName What changes did you make?
533
* ---------------------------------------------------------------------- */
535
NLM_EXTERN void * LIBCALL Nlm_RemoveAppProperty (const char *key)
539
AppContext *context = GetAppContext();
541
if (AppContext_IsLocked(context))
543
TRACE("RemoveAppProperty: ** property list is locked **\n");
547
AppProperty *p1, *p2;
550
AppContext_Lock(context);
551
for (p1=context->proplist, p2=NULL; p1; p1=p1->next)
553
d = strcmp(key,p1->key);
557
void *data = p1->data;
559
context->proplist = p1->next;
562
delete_AppProperty(p1);
563
AppContext_Unlock(context);
564
return data; /* success */
568
AppContext_Unlock(context);
571
return NULL; /* failure */
576
/* ----------------------------------------------------------------------
577
* EnumAppProperties -- Enumerates all application properties, calling
578
* a caller-supplied callback function with the key and data for
582
* Pointer to the enumeration callback procedure.
585
* Number of properties enumerated.
589
* int LIBCALLBACK MyEmumProc (const char *key, void *value)
591
* //--- insert your code here ---
594
* if (strcmp(key,"MyBigBuffer") ==0)
596
* SetAppProperty(key,NULL);
598
* return FALSE; // FALSE to stop enumeration at this point
600
* return TRUE; // TRUE to continue the enumeration.
604
* It is OK to call SetAppProperty() from within the callback function,
605
* but _only_ to change the value of an existing property. Any attempt
606
* to alter the property list, either by calling SetAppProperty() with
607
* a new key or calling RemoveAppProperty() will fail while an enumera-
608
* tion is in progress.
613
* 00-00-00 YourName What changes did you make?
615
* ---------------------------------------------------------------------- */
617
NLM_EXTERN int LIBCALL Nlm_EnumAppProperties (Nlm_AppPropEnumProc proc)
622
AppContext *context = GetAppContext();
625
if (context->enums==0)
626
AppContext_Lock(context);
628
for (prop=context->proplist; prop; prop=prop->next)
631
if ( ! (*proc)(prop->key,prop->data) )
635
if (context->enums==0)
636
AppContext_Unlock(context);
642
/* ----------------------------------------------------------------------
643
* GetAppProcessID [Schuler, 06-04-93]
645
* Returns an identifier for the current application instance.
648
* On the Macintosh, the process ID is a 64-bit value, which is being
649
* condensed down to 32-bits here by XORing the high and low halves
650
* of the value. I can't guarantee it will be unique (although it
651
* seems to be in practice), but this code is not being dynamically
652
* linked on the Mac, so it doesn't matter.
655
* 04-10-93 Schuler Added Macintosh version.
656
* 12-16-93 Schuler Added Borland version contributed by M.Copperwhite
657
* ---------------------------------------------------------------------- */
661
/* (insert other platform-specific versions here as necessary and #undef USE_GETPID) */
664
/* ----- Macintosh Version ----- */
666
#include <Processes.h>
669
NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
672
ProcessSerialNumber psn; /* a 64-bit value*/
674
if (Gestalt (gestaltSystemVersion, &gval) == noErr && (short) gval >= 7 * 256) {
675
GetCurrentProcess(&psn);
676
return (psn.highLongOfPSN ^ psn.lowLongOfPSN); /* merge to 32-bits */
686
/* ----- NCBIDLL mod Borland DLL version - call Windows API to get PSP ----- */
690
NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
692
return GetCurrentPDB();
700
/* ----- Generic Version ----- */
702
#if defined(COMP_MSC) || defined(COMP_BOR)
706
NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
715
#ifdef TEST_MODULE_NCBIPROP
717
/***********************************************************************
719
***********************************************************************/
723
#include <tsprintf.h>
725
FILE *STDOUT = (FILE *)stdout;
726
FILE *STDERR = (FILE *)stderr;
728
static Nlm_VoidPtr TEST__MyThread(Nlm_VoidPtr arg)
730
Nlm_Int4 thread_no = (Nlm_Int4) arg;
732
Nlm_CharPtr str = NULL;
733
const Nlm_Char PNTR s = NULL;
735
fprintf(STDOUT, "TEST__MyThread(): Thread #%ld(%ld) started\n",
736
(long)NlmThreadSelf(), (long)thread_no);
738
n = (thread_no + 1) * 20;
739
str = (Nlm_CharPtr)Nlm_MemNew( n );
740
for (i = 10; i < n; i += 10)
742
Nlm_MemSet(str, 'a', i);
744
s = TSPrintf("TEST__MyThread() #%ld: %u %d %e %g %f %p %s +++\n",
745
(long)thread_no, (unsigned int)i, (int)i, (double)i,
746
(double)i, (double)i, (void *)&i, str);
747
fprintf(STDOUT, "%p %ld: %s\n", (void *)s, Nlm_StrLen( s ), s);
752
#if defined (OS_UNIX)
753
sleep(thread_no + 1);
754
#elif defined (WIN32)
755
Sleep(1000 * (thread_no + 1));
759
fprintf(STDOUT, "TEST__MyThread() after sleep #%ld: %p %ld: %s\n",
760
(long)thread_no, (void *)s, Nlm_StrLen( s ), s);
762
switch (thread_no % 4)
766
"TEST__MyThread() #%ld: Nlm_Message(MSG_OKC)\n",
771
Nlm_MsgAlert (MSG_YN, MSG_POST,
772
"Nlm_MsgAlert(): Caption\n",
773
"TEST__MyThread() #%ld: Nlm_MsgAlert(MSG_YN,MSG_POST)\n",
778
Nlm_ErrPost (CTX_DEBUG/*CTX_UNKNOWN*/, 1,
779
"TEST__MyThread() #%ld: Nlm_ErrPost(CTX_DEBUG,1)\n",
784
Nlm_ErrPostEx (SEV_ERROR, 1, 2,
785
"TEST__MyThread() #%ld: Nlm_ErrPostEx(0,1,2)\n",
794
static void MyExitMessage(Nlm_VoidPtr arg)
796
fprintf(STDERR, "\n%s\n", (Nlm_CharPtr)arg);
800
static Nlm_Int4 TEST__scratch(Nlm_Int4 n_threads)
803
Nlm_Int4 err_code = 0;
804
TNlmThread *threads = Nlm_MemNew(n_threads * sizeof(TNlmThread));
806
for (t = 0; t < n_threads; t++)
811
threads[t] = NlmThreadCreate(TEST__MyThread, (Nlm_VoidPtr)t,
816
Nlm_CharPtr exit_message = Nlm_MemNew( 64 );
817
sprintf(exit_message, "Exit Message, thread #%d", (int)t);
818
threads[t] = NlmThreadCreateEx(
819
TEST__MyThread, (Nlm_VoidPtr)t, THR_RUN,
820
MyExitMessage, (Nlm_VoidPtr)exit_message);
824
fprintf(STDOUT, "TEST__scratch(): Starting thread #%ld\n",
828
for (t = 0; t < n_threads; t++)
831
err_code += NlmThreadJoin(threads[t], &status);
834
fprintf(STDOUT, "TEST__scratch(): Cannot join thread #%ld; \
836
(long)threads[t], (long)err_code);
840
fprintf(STDOUT, "TEST__scratch(): Thread #%ld joined; \
841
terminated, exit status = %p\n",
842
(long)threads[t], status);
845
Nlm_MemFree( threads );
847
fprintf(STDOUT, "TEST__scratch(): FINISHED\n");
852
Nlm_Int2 Nlm_Main( void )
854
#if defined(WIN32) && defined(_WINDOWS)
855
(FILE *)STDOUT = freopen("stdout.w95", "w", stdout);
856
(FILE *)STDERR = freopen("stderr.w95", "w", stderr);
857
if (!STDOUT || !STDERR)
861
return (Nlm_Int2)TEST__scratch( 10 );
864
#endif /* TEST_MODULE_NCBIPROP */