To run the example programs you would need a version of ooRexx from 
"sandbox/rick/sem". The 32-bit
Windows "sandbox/rick/sem" version of ooRexx can be temporarily found here:
<https://www.dropbox.com/sh/olh1mqfwb4brp7r/AABI1X-Le9zDCJ0gyfvMdKB8a?dl=0>.

---

Prologue
------------

Semaphores get used in multithreaded scenarios. As ooRexx allows one to run 
method routines on
different threads in parallel, semaphores could come in handy.

Even regular ooRexx routines can be called asynchroneously, such that one can 
write a Rexx routine
(using the "::routine" directive) and run it on a different thread. This is 
done by fetching the
routine object from the .routines directory (this directory is set up by the 
interpreter and makes
all routines of the Rexx program available to the programmer). To invoke/run a 
routine object one
sends it the "call" message. If arguments should be supplied then one mereley 
adds them.

The following two Rexx samples, one demonstrating an eventSemaphore, one a 
mutexSemaphore, fetch the
routine object representing the "::routine worker" routine. The routine object 
gets asynchroneously
run (executed on another thread in parallel to the main thread) by using the 
start-message (defined
in the root class object), which expects the name of the method to send 
asynchroneously (in this
case "call", which runs the routine) and optionally arguments (in this case the 
semaphore and the
invocation number for sending the "call" message asynchroneously).

The example programs may look a little bit weird, because the output should be 
formatted in a way
that makes it rather easy to find the output from the main thread and the 
output from the three
worker threads (the "worker" object gets three 'call' messages sent in a loop 
causing three threads
to be created on which the routine object gets executed).

------------------------------------------------------------------------------------------------------------------------

EventSemaphore

-----------------------

An "event semaphore" allows one thread "A" to synchronize multiple threads 
(like "B", "C", ...) with
whom the event semaphore gets shared. Thread "A" creates the event semaphore 
and sends it the
"reset" message. Then thread "A" supplies the event semaphore to the threads 
"B", "C", ..., which
each send the received event semaphore a "wait" message. This will block the 
threads "B", "C", ...,
until thread "A" sends the "post" message to the event semaphore. At this time 
all threads waiting
on the event semaphore get released and can run in parallel on their threads.

Using introspection on the new class "EventSemaphore" the following methods are 
currently defined
for it: "close", "isPosted", "post", "reset", "wait".

The following example will fetch the routine object "WORKER" and in a loop will 
send the routine
object the message "start" (defined in the ooRexx root class "Object") 
supplying the message name
("call") to be sent on a new (asynchroneous) thread, and as arguments the event 
semaphore to wait on
and the number of the loop for the respective asynchroneous message.

The main program creates an event semaphore and sends the message "reset" to 
it. Then it sends the
"start" message three times to the routine object in the loop, causing the 
routine "worker" to run
on three different threads, where in each thread the executed routine then 
waits on the event
semaphore ("wait" message to the event semaphore). The main program then sleeps 
for five seconds,
before sending the "post" message to the event semaphore, which will awake the 
routines on the three
threads, ie. they return from the wait message and execute on their threads in 
parallel, causing
each to sleep for two seconds before returning from the routine invocation.

Here is the program "testEventSemaphore.rex":

    /* ---rgf, 20190307, testing new class .EventSemaphore which defines the 
methods:
          close, isPosted, post, reset, wait
    */

    eventSem=.eventSemaphore~new
    say "/main\"~left(11,".")  pp(.dateTime~new) 
"eventSem~isPosted="pp(eventSem~isPosted)
    eventSem~reset
    say "/main\"~left(11,".")  pp(.dateTime~new) "after eventSem~reset, 
eventSem~isPosted="pp(eventSem~isPosted)

    w=.routines~waiter      -- get routine object
    do i=1 to 3
       say "main"~left(11,".")  "running routine 'WAITER' #" pp(i) 
"asynchroneously"
       w~start("call", eventSem, i)  -- run the routine object on a separate 
thread
    end
    say

    sleepTime=5
    say "/main\"~left(11,".")  pp(.dateTime~new) " sleeping" pp(sleepTime) 
"seconds ..."
    call sysSleep sleepTime -- sleep five seconds

    say "/main\"~left(11,".")  pp(.dateTime~new) " awoke, about doing an 
'eventSem~post' ..."
    say
    eventSem~post           -- stop any thread waiting on this event semaphore
    say "\main/"~left(11,".")  pp(.dateTime~new) " leaving main."

    ::routine waiter
       use arg eventSem, nr
       s=.dateTime~new
       say .context~name" #" nr":" pp(s) "arrived, 
eventSem~isPosted="pp(eventSem~isPosted)", waiting..."
       eventSem~wait        -- wait until posted

       e=.dateTime~new
       sleepTime=2
       say .context~name" #" nr":" pp(e)"wait over, 
eventSem~isPosted="pp(eventSem~isPosted)", waited:  " pp(e-s)
       say .context~name" #" nr":" pp(.dateTime~new) "now sleeping" 
