WAProcess>>critical:ifError problem

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

WAProcess>>critical:ifError problem

Esteban A. Maringolo-3
Hi,

I'm having problems with the following method in WAProcess, on a
Dolphin 5 Smalltalk:

critical: aBlock ifError: errorBlock
   |value|
   mutex critical:[
        responseSem := Semaphore new.
        process := [
                        [value := aBlock on: Error do: errorBlock]
                        ensure: [responseSem signal]
                ] fork.
        responseSem wait
   ].
   process := nil.
   ^ value


After evaluating aBlock value gets its contents (a WAResponse).
But when the mutex ends the critical evaluation, value is nil.

Perhaps it has something with the Continuations that happens inside
the process, or something similar.

What can be causing this?


This message is cross posted  to Seaside list, and Dolphin newsgroup
intentionally.

Best regards,

--
Esteban.


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Esteban A. Maringolo-3
Hi,

Without having found a solution to this problem, evaluating the
following code on a workspace, answers the expected value.

|value mutex block errorBlock |
block := [Processor sleep: 1000. 'Ey! I am not nil!'].
errorBlock := [Transcript show: 'Error!';cr].
mutex := Semaphore forMutualExclusion.
mutex critical:[
     responseSem := Semaphore new.
     process := [
             [value := block on: Error do: errorBlock]
             ensure: [responseSem signal]
         ] fork.
     responseSem wait
].
process := nil.
^ value "display-it"


Perhaps the problem in WAProcess>>critical:ifError: is because
aBlock does some continuation stuff inside.

Can that be a reason?

Best regards,

--
Esteban.



Esteban A. Maringolo escribió:

> Hi,
>
> I'm having problems with the following method in WAProcess, on a
> Dolphin 5 Smalltalk:
>
> critical: aBlock ifError: errorBlock
>   |value|
>   mutex critical:[
>     responseSem := Semaphore new.
>     process := [
>             [value := aBlock on: Error do: errorBlock]
>             ensure: [responseSem signal]
>         ] fork.
>     responseSem wait
>   ].
>   process := nil.
>   ^ value
>
>
> After evaluating aBlock value gets its contents (a WAResponse).
> But when the mutex ends the critical evaluation, value is nil.
>
> Perhaps it has something with the Continuations that happens inside
> the process, or something similar.


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Chris Uppal-3
Esteban,

> Perhaps the problem in WAProcess>>critical:ifError: is because
> aBlock does some continuation stuff inside.
>
> Can that be a reason?

Seems quite likely to me.  It might even be what's supposed to happen.
Apologies in advance if I'm missing the point, or if the following is already
obvious to you, but, for instance, if you set up some continuations stuff:

    c := nil.
    [[:cc | c := cc] callCC. Time now] value. "display it"

(The example's much clearer if you do "display it" rather than just evaluating
the expression).  That sets c to a continuation. Now evaluate it:

    c value. "evaluate it"

We end up back in the original "display it", so the continuation is working.
Now set up a simplified version of your example:

    block := [c value. 'Aha!'].
    value := nil.
    [value := block value] fork.
    value.

When you evaluate that lot, the forked process is hijacked and replaced with
the original "display it" again, so the forked code never executes past the
expression
        c value
in the block and so never does return 'Aha!' or assign that to value.

Of course, that's all on the assumption that the real code is doing the
equivalent of
        c value
somewhere, which may not be true.  But if not then I don't see what that
complicated fork-and-wait logic is for.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Esteban A. Maringolo-3
Hi Chris,

Chris Uppal escribió:
> Esteban,

>>Perhaps the problem in WAProcess>>critical:ifError: is because
>>aBlock does some continuation stuff inside.
>>
>>Can that be a reason?

> Seems quite likely to me.  It might even be what's supposed to happen.

To me too.

> Apologies in advance if I'm missing the point, or if the following is already
> obvious to you, but, for instance, if you set up some continuations stuff:

No apology is neccesary, I'm still assimilating this continuation stuff.

