File:  [OS/2 SDKs] / os232sdk / toolkt20 / c / samples / semaph / semaph.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:26:30 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: os2sdk-1990, HEAD
Microsoft OS/2 SDK 2.0 05-30-1990

/*==============================================================*\
 *  Semaph.c - routines for demonstrating semaphore API.        *
 *                                                              *
 *      Created 1990, Microsoft, IBM Corp.                      *
 *--------------------------------------------------------------*
 *  This module contains code to demonstrate the use of         *
 *  semaphores to control access to a resource shared by        *
 *  multiple threads. Event semaphores are used to signal a     *
 *  thread is to give up a resource. A Mutex semaphore is used  *
 *  to provide exclusive access to the resource. A mux          *
 *  semaphore provides a method to check multiple event         *
 *  semaphores.                                                 *
 *                                                              *
 *                                                              *
 *--------------------------------------------------------------*
 *                                                              *
 *  This source file contains the following functions:          *
 *                                                              *
 *        VOID SemError(PSZ,USHORT);                            *
 *        USHORT CreateAllSems(VOID);                           *
 *        VOID StartSemExample(VOID);                           *
 *        VOID ThreadConsumer(ULONG);                           *
 *        VOID SignalUserEvent(PUSHORT pfAutoMode);             *
 *        USHORT SetAutoMode(VOID);                             *
 *        VOID RunAuto(VOID);                                   *
 *        VOID StopSemaphore(VOID);                             *
 *                                                              *
 *                                                              *
\*==============================================================*/

/*--------------------------------------------------------------*\
 *  Include files, macros, defined constants, and externs       *
\*--------------------------------------------------------------*/

#define  LINT_ARGS
#define  INCL_PM
#define INCL_BASE

#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dos.h>
#include "sem_pnt.h"
#include "semaph.h"
#include "sem_main.h"
#include "sem_dlg.h"
#include "sem_xtrn.h"

#define STACKSIZE 0X10000L
#define TIMEOUTPERIOD 33L
#define EVENTSEM 0
#define STOPSEM -1
#define BASETEN 10

/*--------------------------------------------------------------*\
 *  Global variables                                            *
\*--------------------------------------------------------------*/


/* Global variables for semaphore handles.  Used only in this file. */

HEV  hevStop, hevStopAuto, hevItem;
HMTX hmtxOwnResource;
HMUX hmuxResource;  //, hmuxReady;

/* Global variables for number of consumer threads & thread ID's */

USHORT usConsumerThreadsCreated;
TID tidAutoThread;
THRDATA thrConsumers[MAXUSERS];
ULONG ulTimeout = TIMEOUTPERIOD;

static VOID MyMove (USHORT usMyID, ULONG ulUser);

/****************************************************************\
 * Procedure to print error messages to screen in a Message Box.*
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:    SemError(pszAPIName,usErrorCode)                   *
 *                                                              *
 *  Purpose: Used to print name of API and error number when	*
 *           a return code other than 0 is returned from an     *
 *           API call.                                          *
 *                                                              *
 *  Usage:   Called by all procedures in this file whenever     *
 *           an API call fails.                                 *
 *                                                              *        
 *  Method:  The error number is converted to a string.  String * 
 *           functions are used to build the error message.     *
 *           The message is printed in a Message Box.           *
 *                                                              *
 *  NOTE:    This function is called by multiple threads,       *
 *           therefore, only re-entrant functions can be used.  *
 *           Note that string is build according to American    *
 *           English conventions using hard-coded strings, and  *
 *           so portability to other languages would require a  *
 *           more flexible approach.                            *
 *                                                              *
 *  Returns: none.                                              *
 *                                                              *
\****************************************************************/


VOID SemError(PSZ pszAPIName,USHORT usErrorCode)
{
    char acMessage[100],acErrorNumber[10];

    itoa(usErrorCode,acErrorNumber,BASETEN);
    strcpy(acMessage,pszAPIName);
    strcat(acMessage,": error # ");
    strcat(acMessage,acErrorNumber);

    WinMessageBox(HWND_DESKTOP,
		  hwndMain,
		  acMessage,
		  szAppName,
		  1,
		  MB_OK);
}