pp(sleepTime) "second(s)"
       call syssleep sleeptime -- sleep a second

       o=.dateTime~new
       say .context~name" #" nr": leaving at  " pp(o)", duration:" pp(o-s)
       return .context~name" #" nr": start:" pp(s) "end:" pp(e) 
"wait-duration:" pp(e-s) "now:" pp(o)

    ::routine pp   -- "pretty print" ;) (encloses argument i square brackets)
      return "["arg(1)"]"

Here is the output of one of its run:

    
e:\DropBox\Dropbox\xfer\orx\beta\sandbox_ooRexx\misc_tests\semaphore>testEventSemaphore.rex
    /main\..... [2019-03-07T17:46:55.143000] eventSem~isPosted=[1]
    /main\..... [2019-03-07T17:46:55.143000] after eventSem~reset, 
eventSem~isPosted=[0]
    main....... running routine 'WAITER' # [1] asynchroneously
    main....... running routine 'WAITER' # [2] asynchroneously
    main....... running routine 'WAITER' # [3] asynchroneously
    WAITER # 1: [2019-03-07T17:46:55.159000] arrived, eventSem~isPosted=[0], 
waiting...
    WAITER # 2: [2019-03-07T17:46:55.159000] arrived, eventSem~isPosted=[0], 
waiting...

    WAITER # 3: [2019-03-07T17:46:55.174000] arrived, eventSem~isPosted=[0], 
waiting...
    /main\..... [2019-03-07T17:46:55.174000]  sleeping [5] seconds ...
    /main\..... [2019-03-07T17:47:00.194000]  awoke, about doing an 
'eventSem~post' ...

    \main/..... [2019-03-07T17:47:00.194000]  leaving main.
    WAITER # 3: [2019-03-07T17:47:00.194000]wait over, eventSem~isPosted=[1], 
waited:   [00:00:05.020000]
    WAITER # 2: [2019-03-07T17:47:00.194000]wait over, eventSem~isPosted=[1], 
waited:   [00:00:05.035000]
    WAITER # 1: [2019-03-07T17:47:00.194000]wait over, eventSem~isPosted=[1], 
waited:   [00:00:05.035000]
    WAITER # 3: [2019-03-07T17:47:00.209000] now sleeping [2] second(s)
    WAITER # 2: [2019-03-07T17:47:00.209000] now sleeping [2] second(s)
    WAITER # 1: [2019-03-07T17:47:00.209000] now sleeping [2] second(s)
    WAITER # 3: leaving at   [2019-03-07T17:47:02.239000], duration: 
[00:00:07.065000]
    WAITER # 2: leaving at   [2019-03-07T17:47:02.239000], duration: 
[00:00:07.080000]
    WAITER # 1: leaving at   [2019-03-07T17:47:02.255000], duration: 
[00:00:07.096000]

    e:\DropBox\Dropbox\xfer\orx\beta\sandbox_ooRexx\misc_tests\semaphore>

------------------------------------------------------------------------------------------------------------------------

MutexSemaphore

-----------------------

An "mutex semaphore" remembers the sequence in which different threads ("A", 
"B", "C", ...) acquire
it. The first thread "A" that acquired the mutex semaphore is the one that 
holds it and is able to
execute, all other threads "B", "C", ..., that tried to acquire the same mutex 
semaphore afterwards
get blocked (in waiting mode). Once thread "A" releases the mutex semaphore the 
next blocked thread
"B" becomes able to execute, while thread "C", ..., remain blocked. Then, when 
thread "B" releases
the mutex semaphore the next blocked thread gets released to continue running.

Using introspection on the new class "MutexSemaphore" the following methods are 
currently defined
for it: "acquire", "close", "release".

In the following example the main program will create a mutex semaphore and 
acquire it for itself,
such that any other thread that tries to acquire the same mutex semaphore gets 
blocked. The main
program then fetches the routine object "WORKER" and in a loop will send the 
routine object the
message "start" (defined in the ooRexx root class "Object") supplying the 
message name ("call") to
be sent on a new (asynchroneous) thread, and as arguments the mutex semaphore 
that they need to
acquire and the number of the loop for the respective asynchroneous message.

The main program then sleeps for three seconds before releasing the mutex 
semaphore, which allows
the first blocked thread to acquire it successfully and to continue to run, 
which will cause the
routine on that thread to sleep for two seconds, before returning from the 
routine and ending its
thread of execution, which will automatically (!) release the acquired mutex 
semaphore! This in turn
allows the next blocked thread to acquire the mutex semaphore and do the same 
(sleep two seconds and
then return from the invocation ending the thread), which in turn allows the 
last blocked thread to
acquire the mutex semaphore and do the same.

The output will demonstrate that the three routine threads are blocked until 
the main program
releases the mutex semaphore. Then, the output demonstrates that one by one 
after two seconds of
sleep the three blocked routine threads get run in the sequence that they tried 
to acquire the mutex
semaphore.

