#fork and deterministic resumption of the resulting process

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

Re: #fork and deterministic resumption of the resulting process

Louis LaBrunda
Hi,

I ran into a similar problem in VAST that I got around by extending Block and
Process.  I extended Block with #forkReady, #forkReadyAt: and some others and
Process with #ready.  The #forkReady extension to Block basically does what it
says, it creates a new fork or process in the ready state by does not start it
immediately.  The new process will wait for its normal turn to start.  The
#ready extension to Process makes the process ready without running it
immediately and is used by #forkReady.

I didn't want to change the way #fork worked so I made the extensions, but I
don't see any reason why #fork couldn't be changed to work the way I made
#forkReady work.  I would be surprised if anyone was counting on #fork
immediately starting the new process before the process that creates the fork
would yield for other reasons.

Lou

On Mon, 04 Feb 2008 13:04:13 -0800, Andreas Raab <[hidden email]> wrote:

>Hi -
>
>In my never-ending quest for questionable behavior in multi-threaded
>situations just today I ran into a pattern which is dangerously common
>in our code. It basically goes like this:
>
>MyClass>>startWorkerProcess
> "worker is an instance variable"
> worker:= [self runWorkerProcess] fork.
>
>MyClass>>runWorkerProcess
> "Run the worker process"
> [Processor activeProcess == worker] whileTrue:[
> "...do the work..."
> ].
>
>MyClass>>stopWorkerProcess
> "Stop the worker process"
> worker := nil. "let it terminate itself"
>
>Those of you who can immediately tell what the problem is should get a
>medal for an outstanding knack of analyzing concurrency problems ;-)
>
>For the rest of us, the problem is that #fork in the above is not
>deterministic in the way that there is no guarantee whether the "worker"
>variable will be assigned when we enter the worker loop. It *would* be
>deterministic if the priority were below or above the current process'
>priority but when it's the same it can be affected by environmental
>effects (external signals, delay processing etc) leading to some very
>obscure runtime problems (in the above, the process would just not start).
>
>To fix this problem I have changed BlockContext>>fork and
>BlockContext>>forkAt: to read, e.g.,
>
>BlockContext>>fork
>   "Create and schedule a Process running the code in the receiver."
>   ^self forkAt: Processor activePriority
>
>BlockContext>>forkAt: priority
>   "Create and schedule a Process running the code in the receiver
>   at the given priority. Answer the newly created process."
>   | forkedProcess helperProcess |
>   forkedProcess := self newProcess.
>   forkedProcess priority: priority.
>   priority = Processor activePriority ifTrue:[
>     helperProcess := [forkedProcess resume] newProcess.
>     helperProcess priority: priority-1.
>     helperProcess resume.
>   ] ifFalse:[
>     forkedProcess resume
>   ].
>   ^forkedProcess
>
>This will make sure that #fork has (for the purpose of resumption) the
>same semantics as forking at a lower priority has.
>
>What do people think about this?
>
>Cheers,
>   - Andreas
>
-----------------------------------------------------------
Louis LaBrunda
Keystone Software Corp.
SkypeMe callto://PhotonDemon
mailto:[hidden email] http://www.Keystone-Software.com


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Paolo Bonzini-2
In reply to this post by Andreas.Raab

> Err, it is not what? Deterministic? Or safe? The point about it being
> deterministic did not relate to when exactly the process would resume
> (no real-time guarantee) but rather that it would resume
> deterministically in relation to its parent process (in this case, only
> after the parent process got suspended).

Not completely deterministic.  The behavior may change depending on
whether there are other processes or not, that run at the active process
priority.

What if you instead bumped a little the priority of the active process
until the forked process started running?

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Paolo Bonzini-2
In reply to this post by Louis LaBrunda

> I ran into a similar problem in VAST that I got around by extending Block and
> Process.  I extended Block with #forkReady, #forkReadyAt: and some others and
> Process with #ready.  The #forkReady extension to Block basically does what it
> says, it creates a new fork or process in the ready state by does not start it
> immediately.  The new process will wait for its normal turn to start.  The
> #ready extension to Process makes the process ready without running it
> immediately and is used by #forkReady.

I bet that your #forkReady has the same race condition problem that
Andreas is trying to solve in Squeak's #fork.  :-(

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Igor Stasenko
Let's look at the fork from theoretical POV, not practical implementation.
When developer puts fork in code, he supposing that execution will
spawn another, parallel process:

          /-------- original process
