On 14.03.2019 18:10, Rick McGuire wrote:
> A couple of notes that need to added to the MutexSemaphore section
>
> 1) On a single thread, acquire requests nests, so if a thread calls acquire 
> again while already
> having obtained the semaphore, it will be not blocked. It requires an 
> equivalent number of release
> calls to free up the mutex. 
>
> 2) If the thread holding the mutex ends without releasing the semaphore, the 
> semaphore will be
> released automatically.
>
> Rick 

Thanks, this version now includes this information in the "acquire" part:

    
===============================================================================
    5.4.x EventSemaphore Class

    Semaphores get used in multithreaded scenarios.

    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", ..., each
    of which sends 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.

    5.4.x.1 close

    >>--- close ---><

    Closes the event semaphore.


    5.4.x.2 isPosted

    >>--- isPosted ---><

    Returns .true if the event semaphore is in the "posted" state, such that
    sending the "wait" message to this event semaphore would return
    immediately.  A return value of .false indicates that the event semaphore
    is not in the "posted" state, such that a "wait" message will block.


    5.4.x.3 post

    >>--- post ---><

    Sets the event semaphore to the "posted" state, signalling that the event
    that other threads are waiting on has occurred, such that all threads
    waiting on this event semaphore will be released and start to run again.


    5.4.x.4 reset

    >>--- reset ---><

    Resets (clears) the event semaphore to the non-"posted" state, such that 
"wait"
    messages to this event semaphore will block.


    5.4.x.5 uninit

    >>--- uninit ---><

    This method cleans up the object when it is garbage collected.  It should
    not be invoked directly except via an uninit method of a subclass of the
    EventSemaphore class.


    5.4.x.6 wait

    >>--- wait(--+-------------+--)---><
                 |             |
                 +-- timeout --+

    Waits (blocks) on the event semaphore until it gets "posted".  If the
    optional argument "timeout" was supplied it must be a whole number that
    represents the milliseconds to wait on the event semaphore before
    returning.


    If no "timeout" value was supplied or its value is negative, the wait will
    be forever, ie. until the event semaphore gets "posted".

    If the "timeout" value is zero the method returns immediately with the
    same value that the method "isPosted" would return if sent to the event
    semaphore instead.

    The method returns .true if the event semaphore got "posted", .false if
    the method returns prematurely because the given timeout has occurred.


    Example 5.xxx

    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.

    <code>
            eventSem=.eventSemaphore~new  -- create an event semaphore, will be 
in posted state
            dt=.dateTime~new     -- a .DateTime object to allow measuring 
elapsed time
            say "/main\     "  pp(dt~elapsed) "currently 'eventSem~isPosted':" 
pp(eventSem~isPosted)
            eventSem~reset       -- reset event semaphore, such that "wait" 
will block
            say "/main\     "  pp(dt~elapsed) "after 'eventSem~reset', 
'eventSem~isPosted':" pp(eventSem~isPosted)

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

            sleepTime=5
            say "/main\     "  pp(dt~elapsed)  "sleeping" pp(sleepTime) 
"seconds ..."
            call sysSleep sleepTime -- sleep

            say "/main\     "  pp(dt~elapsed)  "awoke, about doing an 
'eventSem~post' ..."
            say
            eventSem~post        -- wake up all threads waiting on this event 
semaphore
            say "\main/     "  pp(dt~elapsed)  "--> leaving main, other threads 
may still be active!"

            ::routine waiter     -- this routine will be called on a separate 
thread
               use arg eventSem, nr
               s=.dateTime~new   -- we use our own .DateTime object to measure 
elapsed time on this thread
               say .context~name" #" nr":" pp(s~elapsed) "arrived, 
'eventSem~isPosted'="pp(eventSem~isPosted)", about: 'eventSem~wait'"
               eventSem~wait     -- wait until main program posts this event 
semaphore
               say .context~name" #" nr":" pp(s~elapsed) "wait over, 
'eventSem~isPosted':" pp(eventSem~isPosted)

               sleepTime=2
               say .context~name" #" nr":" pp(s~elapsed) "now sleeping" 
pp(sleepTime) "second(s)"
               call syssleep sleeptime -- sleep
               say .context~name" #" nr":" pp(s~elapsed) "sleep over"

            ::routine pp         -- "pretty print" ;) (encloses argument in 
square brackets)
              return "["arg(1)"]"
    </code>

    Running this program may yield the following output:

    <code>
            /main\      [00:00:00.000000] currently 'eventSem~isPosted': [1]
            /main\      [00:00:00.000000] after 'eventSem~reset', 
