@DATABASE Libraries Manual
@NODE MAIN "Amiga RKM Libraries: 25 Exec Semaphores"
@INDEX Lib_Index/MAIN
@TOC Libraries_Manual/Exec
Semaphores are a feature of Exec which provide a general method for tasks
to arbitrate for the use of memory or other system resources they may be
sharing.  This chapter describes the structure of Exec semaphores and the
various support functions provided for their use.  Since the semaphore
system uses Exec lists and signals, some familiarity with these concepts
is helpful for understanding semaphores.

In any multitasking or multi-processing system there is a need to share
data among independently executing tasks.  If the data is static (that is,
it never changes), then there is no problem.  However, if the data is
variable, then there must be some way for a task that is about to make a
change to keep other tasks from looking at the data.

For example, to add a node to a linked list of data, a task would normally
just add the node.  However, if the list is shared with other tasks, this
could be dangerous.  Another task could be walking down the list while the
change is being made and pick up an incorrect pointer.  The problem is
worse if two tasks attempt to add an item to the list at the same time.
Exec semaphores provide a way to prevent such problems.

A semaphore is much like getting a key to a locked data item.  When you
have the key (semaphore), you can access the data item without worrying
about other tasks causing problems.  Any other tasks that try to obtain
the semaphore will be put to sleep until the semaphore becomes available.
When you have completed your work with the data, you return the semaphore.

For semaphores to work correctly, there are two restrictions that must be
observed at all times:

 1) All tasks using shared data that is protected by a semaphore must
    always ask for the semaphore first before accessing the data. If some
    task accesses the data directly without first going through the
    semaphore, the data may be corrupted.  No task will have safe access
    to the data.

 2) A deadlock will occur if a task that owns an @{"exclusive" link 25-1-1-3} semaphore on
    some data inadvertently calls another task which tries to get an
    exclusive semaphore on that same data in blocking mode.  Deadlocks
    and other such issues are beyond the scope of this manual.  For more
    details on deadlocks and other problems of shared data in a
    multitasking system and the methods used to prevent them, refer to a
    textbook in computer science such as Operating Systems by Tannenbaum
    (Prentice-Hall).

@{" Semaphore Functions " link 25-1}  @{" Function Reference " link 25-2}

@ENDNODE

@NODE 25-1 "25 Exec Semaphores / Semaphore Functions"
Exec provides a variety of useful functions for setting, checking and
freeing semaphores.  The prototypes for these functions are as follows.

    VOID  AddSemaphore ( struct SignalSemaphore *sigSem );
    ULONG AttemptSemaphore( struct SignalSemaphore *sigSem );
    struct SignalSemaphore *FindSemaphore( UBYTE *sigSem );
    VOID InitSemaphore( struct SignalSemaphore *sigSem );

    VOID ObtainSemaphore( struct SignalSemaphore *sigSem );
    VOID ObtainSemaphoreList( struct List *sigSem );
    void ObtainSemaphoreShared( struct SignalSemaphore *sigSem );

    VOID ReleaseSemaphore( struct SignalSemaphore *sigSem );
    VOID ReleaseSemaphoreList( struct List *sigSem );
    VOID RemSemaphore( struct SignalSemaphore *sigSem );

@{" The Signal Semaphore " link 25-1-1}  @{" Multiple Semaphores " link 25-1-2}  @{" Semaphore Example " link ADCD_v1.2:Reference_Library/Libraries/Lib_Examples/semaphore.c/MAIN}

@ENDNODE

@NODE 25-1-1 "25 / Semaphore Functions / The Signal Semaphore"
Exec semaphores are signal based. Using signal semaphores is the easiest
way to protect shared, single-access resources in the Amiga.  Your task
will sleep until the semaphore is available for use.  The @{"SignalSemaphore" link ADCD_v1.2:Inc&AD2.1/includes/exec/semaphores.h/MAIN 39}
structure is as follows:

    struct SignalSemaphore {
        struct  Node ss_Link;
        SHORT   ss_NestCount;
        struct  MinList ss_WaitQueue;
        struct  SemaphoreRequest ss_MultipleLink;
        struct  Task *ss_Owner;
        SHORT   ss_QueueCount;
    };

ss_Link
    is the node structure used to link semaphores together.  The @{"ln_Pri" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-1 34}
    and @{"ln_Name" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-1 38} fields are used to set the priority of the semaphore in a
    list and to name the semaphore for @{"public" link 25-1-1-2} access.  If a semaphore is
    not public the ln_Name and ln_Pri fields may be left NULL.

