|
|
1.1 root 1: /*==============================================================*\
2: * Semaph.c - routines for demonstrating semaphore API. *
3: * *
4: * Created 1990, Microsoft, IBM Corp. *
5: *--------------------------------------------------------------*
6: * This module contains code to demonstrate the use of *
7: * semaphores to control access to a resource shared by *
8: * multiple threads. Event semaphores are used to signal a *
9: * thread is to give up a resource. A Mutex semaphore is used *
10: * to provide exclusive access to the resource. A mux *
11: * semaphore provides a method to check multiple event *
12: * semaphores. *
13: * *
14: * *
15: *--------------------------------------------------------------*
16: * *
17: * This source file contains the following functions: *
18: * *
19: * VOID SemError(PSZ,USHORT); *
20: * USHORT CreateAllSems(VOID); *
21: * VOID StartSemExample(VOID); *
22: * VOID ThreadConsumer(ULONG); *
23: * VOID SignalUserEvent(PUSHORT pfAutoMode); *
24: * USHORT SetAutoMode(VOID); *
25: * VOID RunAuto(VOID); *
26: * VOID StopSemaphore(VOID); *
27: * *
28: * *
29: \*==============================================================*/
30:
31: /*--------------------------------------------------------------*\
32: * Include files, macros, defined constants, and externs *
33: \*--------------------------------------------------------------*/
34:
35: #define LINT_ARGS
36: #define INCL_PM
37: #define INCL_BASE
38:
39: #include <os2.h>
40: #include <stdio.h>
41: #include <string.h>
42: #include <stdlib.h>
43: #include <dos.h>
44: #include "sem_pnt.h"
45: #include "semaph.h"
46: #include "sem_main.h"
47: #include "sem_dlg.h"
48: #include "sem_xtrn.h"
49:
50: #define STACKSIZE 0X10000L
51: #define TIMEOUTPERIOD 33L
52: #define EVENTSEM 0
53: #define STOPSEM -1
54: #define BASETEN 10
55:
56: /*--------------------------------------------------------------*\
57: * Global variables *
58: \*--------------------------------------------------------------*/
59:
60:
61: /* Global variables for semaphore handles. Used only in this file. */
62:
63: HEV hevStop, hevStopAuto, hevItem;
64: HMTX hmtxOwnResource;
65: HMUX hmuxResource; //, hmuxReady;
66:
67: /* Global variables for number of consumer threads & thread ID's */
68:
69: USHORT usConsumerThreadsCreated;
70: TID tidAutoThread;
71: THRDATA thrConsumers[MAXUSERS];
72: ULONG ulTimeout = TIMEOUTPERIOD;
73:
74: static VOID MyMove (USHORT usMyID, ULONG ulUser);
75:
76: /****************************************************************\
77: * Procedure to print error messages to screen in a Message Box.*
78: *--------------------------------------------------------------*
79: * *
80: * Name: SemError(pszAPIName,usErrorCode) *
81: * *
82: * Purpose: Used to print name of API and error number when *
83: * a return code other than 0 is returned from an *
84: * API call. *
85: * *
86: * Usage: Called by all procedures in this file whenever *
87: * an API call fails. *
88: * *
89: * Method: The error number is converted to a string. String *
90: * functions are used to build the error message. *
91: * The message is printed in a Message Box. *
92: * *
93: * NOTE: This function is called by multiple threads, *
94: * therefore, only re-entrant functions can be used. *
95: * Note that string is build according to American *
96: * English conventions using hard-coded strings, and *
97: * so portability to other languages would require a *
98: * more flexible approach. *
99: * *
100: * Returns: none. *
101: * *
102: \****************************************************************/
103:
104:
105: VOID SemError(PSZ pszAPIName,USHORT usErrorCode)
106: {
107: char acMessage[100],acErrorNumber[10];
108:
109: itoa(usErrorCode,acErrorNumber,BASETEN);
110: strcpy(acMessage,pszAPIName);
111: strcat(acMessage,": error # ");
112: strcat(acMessage,acErrorNumber);
113:
114: WinMessageBox(HWND_DESKTOP,
115: hwndMain,
116: acMessage,
117: szAppName,
118: 1,
119: MB_OK);
120: }
121:
122:
123: /****************************************************************\
124: * Routine to create semaphores used in this file. *
125: *--------------------------------------------------------------*
126: * *
127: * Name: CreateAllSems(VOID) *
128: * *
129: * Purpose: Create semaphores needed by the consumer threads. *
130: * Checks return codes from semaphore creation. *
131: * *
132: * Usage: Called by StartSemExample. *
133: * *
134: * Method: Semaphores are all anonymous private semaphores *
135: * since the semaphores are used by threads in the *
136: * same process. *
137: * *
138: * Returns: 0 if all semaphores are created successfully. *
139: * Otherwise returns error code for first create *
140: * semaphore API to fail. *
141: * *
142: \****************************************************************/
143:
144: USHORT CreateAllSems(VOID)
145: {
146: USHORT rc;
147: SEMRECORD asr[MAXRESOURCES];
148: INT i;
149:
150: rc = DosCreateMutexSem((PSZ)NULL,&hmtxOwnResource,NULL,FALSE);
151: if (rc)
152: {
153: SemError("DosCreateMutexSem",rc);
154: return(rc);
155: }
156:
157: for (i = 0; i < MAXRESOURCES; i++) {
158: if (rc = DosCreateEventSem((PSZ)NULL, &aSquares[i].hev, NULL, FALSE))
159: {
160: SemError("DosCreateEventSem",rc);
161: return(rc);
162: }
163: else
164: {
165: asr[i].ulUser = i;
166: asr[i].hsemCur = (PVOID) aSquares[i].hev;
167: }
168: }
169:
170: /* this muxwait semaphore contains all of the event semaphores
171: * created in the loop above.
172: */
173: if (rc = DosCreateMuxWaitSem((PSZ)NULL,&hmuxResource,MAXRESOURCES,asr,
174: DCMW_WAIT_ANY))
175: {
176: SemError("DosCreateMuxWaitSem",rc);
177: return(rc);
178: }
179: if (rc = DosCreateEventSem((PSZ)NULL,&hevStop,NULL,FALSE))
180: {
181: SemError("DosCreateEventSem",rc);
182: return(rc);
183: }
184: return(rc);
185: }
186:
187: /****************************************************************\
188: * Routine to start semaphore example *
189: *--------------------------------------------------------------*
190: * *
191: * Name: StartSemExample(VOID) *
192: * *
193: * Purpose: Calls routines to create semaphores and draw *
194: * resources. Creates consumer threads. *
195: * *
196: * Usage: Called in file usercmd.c when the user selects *
197: * start from the semaphore menu. *
198: * *
199: * Method: Uses routines in paint.c to draw consumers and *
200: * resources. This is done by creating a paint *
201: * message, not calling the draw routines directly. *
202: * *
203: * Returns: TRUE if start succeeds, FALSE if start fails *
204: * *
205: \****************************************************************/
206:
207: INT StartSemExample(VOID)
208: {
209: TID tid;
210: USHORT rc;
211: INT i;
212: FONTMETRICS fntmet;
213: HPS hps;
214: SWP swp;
215:
216: InitSemaphExample();
217:
218: rc = CreateAllSems();
219:
220: if (rc)
221: return FALSE;
222:
223: /* Create consumer threads. Note that values can be passed to
224: * threads in OS/2 2.0. We pass the ordinal number of the child
225: * to each child.
226: */
227:
228: for (usConsumerThreadsCreated = 0;
229: usConsumerThreadsCreated < cNumUsers; usConsumerThreadsCreated++)
230: {
231: rc = DosCreateThread(&tid,ThreadConsumer,usConsumerThreadsCreated,1,
232: STACKSIZE);
233: if (rc)
234: {
235: SemError("DosCreateThread",rc);
236: return FALSE;
237: }
238: else
239: {
240: thrConsumers[usConsumerThreadsCreated].tid = tid;
241: thrConsumers[usConsumerThreadsCreated].lHits = 0L;
242: }
243: }
244:
245: for (i = 0; i < cNumUsers; i++) {
246: DosResumeThread (thrConsumers[i].tid);
247: }
248:
249: if (hps = WinGetPS (hwndMain))
250: {
251: GpiQueryFontMetrics (hps, (LONG) sizeof fntmet, &fntmet);
252: WinQueryWindowPos (hwndMain, &swp);
253: SetRectPositions((SHORT)swp.cx, (SHORT)swp.cy, (SHORT) fntmet.lMaxBaselineExt, (SHORT) fntmet.lMaxDescender);
254: DrawRects (hps);
255: WinReleasePS (hps);
256: }
257: return TRUE;
258: }
259:
260:
261: /****************************************************************\
262: * Routine to signal consumer to release resource. *
263: *--------------------------------------------------------------*
264: * *
265: * Name: SignalUserEvent(pfAutoMode) *
266: * *
267: * Purpose: Posts user event semaphore to signal thread to *
268: * release resource. Also posts event to stop *
269: * Auto mode if *pfAutoMode is true. *
270: * *
271: * Usage: Called in file usercmd.c when the user selects *
272: * Event from the semaphore menu. *
273: * *
274: * Method: Turns off Auto mode, if present by posting auto *
275: * semaphore. User event is then posted. *
276: * *
277: * Returns: *
278: * *
279: \****************************************************************/
280:
281: VOID SignalUserEvent(PUSHORT pfAutoMode)
282: {
283: USHORT rc;
284:
285: /* If sample is in auto mode turn auto mode off. */
286:
287: if (*pfAutoMode)
288: {
289: rc = DosPostEventSem(hevStopAuto);
290: if (rc)
291: {
292: SemError("DosPostEventSem Stop Auto",rc);
293: }
294: /* Wait for auto mode thread to die, so we don't end up with multiple *
295: * copies of it later. */
296:
297: rc = DosWaitThread(&tidAutoThread,0L);
298: if (rc)
299: {
300: SemError("DosWaitThread",rc);
301: }
302: *pfAutoMode = FALSE;
303: DosCloseEventSem (hevStopAuto);
304: }
305:
306: /* If Auto mode haas already posted the event this is OK
307: so we will not check error codes here. */
308:
309: DosPostEventSem(aSquares[rand() % MAXRESOURCES].hev);
310: }
311:
312:
313: /****************************************************************\
314: * Routine to start Auto mode. *
315: *--------------------------------------------------------------*
316: * *
317: * Name: SetAutoMode(VOID) *
318: * *
319: * Purpose: Creates thread and semaphore needed to run auto *
320: * mode. *
321: * *
322: * Usage: Called in file usercmd.c when the user selects *
323: * Auto from the semaphore menu. *
324: * *
325: * Returns: NO_ERROR on success, else error from api call. *
326: * *
327: \****************************************************************/
328:
329: USHORT SetAutoMode()
330: {
331: USHORT rc;
332:
333: rc = DosCreateEventSem((PSZ)NULL,&hevStopAuto,NULL,FALSE);
334: if (rc)
335: {
336: SemError("DosCreateEventSem",rc);
337: return(rc);
338: }
339:
340: rc = DosCreateThread(&tidAutoThread,RunAuto,0L,0L,STACKSIZE);
341: if (rc)
342: {
343: DosCloseEventSem (hevStopAuto); /* close semaphore created above */
344: SemError("DosCreateThread",rc);
345: return(rc);
346: }
347:
348: return(NO_ERROR);
349: }
350:
351:
352: /****************************************************************\
353: * Routine to run Auto mode. *
354: *--------------------------------------------------------------*
355: * *
356: * Name: RunAuto(VOID) *
357: * *
358: * Purpose: Posts user event at fixed time interval to signal *
359: * consumers to release resource. *
360: * *
361: * Usage: Thread created by SetAutoMode. *
362: * *
363: * Method: Kills itself when StopAutoMode semaphore is *
364: * posted. *
365: * *
366: * Returns: *
367: * *
368: \****************************************************************/
369:
370: VOID RunAuto(VOID)
371: {
372: USHORT rcWait;
373: HAB habLocal;
374: HMQ hmqLocal;
375: INT i;
376:
377: /* Need a message queue for any thread that wants to print messages. */
378:
379: habLocal = WinInitialize(NULL);
380: hmqLocal = WinCreateMsgQueue(habLocal,NULL);
381:
382: /* while stop auto semaphore not posted, post user event semaphore. */
383:
384: /* Don't check return code from DosPostEventSem(hevUserEvent)
385: since we just want the resource to change hands. We don't
386: care if it changes hands exactly every time it goes through
387: the loop.
388:
389: The event may already be posted if the system is busy and
390: the resource threads don't finish with it fast enough.
391: This is not a problem in this case.
392: */
393:
394: do {
395: /* if ulTimeout is zero, still waitevent for 1 msec to
396: * force yielding of CPU.
397: */
398: rcWait = DosWaitEventSem(hevStopAuto, max (ulTimeout, 1));
399: if (rcWait == ERROR_TIMEOUT) {
400: i = rand () % MAXRESOURCES; /* generate it */
401: DosPostEventSem (aSquares[i].hev);
402: }
403: } while (rcWait == ERROR_TIMEOUT);
404:
405: /* If there was an error, print a message */
406:
407: if (rcWait) {
408: SemError("DosWaitEventSem",rcWait);
409: }
410:
411: WinDestroyMsgQueue (hmqLocal);
412: WinTerminate (habLocal);
413: DosExit(EXIT_THREAD,0);
414: }
415:
416:
417: /****************************************************************
418: * Routine to stop semaphore example.
419: *---------------------------------------------------------------
420: *
421: * Name: BeginStop(pfAutoMode)
422: *
423: * Purpose: Posts stop event semaphore to signal threads to
424: * die. Also posts event to stop Auto mode if
425: * necessary. Then waits for threads to complete,
426: * Creates thread StopSemaphore which posts stop event
427: * and waits for child threads.
428: *
429: * Usage: Called in file usercmd.c when the user selects
430: * Stop from the semaphore menu.
431: *
432: * Method: Execs thread to do waits so that message thread
433: * doesn't hang.
434: * Returns:
435: *
436: \****************************************************************/
437: VOID
438: BeginStop (PUSHORT pfAutoMode)
439: {
440: USHORT rc;
441: TID tidLocal;
442:
443: rc = DosPostEventSem(hevStop);
444: if (rc)
445: {
446: SemError("DosPostEventSem",rc);
447: return;
448: }
449:
450: if (*pfAutoMode)
451: {
452: rc = DosPostEventSem(hevStopAuto);
453: if (rc)
454: {
455: SemError("DosPostEventSem",rc);
456: }
457: }
458:
459: rc = DosCreateThread(&tidLocal,StopSemaphore,(LONG)pfAutoMode,0,STACKSIZE);
460: if (rc)
461: {
462: SemError("DosCreateThread",rc);
463: }
464: }
465:
466: /****************************************************************
467: * Routine to really stop semaphore example.
468: *---------------------------------------------------------------
469: *
470: * Name: StopSemaphore(pfAutoMode)
471: *
472: * Purpose: Waits for threads to complete,
473: * Sends message to message thread to indicate this
474: * has occurred, and exits.
475: *
476: * Usage: Exec'd from BeginStop when user selects Stop from
477: * Semaphore menu.
478: *
479: * Method: Turns off Auto mode, if present by posting auto
480: * semaphore. Then stop event is posted. Waits
481: * for threads to die.
482: * Returns:
483: *
484: \****************************************************************/
485:
486:
487: VOID StopSemaphore(PUSHORT pfAutoMode)
488: {
489: USHORT rc,usCount, i;
490:
491:
492: if (*pfAutoMode)
493: {
494: rc = DosWaitThread(&tidAutoThread,0L);
495: if (rc && (rc != ERROR_INVALID_THREADID))
496: {
497: SemError("DosWaitThread",rc);
498: }
499: *pfAutoMode = FALSE;
500: }
501:
502:
503: /* Wait for usConsumer threads to die. Order of death not important. */
504:
505: for (usCount = 0; usCount < usConsumerThreadsCreated; usCount++)
506: {
507: rc = DosWaitThread(&thrConsumers[usCount].tid,0L);
508:
509: /* rc is ERROR_INVALID_THREADID the thread is already dead. This *\
510: \* is OK and not a error. */
511:
512: if (rc && (rc != ERROR_INVALID_THREADID))
513: {
514: SemError("DosWaitThread",rc);
515: }
516: }
517:
518: /* Threads dead so we don't need semaphores any more. */
519:
520: DosCloseEventSem(hevStopAuto);
521: DosCloseEventSem(hevStop);
522: DosCloseMutexSem(hmtxOwnResource);
523: for (i = 0; i < MAXRESOURCES; i++) {
524: DosCloseEventSem(aSquares[i].hev);
525: }
526: DosCloseMuxWaitSem (hmuxResource );
527: WinPostMsg (hwndMain, WM_COMMAND, (MPARAM)IDM_STOPFINISHED, (MPARAM)NULL);
528: DosExit (EXIT_THREAD, 0);
529: }
530:
531: /****************************************************************\
532: * Routine for consumer threads. *
533: *--------------------------------------------------------------*
534: * *
535: * Name: ThreadConsumer(ULONG) *
536: * *
537: * Purpose: There are NUMUSERS copies of this thread to act *
538: * as consumers of the resource. The thread waits *
539: * for exclusive access to the resource and colors it*
540: * *
541: * Usage: Threads created by StartSemExample. *
542: * *
543: * Method: Waits for mutex to gain ownership of resource. *
544: * Releases resource when user event. Dies when *
545: * Stop event. *
546: * Returns: *
547: * *
548: \****************************************************************/
549:
550: VOID ThreadConsumer(USHORT usMyID)
551: {
552: ULONG ulPostCnt;
553: ULONG ulUser;
554: USHORT rc;
555: HAB hab;
556: HMQ hmq;
557:
558: /* Need a message queue for any thread that wants to print messages. */
559: hab = WinInitialize(NULL);
560: hmq = WinCreateMsgQueue(hab,NULL);
561:
562: /* while the user has not selected stop ... */
563: while ((DosWaitEventSem(hevStop,SEM_IMMEDIATE_RETURN)) == ERROR_TIMEOUT) {
564: /* Wait for exclusive ownership of resource */
565:
566: DosRequestMutexSem(hmtxOwnResource,SEM_INDEFINITE_WAIT);
567:
568: /* the following check is necessary because the stop semaphore
569: * may have been posted while we were waiting on the mutex
570: */
571: if (DosWaitEventSem(hevStop, SEM_IMMEDIATE_RETURN) == ERROR_TIMEOUT) {
572: /* an item is ready, which one?
573: * don't wait forever because there is
574: * a possibility that the stop semaphore
575: * was posted and that no more resource
576: * is forthcoming. we wait twice as long
577: * as we think is necessary.
578: */
579: if (!DosWaitMuxWaitSem (hmuxResource, max (ulTimeout << 1, TIMEOUTPERIOD), &ulUser)) {
580: MyMove (usMyID, ulUser);
581: DosResetEventSem(aSquares[ulUser].hev, &ulPostCnt);
582: }
583: }
584:
585: /*Let some other thread have resource. */
586: if (rc = DosReleaseMutexSem(hmtxOwnResource)) {
587: SemError("DosReleaseMutexSem",rc);
588: }
589: }
590:
591: /* hevStop was posted, kill this thread */
592: WinDestroyMsgQueue (hmq);
593: WinTerminate (hab);
594: DosExit(EXIT_THREAD,0);
595: }
596:
597:
598: /****************************************************************\
599: * called from ThreadConsumer to make and register move
600: *--------------------------------------------------------------
601: *
602: * Name: MyMove (usMyID, ulUser)
603: * USHORT usMyID; id of caller
604: * ULONG ulUser; number of the square to "hit"
605: *
606: * Purpose: color a square on the game board 'my' color.
607: * update display of number of hits for 'my' thread.
608: *
609: * Usage: called only from ThreadConsumer
610: *
611: * Method:
612: *
613: * Returns: none
614: *
615: \****************************************************************/
616: static VOID
617: MyMove (USHORT usMyID, ULONG ulUser)
618: {
619: if (aSquares [ulUser].usOwner != usMyID) {
620: aSquares[ulUser].usOwner = usMyID;
621: thrConsumers[usMyID].lHits++;
622: DrawResource (&aSquares[ulUser].rcl, colors [usMyID]);
623: DrawStats (usMyID);
624: }
625: }
626:
627:
628: /****************************************************************\
629: * Routine to init game board *
630: *--------------------------------------------------------------*
631: * *
632: * Name: InitSemaphExample(VOID) *
633: * *
634: * Purpose: Initializes game board unowned, sets semaphores *
635: * to 0. *
636: * Usage: Called from StartSemExample in semaph.c when *
637: * user selects start from the semaphore menu. *
638: * Method: *
639: * *
640: * Returns: none *
641: * *
642: \****************************************************************/
643: VOID InitSemaphExample(VOID)
644: {
645: int i;
646:
647: for (i = 0; i < MAXRESOURCES; i++) {
648: aSquares[i].hev = 0;
649: aSquares[i].usOwner = UNOWNED;
650: }
651: hevStopAuto = 0L;
652: hevStop = 0L;
653: hmtxOwnResource = 0L;
654: hmuxResource = 0L;
655: }
656:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.