'eventSem~isPosted': [0]
            /main\      [00:00:00.015000] running routine 'WAITER' # [1] 
asynchroneously
            /main\      [00:00:00.015000] running routine 'WAITER' # [2] 
asynchroneously
            WAITER # 1: [00:00:00.000000] arrived, 'eventSem~isPosted'=[0], 
about: 'eventSem~wait'
            /main\      [00:00:00.015000] running routine 'WAITER' # [3] 
asynchroneously
            WAITER # 2: [00:00:00.000000] arrived, 'eventSem~isPosted'=[0], 
about: 'eventSem~wait'

            WAITER # 3: [00:00:00.000000] arrived, 'eventSem~isPosted'=[0], 
about: 'eventSem~wait'
            /main\      [00:00:00.031000] sleeping [5] seconds ...
            /main\      [00:00:05.056000] awoke, about doing an 'eventSem~post' 
...

            \main/      [00:00:05.056000] --> leaving main, other threads may 
still be active!
            WAITER # 2: [00:00:05.041000] wait over, 'eventSem~isPosted': [1]
            WAITER # 1: [00:00:05.041000] wait over, 'eventSem~isPosted': [1]
            WAITER # 3: [00:00:05.025000] wait over, 'eventSem~isPosted': [1]
            WAITER # 2: [00:00:05.057000] now sleeping [2] second(s)
            WAITER # 1: [00:00:05.057000] now sleeping [2] second(s)
            WAITER # 3: [00:00:05.041000] now sleeping [2] second(s)
            WAITER # 3: [00:00:07.069000] sleep over
            WAITER # 2: [00:00:07.085000] sleep over
            WAITER # 1: [00:00:07.085000] sleep over
    </code>






    
===============================================================================

    5.4.x MutexSemaphore Class

    Semaphores get used in multithreaded scenarios.

    A "mutex semaphore" allows a thread "A" to "acquire" it exclusively,
    causing any other thread that sends the message "acquire" to the same
    mutex semaphore to be blocked as long as thread "A" does not "release" the
    mutex semaphore.

    The first thread "A" that acquired the mutex semaphore is the one that
    holds it exclusively and is able to execute, all other threads "B", "C",
    ..., that tried to "acquire" the same mutex semaphore exclusively
    afterwards get blocked.  Once thread "A" releases the mutex semaphore one
    of the other blocked threads "B", "C", ...  becomes able to execute, while
    the other threads remain blocked.  Then, when the executing thread
    releases the mutex semaphore the next blocked thread gets released and
    will be able to continue running, etc.

    Note: there is no predefined sequence that determines which of the blocked
    threads will get scheduled to run next once the mutex semaphore received
    the "release" message as this scheduling is done by the operating system.


    5.4.x.1 acquire

    >>--- acquire(--+-------------+--)---><
                    |             |
                    +-- timeout --+

    Acquires the mutex semaphore exclusively and blocks any other threads that
    also attempt to "acquire" the mutex semaphore until the mutex semaphore
    gets the message "release" sent.

    If the optional argument "timeout" was supplied it must be a whole number
    that represents the milliseconds to wait on the mutex semaphore before
    returning.

    If no "timeout" value was supplied or its value is negative, the blocking
    will be forever, ie.  until the mutex semaphore becomes available as the
    result of another thread releasing it.

    If the "timeout" value is zero the method returns immediately.  The method
    returns .true if the mutex semaphore got acquired exclusively, .false else
    (some other thread holds the mutex semaphore).  This can be used to try to
    acquire a mutex semaphore without being blocked, if currently it is
    already exclusively acquired by another thread.

    The method returns .true if the mutex semaphore got acquired, .false if
    the method returns prematurely because the given timeout has occurred.

    Note 1: a thread that has successfully acquired a mutex semaphore may send
    additional "acquire" messages without blocking.  It requires an equivalent
    number of "release" messages to free up the mutex semaphore.

    Note 2: if the thread that has successfully acquired the mutex semaphore 
    ends without releasing it, the semaphore will be released automatically.  


    5.4.x.2 close

    >>--- close ---><

    Closes the mutex semaphore.


    5.4.x.3 release

    >>--- release ---><

    Releases (clears) the mutex semaphore, such that one of the blocked threads
    becomes able to "acquire" it successfully.


    5.4.x.4 uninit

    >>--- uninit ---><

    This method cleans up the object when it is garbage collected.  It should
    not be invoked directly except via an uninit method of a subclass of the
    MutexSemaphore class.



    Example 5.xxx

    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 one of the blocked threads to acquire it
    successfully.  The routine on that thread will sleep for two seconds and
    then return which causes its thread of execution to end (this will
    automatically release the acquired mutex semaphore).  This in turn allows
    another blocked thread to acquire the mutex semaphore and do the same
    (sleep for two seconds and then return from the invocation ending its
    thread of execution), 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.  Please note that the
    sequence in which the blocked threads get scheduled to be run next is
    dependent on the scheduling algorithms of the operating system.

    <code>
            mutexSem=.mutexSemaphore~new  -- create a mutex semaphore, will be 