/****************************************************************\
 * Routine to create  semaphores used in this file.             *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:    CreateAllSems(VOID)                                *
 *                                                              *
 *  Purpose: Create semaphores needed by the consumer threads.  *
 *           Checks return codes from semaphore creation.       *
 *                                                              *
 *  Usage:   Called by StartSemExample.                         *
 *                                                              *
 *  Method:  Semaphores are all anonymous private semaphores    *
 *           since the semaphores are used by threads in the    *
 *           same process.                                      *
 *                                                              *
 *  Returns: 0 if all semaphores are created successfully.      *
 *           Otherwise returns error code for first create      *
 *           semaphore API to fail.                             *
 *                                                              *
\****************************************************************/

USHORT CreateAllSems(VOID)
{   
    USHORT      rc;
    SEMRECORD   asr[MAXRESOURCES];
    INT         i;

    rc =  DosCreateMutexSem((PSZ)NULL,&hmtxOwnResource,NULL,FALSE);
    if (rc)
    {
        SemError("DosCreateMutexSem",rc);
        return(rc);
    }
    
    for (i = 0; i < MAXRESOURCES; i++) {
        if (rc = DosCreateEventSem((PSZ)NULL, &aSquares[i].hev, NULL, FALSE))
        {
            SemError("DosCreateEventSem",rc);
            return(rc);
        }
        else
        {
            asr[i].ulUser = i;
            asr[i].hsemCur = (PVOID) aSquares[i].hev;
        }
    }

    /* this muxwait semaphore contains all of the event semaphores
     * created in the loop above.
     */
    if (rc = DosCreateMuxWaitSem((PSZ)NULL,&hmuxResource,MAXRESOURCES,asr,
     DCMW_WAIT_ANY))
    {
        SemError("DosCreateMuxWaitSem",rc);
        return(rc);
    }
    if (rc = DosCreateEventSem((PSZ)NULL,&hevStop,NULL,FALSE))
    {
        SemError("DosCreateEventSem",rc);
        return(rc);
    }				      
    return(rc);
}

/****************************************************************\
 * Routine to start semaphore example                           *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     StartSemExample(VOID)                             *
 *                                                              *
 *  Purpose:  Calls routines to create semaphores and draw      *
 *            resources.  Creates consumer threads.             *
 *                                                              *
 *  Usage:    Called in file usercmd.c when the user selects    *
 *            start from the semaphore menu.                    *
 *                                                              *
 *  Method:   Uses routines in paint.c to draw consumers and    *
 *            resources.  This is done by creating a paint      *
 *            message, not calling the draw routines directly.  *
 *                                                              *
 *  Returns:  TRUE if start succeeds, FALSE if start fails      *
 *                                                              *
\****************************************************************/

INT   StartSemExample(VOID)
{
    TID tid;
    USHORT rc;
    INT i;
    FONTMETRICS fntmet;
    HPS         hps;
    SWP         swp;

    InitSemaphExample();

    rc = CreateAllSems();

    if (rc)
        return FALSE;
    
    /* Create consumer threads. Note that values can be passed to 
     * threads in OS/2 2.0. We pass the ordinal number of the child
     * to each child.
     */

    for (usConsumerThreadsCreated = 0;
     usConsumerThreadsCreated < cNumUsers; usConsumerThreadsCreated++)
    {
        rc = DosCreateThread(&tid,ThreadConsumer,usConsumerThreadsCreated,1,
                             STACKSIZE);
        if (rc)
        {
            SemError("DosCreateThread",rc);
            return FALSE;
        }
        else
        {
            thrConsumers[usConsumerThreadsCreated].tid = tid;
            thrConsumers[usConsumerThreadsCreated].lHits = 0L;
        }
    }
    
    for (i = 0; i < cNumUsers; i++) {
        DosResumeThread (thrConsumers[i].tid);
    }
    
    if (hps = WinGetPS (hwndMain))
    {
        GpiQueryFontMetrics (hps, (LONG) sizeof fntmet, &fntmet);
        WinQueryWindowPos (hwndMain, &swp);
        SetRectPositions((SHORT)swp.cx, (SHORT)swp.cy, (SHORT) fntmet.lMaxBaselineExt, (SHORT) fntmet.lMaxDescender);
        DrawRects (hps);
        WinReleasePS (hps);
    }
    return TRUE;
}
			   

