🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sys.amiga.tech
Article #4169

Re: Semaphores (Why don't we use 'em?)

#4169
From: Tom Limoncelli
Date: Tue, 03 Jul 1990 04:00
570 lines
17704 bytes
People have asked me to post the docs I have on semaphores.  I found
this in a very old directory on my hard drive.  I can't find a source,
but I'm sure it was posted to Usenet.  One of the files mentions that
the author got help from the people at C-A, so I assume it was written
by someone that isn't a C-A Employee.

-Tom
Here it is:

>>>>>>>>>>sem.doc<<<<<<<<<<
I've been looking through the Autodocs on message & signal semaphores
and I find them to be a little bit lacking (I know, this has been
mentioned before).  So I was hoping that I might clarify the
use of them just a bit.  These programs demonstrate what I think is
going on, I invite you all to try to find problems with them...  They seem
OK to me but semaphores in any O/S are a really touchy area.

----

Message based semaphores (usually refered to as just 'Semaphores') make use
of the Message sending/receiving/waiting protocols.  In order to use
them you must initialize a 'struct Semaphore'  if you examine just what
a struct Semaphore is you'll see that it is defined as

struct Semaphore {
       struct MsgPort sm_MsgPort;
       WORD sm_Bids;
}

(it's in <exec/semaphores.h> like all of the semaphore definitions).

To initialize it, you must allocate the memory for the structure and
then do all of the usual MsgPort initialization things.  Normally
CreatePort() does all of this for you but you can't use it as you have
do to a slightly different initialization for a semaphore.  See the
CreateSemaphore() function in the msgsem example.  You can make a
semaphore public by making its MsgPort public (i.e. giving the MsgPort
a name & priority and using AddPort() to add the port to the system list
of named ports).  You can then find such a semaphore by using the FindPort()
call... To remove the semaphore from the system you have do remove it from
the public port list using RemPort() (iff you added it to that list) and
then simply free the memory allocated for the structure.

Assuming you have created the Semaphore, to use it you must allocate a
message (to be used as your 'bid' for the semaphore) and port that this
message will be sent to.  Initialize the mn_ReplyPort field of your
message to point to your port.  You then use these two functions
to lock/unlock the semaphore.

Procure(Semaphore, BidMessage)
struct Semaphore *Semaphore;
struct Message *BidMessage;

       Procure() returns true if the semaphore can be locked for your
       use immediately.  If the semaphore cannot be locked, your
       bid is queued up and when it becomes free your BidMessage
       will be sent the the port you allocated (the one that nm_ReplyPort
       in the BidMessage is supposed to point to).  So if you need
       to get the Semaphore and you can't go on any further without
       it you must do something like

       if (!Procure(semaphore,message)) {
               WaitPort(message->mn_ReplyPort);
               GetMsg(message->nm_ReplyPort);
       }

       ... put code that needs the semaphore here ...

       This assumes that there is only one semaphore using this port
       for it's replies.  Otherwise you have to be more complicated.

Vacate(Semaphore)
struct Semaphore *Semaphore;

       Vacate() unlocks the semaphore so that some other Procure()
       will succeed or if there is an outstanding bid then the
       bid message will sent to the appropriate port.

       So much for straight Semaphores.  Now onto SignalSemaphores.

----

SignalSemaphores seem much easier to use on the surface only they
are slightly broken which makes up for this.  The AddSemaphore(),
RemSemaphore() and FindSemaphore() calls all need special attention
as they don't work as advertised in the Autodocs.  Other than this
difficulty, signal semaphores are very easy to deal with as
there are no painful initialization rituals to remember as with
message semaphores above... just allocate enough memory for the
SignalSemaphore and you're off to the races.

InitSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       Initializes a signal semaphore structure.  Use this function
       for "private" semaphores (i.e. known only to tasks that
       have easy access to the creator's memory).

AddSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       Adds the semaphore to the system list of semaphores.  You
       must fill in the ss_Link.ln_{Type,Name,Pri} fields before
       calling this.  AddSemaphore() calls InitSemaphore() so
       you don't need to do call this yourself if you use AddSemaphore().

       N.B.  The Exec version of this function seems to be broken.
       Use Dale's "hand crafted" version instead.  (it's just a
       InitSemaphore() call followed by an atomic Enqueue on the
       system signal semaphore list).  See example.


RemSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       Removes the given semaphore from the system list of public
       semaphores.

       N.B. The amiga.lib and c[32].lib bindings for this function
       are broken.  You must include your own bindings for this
       to work.  See example.


struct SignalSemaphore *FindSemaphore(name)
char *name;

       Search the system list for semaphores for one with the given
       name.  Returns a pointer to the semaphore if one is found,
       NULL otherwise.

       N.B. The amiga.lib and c[32].lib bindings for this function
       are broken.  You must include your own bindings for this
       to work.  See example.


ObtainSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       This function locks the given semaphore for your use.  If the
       semaphore is currently in use by someone else it blocks until
       it is Release'd by the current owner.  A nesting count is
       maintained if you try to Obtain a semaphore that you already
       have.

ReleaseSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       This function frees the given semaphore for use by someone
       else.  If you Obtained the semaphore more than once the
       the nesting count is simply decremented rather than
       actually Releasing the semaphore.  Bad things happen if
       you Release more times than you Obtain.


AttemptSemaphore(SignalSemaphore)
struct SignalSemaphore *SignalSemaphore;

       This function tries to Obtain the semaphore provided.  If this
       can be done it locks the semaphore and returns true.  If the
       semaphore is currently in use by someone else it returns false.
       Use this function if you don't want to block your task waiting
       for a semaphore.


ObtainSemaphoreList(SemaphoreList)
struct SemaphoreList *SemaphoreList;

ReleaseSemaphoreList(SemaphoreList)
struct SemaphoreList *SemaphoreList;

       Haven't played with these two yet... They're used for locking
       several semaphores in one atomic action.  This ability can
       be used to prevent deadlock.

----

Note:
       The Kernel doesn't seem to do any kind of deadlock prevention
       or recovery.  This means you have to decide for yourself if
       deadlock is an issue in your application.  There are several
       good books which deal with deadlock avoidance and so forth
       so I won't go into that here.

>>>>>>>>>>Makefile<<<<<<<<<<
CFLAGS= +L

all: msgsem sigsem

msgsem:        msgsem.o
       ln -o msgsem msgsem.o -lc32

sigsem:        sigsem.o
       ln -o sigsem sigsem.o -lc32

>>>>>>>>>>msgsem.c<<<<<<<<<<
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include <exec/memory.h>

struct MsgPort *CreatePort();
struct MsgPort *FindPort();
struct XSemaphore *CreateSemaphore();
void DeleteSemaphore();
void *AllocMem();
void cleanup();

struct XSemaphore {
       struct Semaphore s;
       WORD Users;
};

struct MsgPort *port = 0;
struct XSemaphore *sem = 0;
struct Message msg;

/* I made this extended semaphore so that there is an easy way to
 * keep track of when it is safe to delete it.  If anyone can think
 * of a better way to do this, I'd love to hear it.
 */

extern int Enable_Abort;

main()
{
       int i;
       Enable_Abort = 0;

       /* the existance check/creation must be atomic --> Forbid/Permit */
       Forbid();
       if (!(sem = (struct XSemaphore *)FindPort("my semaphore")))
               sem = CreateSemaphore("my semaphore", 0);
       else
               sem->Users++;

       Permit();

       if (!sem) {
               printf("Error, can't find *or* create semaphore\n");
               cleanup();
               exit(100);
       }

       port = CreatePort(0,0);
       if (!port) {
               printf("Error, can't create my reply port\n");
               cleanup();
       }

       msg.mn_Node.ln_Type = NT_MESSAGE;
       msg.mn_Length       = sizeof(struct Message);
       msg.mn_ReplyPort    = port;

       /* for P & V fans, the Procure & Vacate names are mnemonic */
       /*                     -         -                         */

       for (i=0;i<5;i++) {
               if (!Procure(sem, &msg)) {
                       WaitPort(port); /* wait for the message to come */
                       GetMsg(port);   /* get it */
               }

               /* we now have exclusive access */

               printf("\nTask $%06x owns the semaphore\n",FindTask(0));

               /* pretend to so some action requiring semaphore */
               Delay(1 + (rand()&31));

               printf("\t\t...semaphore now released\n");

               /* yield control to someone else */
               Vacate(sem);

               /* pretend to so some action not requiring semaphore */
               Delay(1 + (rand()&31));

       }

       cleanup();

}

void cleanup()
{
       if (port) {
               DeletePort(port);
               port = 0;
       }

       /*
        * we'd like to use the semaphore to lock out other users here
        * but we'd have to somehow P and then not do a V but delete
        * the port instead for this to work... That hurts me more than
        * the Forbid() Permit() pair...
        */

       if (sem) {
               /* the user check/delete must be atomic --> Forbid/Permit */

               Forbid();
               if (--sem->Users == 0)
                       DeleteSemaphore(sem);
               Permit();

               sem = 0;
       }
}

struct XSemaphore *CreateSemaphore(name,pri)
char *name;
int pri;
{
       struct XSemaphore *s;
       char *buf;

       s = AllocMem(sizeof(struct XSemaphore), MEMF_PUBLIC|MEMF_CLEAR);
       if (!s)
               return(0);

       s->s.sm_Bids = -1;
       NewList(&s->s.sm_MsgPort.mp_MsgList);
       s->s.sm_MsgPort.mp_Flags        = PA_IGNORE;
       s->s.sm_MsgPort.mp_Node.ln_Type = NT_SEMAPHORE;
       s->s.sm_MsgPort.mp_Node.ln_Pri  = pri;
       s->Users                        = 1;

       /* Note that the name must be copied as the original creator
        * of the semaphore might exit.  We can't leave a pointer to
        * the original data segment lying around after the program
        * has exited.
        */
       if (name) {
               buf = AllocMem(strlen(name)+1, MEMF_PUBLIC);
               if (!buf) {
                       FreeMem(s, sizeof(struct XSemaphore));
                       return(0);
               }
               strcpy(buf,name);
               s->s.sm_MsgPort.mp_Node.ln_Name = buf;
               AddPort(s);
       }
       return(s);
}

void DeleteSemaphore(s)
struct XSemaphore *s;
{
       char *name;

       if (!s) return;

       name =  s->s.sm_MsgPort.mp_Node.ln_Name;

       if (name) {
               RemPort(s);     /* remove from list of public semaphores */
               FreeMem(name,strlen(name)+1);
       }

       FreeMem(s, sizeof(struct XSemaphore));
}

>>>>>>>>>>sigsem.c<<<<<<<<<<
/*
 * This program plays around with signal semaphores...
 *
 * Thanks to C-A for providing the new bindings & AddSemaphore code
 *
 */<

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/ports.h>
#include <exec/semaphores.h>
#include <exec/memory.h>
#include <exec/execbase.h>

struct XSSemaphore {
       struct SignalSemaphore s;
       WORD Users;
};

void *AllocMem();
void cleanup();
void DeleteSSemaphore();
struct SignalSemaphore *myFindSemaphore();
struct XSSemaphore *CreateSSemaphore();

struct XSSemaphore *sem = 0;

/* I made this extended semaphore so that there is an easy way to
 * keep track of when it is safe to delete it.  If anyone can think
 * of a better way to do this, I'd love to hear it.
 */

extern int Enable_Abort;

main()
{
       int i;
       Enable_Abort = 0;

       /* the existance check/creation must be atomic --> Forbid/Permit */

       Forbid();

       if (!(sem = (struct XSSemaphore *)myFindSemaphore("sigSemaphore")))
               sem = CreateSSemaphore("sigSemaphore",0);
       else
               sem->Users++;

       Permit();

       if (!sem) {
               printf("Error, can't find *or* create semaphore\n");
               cleanup();
               exit(100);
       }

       for (i=0;i<5;i++) {

               /* get use of the semaphore */
               ObtainSemaphore(sem);

               /* we now have exclusive access */

               printf("\nTask $%06x owns the semaphore\n",FindTask(0));

               /* pretend to so some action requiring semaphore */
               Delay(1 + (rand()&31));

               printf("\t\t...semaphore now released\n");

               /* yield control to someone else */
               ReleaseSemaphore(sem);

               /* pretend to so some action not requiring semaphore */
               Delay(1 + (rand()&31));

       }

       cleanup();
}

void cleanup()
{
       if (sem) {
               /* the user check/delete must be atomic --> Forbid/Permit */

               Forbid();
               if (--sem->Users == 0)
                       DeleteSSemaphore(sem);
               Permit();

               sem = 0;
       }
}

struct XSSemaphore *CreateSSemaphore(name,pri)
char *name;
int pri;
{
       struct XSSemaphore *sem;
       char *buf;

       sem = AllocMem(sizeof(struct XSSemaphore), MEMF_PUBLIC|MEMF_CLEAR);

       if (!sem) return(0);

       sem->s.ss_Link.ln_Type = NT_SIGNALSEM;
       sem->s.ss_Link.ln_Pri  = pri;

       /* Note that the name must be copied as the original creator
        * of the semaphore might exit.  We can't leave a pointer to
        * the original data segment lying around after the program
        * has exited.
        */
       if (name) {
               buf = AllocMem(strlen(name)+1, MEMF_PUBLIC);
               if (!buf) {
                       FreeMem(sem, sizeof(struct XSSemaphore));
                       return(0);
               }
               strcpy(buf,name);
               sem->s.ss_Link.ln_Name = buf;
               myAddSemaphore(sem);
       }
       else
               InitSemaphore(sem);

       sem->Users = 1;

       return(sem);
}

void DeleteSSemaphore(sem)
struct XSSemaphore *sem;
{
       char *name;

       if (!sem) return;

       name = sem->s.ss_Link.ln_Name;

       if (name) {
               myRemSemaphore(sem);
               FreeMem(name, strlen(name) + 1 );
       }

       FreeMem(sem, sizeof(struct XSSemaphore));
}


/* The bindings for "AddSemaphore" are broken in 1.2 Amiga.lib
 *
 * Dale's handcrafted AddSemaphore().
 */

myAddSemaphore(ss)
struct SignalSemaphore *ss;
{
    extern struct ExecBase *SysBase;

    InitSemaphore(ss);
    Forbid();
    Enqueue(&SysBase->SemaphoreList,ss);
    Permit();
}

/* The "C" interface code for the following semaphore routines is broken in
 * Amiga.lib and in the Aztec C release 3.4a.
 *
 * @ Lattice people should cut and paste the assembler into a separate file.
 */

#if AZTEC_C
#asm
; The exec.library function "AddSemaphore" is broken in KickStart rel. 33.180
;
; The Aztec bindings think that they should be using a0 to pass the
; argument instead of a1.  This is likely the problem with the Lattice
; bindings.  The Exec AddSemaphore function seems to be just plain broken.
;
;              -Rico

        XREF    _SysBase
        XREF    _LVOFindSemaphore
       XREF    _LVORemSemaphore


       XDEF    _myFindSemaphore
       XDEF    _myRemSemaphore

_myFindSemaphore:
       move.l  4(sp),a1
        move.l         _SysBase,a6
       jmp     _LVOFindSemaphore(a6)

_myRemSemaphore:
       move.l  4(sp),a1
        move.l         _SysBase,a6
       jmp     _LVORemSemaphore(a6)
#endasm
#endif
-- 
tlimonce@drew.edu      Tom Limoncelli
tlimonce@drew.uucp     +1 201 408 5389
tlimonce@drew.Bitnet
limonce@pilot.njin.net
Technical Details
Message-ID: <Jul.3.00.00.46.1990.19048@pilot.njin.net>
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!snorkelwacker!tut.cis.ohio-state.edu!cs.utexas.edu!rutgers!njin!limonce
References: <709.268c893f@desire.wright.edu> <31309@cup.portal.com> <Jul.1.11.54.20.1990.18150@pilot.njin.net>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!snorkelwacker!tut.cis.ohio-state.edu!cs.utexas.edu!rutgers!njin!limonce
From: limonce@pilot.njin.net (Tom Limoncelli)
Newsgroups: comp.sys.amiga.tech
Subject: Re: Semaphores (Why don't we use 'em?)
Message-ID: <Jul.3.00.00.46.1990.19048@pilot.njin.net>
Date: 3 Jul 90 04:00:47 GMT
References: <709.268c893f@desire.wright.edu> <31309@cup.portal.com> <Jul.1.11.54.20.1990.18150@pilot.njin.net>
Distribution: na
Organization: Drew University/NJIN
Lines: 570