>     c := nil.
>     [[:cc | c := cc] callCC. Time now] value. "display it"
>     c value. "evaluate it"
> We end up back in the original "display it", so the continuation is working.

> Now set up a simplified version of your example:
>     block := [c value. 'Aha!'].
>     value := nil.
>     [value := block value] fork.
>     value.
> When you evaluate that lot, the forked process is hijacked and replaced with
> the original "display it" again, so the forked code never executes past the
> expression
>         c value
> in the block and so never does return 'Aha!' or assign that to value.
> somewhere, which may not be true.  But if not then I don't see what that
> complicated fork-and-wait logic is for.

Well... the strange is not that, because continuations have that
expected behaviour.
The strange, to me,  is that inside of the evaluated block, value
got it's value (which in this is case is a WAResponse, kind of
HTTPResponse), but once it lefts the ensured evaluation, value is nil.

responseSem := Semaphore new.
process := [
        [value := aBlock on: Error do: errorBlock]
                ensure: [responseSem signal]
        ] fork.
responseSem wait.


Perhaps it has something to do with how the process copies, or how
it's, but I'm guessing to much beyond my understanding.

Andy, Blair?

One thing to note is this works as it is on VW and Squeak, and I
have the problem with both D5 and Dx6.

Best regards,

--
Esteban.


Reply | Threaded
Open this post in threaded view
|

Mutex or Semaphore forMutualExclusion? [was: WAProcess>>critical:ifError problem]

Esteban A. Maringolo-3
Following with this, which is stopping me to continue the port, some
other questions arise...

What is the difference between a aMutex and a Semaphore
forMutualExclusion?

Both provide a #critical: execution, and Mutex uses a semaphore for
it's implementation.

Which is the difference in Dolphin? Some further read to exercise or
learn about this?

Best regards,

--
Esteban.


Esteban A. Maringolo (yo) escribió:
> Chris Uppal escribió:
>> Esteban,

>>> Perhaps the problem in WAProcess>>critical:ifError: is because
>>> aBlock does some continuation stuff inside.
>>>
>>> Can that be a reason?

>> Seems quite likely to me.  It might even be what's supposed to happen.


Reply | Threaded
Open this post in threaded view
|

Re: Mutex or Semaphore forMutualExclusion? [was: WAProcess>>critical:ifError problem]

Martin Rubi
>What is the difference between a aMutex and a Semaphore
>forMutualExclusion?

I could be wrong, but I think the difference is that the same process can
enter a Mutex critical section multiple times, while a Semaphore
forMutualExclusion can be entered only once, even by the process which had
locked the critical section.

For instance:

m := Mutex new.
m critical: [
    Transcript show: '1'; cr.
    m critical: [Transcript show: '2'; cr.]]

will show '1 2' while

m := Semaphore forMutualExclusion.
m critical: [
    Transcript show: '1'; cr.
    m critical: [Transcript show: '2'; cr.]]

will show '1' and lock.

I don't know if there are more differences, my guess is there might be.

regards


Reply | Threaded
Open this post in threaded view
|

Re: Mutex or Semaphore forMutualExclusion? [was: WAProcess>>critical:ifError problem]

Schwab,Wilhelm K
Martin Rubi wrote:
>>What is the difference between a aMutex and a Semaphore
>>forMutualExclusion?
>
>
> I could be wrong, but I think the difference is that the same process can
> enter a Mutex critical section multiple times, while a Semaphore
> forMutualExclusion can be entered only once, even by the process which had
> locked the critical section.

That is my understanding too.

Have a good one,

Bill


--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Chris Uppal-3
In reply to this post by Esteban A. Maringolo-3
Esteban,

> Perhaps it has something to do with how the process copies, or how
> it's, but I'm guessing to much beyond my understanding.

I think you are going to have to create a small example that reproduces this.
It clearly depends on exactly /what/ "continuations stuff" happens in the
block.  I spent a few minutes on it, but was unable to find a case that
displayed the behaviour.  E.g the following does work:

    c := nil.
    block := [[:cc | c := cc. c value: 'Aha!!'] callCC].

    sem := Semaphore new.
    value := nil.
    [
        [value := block value]
            ensure:  [sem signal]
    ] fork.
    sem wait.

