Termination semaphore for Process
Hi all, I have a proposal and Iâd like to hear your opinion. Right now, when I want to wait for a process to finish I use something like #forkAndWait or I write a block with semaphores myself. This works fine for processes that I spawn and then immediately want to wait for. But what if I spawn a process somewhere else and only later know that I want to wait for its termination? Currently I would construct a polling loop to do that. Hereâs a different idea (which Iâve tested): 1. add instance variable âterminationSemaphoreâ to Process 2. at the end of Process>>terminate #ensure: that all waiting processes are signaled 3. add #waitForTermination message to Process: waitForTermination terminationSemaphore ifNil: [ terminationSemaphore := Semaphore new ]. self resume. terminationSemaphore wait This enables code like the following: result := 0. p := [ result := 2. 2 seconds asDelay wait ] newProcess priority: 20; yourself. âdonât run the new process yet" [ p waitForTermination. result2 := 3. ] forkAt: 30. âp is now running, the second process is waiting" p waitForTermination. âp is still running, two processes are waitingâ âbecause processes waiting on a semaphore are signaled in the order they sent #wait we even achieve ordering (at least with equal process priorities) and so âresult2â is not nil but 3â {result. result2} â#(2 3) I wrote this in 10 minutes, so thereâs room for polishing. I just want to know if this is something other people might want and if there are potential problems with process termination and semaphores which I am not aware of. Cheers, Max
Hi Max, On Fri, Feb 12, 2016 at 6:59 AM, Max Leske <maxleske@gmail.com> wrote:
Hi all,
I have a proposal and Iâd like to hear your opinion.
Right now, when I want to wait for a process to finish I use something like #forkAndWait or I write a block with semaphores myself. This works fine for processes that I spawn and then immediately want to wait for. But what if I spawn a process somewhere else and only later know that I want to wait for its termination? Currently I would construct a polling loop to do that.
Hereâs a different idea (which Iâve tested):
1. add instance variable âterminationSemaphoreâ to Process 2. at the end of Process>>terminate #ensure: that all waiting processes are signaled 3. add #waitForTermination message to Process:
waitForTermination terminationSemaphore ifNil: [ terminationSemaphore := Semaphore new ]. self resume. terminationSemaphore wait
This enables code like the following:
result := 0. p := [ result := 2. 2 seconds asDelay wait ] newProcess priority: 20; yourself.
âdonât run the new process yet" [ p waitForTermination. result2 := 3. ] forkAt: 30. âp is now running, the second process is waiting"
p waitForTermination. âp is still running, two processes are waitingâ
âbecause processes waiting on a semaphore are signaled in the order they sent #wait we even achieve ordering (at least with equal process priorities) and so âresult2â is not nil but 3â
{result. result2} â#(2 3)
I wrote this in 10 minutes, so thereâs room for polishing. I just want to know if this is something other people might want and if there are potential problems with process termination and semaphores which I am not aware of.
Seems very nice. But why not add both terminationSemaphore and result? So we have Process class methods for instance creation forBlock: aBlockClosure priority: anInteger "Answer an instance of me that has suspended aContext at priority anInteger." <primitive: 19> "Simulation guard" | newProcess | (newProcess := self new) suspendedContext: [newProcess result: aBlockClosure value. newProcess terminationSemaphore ifNotNil: [:ts| ts signal]. "Since control is now at the bottom there is no need to terminate (which runs unwinds) since all unwinds have been run. Simply suspend. Note that we must use this form rather than e.g. Processor suspendActive so that isTerminated answers true. isTerminated requires that if there is a suspended context it is the bottom-most, but using a send would result in the process's suspendedContext /not/ being the bottom-most." newProcess suspend] asContext; priority: anInteger. ^newProcess BlockClosure methods for scheduling newProcess "Answer a Process running the code in the receiver. The process is not scheduled." ^Process forBlock: self priority: Processor activePriority Process methods for termination waitForTermination terminationSemaphore ifNil: [ terminationSemaphore := Semaphore new ]. self resume. terminationSemaphore wait. ^result and hence ((1 to: 3) collect: [:i| [(Delay forSeconds: i) wait. i] forkAt: Processor activePriority - 1]) collect: [:p| p waitForTermination] #(1 2 3) [find attached, leaving BlockClosure>>newProcessWith: as an exercise for the reader ;-) ] _,,,^..^,,,_ best, Eliot
Thanks Eliot. Nice idea to return the result. What didnât work for me was to only suspend the process in #forBlock:priority:. I had to send #terminate instead. There are two main reasons: 1. #isTerminating (not #isTerminated) will not be correctly set if #terminate hasnât been sent 2. when signalling the semaphore I want *all* processes that are waiting to be signalled so that the terminating process canât be preempted while signalling the semaphore. Otherwise it may resume a higher priority process that will not allow the other processes still waiting on the semaphore to run. To do this I have to use different approaches for active and inactive processes, such that #isTerminated is always true when the termination semaphore has been signalled. Iâd appreciate it if you could take a look. Maybe youâll spot something I could have done better. Cheers, Max Fogbugz issue: https://pharo.fogbugz.com/f/cases/17588/Termination-semaphore-for-Process
On 12 Feb 2016, at 19:35, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Max,
On Fri, Feb 12, 2016 at 6:59 AM, Max Leske <maxleske@gmail.com <mailto:maxleske@gmail.com>> wrote:
Hi all,
I have a proposal and Iâd like to hear your opinion.
Right now, when I want to wait for a process to finish I use something like #forkAndWait or I write a block with semaphores myself. This works fine for processes that I spawn and then immediately want to wait for. But what if I spawn a process somewhere else and only later know that I want to wait for its termination? Currently I would construct a polling loop to do that.
Hereâs a different idea (which Iâve tested):
1. add instance variable âterminationSemaphoreâ to Process 2. at the end of Process>>terminate #ensure: that all waiting processes are signaled 3. add #waitForTermination message to Process:
waitForTermination terminationSemaphore ifNil: [ terminationSemaphore := Semaphore new ]. self resume. terminationSemaphore wait
This enables code like the following:
result := 0. p := [ result := 2. 2 seconds asDelay wait ] newProcess priority: 20; yourself.
âdonât run the new process yet" [ p waitForTermination. result2 := 3. ] forkAt: 30. âp is now running, the second process is waiting"
p waitForTermination. âp is still running, two processes are waitingâ
âbecause processes waiting on a semaphore are signaled in the order they sent #wait we even achieve ordering (at least with equal process priorities) and so âresult2â is not nil but 3â
{result. result2} â#(2 3)
I wrote this in 10 minutes, so thereâs room for polishing. I just want to know if this is something other people might want and if there are potential problems with process termination and semaphores which I am not aware of.
Seems very nice. But why not add both terminationSemaphore and result? So we have
Process class methods for instance creation forBlock: aBlockClosure priority: anInteger "Answer an instance of me that has suspended aContext at priority anInteger."
<primitive: 19> "Simulation guard" | newProcess | (newProcess := self new) suspendedContext: [newProcess result: aBlockClosure value. newProcess terminationSemaphore ifNotNil: [:ts| ts signal]. "Since control is now at the bottom there is no need to terminate (which runs unwinds) since all unwinds have been run. Simply suspend. Note that we must use this form rather than e.g. Processor suspendActive so that isTerminated answers true. isTerminated requires that if there is a suspended context it is the bottom-most, but using a send would result in the process's suspendedContext /not/ being the bottom-most." newProcess suspend] asContext; priority: anInteger. ^newProcess
BlockClosure methods for scheduling newProcess "Answer a Process running the code in the receiver. The process is not scheduled." ^Process forBlock: self priority: Processor activePriority
Process methods for termination waitForTermination terminationSemaphore ifNil: [ terminationSemaphore := Semaphore new ]. self resume. terminationSemaphore wait. ^result
and hence
((1 to: 3) collect: [:i| [(Delay forSeconds: i) wait. i] forkAt: Processor activePriority - 1]) collect: [:p| p waitForTermination] #(1 2 3)
[find attached, leaving BlockClosure>>newProcessWith: as an exercise for the reader ;-) ]
_,,,^..^,,,_ best, Eliot <waitForTermination.st>
participants (2)
-
Eliot Miranda -
Max Leske