O.K., got some time for MutexSemaphore and in the course of writing it, I
corrected the draft for
EventSemaphore as well, including the code sample.
Therefore, here the suggestions/drafts for documenting the two classes
"EventSemaphore" and
"MutexSemaphore":
===============================================================================
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", ...,
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.
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.
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>
Correct, change it as you see fit.
---rony
_______________________________________________
Oorexx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/oorexx-devel