File RXSEM.TXT                                   Ch. d'H. Nov 2, 1992
Copyright (C) 1992 Inventec Informatik AG
All rights reserved.



RXSEM  -  Semaphore Interface for REXX
======================================

The dynamic link library RXSEM.DLL provides semaphore functions
to an OS/2 REXX program.



Registering the DLL with REXX
-----------------------------

Before the DLL can be used, it must be registered with the REXX
interpreter. The following REXX statements can be used to register
the DLL:

   if rxfuncquery("RXSEM")<>0 then do
      rc = rxfuncadd("RXSEM","RXSEM","RXSEM")
      if rc <> 0 then call abend "rxfuncadd of RXSEM failed, RC="rc
      end

If the file RXSEM.DLL is not in the current directory or in the
LIBPATH, the complete path name must be included.

Refer to RXDLLOS2.TXT for additional information about using this
DLL with REXX.



The <rxsem> function
--------------------

The DLL is called through the external REXX routine with the same
name, <rxsem>.

The <rxsem> routine can be called as a function or as a routine.
Example to call <rxsem> as a function:
   result = rxsem(statement)
Example to call <rxsem> as a routine:
   call rxsem statement

The <rxsem> routine has a single argument <statement> and returns
the value <result>. <result> contains either an empty string (if no
error has occured) or an error code character followed by an error
message.

<result> must be tested after each call to <rxsem>. If <result> is
not empty and no error processing is done by the REXX program, an
error exit routine (i.e. <abend>) should be called.
Example:
   call rxsem statement
   if result <> "" then call abend "rxsem failed," result

The first character of <result> can be used to identify certain
error conditions.
Example:
   call rxsem "open sem_name=test sem_handle_var=sem1"
   select
      when result="" then nop /* no error */
      when left(result,1)="A" then
         call abend "a semaphore with the name TEST does already exist"
      otherwise call abend "rxsem failed," result
      end



Statements that can be passed to <rxsem>
----------------------------------------

The following statements can be passed as parameters to <rxsem>:

   open         open a semaphore
   close        close a semaphore
   get_state    get the state of a semaphore
   wait         wait on a semaphore
   post         post an event semaphore
   reset        reset an event semaphore
   request      request a mutex semaphore
   release      release a mutex semaphore
   group_wait   wait on a group of semaphores

Before a semaphore can be used, it must be opened though <open>.
<Open> returns a semaphore handle that is used to identify the
semaphore with further statements.

When a semaphore is no longer used, <close> should be executed to
release the internal buffer space allocated to the semaphore.
After <close> has been executed, the semaphore handle is no longer
valid.

There are two types of semaphores: event semaphores and mutex
(mutual exclusion) semaphores. Refer to OS/2 technical
documentation for additional information about semaphores.



Notes to the statement syntax and parameters
--------------------------------------------

- The statements have parameters in the form "keyword=value".
  These keyword parameters can appear in any order.
- Most parameters have default values that will be assumed when
  the parameter is not specified.
- A parameter value must be enclosed in single or double quotes if
  it contains blanks or special characters or if it is an empty
  string.
- Keywords names are case-independent. They can be written in
  uppercase, lowercase or mixed-case characters.
- Boolean output values are returned according to REXX conventions
  as 1 for <true> or 0 for <false>.
  Boolean input parameters can be 1, "TRUE" or "YES" for <true>,
  or 0, "FALSE" or "NO" for <false> (case-independent).

The following sections describe each statement in detail.



Statement:  open  -  open a semaphore
-------------------------------------

<open> opens a semaphore. It provides a semaphore handle that can
be used to further identify the semaphore. When the semaphore is
no longer used, <close> should be called.
If a new semaphore is created, it's initial state is "reset" for
an event semaphore or "unowned" for a mutex semaphore.

Syntax:
   open type=id sem_name=id access_mode=id
        sem_handle_var=rexxx_var_name