So it seems that /making/ a copy does not cause problems (which is not
surprising).  Using a copy (jumping back to its state) does not inherently
cause problems, at least it works in the simple case above.  And I wasn't able
to think of a more complicated pattern of "jumps" that still "should" assign to
'value'.

BTW, code executed from workspaces isn't quite the same as "real" methods, so
it's probably easier to try to create a very small class that displays the
problem and only then (if at all) to try to translate that into workspace code.

    -- chris


Reply | Threaded
Open this post in threaded view
|

WAProcessMonitor>>critical:ifError workaround found

Esteban A. Maringolo-3
In reply to this post by Esteban A. Maringolo-3
Hi,

I've found something, that may have relation to how the process are
copied.

Esteban A. Maringolo escribió:
> Hi Chris,
> Chris Uppal escribió:

>>> Perhaps the problem in WAProcess>>critical:ifError: is because
>>> aBlock does some continuation stuff inside.
>>> Can that be a reason?
>> Seems quite likely to me.  It might even be what's supposed to happen.
> To me too.

> The strange, to me,  is that inside of the evaluated block, value got
> it's value (which in this is case is a WAResponse, kind of
> HTTPResponse), but once it lefts the ensured evaluation, value is nil.

> responseSem := Semaphore new.
> process := [
>     [value := aBlock on: Error do: errorBlock]
>         ensure: [responseSem signal]
>     ] fork.
> responseSem wait.

> Perhaps it has something to do with how the process copies, or how it's,
> but I'm guessing to much beyond my understanding.
> Andy, Blair?

I've found that the ensured block was executed twice, it is a
semaphore signaled twice too. Removing the ensuring everything works
ok. What is the relation of this to continuations... I can't tell,
but my guess is that has something with ProcessCopier.

So... replacing:

WAProcessMonitor>>critical: aBlock ifError: errorBlock
| value |
value := nil.
mutex critical: [ "mutex is a mutually exclusive semaphore"
   responseSem := Semaphore new.
   process := [
     [value := aBlock on: Error do: errorBlock]
        ensure: [responseSem signal]
   ] fork.
   responseSem wait.
].
process := nil.
^value

By:

WAProcessMonitor>>critical: aBlock ifError: errorBlock
| value |
value := nil.
mutex critical: [
   responseSem := Semaphore new.
   process := [
        value := aBlock on: Error do: errorBlock.
        responseSem signal
        ] fork.
   responseSem wait.
].
process := nil.
^value

Everything starts working magically. Of course, no ensuring is done,
but by now it wasn't necessary, in the future this has to be solved.

Best regards,

--
Esteban.


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Esteban A. Maringolo-3
In reply to this post by Chris Uppal-3
Chris Uppal escribió:
> Esteban,
>>Perhaps it has something to do with how the process copies, or how
>>it's, but I'm guessing to much beyond my understanding.

> I think you are going to have to create a small example that reproduces this.

Yes, I'll make a test case to reproduce the behavior, someday...

> It clearly depends on exactly /what/ "continuations stuff" happens in the
> block.  I spent a few minutes on it, but was unable to find a case that
> displayed the behaviour.  E.g the following does work:
>
>     c := nil.
>     block := [[:cc | c := cc. c value: 'Aha!!'] callCC].
>
>     sem := Semaphore new.
>     value := nil.
>     [
>         [value := block value]
>             ensure:  [sem signal]
>     ] fork.
>     sem wait.
>
> So it seems that /making/ a copy does not cause problems (which is not
> surprising).  

Sometimes it worked, and sometimes it doesn't, definitely it has to
be something related with what happens inside of the protected block.

 > Using a copy (jumping back to its state) does not inherently
> cause problems, at least it works in the simple case above.  And I wasn't able
> to think of a more complicated pattern of "jumps" that still "should" assign to
> 'value'.