/****************************************************************\
 * Routine to signal consumer to release resource.              *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     SignalUserEvent(pfAutoMode)                       *
 *                                                              *
 *  Purpose:  Posts user event semaphore to signal thread to    *
 *            release resource.  Also posts event to stop       *
 *            Auto mode if *pfAutoMode is true.                 *
 *                                                              *
 *  Usage:    Called in file usercmd.c when the user selects    *
 *            Event from the semaphore menu.                    *
 *                                                              *
 *  Method:   Turns off Auto mode, if present by posting auto   *
 *            semaphore.  User event is then posted.            *
 *                                                              *
 *  Returns:                                                    *
 *                                                              *
\****************************************************************/

VOID SignalUserEvent(PUSHORT pfAutoMode)
{
  USHORT rc;
	  
  /* If sample is in auto mode turn auto mode off. */

  if (*pfAutoMode)
  {
      rc = DosPostEventSem(hevStopAuto);
      if (rc)
      {
         SemError("DosPostEventSem Stop Auto",rc);
      }
      /* Wait for auto mode thread to die, so we don't end up with multiple *
       * copies of it later.    */

      rc = DosWaitThread(&tidAutoThread,0L);
      if (rc)
      {
         SemError("DosWaitThread",rc);
      }
      *pfAutoMode = FALSE;
      DosCloseEventSem (hevStopAuto);
  }

  /* If Auto mode haas already posted the event this is OK
     so we will not check error codes here. */

  DosPostEventSem(aSquares[rand() % MAXRESOURCES].hev);
}


/****************************************************************\
 * Routine to start Auto mode.                                  *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     SetAutoMode(VOID)                                 *
 *                                                              *
 *  Purpose:  Creates thread and semaphore needed to run auto   *
 *            mode.                                         	*
 *                                                              *
 *  Usage:    Called in file usercmd.c when the user selects    *
 *            Auto from the semaphore menu.                     *
 *                                                              *
 *  Returns:  NO_ERROR on success, else error from api call.    *
 *                                                              *
\****************************************************************/

USHORT SetAutoMode()
{
    USHORT rc;

    rc = DosCreateEventSem((PSZ)NULL,&hevStopAuto,NULL,FALSE);
    if (rc)
    {
        SemError("DosCreateEventSem",rc);
        return(rc);
    }

    rc = DosCreateThread(&tidAutoThread,RunAuto,0L,0L,STACKSIZE);
    if (rc)
    {
        DosCloseEventSem (hevStopAuto); /* close semaphore created above */
        SemError("DosCreateThread",rc);
        return(rc);
    }
    
    return(NO_ERROR);
}


/****************************************************************\
 * Routine to run  Auto mode.                                   *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     RunAuto(VOID)                                     *
 *                                                              *
 *  Purpose:  Posts user event at fixed time interval to signal *
 *            consumers to release resource.                    *
 *                                                              *
 *  Usage:    Thread created by SetAutoMode.                    *
 *                                                              *
 *  Method:   Kills itself when StopAutoMode semaphore is       *
 *            posted.                                           *
 *                                                              *
 *  Returns:                                                    *
 *                                                              *
\****************************************************************/

VOID RunAuto(VOID)
{
    USHORT rcWait;
    HAB habLocal;
    HMQ hmqLocal;
    INT i;

    /* Need a message queue for any thread that wants to print messages. */

    habLocal = WinInitialize(NULL);
    hmqLocal = WinCreateMsgQueue(habLocal,NULL);

    /* while stop auto semaphore not posted, post user event semaphore. */

    /* Don't check return code from DosPostEventSem(hevUserEvent)
       since we just want the resource to change hands.  We don't
       care if it changes hands exactly every time it goes through
       the loop.

       The event may already be posted if the system is busy and
       the resource threads don't finish with it fast enough.
       This is not a problem in this case.
     */

    do {
        /* if ulTimeout is zero, still waitevent for 1 msec to
         * force yielding of CPU.
         */
        rcWait = DosWaitEventSem(hevStopAuto, max (ulTimeout, 1));
        if (rcWait == ERROR_TIMEOUT) {
            i = rand () % MAXRESOURCES;      /* generate it */
            DosPostEventSem (aSquares[i].hev); 
        }
    } while (rcWait == ERROR_TIMEOUT);

    /* If there was an error, print a message */

    if (rcWait) {
       SemError("DosWaitEventSem",rcWait);
    }

    WinDestroyMsgQueue (hmqLocal);
    WinTerminate (habLocal);
    DosExit(EXIT_THREAD,0);
}