ss_NestCount
    is the count of number of locks the current owner has on the
    semaphore.

ss_WaitQueue
    is the @{"List" link ADCD_v1.2:Reference_Library/Libraries/Lib_23/23-1-3 23} header for the list of other tasks waiting for this
    semaphore.

ss_MultipleLink
    is the @{"SemaphoreRequest" link ADCD_v1.2:Inc&AD2.1/includes/exec/semaphores.h/MAIN 33} used by @{"ObtainSemaphoreList()" link 25-1-2}.

ss_Owner
    is the pointer to the current owning task.

ss_QueueCount
    is the number of other tasks waiting for the semaphore.

A practical application of a SignalSemaphore would be to use it as the
base of a shared data structure.  For example:

    struct SharedList {
        struct SignalSemaphore sl_Semaphore;
        struct MinList         sl_List;
    };

@{" Creating a SignalSemaphore Structure " link 25-1-1-1}
@{" Making a SignalSemaphore Available to the Public " link 25-1-1-2}
@{" Obtaining a SignalSemaphore Exclusively " link 25-1-1-3}
@{" Obtaining a Shared SignalSemaphore " link 25-1-1-4}
@{" Checking a SignalSemaphore " link 25-1-1-5}
@{" Releasing a SignalSemaphore " link 25-1-1-6}
@{" Removing a SignalSemaphore Structure " link 25-1-1-7}

@ENDNODE