---------
          \-------- spawned fork

--------> time

Suppose, in ideal environment both processes start running in
parallel, and there is no scheduling, preemption and another gory
details.

So, it's obvious: if you need some state to be consistent in both
execution threads, you should make sure to initialize it before fork
happens.

Andreas gave a good example of typical error, when you writing a
concurrent code: trying to initialize the state after doing fork,
making assumption that we not working under ideal environment and
bound solution to specific implementation (knowledge on how scheduler
works e.t.c.).

Also, if changes, what Andreas proposed will take place, then fork
will be not a fork anymore (in original context), since it's semantics
puts much favor on execution of original process.

Also, in case when you will need/want to port your code on another
smalltalk dialect, your code have a good chances to not work
correctly. And if you may know, catching such errors can take a lot of
time.

--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Andreas.Raab
In reply to this post by Paolo Bonzini-2
Paolo Bonzini wrote:
>> Err, it is not what? Deterministic? Or safe? The point about it being
>> deterministic did not relate to when exactly the process would resume
>> (no real-time guarantee) but rather that it would resume
>> deterministically in relation to its parent process (in this case,
>> only after the parent process got suspended).
>
> Not completely deterministic.  The behavior may change depending on
> whether there are other processes or not, that run at the active process
> priority.

It is entirely deterministic in such that the forked process will not be
resumed until the parent process is suspended. No amount of other
processes change that; even if they take 100% CPU it won't change the
fact that the process will not be resumed before its parent is suspended.

> What if you instead bumped a little the priority of the active process
> until the forked process started running?

And how does the parent process know that the forked process is running?

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Louis LaBrunda
In reply to this post by Paolo Bonzini-2
On Tue, 05 Feb 2008 22:12:37 +0100, Paolo Bonzini <[hidden email]> wrote:

>> I ran into a similar problem in VAST that I got around by extending Block and
>> Process.  I extended Block with #forkReady, #forkReadyAt: and some others and
>> Process with #ready.  The #forkReady extension to Block basically does what it
>> says, it creates a new fork or process in the ready state by does not start it
>> immediately.  The new process will wait for its normal turn to start.  The
>> #ready extension to Process makes the process ready without running it
>> immediately and is used by #forkReady.
>
>I bet that your #forkReady has the same race condition problem that
>Andreas is trying to solve in Squeak's #fork.  :-(
>
>Paolo
>

I haven't shown any code because VAST and Squeak are different enough in this
area that my #forkReady code would not be helpful but if there is still a race
condition is should be greatly reduced because the #forkReady code puts the
newly created process in the queue of ready processes and returns.  The setting
of the instance variable would have to be interrupted immediately by a task
switch, caused by a timer or some other interrupt.  At least the task switch
would not because by the fork code itself.  Andreas' suggested code looks like
it is trying to delay the task switch to the new process by what seems to me to
be a somewhat convoluted means (sorry Andreas, no offence intended).

Lou
-----------------------------------------------------------
Louis LaBrunda
Keystone Software Corp.
SkypeMe callto://PhotonDemon
mailto:[hidden email] http://www.Keystone-Software.com


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Andreas.Raab
Louis LaBrunda wrote:
> I haven't shown any code because VAST and Squeak are different enough in this
> area that my #forkReady code would not be helpful but if there is still a race
> condition is should be greatly reduced because the #forkReady code puts the
> newly created process in the queue of ready processes and returns.  The setting
> of the instance variable would have to be interrupted immediately by a task
> switch, caused by a timer or some other interrupt.

Which is *precisely* the problem we're talking about. Can you imagine
how annoying it is that the code works in all your tests and when you
ship it to a couple hundred customers it blows up just for statistical
reasons?

Cheers,
   - Andreas


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Joshua Gargus-2
In reply to this post by Paolo Bonzini-2

On Feb 5, 2008, at 1:12 PM, Paolo Bonzini wrote:

>
>> I ran into a similar problem in VAST that I got around by extending  
>> Block and
>> Process.  I extended Block with #forkReady, #forkReadyAt: and some  
>> others and
>> Process with #ready.  The #forkReady extension to Block basically  
>> does what it
>> says, it creates a new fork or process in the ready state by does  
>> not start it
>> immediately.  The new process will wait for its normal turn to  
>> start.  The
>> #ready extension to Process makes the process ready without running  
>> it
>> immediately and is used by #forkReady.
>
> I bet that your #forkReady has the same race condition problem that  
> Andreas is trying to solve in Squeak's #fork.  :-(

