[squeak-dev] Re: [Pharo-project] #ensure: issues

Igor Stasenko siguctua at gmail.com
Thu Mar 4 03:31:17 UTC 2010


On 4 March 2010 04:34, Eliot Miranda <eliot.miranda at gmail.com> wrote:
>
>
> On Wed, Mar 3, 2010 at 6:10 PM, Igor Stasenko <siguctua at gmail.com> wrote:
>>
>> 2010/3/4 Levente Uzonyi <leves at elte.hu>:
>> > On Thu, 4 Mar 2010, Igor Stasenko wrote:
>> >
>> >> 2010/3/4 Levente Uzonyi <leves at elte.hu>:
>> >>>
>> >>> On Thu, 4 Mar 2010, Igor Stasenko wrote:
>> >>>
>> >>>> On 4 March 2010 01:56, Nicolas Cellier
>> >>>> <nicolas.cellier.aka.nice at gmail.com> wrote:
>> >>>>>
>> >>>>> 2010/3/4 Levente Uzonyi <leves at elte.hu>:
>> >>>>>>
>> >>>>>> On Thu, 4 Mar 2010, Nicolas Cellier wrote:
>> >>>>>>
>> >>>>>>> 2010/3/3 Levente Uzonyi <leves at elte.hu>:
>> >>>>>>>>
>> >>>>>>>> On Wed, 3 Mar 2010, Andreas Raab wrote:
>> >>>>>>>>
>> >>>>>>>>> On 3/3/2010 2:07 PM, Levente Uzonyi wrote:
>> >>>>>>>>>>
>> >>>>>>>>>> On Wed, 3 Mar 2010, Igor Stasenko wrote:
>> >>>>>>>>>>>
>> >>>>>>>>>>> i don't get it. Just before that, you said: ' I'd expect it to
>> >>>>>>>>>>> be
>> >>>>>>>>>>> evaluated no matter what happens.' ?
>> >>>>>>>>>>> But now you saying that it may not be executed in some
>> >>>>>>>>>>> conditions
>> >>>>>>>>>>> (when user pressing abandon button, causing process to be
>> >>>>>>>>>>> terminated).
>> >>>>>>>>>>
>> >>>>>>>>>> It's simple: don't terminate process X from another process if
>> >>>>>>>>>> process
>> >>>>>>>>>> X
>> >>>>>>>>>> is executing a termiation block (aka #ensure: block). Or if you
>> >>>>>>>>>> terminate it, make sure that the execution of the block will
>> >>>>>>>>>> continue
>> >>>>>>>>>> somehow (I don't care how).
>> >>>>>>>>>
>> >>>>>>>>> You're missing Igors point which is that in his example the halt
>> >>>>>>>>> /
>> >>>>>>>>> Transcript *was* in the ensure block and as a result you're
>> >>>>>>>>> contradicting
>> >>>>>>>>> yourself here. Let's go back to Igor's example:
>> >>>>>>>>>
>> >>>>>>>>> [self boom ] ensure: [ self halt. Transcript show: 'boom']
>> >>>>>>>>>
>> >>>>>>>>> The halt is inside the ensure block. If you terminate the
>> >>>>>>>>> process
>> >>>>>>>>> from
>> >>>>>>>>> the
>> >>>>>>>>> debugger, it would be logical from your statement that the
>> >>>>>>>>> Transcript
>> >>>>>>>>> message would be executed - after all it's " executing a
>> >>>>>>>>> termiation
>> >>>>>>>>> block
>> >>>>>>>>> (aka #ensure: block)" and so it can't be terminated by your
>> >>>>>>>>> reasoning.
>> >>>>>>>>> However, when Igor was pointing this out you replied with "I
>> >>>>>>>>> didn't
>> >>>>>>>>> say
>> >>>>>>>>> that. I said evaluate it the same way as normal code." which is
>> >>>>>>>>> inconsistent
>> >>>>>>>>> with the other statement.
>> >>>>>>>>
>> >>>>>>>> That shows my lack of knowledge about how the debugger works.
>> >>>>>>>>
>> >>>>>>>>>
>> >>>>>>>>>> I think every user of #ensure: expects that the termination
>> >>>>>>>>>> blocks
>> >>>>>>>>>> are
>> >>>>>>>>>> executed even if the process which is executing the receiver of
>> >>>>>>>>>> #ensure:
>> >>>>>>>>>> is terminated. And it actually happens in all but this case.
>> >>>>>>>>>
>> >>>>>>>>> The question of terminating processes is always tricky. I don't
>> >>>>>>>>> think
>> >>>>>>>>> that
>> >>>>>>>>> your proposal would actually work in practice - it could easily
>> >>>>>>>>> result
>> >>>>>>>>> in
>> >>>>>>>>> processes that cannot be terminated due to a simple bug in an
>> >>>>>>>>> ensure
>> >>>>>>>>> block.
>> >>>>>>>>> Personally, I'd rather say that the more useful behavior would
>> >>>>>>>>> be
>> >>>>>>>>> something
>> >>>>>>>>> along the lines of saying that process termination either skips
>> >>>>>>>>> the
>> >>>>>>>>> current
>> >>>>>>>>> ensure block (assuming there's a bug and it should get the heck
>> >>>>>>>>> out
>> >>>>>>>>> of
>> >>>>>>>>> it
>> >>>>>>>>> but try to evaluate the remaining ones) or that there need to be
>> >>>>>>>>> two
>> >>>>>>>>> terminations - one that is 'soft' and won't allow ensure blocks
>> >>>>>>>>> to
>> >>>>>>>>> be
>> >>>>>>>>> skipped and one that is 'hard' (kill -9 hard) and just ignores
>> >>>>>>>>> all
>> >>>>>>>>> the
>> >>>>>>>>> ensure blocks.
>> >>>>>>>>
>> >>>>>>>> I'm only saying that normal usage (aka #terminate) shouldn't do
>> >>>>>>>> unexpected
>> >>>>>>>> things like this.
>> >>>>>>>> If you read the comment of Process >> #terminate, you may assume
>> >>>>>>>> that
>> >>>>>>>> #ensure: and #ifCurtailed: blocks will be excuted even if you use
>> >>>>>>>> #terminate, but that's not true.
>> >>>>>>>>
>> >>>>>>>> "Stop the process that the receiver represents forever.  Unwind
>> >>>>>>>> to
>> >>>>>>>> execute
>> >>>>>>>> pending ensure:/ifCurtailed: blocks before terminating."
>> >>>>>>>>
>> >>>>>>>>
>> >>>>>>>> Levente
>> >>>>>>>>
>> >>>>>>>
>> >>>>>>> The only way I see to solve your problem would be to execute the
>> >>>>>>> unwind block in another process...
>> >>>>>>> Quite technical and costly !
>> >>>>>>
>> >>>>>> It's our problem. Just look at the senders of #ensure: and imagine
>> >>>>>> what
>> >>>>>> will
>> >>>>>> happen if the termination block is not evaluated.
>> >>>>>> I think there's another way (though it might be my lack of
>> >>>>>> knowledge
>> >>>>>> again).
>> >>>>>> After suspending the process which is about to be terminated we can
>> >>>>>> check if
>> >>>>>> it's executing a termination block. It it's not, we are safe to
>> >>>>>> continue
>> >>>>>> the
>> >>>>>> termination, otherwise we can do something else which ensures that
>> >>>>>> the
>> >>>>>> termination block is evaluated.
>> >>>>>>
>> >>>>>
>> >>>>> Maybe...
>> >>>>> Unfortunately, you did not tell how you will distinguish well
>> >>>>> behaved
>> >>>>> unwind-blocks from Igor's example...
>> >>>>>
>> >>>>
>> >>>> Yes, then what prevents me from writing:
>> >>>>
>> >>>> [ [ ] ensure: [ self doCrazyThings ] ] fork.
>> >>>
>> >>> What prevents you from writing: Object superclass: Object. ?
>> >>> Nothing, but you don't do that, do you?
>> >>>
>> >> So, why at all, you care about using #ensure: then? If you putting
>> >> everything up to the hands of developer,
>> >> then obviously you won't need to use this message, because you always
>> >> know that you're running a reliable code which
>> >> will always let you to run your things in the end. :)
>> >>
>> >>>>
>> >>>> and now given assumption that any code which placed inside ensure
>> >>>> block should always run to the end, without chances being terminated,
>> >>>> will have ill side effects.
>> >>>
>> >>> You can terminate it (maybe not the usual way).
>> >>>
>> >> that's the point. Why do we need two (or more) ways to terminate a
>> >> process?
>> >>
>> >>>> The #ensure: means, that interpreter will have a chance to enter that
>> >>>> block eventually, but it should not mean that it will keep running
>> >>>> the
>> >>>> code there until normal or non-local return from that block.
>> >>>
>> >>> Then it doesn't ensure anything at all, so it should be called
>> >>> #tryToEvaluateThisToo:.
>> >>>
>> >> in fact, this is the actual behavior :)
>> >> If i press the power button on your PC, or plug out the power cord,
>> >> any #ensure: blocks will have no chances to run either way.
>> >> So, relying on something, which is not reliable is a fallacy :)
>> >
>> > Here's a simple example (replace file with any external resource):
>> > My process opened a file, a termination block will close it if it's
>> > evaluated. If I send #terminate to the process I expect my file to be
>> > closed, so I won't leak a file descriptor.
>> > If the file can't be closed (aka the termination block raised an error)
>> > then
>> > there's a serious problem. It doesn't really matter what happens then.
>> >
>> > But I don't have to try to convince you anymore, because Andreas is
>> > about to
>> > solve the issue.
>> >
>>
>> I'm not trying to convince anyone, i just wanted to show you that
>> there is no good solution in that plane.
>> More workarounds means more code to run (and makes things more complex,
>> btw).
>> But you will be still unsafe.You will be safe, once you stop relying
>> on #ensure: in your code and use different approach.
>>
>> As for your example: use weak finalizer to close your file.
>> This will make sure that no matter what were happen, you wont leave
>> the file open. Working with external resources is a pain. But lets try
>> to not poison ourselves with manual resource management, which comes
>> from C world.
>
> For example the weak finalizer won't help. If the system quits without doing
> a GC (which could happen for a number of internal or external reasons) the
> finalizer won't get run.  In general in the presence of errors all bets are
> off.  In fact, the finalizer is worse because there are no timely guarantees
> as to the finalizer running and therefore errors could go unreported.  At
> least with an unwind-protect it is easy to arrange that if there is an
> you'll get notified about it.
> need to remember to say "best" instead of rattling off replies :)

You're right. Lets not bet on anything, unless the whole system using
an automatic resource management.
Then you won't have an open/closed state, but just objects and garbage
to be collected.

As for error reporting. Well, same thing, what if an error reporting
relies on a file-system which is broken somehow?
Then you won't be able to report any errors and none of unwind-protect
blocks will make it any safer. :)
So, where you think ends an unreliable code, and starts a reliable?
The problem, i trying to articulate is, that we are trying to build a
castle on a swamp to protect ourselves from swamp monsters, but at
same time we not paying attention that castle can be submerged at any
moment, and then the swamp monsters will be the least hazard we'll
need to worry about.


-- 
Best regards,
Igor Stasenko AKA sig.



More information about the Squeak-dev mailing list