@NODE 25-1-1-1 "25 / / The Signal Semaphore / Creating a SignalSemaphore Structure"
To initialize a @{"SignalSemaphore" link 25-1-1} structure use the @{"InitSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/InitSemaphore()}
function.  This function initializes the list structure and the nesting
and queue counters.  It does not change the semaphore's name or priority
fields.

This fragment creates and initializes a semaphore for a data item such as
the @{"SharedList" link 25-1-1 37} structure above.

    struct SharedList *slist;

    if (slist=(struct SharedList *)
        AllocMem(sizeof(struct SharedList),MEMF_PUBLIC|MEMF_CLEAR))
    {
        NewList(&slist->sl_List);       /* Initialize the MinList       */
        InitSemaphore((struct SignalSemaphore *)slist);
                                        /* And initialize the semaphore */

        /* The semaphore can now be used. */
    }
    else printf("Can't allocate structure\n");

@ENDNODE

@NODE 25-1-1-2 "25 / / / Making a SignalSemaphore Available to the Public"
A semaphore should be used internally in your program if it has more than
one task operating on shared data structures.  There may also be cases
when you wish to make a data item public to other applications but still
need to restrict its access via semaphores.  In that case, you would give
your semaphore a unique name and add it to the public @{"SignalSemaphore" link 25-1-1} list
maintained by Exec.  The @{"AddSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddSemaphore()} function does this for you.  This
works in a manner similar to @{"AddPort()" link ADCD_v1.2:Reference_Library/Libraries/Lib_24/24-1-1} for message ports.

To create and initialize a public semaphore for a data item and add it to
the public semaphore list maintained by Exec, the following function
should be used.  (This will prevent the semaphore from being added or
removed more than once by separate programs that use the semaphore).

   UBYTE *name;   /* name of semaphore to add */
   struct SignalSemaphore *semaphore;

   Forbid();
   /* Make sure the semaphore name is unique */
   if (!FindSemaphore(name)) {
       /* Allocate memory for the structure */
       if (sema=(struct SignalSemaphore *)
           AllocMem(sizeof(struct SignalSemaphore),MEMF_PUBLIC|MEMF_CLEAR))
       {
           sema->ss_Link.ln_Pri=0;         /* Set the priority to zero */
           sema->ss_Link.ln_Name=name;
           /* Note that the string 'name' is not copied. If that is    */
           /* needed, allocate memory for it and copy the string. And  */
           /* add the semaphore the the system list                    */
           AddSemaphore(semaphore);
       }
   }
   Permit();

A value of NULL for semaphore means that the semaphore already exists or
that there was not enough free memory to create it.

Before using the data item or other resource which is protected by a
semaphore, you must first obtain the semaphore.  Depending on your needs,
you can get either @{"exclusive" link 25-1-1-3} or @{"shared" link 25-1-1-4} access to the semaphore.

@ENDNODE

@NODE 25-1-1-3 "25 / / The Signal Semaphore / Obtaining a SignalSemaphore Exclusively"
The @{"ObtainSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphore()} function can be used to get an exclusive lock on a
semaphore.  If another task currently has an exclusive or @{"shared" link 25-1-1-4} lock(s)
on the semaphore, your task will be put to sleep until all locks on the
the semaphore are released.

    Semaphore Nesting.
    ------------------
    @{"SignalSemaphore" link 25-1-1}s have nesting.  That is, if your task already
    owns the semaphore, it will get a second ownership of that semaphore.
    This simplifies the writing of routines that must own the semaphore
    but do not know if the caller has obtained it yet.

To obtain a semaphore use:

    struct SignalSemaphore *semaphore;
    ObtainSemaphore(semaphore);

To get an exclusive lock on a @{"public" link 25-1-1-2} semaphore, the following code should
be used:

    UBYTE *name;
    struct SignalSemaphore *semaphore;

    Forbid();     /* Make sure the semaphore will not go away if found. */
    if (semaphore=FindSemaphore(name))
        ObtainSemaphore(semaphore);
    Permit();

The value of semaphore is NULL if the semaphore does not exist.  This is
only needed if the semaphore has a chance of going away at any time (i.e.,
the semaphore is @{"public" link 25-1-1-2} and  might be removed by some other program).  If
there is a guarantee that the semaphore will not disappear, the semaphore
address could be cached, and all that would be needed is a call to the
@{"ObtainSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphore()} function.

@ENDNODE

@NODE 25-1-1-4 "25 / / The Signal Semaphore / Obtaining a Shared SignalSemaphore"
For read-only purposes, multiple tasks may have a shared lock on a signal
semaphore.  If a semaphore is already exclusively locked, all attempts to
obtain the semaphore shared will be blocked until the @{"exclusive lock" link 25-1-1-3} is
@{"released" link 25-1-1-6}.  At that point, all shared locks will be obtained and the
calling tasks will wake up.

To obtain a shared semaphore, use:

    struct SignalSemaphore *semaphore;
    ObtainSemaphoreShared(semaphore);

To obtain a @{"public" link 25-1-1-2} shared semaphore, the following code should be used:

    UBYTE *name;
    struct SignalSemaphore *semaphore;

    Forbid();
    if (semaphore = FindSemaphore(name))
        ObtainSemaphoreShared(semaphore);
    Permit();

@ENDNODE

@NODE 25-1-1-5 "25 / / The Signal Semaphore / Checking a SignalSemaphore"
When you attempt to obtain a semaphore with @{"ObtainSemaphore()" link 25-1-1-3}, your task
will be put to sleep if the semaphore is not currently available. If you
do not want to wait, you can call @{"AttemptSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AttemptSemaphore()} instead. If the
semaphore is available for @{"exclusive locking" link 25-1-1-3}, AttemptSemaphore() obtains
it for you and returns TRUE.  If it is not available, the function returns
FALSE immediately instead of waiting for the semaphore to be released.

To attempt to obtain a semaphore, use the following:

    struct SignalSemaphore *semaphore;
    AttemptSemaphore(semaphore);

To make an attempt to obtain a @{"public" link 25-1-1-2} semaphore, the following code should
be used:

    UBYTE *name;
    struct SignalSemaphore *semaphore;

    Forbid();
    if (semaphore = FindSemaphore(name)) AttemptSemaphore(semaphore);
    Permit();

@ENDNODE

@NODE 25-1-1-6 "25 / / The Signal Semaphore / Releasing a SignalSemaphore"
Once you have obtained the semaphore and completed any operations on the
semaphore protected object, you should release the semaphore.  The
@{"ReleaseSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ReleaseSemaphore()} function does this.  For each successful
@{"ObtainSemaphore()" link 25-1-1-3}, @{"ObtainSemaphoreShared()" link 25-1-1-4} and @{"AttemptSemaphore()" link 25-1-1-5} call you
make, you must have a matching ReleaseSemaphore() call.

@ENDNODE

@NODE 25-1-1-7 "25 / / The Signal Semaphore / Removing a SignalSemaphore Structure"
Semaphore resources can only be freed if the semaphore is not locked.  A
@{"public" link 25-1-1-2} semaphore should first be removed from the system semaphore list
with the @{"RemSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemSemaphore()} function. This prevents other tasks from finding
the semaphore and trying to lock it. Once the semaphore is removed from
the system list, the semaphore should be @{"locked exclusively" link 25-1-1-3} so no other
task can lock it.  Once the lock is obtained, it can be released again,
and the resources can be deallocated.

The following code should be used to remove a @{"public" link 25-1-1-2} semaphore:

    UBYTE *name;
    struct SignalSemaphore *semaphore;

    Forbid();
    if (semaphore=FindSemaphore(name))
    {
        RemSemaphore(semaphore);       /* So no one else can find it... */
        ObtainSemaphore(semaphore);    /* Wait for us to be last user...*/
        ReleaseSemaphore(semaphore);   /* Ready for cleanup...          */
    }
    FreeMem(semaphore, sizeof(struct SignalSemaphore));
    Permit();

@ENDNODE

@NODE 25-1-2 "25 / Semaphore Functions / Multiple Semaphores"
The semaphore system has the ability to ask for ownership of a complete
list of semaphores.  This can help prevent deadlocks when there are two or
more tasks trying to get the same set of semaphores.  If task A gets
semaphore 1 and tries to obtain semaphore 2 after task B has obtained
semaphore 2 but before task B tries to obtain semaphore 1 then both tasks
will hang.  Exec provides @{"ObtainSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphoreList()} and @{"ReleaseSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ReleaseSemaphoreList()}
to prevent this problem.

A semaphore list is a list header to a list that contains @{"SignalSemaphore" link 25-1-1}
structures.  The semaphore list must not contain any @{"public" link 25-1-1-2} semaphores.
This is because the semaphore list functions use the standard node
structures in the semaphore.

To arbitrate access to a semaphore list use another semaphore.  Create a
@{"public" link 25-1-1-2} semaphore and use it to arbitrate access to the list header of the
semaphore list.  This also gives you a locking semaphore, protecting the
@{"ObtainSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphoreList()} call.  Once you have gained access to the list with
@{"ObtainSemaphore()" link 25-1-1-3}, you may obtain all the semaphores on the list via
ObtainSemaphoreList() (or get individual semaphores with
ObtainSemaphore()).  When you are finished with the protected objects,
release the semaphores on the list with @{"ReleaseSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ReleaseSemaphoreList()}, and then
release the list semaphore via @{"ReleaseSemaphore()" link 25-1-1-6}.

For example:

    ObtainSemaphore((struct SignalSemaphore *)SemaphoreList);
    ObtainSemaphoreList(SemaphoreList->sl_List);

    /* At this point the objects are protected, and can be manipulated */

    ReleaseSemaphoreList(SemaphoreList->sl_List);
    ReleaseSemaphore((struct SignalSemaphore *)SemaphoreList);

See the @{"SharedList" link 25-1-1 37} structure above for an example of a semaphore structure
with a list header.

@ENDNODE

@NODE 25-2 "25 Exec Semaphores / Function Reference"
The following charts give a brief description of the Exec semaphore
functions.  See the Amiga ROM Kernel Reference Manual: Includes and
Autodocs for details about each call.


                   Table 25-1: Exec Semaphore Functions
  ________________________________________________________________________
 |                                                                        |
 |  Exec Semaphore Function                 Description                   |
 |========================================================================|
 |           @{"AddSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddSemaphore()}  Initialize and add a signal semaphore to the |
 |                           system.                                      |
 |       @{"AttemptSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AttemptSemaphore()}  Try to get an exclusive lock on a signal     |
 |                           semaphore without blocking.                  |
 |          @{"FindSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FindSemaphore()}  Find a given system signal semaphore.        |
 |          @{"InitSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/InitSemaphore()}  Initialize a signal semaphore.               |
 |        @{"ObtainSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphore()}  Try to get exclusive access to a signal      |
 |                           semaphore.                                   |
 |    @{"ObtainSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphoreList()}  Try to get exclusive access to a list of     |
 |                           signal semaphores.                           |
 |  @{"ObtainSemaphoreShared()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ObtainSemaphoreShared()}  Try to get shared access to a signal         |
 |                           semaphore (V36).                             |
 |       @{"ReleaseSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ReleaseSemaphore()}  Release the lock on a signal semaphore.      |
 |   @{"ReleaseSemaphoreList()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/ReleaseSemaphoreList()}  Release the locks on a list of signal        |
 |                           semaphores.                                  |
 |           @{"RemSemaphore()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemSemaphore()}  Remove a signal semaphore from the system.   |
 |________________________________________________________________________|

@ENDNODE