Parameters:
   type  -  semaphore type (event or mutex)
      <type> is "event" for an event semaphore, or "mutex" for
      a mutex semaphore.
      Default value for <type> is "event".
   sem_name  -  semaphore name
      If <sem_name> is empty (the default), an unnamed private semaphore
      is created, that can only be used within the current process.
      If <sem_name> is not empty, it specifies the name of a global
      shared semaphore that can be used by all processes.
      (For OS/2, the prefix "\SEM32\" is automatically added to the
      semaphore name. )
      Default value for <sem_name> is empty.
   access_mode  -  semaphore access mode
      <access_mode> specifies how a named shared semaphore is opened.
      It can have the following values:
         "new":
            A new semaphore is created. An old semaphore with the same
            name must not exist. Otherwise <result> returns the error
            code "A" followed by an error message.
            This is the default access mode.
         "old":
            This mode is used to access an old existing semaphore. If
            a semaphore with the specified name does not exist, <result>
            returns error code "N" followed by an error message.
            This mode can only be used for named semaphores.
         "use":
            If a semaphore with the specified name already exists, this
            semaphore is used. If not, a new semaphore is created. This
            mode can only be used for named semaphores.
      Default value for <access_mode> is "new".
   sem_handle_var  -  name of REXX variable for semaphore handle
      <sem_handle_var> specifies the name of a REXX variable
      where the semaphore handle will be stored. The semaphore
      handle is an integer number that is used to uniquely
      identify the semaphore. It can be used by all threads within
      the current process.

Error codes:
   The following error conditions can be intercepted by testing
   <left(result,1)>:
      "N"  -  semaphore does not exist
         A semaphore with the specified name does not exist.
      "A"  -  semaphore already exists
         A semaphore with the specified name already exists.

Example:
   call rxsem "open type=event sem_name=TESTSEM1 access_mode=use",
        "sem_handle_var=sem1"
   if result <> "" then call abend result
   say "the semaphore handle is" sem1



Statement:  close  -  close a semaphore
---------------------------------------

<close> closes a semaphore. This releases internal memory blocks
that are allocated with the semaphore. After <close> has been
executed, the semaphore handle is no longer valid and must not
be used any more.
The semaphore is deleted, if no other programs have the same
semaphore open.

Syntax:
   close sem_handle=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the semaphore handle associated with the
      semaphore to be closed.

Example:
   call rxsem "close sem_handle="sem1
   if result <> "" then call abend result



Statement:  get_state  -  get semaphore state
---------------------------------------------

<get_state> is used to query the state of a semaphore.

Syntax:
   get_state sem_handle=nr state_var=rexx_var_name

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.
   state_var  -  name of REXX variable for semaphore state
      <state_var> specifies the name of a REXX variable where a
      semaphore state code is to be stored.
      The following values are used for event semaphores:
         0  -  reset
            The event semaphore is in "reset" state.
         1  -  posted
            The event semaphore is in "posted" state.
      The following codes are used for mutex semaphores:
         "U"  -  unowned
            The mutex semaphore is not owned.
         "C"  -  owned by current
            The mutex semaphore is owned by the current thread.
         "O"  -  owned by other
            The mutex semaphore is owned by another thread.
         "G"  -  owner gone
            The owner of the mutex semaphore ended without
            without releasing the semaphore.

Example:
   call rxsem "get_state sem_handle="event_sem1 "state_var=s"
   if result <> "" then call abend result
   if s
      then say "event_sem1 is posted"
      else say "event_sem1 is reset"
   call rxsem "get_state sem_handle="mutex_sem1 "state_var=s"
   if result <> "" then call abend result
   select
      when s = "U" then say "mutex_sem1 is unowned"
      when s = "C" then say "mutex_sem1 is owned by current thread"
      when s = "O" then say "mutex_sem1 is owned by another thread"
      when s = "G" then say "mutex_sem1 is in 'owner-gone' state"
      otherwise call abend "unexpected semaphore status code"
      end



Statement:  wait  -  wait for a semaphore
-----------------------------------------

