Re: [Pharo-project] [squeak-dev] My solution to handling errors in system-critical processes
On 20 May 2011 19:33, Eliot Miranda <eliot.miranda@gmail.com> wrote:
Hi Igor,    I get the idea but I does one really need on:fork: ?  Why not add Exception>fork, a la Exception>pass?  So one writes [ self doSomething  ] on: Error do: [:ex | ex fork ]. and if you want to specify a handler function you pass in a block:
[ self doSomething  ] on: Error do: [:ex | ex fork: [block to handle error in another process here] ].
Good question. You can implement it anywhere. Except then how to correctly determine stack slicing point? Exception>>fork: aBlock contextToSlice := thisContext sender home ? see, the above is too fragile. Where the guarantees that you sending #fork: inside a block, but not like: Error new fork: [ .... ] ? If you looked at implementation, i capturing the context of a method before sending #on:do: in this way, i know the stack slicing point beforehand. But if i place it into exception, then correct behavior will have to rely on developer's sanity that he using it as intended by design. So, why putting pitfalls when we can avoid them? My choice for stack slicing point was to let user to see the closure context with on:do: while debugging, otherwise it will be harder to determine the origin of code, which led to error. Right now, if you evaluate [ 1/0 ] on: Error fork:[ :ex | ex pass ] in debugger, you will see the 2nd bottom context will be an on:do: method, and next one will contain the context for closure that is receiver of #on:do: so in this way you can clearly see the method with exception handler, who wanted to handle errors in forked process.
? Further, re the finalization issue why are you so resistant to the tried-and-tested VW solution that's been in use for about a decade?
i am not resistant at all. I had no time to do that. Actually, i came to this idea when looking at Henrik's code for Announcements.. and then i found that it can serve for both finalization and Announcements (and lot of other critical places, where you don't want to be killed by errors). For instance , today i shown to Stef, that by placing it at single point in WorldState>>doOneCycleNowFor: aWorld we could avoid respawning UI process each time you open debugger. You slicing the stack with error, putting it into forked process, where you can do anything with it, while UI process continues to run from known safe point. So it solves more problems than just in finalization. Also i think that it is more efficient: you pay the high price only when it is necessary - when exception happens, but if things go as expected , the overhead is minimal. While in solution which you describing, the price was higher, since you have to pass the finalizer to forked process and signal semaphore, and then wait till it finish or fork another process. And synchronization is not cheap, and moreover, you will have 1 extra process hanging around all the time. While now i can simply do: executors do: [:executor | [ executor finalize ] on: Exception fork: [ :ex | ex pass ] ]
best, Eliot
-- Best regards, Igor Stasenko AKA sig.
Another application to elegantly solve well known problem: - spawning debugger windows in the loop, which effectively kills your image, because you cannot close those windows at the same rate they appear. So, consider a following : 100 timesRepeat: [ [ 1/0 ] on: Error fork: [:ex | ex pass ] ] this is what you usually get, if you are not careful or not doing self haltOnce etc.. Which actually makes debugging UI a hellishly tedious and hard task. Now what we can do instead? | sema | sema := Semaphore forMutualExclusion. 100 timesRepeat: [ [ 1/0 ] on: Error fork: [:ex | sema critical: [ ex pass ] ] ] now, you will see only a single window opened at one point of time. And once you deal with error, if there's another one, it will open second one and so on.. But not all of them at once. And later, if we are not happy with this, we could add a feature to 'Abandon same class of errors' , i.e. terminate forks with all errors which originated from same method. In that way, you can deal with error once, and kill the rest which otherwise will annoy you to death. -- Best regards, Igor Stasenko AKA sig.
Here the revised version, which more optimal. Initially i was capturing thisContext into temporary variable, which when running on Stack-based VMs leads to reifying context on heap. This can be avoided, since we can simply find this context from closure's home. So if your code runs without errors you will pay usual price of #on:do: handler. -- Best regards, Igor Stasenko AKA sig.
I really like the pros that you mentioned early. Excellent work. Stef On May 21, 2011, at 2:50 AM, Igor Stasenko wrote:
Here the revised version, which more optimal. Initially i was capturing thisContext into temporary variable, which when running on Stack-based VMs leads to reifying context on heap. This can be avoided, since we can simply find this context from closure's home.
So if your code runs without errors you will pay usual price of #on:do: handler.
-- Best regards, Igor Stasenko AKA sig. <on-fork.1.cs>
participants (2)
-
Igor Stasenko -
Stéphane Ducasse