[Pharo-dev] [Vm-dev] Termination semaphore for Process

Max Leske maxleske at gmail.com
Sat Feb 13 16:42:46 UTC 2016

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. 


Fogbugz issue: https://pharo.fogbugz.com/f/cases/17588/Termination-semaphore-for-Process

> On 12 Feb 2016, at 19:35, Eliot Miranda <eliot.miranda at gmail.com> wrote:
> Hi Max,
> On Fri, Feb 12, 2016 at 6:59 AM, Max Leske <maxleske at gmail.com <mailto:maxleske at 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>

-------------- next part --------------
Skipped content of type multipart/mixed

More information about the Vm-dev mailing list