When used with an event semaphore, <wait> blocks the current
thread until the semaphore is posted. If the semaphore is already
in "posted" state, the routine returns immediately and the current
thread is not put into a wait state.
For OS/2, event semaphores are edge-triggered (except if they
are used in a group-wait). This means that if the semaphore is
posted and then immediately reset before the waiting thread gets
a chance to run, the thread is thrown out of the wait and does
not have to wait for the semaphore to be posted again.
Multiple threads can wait on the same semaphore.

When used with a mutex semaphore, <wait> waits until the semaphore
becomes available, but does not establish ownership of the
semaphore. If the semaphore is owned by the current thread, the
routine has no effect. If the previous owning thread of the
semaphore ended without releasing the semaphore, <result> returns
the error code "G" followed by an error message.
For OS/2, <wait> has the same effect as calling <request>
followed by an immediate call to <release>.

Syntax:
   wait sem_handle=nr timeout_ms=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.
   timeout_ms  -  maximum wait time in milli-seconds or -1
      <timeout_ms> specifies the maximum time in milli-seconds to
      wait. If the timeout expires before the semaphore is posted
      (for an event semaphore) or becomes available (for a mutex
      semaphore), <result> returns the error code "T" followed by
      an error message.
      A <timeout_ms> value of -1 is used to wait infinite.
      Default value for <timeout_ms> is -1 (infinite).

Error codes:
   The following error conditions can be intercepted by testing
   <left(result,1)>:
      "T"  -  timeout
         The wait timeout time has expired before the semaphore
         was posted (event sem) or became available (mutex sem).
      "G"  -  owner gone
         The thread that previously owned the mutex semaphore has
         ended without releasing the semaphore. The semaphore
         cannot be used any more and must be closed.

Example:
   call rxsem "wait sem_handle="sem1 "timeout_ms=5000"
   select
      when result = "" then nop /* successfull wait */
      when left(result,1)="T" then call abend "wait-timeout for sem1"
      otherwise call abend result
      end



Statement:  post  -  post an event semaphore
--------------------------------------------

<post> posts an event semaphore. If the semaphore is already
in the "posted" state, this routine has no effect.

Syntax:
   post sem_handle=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.

Example:
   call rxsem "post sem_handle="sem1
   if result <> "" then call abend result



Statement:  reset  -  reset an event semaphore
----------------------------------------------

<reset> resets an event semaphore. If the semaphore is already
in the "reset" state, this routine has no effect.

Syntax:
   reset sem_handle=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.

Example:
   call rxsem "reset sem_handle="sem1
   if result <> "" then call abend result



Statement:  request  -  request a mutex semaphore
-------------------------------------------------

<request> requests ownership of a mutex semaphore. If the
semaphore is currently owned by another thread, the current
thread is put into a wait state until the semaphore becomes
available. If the current thread already owns the semaphore,
the semaphores request count is incremented.
If the previous owning thread of the semaphore ended without
releasing the semaphore, <result> returns the error code "G"
followed by an error message. The semaphore cannot be used
any more in this case.

Syntax:
   request sem_handle=nr timeout_ms=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.
   timeout_ms  -  maximum wait time in milli-seconds or -1
      <timeout_ms> specifies the maximum time in milli-seconds to
      wait. If the timeout expires before the semaphore becomes
      available, <result> returns the error code "T" followed by
      an error message.
      A <timeout_ms> value of -1 is used to wait infinite.
      Default value for <timeout_ms> is -1 (infinite).

Error codes:
   The following error conditions can be intercepted by testing
   <left(result,1)>:
      "T"  -  timeout
         The wait timeout time has expired before the semaphore
         became available.
      "G"  -  owner gone
         The thread that previously owned the mutex semaphore has
         ended without releasing the semaphore. The semaphore
         cannot be used any more and must be closed.

Example:
   call rxsem "request sem_handle="sem1 "timeout_ms=30000"
   if result <> "" then call abend result



Statement:  release  -  release a mutex semaphore
-------------------------------------------------