I think you misunderstand (unless I misunderstand)... it sounds like  
the Process created by #forkReady doesn't start until explicitly  
resumed:

worker := [self workerLoop] forkReady.
worker resume.

Also, Andreas is not trying to solve a race condition (for example,  
the race condition still exists if you fork at a higher priority).  
He's just trying to make it deterministic: make it either work all the  
time (as with forking a lower-priority process, or with Andreas's  
patch, a process of the same priority), or fail all of the time (as  
with forking a higher-priority process).  This makes it easier to  
debug than having something that works 9999 times out of 10000.

Josh



>
>
> Paolo
>


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Joshua Gargus-2

On Feb 5, 2008, at 2:57 PM, Joshua Gargus wrote:

>
> On Feb 5, 2008, at 1:12 PM, Paolo Bonzini wrote:
>
>>
>>> I ran into a similar problem in VAST that I got around by  
>>> extending Block and
>>> Process.  I extended Block with #forkReady, #forkReadyAt: and some  
>>> others and
>>> Process with #ready.  The #forkReady extension to Block basically  
>>> does what it
>>> says, it creates a new fork or process in the ready state by does  
>>> not start it
>>> immediately.  The new process will wait for its normal turn to  
>>> start.  The
>>> #ready extension to Process makes the process ready without  
>>> running it
>>> immediately and is used by #forkReady.
>>
>> I bet that your #forkReady has the same race condition problem that  
>> Andreas is trying to solve in Squeak's #fork.  :-(
>
> I think you misunderstand (unless I misunderstand)... it sounds like  
> the Process created by #forkReady doesn't start until explicitly  
> resumed:

I'm wrong again, I misunderstood how #forkReady is supposed to work.  
I don't know how the VAST scheduler works, so maybe there is no race-
condition, but it sounds very similar to  the Squeak race-condition  
that we're talking about.

I should really stop posting on this topic.

Josh


>>
>>
>> Paolo
>>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Michael van der Gulik-2
In reply to this post by Bert Freudenberg


On Feb 5, 2008 11:02 PM, Bert Freudenberg <[hidden email]> wrote:
On Feb 5, 2008, at 10:29 , Andreas Raab wrote:

> Paolo Bonzini wrote:
>> I'm with Terry on the correct idiom to use, i.e.
>>     workerProcess := [self runWorkerProcess] newProcess.
>>     workerProcess resume.
>
> Sigh. One of the problems with examples is that they are ... well
> examples. They are not the actual code. The above solution is
> simply not applicable in our context (if it were, I would agree
> with it as the better solution).
>
> [BTW, I'm gonna drop out of this thread since it's clear that there
> is too much opposition for such a change to get into Squeak. Which
> is fine by me - I'll wait until you will get bitten in some really
> cruel and unusual ways and at that point you might be ready to
> understand why this fix is valuable. Personally, I think that
> changes that take out an unusual case of non-determinism like here
> are always worth it - if behavior is deterministic you can test it
> and fix it. If it's not you might get lucky a hundred times in a
> row. And in the one critical situation it will bite you].

Well, you should give us a bit more than a few hours ;) Until now
most posters did not even understand the proposal.

I for one would appreciate getting your fix in. It does not change
the current semantics, and makes one very common idiom (var := [...]
fork) safer to use. There may be better idioms, granted. However, for
now Squeak's scheduling policy is beautifully deterministic, and I
like keeping simple things simple.


Andreas's original code was buggy and his proposed fix was incorrect.

You should never make any assumptions about the scheduling behaviour of your environment. It can and will change in the future. Also, it makes your code less portable across dialects.

Gulik.


--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Andreas.Raab
Michael van der Gulik wrote:
> Andreas's original code was buggy and his proposed fix was incorrect.

If I would need any more proof that people don't get what I'm trying to
fix, this would do it ;-) Seriously, you *really* don't get what I'm
trying to address with the fix.

Anyway, I don't care. Croquet has that fix and it's your choice to
ignore it and deal with the consequences. And with that I'm out of this
thread for real and apologize for the pointless waste of bandwidth.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Paolo Bonzini-2
In reply to this post by Andreas.Raab

