1 /*   ncbiprop.c
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 * File Name:  ncbiprop.c
27 *
28 * Author:  Schuler
29 *
30 * Version Creation Date:   06-04-93
31 *
32 * $Revision: 6.4 $
33 *
34 * File Description:		Application Property Functions.
35 *
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.
53 *
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.
58 *
59 * Modifications:
60 * --------------------------------------------------------------------------
61 * $Log: ncbiprop.c,v $
62 * Revision 6.4  2008/04/29 13:40:53  kans
63 * fixes for warnings caught by mingw cross-compiler
64 *
65 * Revision 6.3  2001/03/02 19:52:34  vakatov
66 * Do not use "pid" in the app.context anymore.
67 * It was needed for 16-bit MS-Win DLLs, a long time ago, and now it's
68 * just eating resources...
69 *
70 * Revision 6.2  1998/08/24 17:42:02  kans
71 * fixed old style function definition warnings
72 *
73 * Revision 6.1  1997/10/29 02:44:20  vakatov
74 * Type castings to pass through the C++ compiler
75 *
76 * Revision 6.0  1997/08/25 18:16:56  madden
77 * Revision changed to 6.0
78 *
79 * Revision 5.6  1997/01/28 21:19:12  kans
80 * <Desk.h>, <OSEvents.h> and <GestaltEqu.h> are obsolete in CodeWarrior
81 *
82  * Revision 5.5  1997/01/08  22:55:36  shavirin
83  * Added threads comparison through NlmThreadCompare
84  *
85  * Revision 5.4  1996/12/03  21:48:33  vakatov
86  * Adopted for 32-bit MS-Windows DLLs
87  *
88  * Revision 5.3  1996/11/25  19:05:30  vakatov
89  * Made Get/ReleaseAppContext() MT-safe
90  *
91  * Revision 5.2  1996/07/16  19:58:01  vakatov
92  * Nlm_GetScratchBuffer() now has a "size" parameter and has a variable
93  * size -- thus it works as an fast and smart "ad hoc" memory allocator.
94  * Added "corelib.h" header;  thread_id argum. added to new_AppContext(...);
95  * delete_AppContext(): new cleaning funcs ReleaseApp{Err|Msg}Info called.
96  * Added a code to test error posting functions(they use the ScratchBuffer
97  * and Properties intensively) for multiple concurrent threads.
98  *
99  * Revision 5.1  1996/06/12  20:18:29  shavirin
100  * Added multy-thread ability of toolkit to handle error posting
101 * ==========================================================================
102 */
103 
104 #include "corepriv.h"
105 
106 
107 
108 /*	----------------------------------------------------------------------
109  *	new_AppProperty			allocates/constructs an AppProperty struct
110  *	delete_AppProperty		deallocates/destructs an AppProperty struct
111  *
112  *	Created:
113  *		06-04-93 Schuler
114  *	Modified:
115  *		00-00-00 YourName	What changes did you make?
116  *	----------------------------------------------------------------------	*/
117 
118 typedef struct _AppProperty_
119 {
120 	struct _AppProperty_	*next;
121 	char *key;
122 	void *data;
123 }
124 AppProperty;
125 
126 static AppProperty * new_AppProperty PROTO((const char *key, void *data));
127 static void delete_AppProperty PROTO((AppProperty *prop));
128 
new_AppProperty(const char * key,void * data)129 static AppProperty * new_AppProperty (const char *key, void *data)
130 {
131   AppProperty *prop = (AppProperty *)Calloc(1, sizeof(AppProperty));
132   ASSERT_HARD(key != NULL);
133   ASSERT_HARD(*key != '\0');
134 
135   if (prop != NULL)
136     {
137       if ( (prop->key = StrSave(key)) == NULL )
138         {
139           Free(prop);
140           prop = NULL;
141         }
142       else
143         prop->data = data;
144     }
145 
146   return prop;
147 }
148 
149 
delete_AppProperty(AppProperty * prop)150 static void delete_AppProperty (AppProperty *prop)
151 {
152 	ASSERT_HARD(prop != NULL);
153 	MemFree(prop->key);
154         MemFill(prop, '\xFF', sizeof(*prop));
155 	Free(prop);
156 }
157 
158 
159 
160 /*	----------------------------------------------------------------------
161  *	new_AppContext			allocates/constructs an AppContext struct
162  *	delete_AppContext		deallocates/destructs an AppContext struct
163  *
164  *  Notes:  (1) If a valid context cannot be created, the application halts.
165  *	    (2) The memory allocated for the AppContext struct is owned by
166  *		the DLL and the scratch buffer is owned by the application.
167  *	    (3) It is important that this function NOT call TRACE, Message,
168  *		or ErrPost (or anything else that could result in any of these
169  *		being called) as they use the applicaion context's scratch buffer.
170  *
171  *          For every non-thread capable platform tid == 0
172  *
173  *	Created:
174  *		06-04-93 Schuler
175  *	Modified:
176  *		00-00-00 YourName	What changes did you make?
177                 06-12-96 Shavirin       Added TNlmThread tid element to
178                                         distinguish contexes belong to diffrent threads
179  *	----------------------------------------------------------------------	*/
180 
181 /*
182 #define SCRATCH_SIZE_DEFAULT	(2*KBYTE)
183 */
184 
185 typedef struct _AppContext_
186 {
187 	struct _AppContext_ 	*next;
188 	struct _AppProperty_	*proplist;
189    TNlmThread tid;
190 
191 	unsigned	enums	:15; /* number of nested enumerations in-progress */
192 	unsigned	lock	:1;  /* if TRUE, property list is locked */
193 	size_t	scratch_size;
194 	void	*scratch;
195 }
196 AppContext;
197 
198 		/* this is the only global variable in this file: */
199 static AppContext * g_appList;	/* Application Context List */
200 
201 static AppContext * new_AppContext(TNlmThread thread_id);
202 static void delete_AppContext PROTO((AppContext *prop));
203 INLINE static void  AppContext_Lock PROTO((AppContext *context));
204 INLINE static void  AppContext_Unlock PROTO((AppContext *context));
205 INLINE static unsigned  AppContext_IsLocked PROTO((AppContext *context));
206 
207 
new_AppContext(TNlmThread thread_id)208 static AppContext * new_AppContext(TNlmThread thread_id)
209 {
210 	AppContext *context = (AppContext *)dll_Malloc(sizeof(AppContext));
211 
212 	if (context == NULL)
213 		AbnormalExit(1);
214 
215 	memset((void*)context,0,sizeof(struct _AppContext_));
216    context->tid = thread_id; /* thread id for thread-capable OS */
217 	context->scratch_size = 0;
218 	context->scratch = NULL;
219 	return context;
220 }
221 
delete_AppContext(AppContext * context)222 static void delete_AppContext (AppContext *context)
223 {
224   AppProperty *p1, *p2;
225 
226   ASSERT_HARD(context != NULL);
227 
228   ReleaseAppErrInfo();
229   ReleaseAppMsgInfo();
230 
231   for (p1=context->proplist; p1; p1=p2)
232     {
233       p2 = p1->next;
234       delete_AppProperty(p1);
235     }
236 
237   Nlm_GetScratchBuffer( 0 );
238   dll_Free(context);
239 }
240 
AppContext_Lock(AppContext * context)241 static INLINE void AppContext_Lock (AppContext *context)
242 {
243 	ASSERT(context->lock==0);
244 	context->lock = 1;
245 }
246 
AppContext_Unlock(AppContext * context)247 static INLINE void AppContext_Unlock (AppContext *context)
248 {
249 	ASSERT(context->lock==1);
250 	context->lock = 0;
251 }
252 
AppContext_IsLocked(AppContext * context)253 static INLINE unsigned AppContext_IsLocked (AppContext *context)
254 {
255 	return context->lock;
256 }
257 
258 
259 
260 /*	----------------------------------------------------------------------
261  *	InitAppContext -- Initializes a context struct for current application.
262  *
263  *  Notes:
264  *		If a valid context cannot be created, the application halts.
265  *		Although it is not strictly necessary to call InitAppContext() as
266  *		contexts are created on-demand, calling it once at the beginning
267  *		of the program may reduce heap fragmentation.
268  *
269  *	Created:
270  *		06-04-93 Schuler
271  *	Modified:
272  *              06-12-96 Shavirin   Added multy-thread ability of context handling
273  *
274  *
275  *	----------------------------------------------------------------------	*/
276 
277 /* helper functions for internal use only */
278 
GetAppContext(void)279 static AppContext *GetAppContext (void)
280 {
281   AppContext *p1, *p2;
282   AppContext *app;
283   TNlmThread thread_id = NlmThreadSelf(); /* thread ID */
284 
285   NlmMutexLockEx( &corelibMutex );
286 
287   /*
288    *	First we scan the list of contexts for one with the current
289    *	application's process ID.
290    */
291   for (p1=g_appList,p2=NULL; p1; p1=p1->next)
292     {
293       if ( NlmThreadCompare(p1->tid, thread_id) )
294         {
295           NlmMutexUnlock( corelibMutex );
296           return p1;
297         }
298       p2 = p1;
299     }
300 
301   /*
302    *	If we reach this point, the context for current does not
303    *	exist yet, so we need to create one and link it into the list.
304    */
305   app = new_AppContext(thread_id);
306 
307   if (p2 == NULL)
308     g_appList = app;
309   else
310     p2->next = app;
311   app->next = p1;
312 
313   NlmMutexUnlock( corelibMutex );
314 
315   return app;
316 }
317 
318 
Nlm_InitAppContext(void)319 NLM_EXTERN void LIBCALL Nlm_InitAppContext (void)
320 {
321   GetAppContext ();
322 }
323 
324 
Nlm_GetScratchBuffer(size_t size)325 NLM_EXTERN char * LIBCALL Nlm_GetScratchBuffer(size_t size)
326 {
327   AppContext *context = GetAppContext();
328   if (context == NULL)
329     abort();
330 
331   if ((size == 0  ||  context->scratch_size < size)  &&
332       context->scratch != NULL)
333     {
334       /* reset scratch buffer */
335       Free( context->scratch );
336       context->scratch_size = 0;
337       context->scratch = NULL;
338     }
339 
340   /* nothing but reset */
341   if (size == 0)
342     return NULL;
343 
344   /* do we have enough allocated memory already? */
345   if (context->scratch_size >= size)
346     return (char *) context->scratch;
347 
348   /* allocate new buffer */
349   size = (size + 15) / 16;
350   size *= 16;
351   if ((context->scratch = (char *) Malloc( size )) == NULL)
352     return NULL;
353 
354   context->scratch_size = size;
355   return (char *) context->scratch;
356 }
357 
358 
359 
360 /*	----------------------------------------------------------------------
361  *	ReleaseAppContext -- frees application context struct for current app.
362  *
363  *	Notes:
364  *		For most platforms, memory will be recovered automatically by the
365  *		operating system.  However, since we cannot guarantee this for
366  *		all systems, it might be wise to call ReleaseAppContext() once
367  *		just before the application exits.
368  *
369  *	Created:
370  *		06-04-93 Schuler
371  *	Modified:
372  *              06-12-96 Shavirin Added multi-thread ability of context handling
373  *
374  *
375  *	----------------------------------------------------------------------	*/
376 
Nlm_ReleaseAppContext(void)377 NLM_EXTERN void LIBCALL Nlm_ReleaseAppContext (void)
378 {
379   AppContext *p1, *p2;
380   TNlmThread thread_id = NlmThreadSelf();
381 
382   NlmMutexLockEx( &corelibMutex );
383 
384   /*
385    *	Scan the list for the context of the current app
386    */
387   for (p1=g_appList,p2=NULL; p1; p1=p1->next)
388     {
389       if ( NlmThreadCompare(p1->tid, thread_id) )
390         break;
391       p2 = p1;
392     }
393   /*
394    *	Adjust links and release memory
395    */
396   if (p1 != NULL)
397     {
398       AppContext *next = p1->next;
399       delete_AppContext( p1 );
400 
401       if (p2 == NULL)
402         g_appList = next;
403       else
404         p2->next  = next;
405     }
406 
407   NlmMutexUnlock( corelibMutex );
408 }
409 
410 
411 
412 /*	----------------------------------------------------------------------
413  *	SetAppProperty -- Sets a data item int the application context's
414  *		property list, replacing the existing one or creating a new
415  *		one if no property with that key exists.
416  *
417  *	Parameters:
418  *		key:		key identifying the property
419  *		value:		pointer to arbitrary data
420  *
421  *	Return value:
422  *		Previous value of the property, if any, or NULL otherwise.
423  *
424  *	Created:
425  *		06-08-93	Schuler
426  *	Modified:
427  *		00-00-00	YourName	What changes did you make?
428  *	----------------------------------------------------------------------	*/
429 
Nlm_SetAppProperty(const char * key,void * data)430 NLM_EXTERN void * LIBCALL Nlm_SetAppProperty (const char *key, void *data)
431 {
432 	if (key && *key)
433 	{
434 		AppContext *context = GetAppContext();
435 		AppProperty *p1, *p2, *prop;
436 		void *prev;
437 		int d;
438 
439 		for (p1=context->proplist, p2=NULL; p1; p1=p1->next)
440 		{
441 			d = strcmp(key,p1->key);
442 			if (d < 0)  break;
443 			if (d==0)
444 			{
445 				prev = p1->data;
446 				p1->data = data;
447 				return prev;	/* previous value */
448 			}
449 			p2 = p1;
450 		}
451 
452 		/*
453 		 *	If we reach here, a property with the given key does not exist, so
454 		 *	let's create a new one and link it into the list.
455 		 */
456 
457 		if (AppContext_IsLocked(context))
458 		{
459 			TRACE("SetAppProperty:  ** property list is locked **\n");
460 		}
461 		else
462 		{
463 			AppContext_Lock(context);
464 			if ((prop = new_AppProperty(key,data)) != NULL)
465 			{
466 				if (p2 == NULL)
467 					context->proplist = prop;
468 				else
469 					p2->next = prop;
470 				prop->next = p1;
471 			}
472 			AppContext_Unlock(context);
473 		}
474 	}
475 	return NULL;	/* no previous value */
476 }
477 
478 
479 
480 /*	----------------------------------------------------------------------
481  *	GetAppProperty -- Retrieves data value that was set with SetAppProperty.
482  *
483  *	Parameters:
484  *		key:	key identifying the property
485  *
486  *	Return value:
487  *		Value that was set with SetAppProperty or NULL if no property with
488  *		that key exists.
489  *
490  *	Created:
491  *		06-08-93	Schuler
492  *	Modified:
493  *		00-00-00	YourName	What changes did you make?
494  *
495  *	----------------------------------------------------------------------	*/
496 
Nlm_GetAppProperty(const char * key)497 NLM_EXTERN void * LIBCALL Nlm_GetAppProperty (const char *key)
498 {
499 	if (key && *key)
500 	{
501 		AppContext *context = GetAppContext();
502 		AppProperty *prop;
503 
504 		for (prop=context->proplist; prop; prop=prop->next)
505 		{
506 			if (strcmp(prop->key,key) == 0)
507 				return prop->data;
508 		}
509 	}
510 	return NULL;
511 }
512 
513 
514 
515 /*	----------------------------------------------------------------------
516  *	RemoveAppProperty -- Removes a property from the application context's
517  *		property list (if it exists) and returns the data value that was
518  *		set with SetAppParam().
519  *
520  *	Parameters:
521  *		key:	key identifying the property
522  *
523  *	Return value:
524  *		Value that was set with SetAppProperty or NULL if no property with
525  *		that key exists.
526  *
527  *	Notes:
528  *		It is the responsibiliy of the caller to free whatever resources
529  *		the property's data (return value) may happen to point to.
530  *
531  *	Created:
532  *		06-08-93	Schuler
533  *	Modified:
534  *		00-00-00	YourName	What changes did you make?
535  *
536  *	----------------------------------------------------------------------	*/
537 
Nlm_RemoveAppProperty(const char * key)538 NLM_EXTERN void * LIBCALL Nlm_RemoveAppProperty (const char *key)
539 {
540 	if (key && *key)
541 	{
542 		AppContext *context = GetAppContext();
543 
544 		if (AppContext_IsLocked(context))
545 		{
546 			TRACE("RemoveAppProperty:  ** property list is locked **\n");
547 		}
548 		else
549 		{
550 			AppProperty *p1, *p2;
551 			int d;
552 
553 			AppContext_Lock(context);
554 			for (p1=context->proplist, p2=NULL; p1; p1=p1->next)
555 			{
556 				d = strcmp(key,p1->key);
557 				if (d < 0)  break;
558 				if (d==0)
559 				{
560 					void *data = p1->data;
561 					if (p2 == NULL)
562 						context->proplist = p1->next;
563 					else
564 						p2->next = p1->next;
565 					delete_AppProperty(p1);
566 					AppContext_Unlock(context);
567 					return data;	/* success */
568 				}
569 				p2 = p1;
570 			}
571 			AppContext_Unlock(context);
572 		}
573 	}
574 	return NULL;	/* failure */
575 }
576 
577 
578 
579 /*	----------------------------------------------------------------------
580  *	EnumAppProperties -- Enumerates all application properties, calling
581  *		a caller-supplied callback function with the key and data for
582  *		each one.
583  *
584  *	Parameters:
585  *		Pointer to the enumeration callback procedure.
586  *
587  *	Return value:
588  *		Number of properties enumerated.
589  *
590  *	Callback function:
591  *
592  *		int LIBCALLBACK MyEmumProc (const char *key, void *value)
593  *		{
594  *			//--- insert your code here ---
595  *
596  *			// for example:
597  *			if (strcmp(key,"MyBigBuffer") ==0)
598  *			{
599  *				SetAppProperty(key,NULL);
600  *				MemFree(data)
601  *				return FALSE;	// FALSE to stop enumeration at this point
602  *			}
603  *			return TRUE;		// TRUE to continue the enumeration.
604  *		}
605  *
606  *	Notes:
607  *		It is OK to call SetAppProperty() from within the callback function,
608  *		but _only_ to change the value of an existing property.  Any attempt
609  *		to alter the property list, either by calling SetAppProperty() with
610  *		a new key or calling RemoveAppProperty() will fail while an enumera-
611  *		tion is in progress.
612  *
613  *	Created:
614  *		06-09-93	Schuler
615  *	Modified:
616  *		00-00-00	YourName	What changes did you make?
617  *
618  *	----------------------------------------------------------------------	*/
619 
Nlm_EnumAppProperties(Nlm_AppPropEnumProc proc)620 NLM_EXTERN int LIBCALL Nlm_EnumAppProperties (Nlm_AppPropEnumProc proc)
621 {
622 	int count = 0;
623 	if (proc != NULL)
624 	{
625 		AppContext *context = GetAppContext();
626 		AppProperty *prop;
627 
628 		if (context->enums==0)
629 			AppContext_Lock(context);
630 		context->enums++;
631 		for (prop=context->proplist; prop; prop=prop->next)
632 		{
633 			count ++;
634 			if ( ! (*proc)(prop->key,prop->data) )
635 				break;
636 		}
637 		context->enums--;
638 		if (context->enums==0)
639 			AppContext_Unlock(context);
640 	}
641 	return count;
642 }
643 
644 
645 /*	----------------------------------------------------------------------
646  *	GetAppProcessID  [Schuler, 06-04-93]
647  *
648  *	Returns an identifier for the current application instance.
649  *
650  *	Notes:
651  *	On the Macintosh, the process ID is a 64-bit value, which is being
652  *	condensed down to 32-bits here by XORing the high and low halves
653  *	of the value.  I can't guarantee it will be unique (although it
654  *	seems to be in practice), but this code is not being dynamically
655  *	linked on the Mac, so it doesn't matter.
656  *
657  *	MODIFICATIONS
658  *	04-10-93  Schuler  Added Macintosh version.
659  *	12-16-93  Schuler  Added Borland version contributed by M.Copperwhite
660  *	----------------------------------------------------------------------	*/
661 
662 #define USE_GETPID
663 
664 /* (insert other platform-specific versions here as necessary and #undef USE_GETPID) */
665 
666 
667 /* ----- Macintosh Version ----- */
668 #ifdef OS_MAC
669 #include <Processes.h>
670 #include <Gestalt.h>
671 
Nlm_GetAppProcessID(void)672 NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
673 {
674 	long gval;
675 	ProcessSerialNumber psn;	/* a 64-bit value*/
676 
677     if (Gestalt (gestaltSystemVersion, &gval) == noErr && (short) gval >= 7 * 256) {
678 		GetCurrentProcess(&psn);
679 		return (psn.highLongOfPSN ^ psn.lowLongOfPSN);	/* merge to 32-bits */
680 	} else {
681 		return 1;
682 	}
683 }
684 
685 #undef USE_GETPID
686 #endif
687 
688 
689 /* ----- NCBIDLL mod Borland DLL version - call Windows API to get PSP ----- */
690 #ifdef _WINDLL
691 #ifdef COMP_BOR
692 
Nlm_GetAppProcessID(void)693 NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
694 {
695   return GetCurrentPDB();
696 }
697 
698 #undef USE_GETPID
699 #endif
700 #endif
701 
702 
703 /* ----- Generic Version ----- */
704 #ifdef USE_GETPID
705 #if defined(COMP_MSC) || defined(COMP_BOR) || defined(COMP_MINGW)
706 #include <process.h>
707 #endif
708 
Nlm_GetAppProcessID(void)709 NLM_EXTERN long LIBCALL Nlm_GetAppProcessID (void)
710 {
711 	return getpid();
712 }
713 
714 #endif
715 
716 
717 
718 #ifdef TEST_MODULE_NCBIPROP
719 
720 /***********************************************************************
721  *  TEST
722  ***********************************************************************/
723 
724 #include <stdio.h>
725 #include <ncbistd.h>
726 #include <tsprintf.h>
727 
728 FILE *STDOUT = (FILE *)stdout;
729 FILE *STDERR = (FILE *)stderr;
730 
TEST__MyThread(Nlm_VoidPtr arg)731 static Nlm_VoidPtr TEST__MyThread(Nlm_VoidPtr arg)
732 {
733   Nlm_Int4 thread_no = (Nlm_Int4) arg;
734   Nlm_Int4 n, i;
735   Nlm_CharPtr str = NULL;
736   const Nlm_Char PNTR s = NULL;
737 
738   fprintf(STDOUT, "TEST__MyThread():  Thread #%ld(%ld) started\n",
739           (long)NlmThreadSelf(), (long)thread_no);
740 
741   n = (thread_no + 1) * 20;
742   str = (Nlm_CharPtr)Nlm_MemNew( n );
743   for (i = 10;  i < n;  i += 10)
744     {
745       Nlm_MemSet(str, 'a', i);
746       str[i] = '\0';
747       s = TSPrintf("TEST__MyThread() #%ld: %u %d %e %g %f %p %s +++\n",
748                    (long)thread_no, (unsigned int)i, (int)i, (double)i,
749                    (double)i, (double)i, (void *)&i, str);
750       fprintf(STDOUT, "%p  %ld: %s\n", (void *)s, Nlm_StrLen( s ), s);
751     }
752 
753   Nlm_MemFree( str );
754 
755 #if defined (OS_UNIX)
756   sleep(thread_no + 1);
757 #elif defined (WIN32)
758   Sleep(1000 * (thread_no + 1));
759 #endif
760 
761 
762   fprintf(STDOUT, "TEST__MyThread() after sleep #%ld: %p  %ld: %s\n",
763           (long)thread_no, (void *)s, Nlm_StrLen( s ), s);
764 
765   switch (thread_no % 4)
766     {
767     case 0:
768       Nlm_Message(MSG_OKC,
769                   "TEST__MyThread() #%ld: Nlm_Message(MSG_OKC)\n",
770                   (long)thread_no);
771       break;
772 
773     case 1:
774       Nlm_MsgAlert (MSG_YN,  MSG_POST,
775                     "Nlm_MsgAlert(): Caption\n",
776                     "TEST__MyThread() #%ld: Nlm_MsgAlert(MSG_YN,MSG_POST)\n",
777                     (long)thread_no);
778       break;
779 
780     case 2:
781       Nlm_ErrPost (CTX_DEBUG/*CTX_UNKNOWN*/,  1,
782                     "TEST__MyThread() #%ld: Nlm_ErrPost(CTX_DEBUG,1)\n",
783                     (long)thread_no);
784       break;
785 
786     case 3:
787       Nlm_ErrPostEx (SEV_ERROR, 1, 2,
788                     "TEST__MyThread() #%ld: Nlm_ErrPostEx(0,1,2)\n",
789                     (long)thread_no);
790       break;
791     }
792 
793   return NULL;
794 }
795 
796 
MyExitMessage(Nlm_VoidPtr arg)797 static void MyExitMessage(Nlm_VoidPtr arg)
798 {
799   fprintf(STDERR, "\n%s\n", (Nlm_CharPtr)arg);
800   Nlm_MemFree( arg );
801 }
802 
TEST__scratch(Nlm_Int4 n_threads)803 static Nlm_Int4 TEST__scratch(Nlm_Int4 n_threads)
804 {
805   Nlm_Int4 t;
806   Nlm_Int4 err_code = 0;
807   TNlmThread *threads = Nlm_MemNew(n_threads * sizeof(TNlmThread));
808 
809   for (t = 0;  t < n_threads;  t++)
810     {
811       switch (t % 2)
812         {
813         case 0:
814           threads[t] = NlmThreadCreate(TEST__MyThread, (Nlm_VoidPtr)t,
815                                        THR_RUN);
816           break;
817         case 1:
818           {
819             Nlm_CharPtr exit_message = Nlm_MemNew( 64 );
820             sprintf(exit_message, "Exit Message, thread #%d", (int)t);
821             threads[t] = NlmThreadCreateEx(
822               TEST__MyThread, (Nlm_VoidPtr)t, THR_RUN,
823               MyExitMessage, (Nlm_VoidPtr)exit_message);
824             break;
825           }
826         }
827       fprintf(STDOUT, "TEST__scratch():  Starting thread #%ld\n",
828               (long)threads[t]);
829     }
830 
831   for (t = 0;  t < n_threads;  t++)
832     {
833       Nlm_VoidPtr status;
834       err_code += NlmThreadJoin(threads[t], &status);
835       if (err_code != 0)
836         {
837           fprintf(STDOUT, "TEST__scratch():  Cannot join thread #%ld;  \
838 error code = %ld\n",
839                   (long)threads[t], (long)err_code);
840           break;
841         }
842 
843       fprintf(STDOUT, "TEST__scratch():  Thread #%ld joined;  \
844 terminated, exit status = %p\n",
845               (long)threads[t], status);
846     }
847 
848   Nlm_MemFree( threads );
849 
850   fprintf(STDOUT, "TEST__scratch():  FINISHED\n");
851   return err_code;
852 }
853 
854 
Nlm_Main(void)855 Nlm_Int2 Nlm_Main( void )
856 {
857 #if defined(WIN32) && defined(_WINDOWS)
858   (FILE *)STDOUT = freopen("stdout.w95", "w", stdout);
859   (FILE *)STDERR = freopen("stderr.w95", "w", stderr);
860   if (!STDOUT  ||  !STDERR)
861     abort();
862 #endif
863 
864   return (Nlm_Int2)TEST__scratch( 10 );
865 }
866 
867 #endif  /* TEST_MODULE_NCBIPROP */
868