summaryrefslogtreecommitdiffstats
path: root/private/crt32/misc/mtest.c
blob: 0e95bcee1b551a66a6671f00ce4f404b25f1dce3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
/***
* mtest.c - Multi-thread debug testing module
*
*	Copyright (c) 19xx-1988, Microsoft Corporation.  All rights reserved.
*
*Purpose:
*	This source contains a group of routines used for multi-thread
*	testing.  In order to use the debug flavor of these routines, you
*	MUST link special debug versions of multi-thread crt0dat.obj and
*	mlock.obj into your program.
*
*	[NOTE:	This source module is NOT included in the C runtime library;
*	it is used only for testing and must be explicitly linked into the
*	test program.]
*
*Revision History:
*	12-??-87   JCR	Module created.
*	06-17-88   JCR	Misc. bug fixes.
*	08-03-88   JCR	Use the stdio.h value of _NFILE
*	10-03-88   JCR	386: Use SYS calls, not DOS calls
*	10-04-88   JCR	386: Removed 'far' keyword
*	10-10-88   GJF	Made API names match DOSCALLS.H
*	06-08-89   JCR	New 386 _beginthread interface; also brought
*			lots of new options across from the C600 tree.
*	07-11-89   JCR	Added _POPEN_LOCK to _locknames[] array
*	07-14-89   JCR	Added _LOCKTAB_LOCK support
*	07-24-90   SBM	Removed '32' from API names
*
*******************************************************************************/

#ifdef M_I386
#ifdef STACKALLOC
#error Can't define STACKALLOC in 386 mode
#endif
#endif

#ifdef M_I386
#ifdef _DOSCREATETHREAD_
#error Currently can't define _DOSCREATETHREAD_ in 386 mode
#endif
#endif

#ifdef _DOSCREATETHREAD_
#ifndef STACKALLOC
#error Can't define _DOSCREATETHREAD_ without STACKALLOC
#endif
#endif

/*
Multi-thread core tester module.
*/
#include <malloc.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <io.h>
#include <mtest.h>
#ifdef DEBUG
#include <os2dll.h>
#include <file2.h>
#endif

/* Define FAR to be blank for the 386 and far otherwise. */

#undef	FAR
#ifdef	M_I386
#define FAR
#else
#define FAR	far
#endif

/* define stack size */
#ifdef M_I386
#define _STACKSIZE_ 8192
#else
#define _STACKSIZE_ 2048
#endif


/* routines */
#ifdef M_I386
unsigned _syscall DOSSLEEP (unsigned long) ;
#else
unsigned FAR pascal DOSSLEEP (unsigned long) ;
#endif
int main ( int argc , char * * argv ) ;
int minit(void);
void childcode ( void FAR * arg ) ;
#ifdef _DOSCREATETHREAD_
#ifndef M_I386
void childcode ( void ) ;
unsigned FAR pascal DOSCREATETHREAD (void FAR *, void FAR *, void FAR *);
#endif
#else
void childcode ( void FAR * arg ) ;
#endif
int mterm(void);

/* global data */
char Result [ _THREADMAX_ ] ;
unsigned Synchronize ;

#ifdef DEBUG
/* Array of lock names.  This order must match the declarations in
   os2dll.h and os2dll.inc. */

char *_locknames[] = {
	"** NO LOCK 0 ** ",    /* lock values are 1-based */
	"_SIGNAL_LOCK    ",
	"_IOB_SCAN_LOCK  ",
	"_TMPNAM_LOCK    ",
	"_INPUT_LOCK     ",
	"_OUTPUT_LOCK    ",
	"_CSCANF_LOCK    ",
	"_CPRINTF_LOCK   ",
	"_CONIO_LOCK     ",
	"_HEAP_LOCK      ",
	"_BHEAP_LOCK     ",
	"_TIME_LOCK      ",
	"_ENV_LOCK       ",
	"_EXIT_LOCK1     ",
	"_EXIT_LOCK2     ",
	"_THREADDATA_LOCK",
	"_POPEN_LOCK     ",
	"_SSCANF_LOCK    ",
	"_SPRINTF_LOCK   ",
#ifdef M_I386
	"_VSPRINTF_LOCK  ",
	"_LOCKTAB_LOCK   "
#else
	"_VSPRINTF_LOCK  "
#endif
	};

/* Minimal sanity check on above array. */
#ifdef M_I386

#if ((_LOCKTAB_LOCK+1)-_STREAM_LOCKS)
#error *** _locknames[] does agree with lock values ***
#endif

#else	/* !M_I386 */

#if ((_VSPRINTF_LOCK+1)-_STREAM_LOCKS)
#error *** _locknames[] does agree with lock values ***
#endif

#endif	/* M_I386 */

#endif	/* DEBUG */