>> Not completely deterministic.  The behavior may change depending on
>> whether there are other processes or not, that run at the active
>> process priority.
>
> It is entirely deterministic in such that the forked process will not be
> resumed until the parent process is suspended. No amount of other
> processes change that; even if they take 100% CPU it won't change the
> fact that the process will not be resumed before its parent is suspended.

I'm talking about starvation, due to the forked process not entering
Squeak's round-robin scheduling at the given priority.  It cannot happen
without your patch, and can with.

>> What if you instead bumped a little the priority of the active process
>> until the forked process started running?
>
> And how does the parent process know that the forked process is running?

The forked process could reset the priority of the parent process:

fork
     ^self forkAt: (Processor activePriority bitAnd: -2)

forkAt: priority
     Processor activePriority = priority ifTrue: [
         p := Processor activeProcess.
         p priority: (priority bitOr: 1).
         forkedProcess :=
             [p priority = (priority bitOr: 1)
                 ifTrue: [p priority: priority].
             self value] newProcess.
     ] ifFalse: [
         forkedProcess := self newProcess
     ].
     forkedProcess priority: priority.
     forkedProcess resume.
     ^forkedProcess

Maybe this causes a different kind of race condition though.

Paolo

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Michael van der Gulik-2
In reply to this post by Andreas.Raab
On 2/6/08, Andreas Raab <[hidden email]> wrote:

> Michael van der Gulik wrote:
> > Andreas's original code was buggy and his proposed fix was incorrect.
>
> If I would need any more proof that people don't get what I'm trying to
> fix, this would do it ;-) Seriously, you *really* don't get what I'm
> trying to address with the fix.
>
> Anyway, I don't care. Croquet has that fix and it's your choice to
> ignore it and deal with the consequences. And with that I'm out of this
> thread for real and apologize for the pointless waste of bandwidth.

Sorry; I think my original email was a bit blunt and direct. No
personal attack was intended and I do have a lot of respect for you.

Gulik.

--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Mathieu SUEN
In reply to this post by Andreas.Raab

On Feb 5, 2008, at 7:51 PM, Andreas Raab wrote:

> Paolo Bonzini wrote:
>>> That's part of the reason why I won't pursue these changes here.  
>>> To me these changes are just as important as the ones that I  
>>> posted for Delay and Semaphore. However, unless one understands  
>>> the kinds of problems that are caused by the current code it is  
>>> pointless to argue that fixing them is important - I'm sure that  
>>> unless people had been bitten by Delay and Semaphore we would have  
>>> the same kinds of debates with all sorts of well-meant advise on  
>>> how you "ought" to write your code ;-)
>> It's not that I don't think it's important.  I think the *bugs* are  
>> important to fix, but that the root cause just *cannot* be fixed.
>
> This completely depends on your definition of "root cause" and  
> "cannot". For me, it's the fact that fork will behave in 99.99% of  
> the cases in one way and in 0.01% in a different way. That kind of  
> non-determinism is probably the root cause for many lingering bugs  
> in our system and it *can* be eliminated.
>
>> It's just that:
>> 1) the many people who made the same mistake maybe were just  
>> cutting'n'pasting buggy code;
>
> That is of course a possibility but unless you think the majority of  
> people recognized the bug in the code snippet I posted, I fail to  
> see how this makes a difference.
>
>> 2) especially, the fix is not 100% safe unless I'm mistaken.
>
> What do you mean by "100% safe"? It is 100% deterministic (which is  
> what I care about); I'm not sure what you mean when you use the term  
> "safe" here.

It may be a stupid remark but I try :)
Even with your fix that could be not safe if the event handler have a  
higher priority.
Am I right?

>
>
> Cheers,
>  - Andreas
>

        Mth





Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Bert Freudenberg
In reply to this post by Michael van der Gulik-2
On Feb 6, 2008, at 0:08 , Michael van der Gulik wrote:

> You should never make any assumptions about the scheduling  
> behaviour of your environment.

Nonsense. Squeak *defines* the scheduling behavior. That's one, maybe  
even *the* major benefit over system threads.

- Bert -


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Michael van der Gulik-2
On 2/7/08, Bert Freudenberg <[hidden email]> wrote:
> On Feb 6, 2008, at 0:08 , Michael van der Gulik wrote:
>
> > You should never make any assumptions about the scheduling
> > behaviour of your environment.
>
> Nonsense. Squeak *defines* the scheduling behavior. That's one, maybe
> even *the* major benefit over system threads.