in released state
            dt=.dateTime~new     -- a .DateTime object to allow measuring 
elapsed time
            mutexSem~acquire     -- we block everyone else
            say "/main\     "  pp(dt~elapsed) "'mutexSem' created and acquired"
            w=.routines~waiter   -- get routine 'WAITER' as a routine object
            do i=1 to 3
               say "/main\     "  pp(dt~elapsed) "running routine 'WAITER' #" 
pp(i) "asynchroneously"
               w~start("call", mutexSem, i)  -- call (run) the routine object 
on a separate thread
            end
            say
            sleepTime=3
            say "/main\     "  pp(dt~elapsed) "sleeping" pp(sleepTime) 
"seconds" "--> "~copies(5)
            call sysSleep sleepTime -- sleep
            say "/main\     "  pp(dt~elapsed) "awoke, about doing a 
'mutexSem~release' <--"
            mutexSem~release     -- now allow one of the 'worker' threads 
waiting on the mutexSem to run

            sleepTime=7
            say "/main\     "  pp(dt~elapsed) "sleeping" pp(sleepTime) 
"seconds" "--> "~copies(5)
            call sysSleep sleepTime -- sleep
            say "\main/     "  pp(dt~elapsed) "awoke, leaving main" "<-- 
"~copies(5)

            ::routine waiter     -- this routine will be called on a separate 
'worker' thread
               use arg mutexSem, nr
               s=.dateTime~new   -- we use our own .DateTime object to measure 
elapsed time on this thread
               say .context~name" #" nr":" pp(s~elapsed) "arrived, about doing 
'mutexSem~acquire' ..."
               mutexSem~acquire  -- wait until semaphore becomes (exclusively) 
available to us
               say .context~name" #" nr":" pp(s~elapsed) "after 
'mutexSem~acquire'"

               sleepTime=2
               say .context~name" #" nr":" pp(s~elapsed) "now sleeping" 
pp(sleepTime) "second(s)"
               call syssleep sleeptime -- sleep
               say .context~name" #" nr":" pp(s~elapsed) "sleep over"
               say

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

    Running this program may yield the following output:

    <code>
            /main\      [00:00:00.000000] 'mutexSem' created and acquired
            /main\      [00:00:00.000000] running routine 'WAITER' # [1] 
asynchroneously
            /main\      [00:00:00.000000] running routine 'WAITER' # [2] 
asynchroneously
            WAITER # 1: [00:00:00.000000] arrived, about doing 
'mutexSem~acquire' ...
            /main\      [00:00:00.000000] running routine 'WAITER' # [3] 
asynchroneously
            WAITER # 2: [00:00:00.000000] arrived, about doing 
'mutexSem~acquire' ...

            WAITER # 3: [00:00:00.000000] arrived, about doing 
'mutexSem~acquire' ...
            /main\      [00:00:00.000000] sleeping [3] seconds --> --> --> --> 
-->
            /main\      [00:00:03.011000] awoke, about doing a 
'mutexSem~release' <--
            /main\      [00:00:03.011000] sleeping [7] seconds --> --> --> --> 
-->
            WAITER # 1: [00:00:03.011000] after 'mutexSem~acquire'
            WAITER # 1: [00:00:03.011000] now sleeping [2] second(s)
            WAITER # 1: [00:00:05.023000] sleep over

            WAITER # 2: [00:00:05.023000] after 'mutexSem~acquire'
            WAITER # 2: [00:00:05.023000] now sleeping [2] second(s)
            WAITER # 2: [00:00:07.036000] sleep over

            WAITER # 3: [00:00:07.036000] after 'mutexSem~acquire'
            WAITER # 3: [00:00:07.036000] now sleeping [2] second(s)
            WAITER # 3: [00:00:09.048000] sleep over

            \main/      [00:00:10.017000] awoke, leaving main <-- <-- <-- <-- 
<--
    </code>




    
===============================================================================

---rony

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

Reply via email to