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