Er... I can't tell if you're joking or if you're serious.

I always write my code under the assumption that some day, in the
future, some bright spark will write a better VM that will run
separate Smalltalk Processes on separate OS processes/threads by
whatever means, thus better utilising multi-cored or multiple CPUs.

When that day comes, my code will run faster and your code will break.

Gulik.

--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/

Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Nicolas Cellier-3
In reply to this post by Andreas.Raab
Andreas Raab a écrit :
>
> Anyway, I don't care. Croquet has that fix and it's your choice to
> ignore it and deal with the consequences. And with that I'm out of this
> thread for real and apologize for the pointless waste of bandwidth.
>

Yeah, after discussing for so long licence issues, underscores, the
future of this or whatever, we sure cannot waste any more time on
pragmatic things.

Your change has my vote, would you be kind to open a Mantis issue?

Any interest in completing the api with opposite alternative by way of a
helperProcess at priority+1?
Don't know how to name it, #forkFront ?

Nicolas

> Cheers,
>   - Andreas
>
>


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Nicolas Cellier-3
In reply to this post by Michael van der Gulik-2
Michael van der Gulik a écrit :
> Er... I can't tell if you're joking or if you're serious.
>
> I always write my code under the assumption that some day, in the
> future, some bright spark will write a better VM that will run
> separate Smalltalk Processes on separate OS processes/threads by
> whatever means, thus better utilising multi-cored or multiple CPUs.
>
> When that day comes, my code will run faster and your code will break.
>

I see, sustainable development. Your writing code for our children.


> Gulik.
>


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Igor Stasenko
On 06/02/2008, nicolas cellier <[hidden email]> wrote:

> Michael van der Gulik a écrit :
> > Er... I can't tell if you're joking or if you're serious.
> >
> > I always write my code under the assumption that some day, in the
> > future, some bright spark will write a better VM that will run
> > separate Smalltalk Processes on separate OS processes/threads by
> > whatever means, thus better utilising multi-cored or multiple CPUs.
> >
> > When that day comes, my code will run faster and your code will break.
> >
>
> I see, sustainable development. Your writing code for our children.
>
Oh, come on, how you can be so blind?
You need a ground, based on which you can build your application.
You need some API with clear rules for play.
And these rules defined by shared principles and common terms, easily
understandable by people.
Newcomer who will start doing things should not spend time harvesting,
how #fork works on particular squeak version.

So, when i say, #fork, it should fork today, and should fork tomorrow,
and should fork after 20 years. Because it's a generic rule, and API
should behave as generic rule dictates: do whatever it take to make
two processes run in parallel.
But if you start doing reverse: let API influence rules, then what you
will get in result?
10 APIs and each defining own semantics of fork?


--
Best regards,
Igor Stasenko AKA sig.


Reply | Threaded
Open this post in threaded view
|

Re: #fork and deterministic resumption of the resulting process

Michael van der Gulik-2
On 2/7/08, Igor Stasenko <[hidden email]> wrote:

> On 06/02/2008, nicolas cellier <[hidden email]> wrote:
> > Michael van der Gulik a écrit :
> > > Er... I can't tell if you're joking or if you're serious.
> > >
> > > I always write my code under the assumption that some day, in the
> > > future, some bright spark will write a better VM that will run
> > > separate Smalltalk Processes on separate OS processes/threads by
> > > whatever means, thus better utilising multi-cored or multiple CPUs.
> > >
> > > When that day comes, my code will run faster and your code will break.
> > >
> >
> > I see, sustainable development. Your writing code for our children.
> >
>
> Oh, come on, how you can be so blind?
> You need a ground, based on which you can build your application.
> You need some API with clear rules for play.
> And these rules defined by shared principles and common terms, easily
> understandable by people.
> Newcomer who will start doing things should not spend time harvesting,
> how #fork works on particular squeak version.
>
> So, when i say, #fork, it should fork today, and should fork tomorrow,
> and should fork after 20 years. Because it's a generic rule, and API
> should behave as generic rule dictates: do whatever it take to make
> two processes run in parallel.
> But if you start doing reverse: let API influence rules, then what you
> will get in result?
> 10 APIs and each defining own semantics of fork?


Sig: I think that people are now just saying rubbish to provoke a
reaction. I wouldn't bother replying.

Gulik.

--
http://people.squeakfoundation.org/person/mikevdg
http://gulik.pbwiki.com/

123456