Here is the program "testMutexSemaphore.rex":

    /* ---rgf, 20190307, testing new class .EventSemaphore which defines the 
methods:
          acquire, close, release
    */

    mutexSem=.mutexSemaphore~new
    mutexSem~acquire        -- we block everyone else
    say "/main\"~left(11,".") pp(.dateTime~new) "mutexSem" pp(mutexSem) 
"created"

    w=.routines~waiter      -- get routine object
    do i=1 to 3
       say "/main\"~left(11,".") "running routine 'WAITER' #" pp(i) 
"asynchroneously"
       w~start("call", mutexSem, i)  -- run the routine object on a separate 
thread
    end
    say

    sleepTime=3
    say "/main\"~left(11,".") pp(.dateTime~new) " sleeping" pp(sleepTime) 
"seconds" "--> "~copies(6)
    call sysSleep sleepTime -- sleep five seconds
    say "/main\"~left(11,".") pp(.dateTime~new) " awoke" "<-- "~copies(6)
    say
    mutexSem~release        -- now allow the next 'worker' thread waiting on 
the mutexSem to run

    sleepTime=7
    say "/main\"~left(11,".") pp(.dateTime~new) " sleeping" pp(sleepTime) 
"seconds" "--> "~copies(6)
    call sysSleep sleepTime -- sleep five seconds

    say "/main\"~left(11,".") pp(.dateTime~new) " awoke" "<-- "~copies(6)
    say
    say "\main/"~left(11,".") pp(.dateTime~new) " leaving main."


    ::routine waiter
       use arg mutexSem, nr
       s=.dateTime~new
       say .context~name" #" nr":" pp(s) "arrived, about doing mutexSem~acquire 
..."
       mutexSem~acquire     -- wait until semaphore becomes available to us

       e=.dateTime~new
       sleepTime=2
       say .context~name" #" nr":" pp(e) "after mutexSem~acquire, duration:  " 
pp(e-s)
       say .context~name" #" nr":" pp(.dateTime~new) "now sleeping" 
pp(sleepTime) "second(s)"
       call syssleep sleeptime -- sleep a second

       o=.dateTime~new
       say .context~name" #" nr": leaving at  " pp(o)", duration:" pp(o-s)
    say
       return .context~name" #" nr": start:" pp(s) "end:" pp(e) 
"wait-duration:" pp(e-s) "now:" pp(o)


    ::routine pp   -- "pretty print" ;) (encloses argument i square brackets)
      return "["arg(1)"]"

Here is the output of one of its run:

    
e:\DropBox\Dropbox\xfer\orx\beta\sandbox_ooRexx\misc_tests\semaphore>testMutexSemaphore.rex
    /main\..... [2019-03-07T18:11:46.830000] mutexSem [a MutexSemaphore] created
    /main\..... running routine 'WAITER' # [1] asynchroneously
    /main\..... running routine 'WAITER' # [2] asynchroneously
    WAITER # 1: [2019-03-07T18:11:46.830000] arrived, about doing 
mutexSem~acquire ...
    /main\..... running routine 'WAITER' # [3] asynchroneously
    WAITER # 2: [2019-03-07T18:11:46.830000] arrived, about doing 
mutexSem~acquire ...

    WAITER # 3: [2019-03-07T18:11:46.845000] arrived, about doing 
mutexSem~acquire ...
    /main\..... [2019-03-07T18:11:46.861000]  sleeping [3] seconds --> --> --> 
--> --> -->
    /main\..... [2019-03-07T18:11:49.878000]  awoke <-- <-- <-- <-- <-- <--

    /main\..... [2019-03-07T18:11:49.878000]  sleeping [7] seconds --> --> --> 
--> --> -->
    WAITER # 1: [2019-03-07T18:11:49.878000] after mutexSem~acquire, duration:  
 [00:00:03.048000]
    WAITER # 1: [2019-03-07T18:11:49.893000] now sleeping [2] second(s)
    WAITER # 1: leaving at   [2019-03-07T18:11:51.908000], duration: 
[00:00:05.078000]

    WAITER # 2: [2019-03-07T18:11:51.908000] after mutexSem~acquire, duration:  
 [00:00:05.078000]
    WAITER # 2: [2019-03-07T18:11:51.908000] now sleeping [2] second(s)
    WAITER # 2: leaving at   [2019-03-07T18:11:53.930000], duration: 
[00:00:07.100000]

    WAITER # 3: [2019-03-07T18:11:53.930000] after mutexSem~acquire, duration:  
 [00:00:07.085000]
    WAITER # 3: [2019-03-07T18:11:53.930000] now sleeping [2] second(s)
    WAITER # 3: leaving at   [2019-03-07T18:11:55.958000], duration: 
[00:00:09.113000]

    /main\..... [2019-03-07T18:11:56.894000]  awoke <-- <-- <-- <-- <-- <--

    \main/..... [2019-03-07T18:11:56.894000]  leaving main.

    e:\DropBox\Dropbox\xfer\orx\beta\sandbox_ooRexx\misc_tests\semaphore>

---rony


_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to