[connect.audit] Making a non exclusive semaphore

duncanb@ibmpcug.co.uk (D G Booth) (03/12/91)

Christophe Wolfhugel asks whether OS/2 had any mechanism similar to 
the classic counting semaphores. There is no such mechanism built-in
to OS/2, but I have used the code below quite successfully.
 
The code defines a new structure type COUNTSEM which is a counting
semaphore, and functions P and V that operate on it. P decrements the
counter, but blocks if the counter is 0 until it is non-zero. V
increments the counter (and unblocks any blocked threads).
 
The code can be in an application managing mutual exclusion between
threads. It may also be in a DLL to manage semaphores between
processes, but in this case the MutualExclusion semaphore, and the
COUNTSEM structures should all be put in shared memory.
 
Duncan Booth <duncanb@ibmpcug.co.uk>
 
======= File: SEM.H ============
 
typedef struct COUNTSEM {
   long countSem;
   USHORT count;
} COUNTSEM, far *PCOUNTSEM;
 
void P (PCOUNTSEM s);	/* Wait till non-zero then decrement. */
BOOL P_timeout (PCOUNTSEM s, LONG timeout);	/* as P - may time out. */
void V (PCOUNTSEM s);				/* Increment */
======== End of SEM.H ==========
======= File: SEM.C ============
/*
 * Useful code using semaphores.
 * 
 * Author: Duncan Booth
 */
#define INCL_DOSPROCESS
#include <os2.h>
#include "sem.h"
 
/*
 * This semaphore is local to this file. It is used to control requests
 * to the counting semaphore structures.
 */
static long MutualExclusion = 0L;
 
#define	GET_SEM() (DosSemRequest (&MutualExclusion, SEM_INDEFINITE_WAIT))
#define	RLS_SEM() (DosSemClear (&MutualExclusion))
 
void P (PCOUNTSEM s)
{
   int blocked=1;
 
   while (blocked)
   {
      DosSemWait (&s->countSem, SEM_INDEFINITE_WAIT); /* wait til maybe ok */
 
      GET_SEM();
      if (s->count == 0)		/* not ready yet */
	 DosSemSet (&s->countSem);	/* set up block */
      else
      {
	 s->count--;	/* decrement count */
	 blocked = 0;	/* set up loop exit */
      }
      RLS_SEM();
   }
}
 
BOOL P_timeout (PCOUNTSEM s, LONG timeout)
{
   int blocked=1;
 
   while (blocked == 1)
   {
      if (DosSemWait (&s->countSem, timeout) != 0)
	 return FALSE;		/* wait til maybe ok */
 
      if (GET_SEM() != 0) return FALSE;
      if (s->count == 0)		/* not ready yet */
	 DosSemSet (&s->countSem);	/* set up block */
      else
      {
	 s->count--;	/* decrement count */
	 blocked = 0;	/* set up loop exit */
      }
      RLS_SEM();
   }
   return TRUE;
}
 
void V (PCOUNTSEM s)
{
   GET_SEM();			/* mutual excl */
   s->count++;			/* increment count */
   DosSemClear (&s->countSem);	/* free waiters */
   RLS_SEM();			/* mutual excl */
}
 
======== End of SEM.C ==========
-- 
Automatic Disclaimer:
The views expressed above are those of the author alone and may not
represent the views of the IBM PC User Group.
-- 
Duncan Booth, RCP Ltd.                    Didcot, Oxon, UK
duncanb@ibmpcug.co.uk                     Bix: jrichards

kushner@ux1.lbl.gov (Gary Kushner) (04/08/91)

Your code uses polling which should be avoided if at all possible.  Here
is the code I use:
----------
pv.h:
----------

/*------------
  Type for PVsems
  ------------*/

typedef struct _pvsem {
	long count;
	long more;				// sem clear if count > 0
	long mutex;
	} _far PVSEM;


/*-----------
  Protos
  -----------*/
PVSEM _far *PVSemCreate(void);
void _far _fastcall P(PVSEM _far *s);
void _far _fastcall V(PVSEM _far *s);

--------
pv.c
---------

#define INCL_DOSSEMAPHORES

#include <os2.h>
#include <malloc.h>
#include "pv.h"


/**************
*          PVSEMCREATE - allocate sem
**************/
PVSEM _far *PVSemCreate(void)
{
	PVSEM *s;

	if ( (s=calloc(1, sizeof(PVSEM))) != NULL)
		s->count=0;

	return s;
}


/**************
*          P - 
**************/
void _far _fastcall P(PVSEM _far *s)
{
		// any V's pending?  if not then wait...
	DosSemRequest(&s->more, SEM_INDEFINITE_WAIT);
	DosSemRequest(&s->mutex, SEM_INDEFINITE_WAIT);

		// if there are still more V's, then leave more clear
	if (--(s->count) > 0)
		DosSemClear(&s->more);

	DosSemClear(&s->mutex);
}


/**************
*          V - count them up
**************/
void _far _fastcall V(PVSEM _far *s)
{
	DosSemRequest(&s->mutex, SEM_INDEFINITE_WAIT);

	s->count++;
	DosSemClear(&s->more);		// ok guys, come and get it

	DosSemClear(&s->mutex);
}