Termination semaphore for Process

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Termination semaphore for Process

Max Leske
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
Reply | Threaded
Open this post in threaded view
|

Re: [Vm-dev] Termination semaphore for Process

Eliot Miranda-2
Hi Max,

On Fri, Feb 12, 2016 at 6:59 AM, Max Leske <[hidden email]> 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 (2K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

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

Max Leske
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






On 12 Feb 2016, at 19:35, Eliot Miranda <[hidden email]> wrote:

Hi Max,

On Fri, Feb 12, 2016 at 6:59 AM, Max Leske <[hidden email]> 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>




Kernel-MaxLeske.2227.cs (22K) Download Attachment