> BTW, code executed from workspaces isn't quite the same as "real" methods, so
> it's probably easier to try to create a very small class that displays the
> problem and only then (if at all) to try to translate that into workspace code.

Ha!, Process bug hunting, does it qualify as an olympic sport? :-)

Best regards,

--
Esteban.


Reply | Threaded
Open this post in threaded view
|

Re: WAProcessMonitor>>critical:ifError workaround found

Chris Uppal-3
In reply to this post by Esteban A. Maringolo-3
Esteban,

> I've found that the ensured block was executed twice, it is a
> semaphore signaled twice too. Removing the ensuring everything works
> ok. What is the relation of this to continuations... I can't tell,
> but my guess is that has something with ProcessCopier.

Ahh.... Now it's starting to come a little bit clearer; not yet perfectly
clear, but getting closer...

Given this:

    c := nil.
    block := [[:cc | c := cc. c value: 'Aha!!'] callCC].

    sem := Semaphore new.
    value := nil.
    [
        [value := block value]
            ensure:  [Transcript display: 'ooerr...'; cr. sem signal]
    ] fork.
    sem wait.

(Which is just as before except that I've added some tracing to the #ensure:)

When block is evaluated, the #callCC makes a copy of the process's
#exceptionEnvironment, which includes the cleanup code in the #ensure:.

Next when we send #value to the Continuation in 'c', a new Process is created
from the Continuation (with a copy of the original #exceptionEnvironment) and
the executing process (the one that was #fork-ed) is terminated.  Terminating
it, as always, runs any #ensure: blocks.  So the Semaphore is signalled.

But the new process is a copy of the original one, and has the /same/ #ensure
block. So as it dies (having assigned to value and then run out of things to
do) its #ensure block is (as always) executed.  So the semaphore is signalled
again.

If you now evaluate:
    c value: 22.
 (just as an experiment), then 22 will be assigned to value, and the #ensure
block will be executed a third time.

Lastly, if you set c to nil, and then wait for a little while for the GC to
reclaim it, the Continuation's finalise will run, and that too will execute the
cleanup block.

I haven't been able to make 'value' come out as nil with this, but if I had
executed
    c value.
in the last paragraph but one, then value would have been set to nil.  Is it
possible that that is what you are seeing ?

I'm not sure what the behaviour of #ensure: (and similar) should be when
continuations are used.  In one way it seems perfectly natural that the block
should be executed as many times as the Continuation is restarted.  In another
way that's might be thought a bit surprising, as you'd normally want it to
execute exactly once, but then /when/ should the block be executed ?

It doesn't seem right to me that the ensure blocks of the Process that is being
hijacked should run -- I think the intended semantics is that the data stored
in the Continuation is used to /replace/ the current Process, overwriting it.
If so, then the hijacked Process's #ensure blocks should just be lost.  I tried
to hack the system to make that happen by redefining
Continuation>>continue:with: as:

    continue: aProcess with: anObject
           Processor activeProcess exceptionEnvironment: nil.
           aProcess continueWith: anObject

Which seems to remove that particular problem.  I don't really know what I'm
doing here, though, so it's almost certainly not the correct fix, but it would
be interesting to see if it (as a temporary hack) solves your problem.

Also, I'm not at all sure that #ensure blocks should execute as part of the
Continuation's finalisation, I can't see a good logical justification for that.
I /think/ that Continuation>>stack: should remove its stored Process's
finalisability and only restore it again in #continue:with: -- but I haven't
attempted to test that "fix".

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Chris Uppal-3
In reply to this post by Esteban A. Maringolo-3
Esteban,

> Ha!, Process bug hunting, does it qualify as an olympic sport? :-)

<grin/>

You might be surprised.  According to the BBC news website, the last time the
Olympics were run in London, you could win medals for painting or writing
poetry...

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: WAProcessMonitor>>critical:ifError workaround found

Chris Uppal-3
In reply to this post by Chris Uppal-3
I wrote:

> Next when we send #value to the Continuation in 'c', a new Process is
> created from the Continuation (with a copy of the original
> #exceptionEnvironment) and the executing process (the one that was
> #fork-ed) is terminated.  Terminating it, as always, runs any #ensure:
> blocks.  So the Semaphore is signalled.

So the waiting Process can now execute.  If it wakes up quickly, it will read
'value' before the new Process has a chance to write it.  If it takes a long
time to wake up (like me today ;-) then it'll see the correct value.

I think that completes the picture.  Whee!!  ;-)

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: WAProcessMonitor>>critical:ifError workaround found

Esteban A. Maringolo-3
Hi Chris,

Chris Uppal escribió:
> I wrote:

>>Next when we send #value to the Continuation in 'c', a new Process is
>>created from the Continuation (with a copy of the original
>>#exceptionEnvironment) and the executing process (the one that was
>>#fork-ed) is terminated.  Terminating it, as always, runs any #ensure:
>>blocks.  So the Semaphore is signalled.

> So the waiting Process can now execute.  If it wakes up quickly, it will read
> 'value' before the new Process has a chance to write it.  If it takes a long
> time to wake up (like me today ;-) then it'll see the correct value.

> I think that completes the picture.  Whee!!  ;-)

This explains why when debugging and restarting the method
evaluation it worked (sometimes).

Thanks for this, and all the dedication to test and reproduce the
situation.

I can't test your suggested hack to Continuation until I get home,
meanwhile we wait for an authoritative answer from Andy or Blair,
and if the hack (bugfix?) is acceptable, put it into the mainstream
Dolphin x6 first release.

Best regards,

--
Esteban.


Reply | Threaded
Open this post in threaded view
|

Re: WAProcessMonitor>>critical:ifError workaround found

Blair McGlashan-3
"Esteban A. Maringolo" <[hidden email]> wrote in
message news:4373865c$[hidden email]...
>... I can't test your suggested hack to Continuation until I get home,
>meanwhile we wait for an authoritative answer from Andy or Blair, and if
>the hack (bugfix?) is acceptable, put it into the mainstream Dolphin x6
>first release.
>

I'm not really sure what the correct behaviour is here - it goes beyond my
knowledge of how continuations are supposed to work, and I'm afraid I don't
have the time right now to learn more.

All I can say is that nilling out the exceptionEnvironment of the process
only stops the ensure blocks from running because it prevents the process
from being unwound when it is terminated. #ensure: handlers are not linked
into the exception environment chain, but are implemented by special marked
blocks in the stack. Nilling out the exception environment removes the
system's handler at the bottom of the stack which catches the termination
signal, preventing thee process from terminating correctly. This could well
have undesirable effects (or on the other hand it might not).

So my view is that it is not appropriate to include this modification in
X6.0 because:
a) we don't really know what the right behaviour is (are VW/Squeak behaving
correctly, or just in the same way)
b) the full consequences of preventing proper process termination from
taking place need to be considered.
c) there is no time left before the release

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Janos
In reply to this post by Esteban A. Maringolo-3
Esteban,

perhaps it is not to late to show my ignorance... but:

the only place Browse for Containing text WAP is the #ShlwapiLibrary
class, but I have the impression it is not the target of this
discussion., and obviously everyone else knows about it...

So: What is that famous "WAProcess"?

Thanks,
Janos


Reply | Threaded
Open this post in threaded view
|

Re: WAProcess>>critical:ifError problem

Esteban A. Maringolo-3
Hi Janos,

Janos escribió:
> Esteban,
> perhaps it is not to late to show my ignorance... but:
>
> the only place Browse for Containing text WAP is the #ShlwapiLibrary
> class, but I have the impression it is not the target of this
> discussion., and obviously everyone else knows about it...
>
> So: What is that famous "WAProcess"?

In fact, it was a typo, the actual name of the class is
WAProcessMonitor, and is a class of the Seaside Web Framework, which
is already available for Squeak and VisualWorks. I'm porting it to
Dolphin 6, but it is a work in progress yet.

Regards,

--
Esteban.