/***
* main() - Main mthread testing shell
*
*Purpose:
*	Provides a general purpose shell for mthread testing.
*	The module does the following:
*
*		(1) Call minit() to perform test initialization operations.
*
*		(2) Begins one thread for each argument passed to the
*		program.  Each thread is passed the corresponding argument.
*		Thread begin location is assumed to be at routine childcode();
*
*		(3) Waits for all threads to terminate.
*
*		(4) Calls mterm() to perform termination operations.
*
*	Note that minit(), childcode(), and mterm() are routines that
*	are external to this source.  Again, this source doesn't care
*	what their purpose or operation is.
*
*	Also, childcode() is expected to conform to the following rules:
*
*		(1) The childcode should not start running until
*		the variable 'Synchronize' becomes non-zero.
*
*		(2) When the thread is done executing, it should set
*		the value Result[threadid] to a non-zero value so the
*		parent (i.e., this routine) knows it has completed.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int main ( int argc , char * * argv )
{
    int rc ;
    unsigned result = 0 ;
    long ChildCount ;
    int NumThreads ;
    int t ;
    int r ;
    int MaxThread = 0 ;
    long LoopCount ;
#ifdef THREADLOOP
    char **argvsave;
#endif
#ifndef  M_I386
    char * stackbottom ;
#endif

#ifdef DEBUG
    if ( argc > MAXTHREADID) {
	printf("*** ERROR: Mthread debugging only supports %u threads ***\n", MAXTHREADID);
	return(-1);
	}
#endif

    if ( -- argc > (_THREADMAX_-1) )
    {
	printf ( "*** Error: Too many arguments***\n" ) ;
	return (-1) ;
    }

	/* Call the initiation routine */
	
	if (minit() != 0) {
		printf("*** Error: From minit() routine ***\n");
		return(-1);
		}

	/* Bring up the threads */

    printf ( "Process ID = %u, Thread ID = %d, ArgCount= %d\r\n" ,
	getpid ( ) , * _threadid , argc ) ;

#ifndef M_I386
#ifdef STACKALLOC
	printf( "(thread stacks allocated explicilty by mtest suite)\r\n");
#else
	printf( "(thread stacks allocated implicitly via _beginthread)\r\n");
#endif
#endif

#ifdef	THREADLOOP
    /* Bring up all the threads several times (so tids get re-used) */
    argvsave=argv;
    for (threadloop=1;threadloop<=_THREADLOOPCNT_;threadloop++) {
	printf("\nThreadloop = %i\n", threadloop);
	argv=argvsave;
#endif

    NumThreads = 0 ;

    while ( * ++ argv )
    {

	ChildCount = atol ( * argv ) ;

#ifdef M_I386

	rc = _beginthread ( (void FAR *) childcode , _STACKSIZE_ ,
		(void FAR *) ChildCount ) ;

	if ( rc == -1 )

#else	/* !M_I386 */

#ifdef STACKALLOC
	if ( ! ( stackbottom = _fmalloc ( _STACKSIZE_ ) ) )
	{
	    printf ( "*** Error: Could not allocate a stack ***\n" ) ;
	    break ;
	}
#else
	stackbottom = (void FAR *) NULL;
#endif

#ifdef	_DOSCREATETHREAD_
	stackbottom+=_STACKSIZE_-16;	  /* point to end of malloc'd block */
	rc1 = DOSCREATETHREAD( (void FAR *) childcode, &rc,
		(void FAR *) stackbottom);

	if (rc1 != 0)
#else
	rc = _beginthread ( (void FAR *) childcode , (void FAR *) stackbottom ,
	    _STACKSIZE_ , (void FAR *) ChildCount ) ;

	if ( rc == -1 )
#endif

#endif	/* M_I386 */

	{
	    printf ("*** Error: Could not Spawn %d-th Thread (argument=%ld) ***\n" ,
		NumThreads + 1 , ChildCount ) ;
	    break ;
	}

	if ( rc > MaxThread )
	    MaxThread = rc ;

	printf ( "Spawning %d-th Thread %d with argument=%ld\r\n" ,
	    ++ NumThreads , rc , ChildCount ) ;
    }

    printf ( "NumThreads = %d, MaxThread = %d\r\n" ,
	NumThreads, MaxThread ) ;

	/* Let the threads begin and wait for them to term. */

    LoopCount = 0L ;

    Synchronize = 1 ;

    for ( t = 0 ; t < NumThreads ; ++ t )
    {
	r = 0 ;
	while ( ! Result [ r ] )
	{
	    DOSSLEEP ( 0L ) ;
	    if ( ++ r > MaxThread )
	    {
		r = 0 ;
		printf ( "%ld\r" , LoopCount ++ ) ;
	    }
	}

	printf ( "%d: Thread %d Done.\r\n" , t , r) ;

	Result [ r ] = '\0' ;
    }
#ifdef	THREADLOOP
    }
#endif

	/* All the threads have completed.  Call the term routine and return. */

	if (mterm() != 0) {
		printf("*** Error: From mterm() routine ***\n");
		return(-1);
		}

	printf("\nDone!\n");
    return 0 ;
}


#ifdef DEBUG

