Rony,

An excellent write up here. One small correction I would make, when
MutexSempahores have multiple waiters, they are not necessarily queued.
Which waiting thread gets the mutex next is determined by the OS dispatch
mechanisms, so which thread gets it next is not predictable.

Rick

On Thu, Mar 7, 2019 at 12:13 PM Rony G. Flatscher <rony.flatsc...@wu.ac.at>
wrote:

> 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>
> <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
>
_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to