<release> decrements the request count of the semaphore. If the
request count reaches zero, the threads ownership of the semaphore
is relinquished and the semaphore is put into the "unowned" state.
Only the owning thread can release a semaphore.

Syntax:
   release sem_handle=nr

Parameters:
   sem_handle  -  semaphore handle
      <sem_handle> is the handle associated with the semaphore.

Example:
   call rxsem "release sem_handle="sem1
   if result <> "" then call abend result



Statement:  group_wait  -  wait on a group of semaphores
--------------------------------------------------------

<group_wait> is used to wait for multiple event or mutex
semaphores. Event and mutex semaphores cannot be mixed in
the same <group_wait>.

When used with event semaphores, <group_wait> waits until any
or all semaphores of the group are posted. For OS/2 the group
member semaphores are level-triggered if <mode> is "all".
If <mode> is "any" it is not clear whether the member
semaphores are level-triggered or edge-triggered. (?)

When used with mutex semaphores, <group_wait> waits until any or
all semaphores of the group can be requested. If the previous
owning thread of one of the semaphores in the group has ended
without releasing the semaphore, <result> returns the error code
"G" followed by an error message.

Syntax:
   group_wait sem_list_var=rexx_var_name mode=id timeout_ms=nr
         index_var=rexx_var_name

Parameters:
   sem_list_var  -  name of REXX variable with semaphore list
      <sem_list_var> specifies the name of a REXX variable that
      contains a list of the semaphore handles that are part of
      the group. The semaphore handles in the list must be
      separated by blanks.
      Event and mutex semaphores cannot be mixed.
   mode  -  wait mode ("any" or "all")
      <mode> is either "any" to wait for any semaphore or "all"
      to wait for all semaphores in the group.
      When using event semaphores, <mode> has the following
      meaning:
         "any"  -  wait until any semaphore is posted
            <group_wait> waits until any of the semaphores is
            posted. <index_var> is set to the index of a
            posted semaphore.
         "all"  -  wait until all semaphores are posted
            <group_wait> waits until all semaphores are posted.
      When using mutex semaphores, <mode> has the following
      meaning:
         "any"  -  request any semaphore of the group
            <group_wait> waits until any of the semaphores can
            be requested. <index_var> returns the index of the
            semaphore that has been requested. The thread gains
            ownership only of this semaphore. The ownership of all
            other semaphores in the group remains unchanged.
         "all"  -  request all semaphores in the group
            <group_wait> waits until all semaphores have been
            requested. The current thread becomes owner of all
            the semaphores. It does not receive ownership of any
            of the semaphores until all of the semaphores are
            available.
      The default value for <mode> is "any" for event semaphores
      and "all" for mutex semaphores.
   timeout_ms  -  maximum wait time in milli-seconds or -1
      <timeout_ms> specifies the maximum time in milli-seconds to
      wait. If the timeout expires before the <group_wait> can be
      completed, <result> returns the error code "T" followed by
      an error message.
      A <timeout_ms> value of -1 is used to wait infinite.
      Default value for <timeout_ms> is -1 (infinite).
   index_var  -  name of REXX variable to store index value
      <index_var> specifies the name of a REXX variable where
      the index of the posted or requested semaphore is stored.
      The first semaphore in the group has index 0.
      <index_var> is only used if <mode> is <any>.

Error codes:
   The following error conditions can be intercepted by testing
   <left(result,1)>:
      "T"  -  timeout
         The wait timeout time has expired before the <group_wait>
         could be completed.
      "G"  -  owner gone
         The thread that previously owned one of the mutex
         semaphores in the group has ended without releasing the
         semaphore. The semaphore concerned cannot be used any
         more and must be closed.

Example:
   sem_list = event0_sem event1_sem  /* event semaphore handles */
   call rxsem "group_wait sem_list_var=sem_list timeout_ms=3000",
         "index_var=event"
   select
      when left(result,1)="T" then call process_timer_event
      when result <> "" then call abend result
      when event = 0 then call process_event0
      when event = 1 then call process_event1
      otherwise call abend "unexpected event nr" event
      end