/****************************************************************
 * Routine to stop semaphore example.                           
 *---------------------------------------------------------------
 *                                                              
 *  Name:     BeginStop(pfAutoMode)                         
 *                                                              
 *  Purpose:  Posts stop event semaphore to signal threads to   
 *            die.  Also posts event to stop Auto mode if       
 *            necessary. Then waits for threads to complete,
 *            Creates thread StopSemaphore which posts stop event
 *            and waits for child threads.
 *
 *  Usage:    Called in file usercmd.c when the user selects    
 *            Stop from the semaphore menu.                     
 *                                                              
 *  Method:   Execs thread to do waits so that message thread
 *            doesn't hang.
 *  Returns:                                                    
 *                                                              
\****************************************************************/
VOID
BeginStop (PUSHORT pfAutoMode)
{
    USHORT rc;
    TID tidLocal;

    rc = DosPostEventSem(hevStop);
    if (rc)
    {
        SemError("DosPostEventSem",rc);
        return;
    }

    if (*pfAutoMode)
    {
        rc = DosPostEventSem(hevStopAuto);
        if (rc)
        {
           SemError("DosPostEventSem",rc);
        }
    }

    rc = DosCreateThread(&tidLocal,StopSemaphore,(LONG)pfAutoMode,0,STACKSIZE);
    if (rc)
    {
        SemError("DosCreateThread",rc);
    }
}

/****************************************************************
 * Routine to really stop semaphore example.                           
 *---------------------------------------------------------------
 *                                                              
 *  Name:     StopSemaphore(pfAutoMode)                         
 *                                                              
 *  Purpose:  Waits for threads to complete,
 *            Sends message to message thread to indicate this
 *            has occurred, and exits.
 *                                                              
 *  Usage:    Exec'd from BeginStop when user selects Stop from
 *            Semaphore menu.
 *                                                              
 *  Method:   Turns off Auto mode, if present by posting auto   
 *            semaphore.  Then stop event is posted.  Waits     
 *            for threads to die.                               
 *  Returns:                                                    
 *                                                              
\****************************************************************/


VOID StopSemaphore(PUSHORT pfAutoMode)
{
    USHORT rc,usCount, i;


    if (*pfAutoMode)
    {
        rc = DosWaitThread(&tidAutoThread,0L);
        if (rc && (rc != ERROR_INVALID_THREADID))
        {
           SemError("DosWaitThread",rc);
        }
        *pfAutoMode = FALSE;
    }

    
    /* Wait for usConsumer threads to die.  Order of death not important. */

    for (usCount = 0; usCount < usConsumerThreadsCreated; usCount++)
    {
        rc = DosWaitThread(&thrConsumers[usCount].tid,0L);

        /* rc is ERROR_INVALID_THREADID the thread is already dead. This *\
        \* is OK and not a error.                                   */

        if (rc && (rc != ERROR_INVALID_THREADID))
        {
            SemError("DosWaitThread",rc);
        }
    }

    /* Threads dead so we don't need semaphores any more.  */

    DosCloseEventSem(hevStopAuto);
    DosCloseEventSem(hevStop);
    DosCloseMutexSem(hmtxOwnResource);
    for (i = 0; i < MAXRESOURCES; i++) {
        DosCloseEventSem(aSquares[i].hev);
    }
    DosCloseMuxWaitSem (hmuxResource );
    WinPostMsg (hwndMain, WM_COMMAND, (MPARAM)IDM_STOPFINISHED, (MPARAM)NULL);
    DosExit (EXIT_THREAD, 0);
}