/***
* Debug Print Routines - Display useful mthread lock data
*
*Purpose:
*	The following routines extract information from the multi-thread
*	debug data bases and print them out in various formats.
*	In order to use these routines, you MUST link special debug
*	versions of multi-thread crt0dat.obj and mlock.obj into your program.
*
*Entry:
*
*Exit:
*	0 = success
*	0! = failure
*
*Exceptions:
*
*******************************************************************************/

/*--- Print lock routine ---*/
int printlock(int locknum)
{
	int retval;

#ifdef	_INIT_LOCKS
	if (locknum >= _STREAM_LOCKS)
		printf("\nValidating lock #%i (%s):\n",locknum, "not a 'single lock'");
	else
		printf("\nValidating lock #%i: %s\n",locknum, _locknames[locknum]);
#else
	printf("\nValidating lock #%i (%s, %s):\n",
		locknum,
		(locknum >= _STREAM_LOCKS ?
			"not a 'single' lock" : _locknames[locknum]),
		(_lock_exist(locknum) ?
			"initialized" : "NOT initialized")
		);
#endif

	retval = _check_lock(locknum);
	printf("\tLock count = %u\r\n", _lock_cnt(locknum));
	printf("\tCollision count = %u\r\n", _collide_cnt(locknum));

	if (retval != 0)
		printf("\t*** ERROR: Checking lock ***\n");

	return(retval);
}


/*--- Printf single locks ---*/
int print_single_locks(void)
{
	int locknum;
	int retval=0;
	int lockval;

	printf("\n--- Single Locks ---\n");

#ifdef _INIT_LOCKS
	printf("\t\t\t\tlock count\tcollide count\n");
	for (locknum=1;locknum<_STREAM_LOCKS;locknum++) {
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("#%i / %s\t\t%u\t\t%u\t%s\n",
		    locknum, _locknames[locknum], _lock_cnt(locknum),
		    _collide_cnt(locknum), (lockval ? "*LOCK ERROR*" : "") );
		}
#else
	printf("\t\t\t\tlock count\tcollide count\texists?\n");
	for (locknum=1;locknum<_STREAM_LOCKS;locknum++) {
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("#%i / %s\t\t%u\t\t%u\t\t%s\t%s\n",
		    locknum, _locknames[locknum], _lock_cnt(locknum),
		    _collide_cnt(locknum),
		    (_lock_exist(locknum) ? "YES" : "NO"),
		    (lockval ? "*LOCK ERROR*" : "") );
		}
#endif

	return(retval);
}


/*--- Print all stdio locks ---*/
int print_stdio_locks(void)
{
	int i;
	int locknum;
	int retval=0;
	int lockval;

	printf("\n--- Stdio Locks ---\n");

#ifdef _INIT_LOCKS
	printf("stream\t\tlock count\tcollide count\n");
	for (i=0;i<_NFILE;i++) {
		locknum = _stream_locknum(i);
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("%i\t\t%u\t\t%u\t%s\n",
			i, _lock_cnt(locknum), _collide_cnt(locknum),
			(lockval ? "*LOCK ERROR*" : "") );
		}
#else
	printf("stream\t\tlock count\tcollide count\texists?\n");
	for (i=0;i<_NFILE;i++) {
		locknum = _stream_locknum(i);
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("%i\t\t%u\t\t%u\t\t%s\t%s\n",
			i, _lock_cnt(locknum), _collide_cnt(locknum),
			(_lock_exist(locknum) ? "YES" : "NO"),
			(lockval ? "*LOCK ERROR*" : "") );
		}
#endif

	return(retval);
}


/*--- Print all lowio locks ---*/
int print_lowio_locks(void)
{
	int i;
	int locknum;
	int retval=0;
	int lockval;

	printf("\n--- Lowio locks ---\n");

#ifdef _INIT_LOCKS
	printf("fh\t\tlock count\tcollide count\n");
	for (i=0;i<_NFILE;i++) {
		locknum = _fh_locknum(i);
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("%i\t\t%u\t\t%u\t%s\n",
			i, _lock_cnt(locknum), _collide_cnt(locknum),
			(lockval ? "*LOCK ERROR*" : "") );
		}
#else
	printf("fh\t\tlock count\tcollide count\texists?\n");
	for (i=0;i<_NFILE;i++) {
		locknum = _fh_locknum(i);
		if (lockval = (_check_lock(locknum) != 0))
			retval++;
		printf("%i\t\t%u\t\t%u\t\t%s\t%s\n",
			i, _lock_cnt(locknum), _collide_cnt(locknum),
			(_lock_exist(locknum) ? "YES" : "NO"),
			(lockval ? "*LOCK ERROR*" : "") );
		}
#endif

	return(retval);
}


/*--- Print all I/O locks ---*/
int print_iolocks(void)
{
	int retval=0;

	retval += print_stdio_locks();
	retval += print_lowio_locks();

	return(retval);
}


/*--- Print all Locks ---*/
int print_locks(void)
{
	int retval=0;

	retval += print_single_locks();
	retval += print_iolocks();

	return(retval);
}

#endif