Sample program SAMPSEM1.CMD  -  wait on a public event semaphore
----------------------------------------------------------------

/* SAMPSEM1.CMD  -  sample program for RXSEM.DLL                       */

/* This program opens a named global shared semaphore and waits until  */
/* it is posted 3 times. SAMPSEM2.CMD can be run in another session to */
/* post the semaphore.                                                 */

signal on failure name rexx_exception
signal on novalue name rexx_exception

parse source os . mod_name
select
   when os = "OS/2" then do
      "@echo off"
      parse arg mode main_parms
      if mode <> "INDIRECT" then do
         "cmd /c" mod_name "INDIRECT" arg(1); exit rc; end
      end
   otherwise parse arg main_parms
   end

sem_name = "SAMPSEM1"

call init
call main
return

main:
   call rxsem "open type=event sem_name="sem_name "access_mode=use",
         "sem_handle_var=sem1"
   if result <> "" then call abend result
   do i=1 to 3
      say "waiting, call SAMPSEM2 to post semaphore"
      call rxsem "reset sem_handle="sem1
      if result <> "" then call abend result
      call rxsem "wait sem_handle="sem1
      if result <> "" then call abend result
      say "semaphore has been posted ("i")"
      end
   call rxsem "close sem_handle="sem1
   if result <> "" then call abend result
   return

init:
   if rxfuncquery("RXSEM")<>0 then do
      rc = rxfuncadd("RXSEM","RXSEM","RXSEM")
      if rc <> 0 then call abend "rxfuncadd of RXSEM failed, RC="rc
      end
   return

/*--- exit routines ---------------------------------------------------*/

rexx_exception:
   parse source . . mod_name
   call error_exit 'REXX' condition('C') 'exception in module' mod_name,
         'line' sigl', statement = "'sourceline(sigl)'"'

abend:
   parse arg err_msg
   parse source . . mod_name
   s = 'Error in module' mod_name 'line' sigl
   if err_msg <> '' then s = s',' err_msg
   call error_exit s

error_exit:
   parse arg err_msg
   if err_msg <> '' then say err_msg
   exit 99



Sample program SAMPSEM2.CMD  -  post a public event semaphore
-------------------------------------------------------------

/* SAMPSEM2.CMD  -  sample program for RXSEM.DLL                       */

/* This can be used in conjunction with SAMPSEM1.CMD to post the       */
/* public semaphore.                                                   */

signal on failure name rexx_exception
signal on novalue name rexx_exception

parse source os . mod_name
select
   when os = "OS/2" then do
      "@echo off"
      parse arg mode main_parms
      if mode <> "INDIRECT" then do
         "cmd /c" mod_name "INDIRECT" arg(1); exit rc; end
      end
   otherwise parse arg main_parms
   end

sem_name = "SAMPSEM1"

call init
call main
return

main:
   call rxsem "open type=event sem_name="sem_name "access_mode=use",
         "sem_handle_var=sem1"
   if result <> "" then call abend result
   call rxsem "post sem_handle="sem1
   if result <> "" then call abend result
   say "semaphore posted"
   call rxsem "close sem_handle="sem1
   if result <> "" then call abend result
   return

init:
   if rxfuncquery("RXSEM")<>0 then do
      rc = rxfuncadd("RXSEM","RXSEM","RXSEM")
      if rc <> 0 then call abend "rxfuncadd of RXSEM failed, RC="rc
      end
   return

/*--- exit routines ---------------------------------------------------*/

rexx_exception:
   parse source . . mod_name
   call error_exit 'REXX' condition('C') 'exception in module' mod_name,
         'line' sigl', statement = "'sourceline(sigl)'"'

abend:
   parse arg err_msg
   parse source . . mod_name
   s = 'Error in module' mod_name 'line' sigl
   if err_msg <> '' then s = s',' err_msg
   call error_exit s

error_exit:
   parse arg err_msg
   if err_msg <> '' then say err_msg
   exit 99