/****************************************************************\
 * Routine for consumer threads.                                *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     ThreadConsumer(ULONG)                             *
 *                                                              *
 *  Purpose:  There are NUMUSERS copies of this thread to act   *
 *            as consumers of the resource. The thread waits   *
 *            for exclusive access to the resource and colors it*
 *                                                              *
 *  Usage:    Threads created by StartSemExample.               *
 *                                                              *
 *  Method:   Waits for mutex to gain ownership of resource.    *
 *            Releases resource when user event. Dies when      *
 *            Stop event.                                       *
 *  Returns:                                                    *
 *                                                              *
\****************************************************************/

VOID  ThreadConsumer(USHORT usMyID)
{
    ULONG ulPostCnt;
    ULONG ulUser;
    USHORT rc;
    HAB hab;
    HMQ  hmq;

    /* Need a message queue for any thread that wants to print messages. */
    hab = WinInitialize(NULL);
    hmq = WinCreateMsgQueue(hab,NULL);
    
    /* while the user has not selected stop ... */
    while ((DosWaitEventSem(hevStop,SEM_IMMEDIATE_RETURN)) == ERROR_TIMEOUT) {
        /* Wait for exclusive ownership of resource */

        DosRequestMutexSem(hmtxOwnResource,SEM_INDEFINITE_WAIT);

        /* the following check is necessary because the stop semaphore
         * may have been posted  while we were waiting on the mutex
         */
        if (DosWaitEventSem(hevStop, SEM_IMMEDIATE_RETURN) == ERROR_TIMEOUT) {
            /* an item is ready, which one?
             * don't wait forever because there is
             * a possibility that the stop semaphore
             * was posted and that no more resource
             * is forthcoming. we wait twice as long
             * as we think is necessary.
             */
            if (!DosWaitMuxWaitSem (hmuxResource, max (ulTimeout << 1, TIMEOUTPERIOD), &ulUser)) {
                MyMove (usMyID, ulUser);
                DosResetEventSem(aSquares[ulUser].hev, &ulPostCnt);
            }
        }

        /*Let some other thread have resource. */
        if (rc = DosReleaseMutexSem(hmtxOwnResource)) {
              SemError("DosReleaseMutexSem",rc);
        }
    }
  
    /* hevStop was posted, kill this thread */
    WinDestroyMsgQueue (hmq);
    WinTerminate (hab);
    DosExit(EXIT_THREAD,0);
}


/****************************************************************\
 *  called from ThreadConsumer to make and register move
 *--------------------------------------------------------------
 *
 *  Name: MyMove (usMyID, ulUser)
 *        USHORT    usMyID;     id of caller
 *        ULONG     ulUser;     number of the square to "hit"
 *
 *  Purpose: color a square on the game board 'my' color.
 *           update display of number of hits for 'my' thread.
 *
 *  Usage: called only from ThreadConsumer
 *
 *  Method:
 *
 *  Returns: none
 *
\****************************************************************/
static VOID
MyMove (USHORT usMyID, ULONG ulUser)
{
    if (aSquares [ulUser].usOwner != usMyID) {
        aSquares[ulUser].usOwner = usMyID;
        thrConsumers[usMyID].lHits++;
        DrawResource (&aSquares[ulUser].rcl, colors [usMyID]);
        DrawStats (usMyID);
    }
}


/****************************************************************\
 * Routine to init game board                                   *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     InitSemaphExample(VOID)                           *
 *                                                              *
 *  Purpose:  Initializes game board unowned, sets semaphores   *
 *            to 0.                                             *
 *  Usage:    Called from StartSemExample in semaph.c when      *
 *            user selects start from the semaphore menu.       *
 *  Method:                                                     *
 *                                                              *
 *  Returns:  none                                              *
 *                                                              *
\****************************************************************/
VOID InitSemaphExample(VOID)
{
    int i;
    
    for (i = 0; i < MAXRESOURCES; i++) {
        aSquares[i].hev = 0;
        aSquares[i].usOwner = UNOWNED;
    }
    hevStopAuto = 0L;
    hevStop = 0L;
    hmtxOwnResource = 0L;
    hmuxResource  = 0L;
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.