The Inbox: Kernel-nice.857.mcz

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

The Inbox: Kernel-nice.857.mcz

commits-2
A new version of Kernel was added to project The Inbox:
http://source.squeak.org/inbox/Kernel-nice.857.mcz

==================== Summary ====================

Name: Kernel-nice.857
Author: nice
Time: 6 June 2014, 3:56:46.41 am
UUID: 58fb31c3-1c03-4cf6-80d3-1f51f41f6d95
Ancestors: Kernel-cmm.855

Resolve ExceptionTests>>testHandlerFromAction.

Implementation details:

Do so by intercepting any exception raised by the handle block in the context which is invoking this block (see #handleSignal:) and by allways propagating this new exception to a previously recorded outerHandler (temp at: 3). Since this context is above the handlers (on:do:) on the stack, it just has to be marked as a fake handler (<primitive: 199>) to do its interceptor/propagator job.
 
Do not use (temp at: 3) of handler (on:do:) as a marker of enablement (true) or temporary disablement (false) during handle block execution, since this is completely superseded by above mechanism. We should rather rename the (temp at: 3) as outerHandler and set it to nil.
Since load order counts, and since some #on:do: context down the processes stacks are not going to die soon, we have equipped #handleSignal: with a workaround, so this change can wait.

Attempt to preserve the possibility for an exception to rearmHandlerDuring: aBlock by temporarily resetting the outerHandler temp in the interceptorContext (which must be a new inst.var. recorded by the Exception).

Alas, the #testHandlerReentrancy is now failing, because it expects an inner handler to be triggered by an exception raised from an outer handle block, which is forbidden by above implementation. This test is hardly compatible with testHandlerFromAction.

I suggest adding an expected failure to document the change of behavior, and creating a new test demonstrating how rearmHandlerDuring: could be used.
If we don't find a real interest in this message now that it cannot pass then intercept, we can as well deprecate it.

=============== Diff against Kernel-cmm.855 ===============

Item was changed:
  ----- Method: ContextPart>>canHandleSignal: (in category 'private-exceptions') -----
  canHandleSignal: exception
  "Sent to handler (on:do:) contexts only.  If my exception class (first arg) handles exception then return true, otherwise forward this message to the next handler context.  If none left, return false (see nil>>canHandleSignal:)"
 
+ ^ (((self tempAt: 1) handles: exception))
- ^ (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
  or: [self nextHandlerContext canHandleSignal: exception].
  !

Item was changed:
  ----- Method: ContextPart>>handleSignal: (in category 'private-exceptions') -----
  handleSignal: exception
+ "Sent to handler (on:do:) and propagator (handleSignal:) contexts only.
+ If I am a handler (on:do:), and if my exception class (first arg) handles exception then execute my handle block (second arg), otherwise forward this message to the next handler context.  If none left, execute exception's defaultAction (see nil>>handleSignal:).
+ If I am a propagator (handleSignal:) I just forward the handling to the outerHandler (temp at: 3). The propagator is allways above the handlers on the stack. If an exception occurs while executing the handle block, it will be intercepted by the propagator and redirected to the previously recorded outer handler, effectively bypassing all inner and current handler."
- "Sent to handler (on:do:) contexts only.  If my exception class (first arg) handles exception then execute my handle block (second arg), otherwise forward this message to the next handler context.  If none left, execute exception's defaultAction (see nil>>handleSignal:)."
 
+ | val outerHandler |
+ <primitive: 199>
+ (self tempAt: 3) == true ifTrue: [self tempAt: 3 put: nil]. "preserve compatibility with old handlerContext"
+ ((self tempAt: 1) handles: exception) ifFalse: [
+ ^((self tempAt: 3) ifNil: [self nextHandlerContext]) handleSignal: exception].
+
+ exception privHandlerContext: self contextTag from: thisContext contextTag.
+ outerHandler := self nextHandlerContext.  "disable self and all inner handlers while executing handle block"
+ val := (self tempAt: 2) cull: exception.
- | val |
- (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) ifFalse: [
- ^ self nextHandlerContext handleSignal: exception].
-
- exception privHandlerContext: self contextTag.
- self tempAt: 3 put: false.  "disable self while executing handle block"
- val := [(self tempAt: 2) cull: exception ]
- ensure: [self tempAt: 3 put: true].
  self return: val.  "return from self if not otherwise directed in handle block"
  !

Item was added:
+ ----- Method: ContextPart>>rearmHandler:during: (in category 'private-exceptions') -----
+ rearmHandler: aHandlerContext during: aBlock
+ "Sent to interceptor (handleSignal:) contexts only, with a handler context (on:do:) parameter.
+ Makes aHandlerContext re-entrant for the duration of aBlock. Only works in a closure-enabled image"
+
+ | oldHandler |
+ oldHandler := self tempAt: 3.
+ ^ [self tempAt: 3 put: aHandlerContext. aBlock value]
+ ensure: [self tempAt: 3 put: oldHandler]!

Item was removed:
- ----- Method: ContextPart>>rearmHandlerDuring: (in category 'private-exceptions') -----
- rearmHandlerDuring: aBlock
- "Sent to handler (on:do:) contexts only. Makes me re-entrant for the duration of aBlock. Only works in a closure-enabled image"
-
- ^ [self tempAt: 3 put: true. aBlock value]
- ensure: [self tempAt: 3 put: false]!

Item was changed:
  Object subclass: #Exception
+ instanceVariableNames: 'messageText tag signalContext handlerContext outerContext interceptorContext'
- instanceVariableNames: 'messageText tag signalContext handlerContext outerContext'
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Kernel-Exceptions-Kernel'!
 
  !Exception commentStamp: '<historical>' prior: 0!
  This is the main class used to implement the exception handling system (EHS).  It plays two distinct roles:  that of the exception, and that of the exception handler.  More specifically, it implements the bulk of the protocols laid out in the ANSI specification - those protocol names are reflected in the message categories.
 
  Exception is an abstract class.  Instances should neither be created nor trapped.  In most cases, subclasses should inherit from Error or Notification rather than directly from Exception.
 
  In implementing this EHS, The Fourth Estate Inc. incorporated some ideas and code from Craig Latta's EHS.  His insights were crucial in allowing us to implement BlockContext>>valueUninterruptably (and by extension, #ensure: and #ifCurtailed:), and we imported the following methods with little or no modification:
 
  ContextPart>>terminateTo:
  ContextPart>>terminate
  MethodContext>>receiver:
  MethodContext>>answer:
 
  Thanks, Craig!!!

Item was removed:
- ----- Method: Exception>>privHandlerContext: (in category 'priv handling') -----
- privHandlerContext: aContextTag
-
- handlerContext := aContextTag!

Item was added:
+ ----- Method: Exception>>privHandlerContext:from: (in category 'priv handling') -----
+ privHandlerContext: aContextTag from: anotherContextTag
+
+ handlerContext := aContextTag.
+ interceptorContext := anotherContextTag!

Item was changed:
  ----- Method: Exception>>rearmHandlerDuring: (in category 'handling') -----
  rearmHandlerDuring: aBlock
  "Make the current error handler re-entrant while it is running aBlock. Only works in a closure-enabled image"
 
+ ^ interceptorContext rearmHandler: handlerContext during: aBlock!
- ^ handlerContext rearmHandlerDuring: aBlock!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-nice.857.mcz

Nicolas Cellier
Though I'm pretty happy with this solution, this late coding definitely needs more eyes, more tests and advice for rearmHandlerDuring:


2014-06-06 3:57 GMT+02:00 <[hidden email]>:
A new version of Kernel was added to project The Inbox:
http://source.squeak.org/inbox/Kernel-nice.857.mcz

==================== Summary ====================

Name: Kernel-nice.857
Author: nice
Time: 6 June 2014, 3:56:46.41 am
UUID: 58fb31c3-1c03-4cf6-80d3-1f51f41f6d95
Ancestors: Kernel-cmm.855

Resolve ExceptionTests>>testHandlerFromAction.

Implementation details:

Do so by intercepting any exception raised by the handle block in the context which is invoking this block (see #handleSignal:) and by allways propagating this new exception to a previously recorded outerHandler (temp at: 3). Since this context is above the handlers (on:do:) on the stack, it just has to be marked as a fake handler (<primitive: 199>) to do its interceptor/propagator job.

Do not use (temp at: 3) of handler (on:do:) as a marker of enablement (true) or temporary disablement (false) during handle block execution, since this is completely superseded by above mechanism. We should rather rename the (temp at: 3) as outerHandler and set it to nil.
Since load order counts, and since some #on:do: context down the processes stacks are not going to die soon, we have equipped #handleSignal: with a workaround, so this change can wait.

Attempt to preserve the possibility for an exception to rearmHandlerDuring: aBlock by temporarily resetting the outerHandler temp in the interceptorContext (which must be a new inst.var. recorded by the Exception).

Alas, the #testHandlerReentrancy is now failing, because it expects an inner handler to be triggered by an exception raised from an outer handle block, which is forbidden by above implementation. This test is hardly compatible with testHandlerFromAction.

I suggest adding an expected failure to document the change of behavior, and creating a new test demonstrating how rearmHandlerDuring: could be used.
If we don't find a real interest in this message now that it cannot pass then intercept, we can as well deprecate it.

=============== Diff against Kernel-cmm.855 ===============

Item was changed:
  ----- Method: ContextPart>>canHandleSignal: (in category 'private-exceptions') -----
  canHandleSignal: exception
        "Sent to handler (on:do:) contexts only.  If my exception class (first arg) handles exception then return true, otherwise forward this message to the next handler context.  If none left, return false (see nil>>canHandleSignal:)"

+       ^ (((self tempAt: 1) handles: exception))
-       ^ (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
                or: [self nextHandlerContext canHandleSignal: exception].
  !

Item was changed:
  ----- Method: ContextPart>>handleSignal: (in category 'private-exceptions') -----
  handleSignal: exception
+       "Sent to handler (on:do:) and propagator (handleSignal:) contexts only.
+       If I am a handler (on:do:), and if my exception class (first arg) handles exception then execute my handle block (second arg), otherwise forward this message to the next handler context.  If none left, execute exception's defaultAction (see nil>>handleSignal:).
+       If I am a propagator (handleSignal:) I just forward the handling to the outerHandler (temp at: 3). The propagator is allways above the handlers on the stack. If an exception occurs while executing the handle block, it will be intercepted by the propagator and redirected to the previously recorded outer handler, effectively bypassing all inner and current handler."
-       "Sent to handler (on:do:) contexts only.  If my exception class (first arg) handles exception then execute my handle block (second arg), otherwise forward this message to the next handler context.  If none left, execute exception's defaultAction (see nil>>handleSignal:)."

+       | val outerHandler |
+       <primitive: 199>
+       (self tempAt: 3) == true ifTrue: [self tempAt: 3 put: nil].     "preserve compatibility with old handlerContext"
+       ((self tempAt: 1) handles: exception) ifFalse: [
+               ^((self tempAt: 3) ifNil: [self nextHandlerContext]) handleSignal: exception].
+
+       exception privHandlerContext: self contextTag from: thisContext contextTag.
+       outerHandler := self nextHandlerContext.  "disable self and all inner handlers while executing handle block"
+       val := (self tempAt: 2) cull: exception.
-       | val |
-       (((self tempAt: 1) handles: exception) and: [self tempAt: 3]) ifFalse: [
-               ^ self nextHandlerContext handleSignal: exception].
-
-       exception privHandlerContext: self contextTag.
-       self tempAt: 3 put: false.  "disable self while executing handle block"
-       val := [(self tempAt: 2) cull: exception ]
-               ensure: [self tempAt: 3 put: true].
        self return: val.  "return from self if not otherwise directed in handle block"
  !

Item was added:
+ ----- Method: ContextPart>>rearmHandler:during: (in category 'private-exceptions') -----
+ rearmHandler: aHandlerContext during: aBlock
+       "Sent to interceptor (handleSignal:) contexts only, with a handler context (on:do:) parameter.
+       Makes aHandlerContext re-entrant for the duration of aBlock. Only works in a closure-enabled image"
+
+       | oldHandler |
+       oldHandler := self tempAt: 3.
+       ^ [self tempAt: 3 put: aHandlerContext. aBlock value]
+               ensure: [self tempAt: 3 put: oldHandler]!

Item was removed:
- ----- Method: ContextPart>>rearmHandlerDuring: (in category 'private-exceptions') -----
- rearmHandlerDuring: aBlock
-       "Sent to handler (on:do:) contexts only. Makes me re-entrant for the duration of aBlock. Only works in a closure-enabled image"
-
-       ^ [self tempAt: 3 put: true. aBlock value]
-               ensure: [self tempAt: 3 put: false]!

Item was changed:
  Object subclass: #Exception
+       instanceVariableNames: 'messageText tag signalContext handlerContext outerContext interceptorContext'
-       instanceVariableNames: 'messageText tag signalContext handlerContext outerContext'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Kernel-Exceptions-Kernel'!

  !Exception commentStamp: '<historical>' prior: 0!
  This is the main class used to implement the exception handling system (EHS).  It plays two distinct roles:  that of the exception, and that of the exception handler.  More specifically, it implements the bulk of the protocols laid out in the ANSI specification - those protocol names are reflected in the message categories.

  Exception is an abstract class.  Instances should neither be created nor trapped.  In most cases, subclasses should inherit from Error or Notification rather than directly from Exception.

  In implementing this EHS, The Fourth Estate Inc. incorporated some ideas and code from Craig Latta's EHS.  His insights were crucial in allowing us to implement BlockContext>>valueUninterruptably (and by extension, #ensure: and #ifCurtailed:), and we imported the following methods with little or no modification:

  ContextPart>>terminateTo:
  ContextPart>>terminate
  MethodContext>>receiver:
  MethodContext>>answer:

  Thanks, Craig!!!

Item was removed:
- ----- Method: Exception>>privHandlerContext: (in category 'priv handling') -----
- privHandlerContext: aContextTag
-
-       handlerContext := aContextTag!

Item was added:
+ ----- Method: Exception>>privHandlerContext:from: (in category 'priv handling') -----
+ privHandlerContext: aContextTag from: anotherContextTag
+
+       handlerContext := aContextTag.
+       interceptorContext := anotherContextTag!

Item was changed:
  ----- Method: Exception>>rearmHandlerDuring: (in category 'handling') -----
  rearmHandlerDuring: aBlock
  "Make the current error handler re-entrant while it is running aBlock. Only works in a closure-enabled image"

+       ^ interceptorContext rearmHandler: handlerContext during: aBlock!
-       ^ handlerContext rearmHandlerDuring: aBlock!





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-nice.857.mcz

David T. Lewis
The change behaves as described, fixing testHandlerFromAction and introducing
the (now expected) failure into testHandlerRentrancy.

I am not qualified to offer any more useful critique other than to say that
is clear that Nicolas has put some serious thought into this issue and I
hope that others will take a close look at it.

Dave


On Fri, Jun 06, 2014 at 04:13:39AM +0200, Nicolas Cellier wrote:

> Though I'm pretty happy with this solution, this late coding definitely
> needs more eyes, more tests and advice for rearmHandlerDuring:
>
>
> 2014-06-06 3:57 GMT+02:00 <[hidden email]>:
>
> > A new version of Kernel was added to project The Inbox:
> > http://source.squeak.org/inbox/Kernel-nice.857.mcz
> >
> > ==================== Summary ====================
> >
> > Name: Kernel-nice.857
> > Author: nice
> > Time: 6 June 2014, 3:56:46.41 am
> > UUID: 58fb31c3-1c03-4cf6-80d3-1f51f41f6d95
> > Ancestors: Kernel-cmm.855
> >
> > Resolve ExceptionTests>>testHandlerFromAction.
> >
> > Implementation details:
> >
> > Do so by intercepting any exception raised by the handle block in the
> > context which is invoking this block (see #handleSignal:) and by allways
> > propagating this new exception to a previously recorded outerHandler (temp
> > at: 3). Since this context is above the handlers (on:do:) on the stack, it
> > just has to be marked as a fake handler (<primitive: 199>) to do its
> > interceptor/propagator job.
> >
> > Do not use (temp at: 3) of handler (on:do:) as a marker of enablement
> > (true) or temporary disablement (false) during handle block execution,
> > since this is completely superseded by above mechanism. We should rather
> > rename the (temp at: 3) as outerHandler and set it to nil.
> > Since load order counts, and since some #on:do: context down the processes
> > stacks are not going to die soon, we have equipped #handleSignal: with a
> > workaround, so this change can wait.
> >
> > Attempt to preserve the possibility for an exception to
> > rearmHandlerDuring: aBlock by temporarily resetting the outerHandler temp
> > in the interceptorContext (which must be a new inst.var. recorded by the
> > Exception).
> >
> > Alas, the #testHandlerReentrancy is now failing, because it expects an
> > inner handler to be triggered by an exception raised from an outer handle
> > block, which is forbidden by above implementation. This test is hardly
> > compatible with testHandlerFromAction.
> >
> > I suggest adding an expected failure to document the change of behavior,
> > and creating a new test demonstrating how rearmHandlerDuring: could be used.
> > If we don't find a real interest in this message now that it cannot pass
> > then intercept, we can as well deprecate it.
> >
> > =============== Diff against Kernel-cmm.855 ===============
> >
> > Item was changed:
> >   ----- Method: ContextPart>>canHandleSignal: (in category
> > 'private-exceptions') -----
> >   canHandleSignal: exception
> >         "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then return true, otherwise forward this
> > message to the next handler context.  If none left, return false (see
> > nil>>canHandleSignal:)"
> >
> > +       ^ (((self tempAt: 1) handles: exception))
> > -       ^ (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> >                 or: [self nextHandlerContext canHandleSignal: exception].
> >   !
> >
> > Item was changed:
> >   ----- Method: ContextPart>>handleSignal: (in category
> > 'private-exceptions') -----
> >   handleSignal: exception
> > +       "Sent to handler (on:do:) and propagator (handleSignal:) contexts
> > only.
> > +       If I am a handler (on:do:), and if my exception class (first arg)
> > handles exception then execute my handle block (second arg), otherwise
> > forward this message to the next handler context.  If none left, execute
> > exception's defaultAction (see nil>>handleSignal:).
> > +       If I am a propagator (handleSignal:) I just forward the handling
> > to the outerHandler (temp at: 3). The propagator is allways above the
> > handlers on the stack. If an exception occurs while executing the handle
> > block, it will be intercepted by the propagator and redirected to the
> > previously recorded outer handler, effectively bypassing all inner and
> > current handler."
> > -       "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then execute my handle block (second arg),
> > otherwise forward this message to the next handler context.  If none left,
> > execute exception's defaultAction (see nil>>handleSignal:)."
> >
> > +       | val outerHandler |
> > +       <primitive: 199>
> > +       (self tempAt: 3) == true ifTrue: [self tempAt: 3 put: nil].
> > "preserve compatibility with old handlerContext"
> > +       ((self tempAt: 1) handles: exception) ifFalse: [
> > +               ^((self tempAt: 3) ifNil: [self nextHandlerContext])
> > handleSignal: exception].
> > +
> > +       exception privHandlerContext: self contextTag from: thisContext
> > contextTag.
> > +       outerHandler := self nextHandlerContext.  "disable self and all
> > inner handlers while executing handle block"
> > +       val := (self tempAt: 2) cull: exception.
> > -       | val |
> > -       (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> > ifFalse: [
> > -               ^ self nextHandlerContext handleSignal: exception].
> > -
> > -       exception privHandlerContext: self contextTag.
> > -       self tempAt: 3 put: false.  "disable self while executing handle
> > block"
> > -       val := [(self tempAt: 2) cull: exception ]
> > -               ensure: [self tempAt: 3 put: true].
> >         self return: val.  "return from self if not otherwise directed in
> > handle block"
> >   !
> >
> > Item was added:
> > + ----- Method: ContextPart>>rearmHandler:during: (in category
> > 'private-exceptions') -----
> > + rearmHandler: aHandlerContext during: aBlock
> > +       "Sent to interceptor (handleSignal:) contexts only, with a handler
> > context (on:do:) parameter.
> > +       Makes aHandlerContext re-entrant for the duration of aBlock. Only
> > works in a closure-enabled image"
> > +
> > +       | oldHandler |
> > +       oldHandler := self tempAt: 3.
> > +       ^ [self tempAt: 3 put: aHandlerContext. aBlock value]
> > +               ensure: [self tempAt: 3 put: oldHandler]!
> >
> > Item was removed:
> > - ----- Method: ContextPart>>rearmHandlerDuring: (in category
> > 'private-exceptions') -----
> > - rearmHandlerDuring: aBlock
> > -       "Sent to handler (on:do:) contexts only. Makes me re-entrant for
> > the duration of aBlock. Only works in a closure-enabled image"
> > -
> > -       ^ [self tempAt: 3 put: true. aBlock value]
> > -               ensure: [self tempAt: 3 put: false]!
> >
> > Item was changed:
> >   Object subclass: #Exception
> > +       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext interceptorContext'
> > -       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext'
> >         classVariableNames: ''
> >         poolDictionaries: ''
> >         category: 'Kernel-Exceptions-Kernel'!
> >
> >   !Exception commentStamp: '<historical>' prior: 0!
> >   This is the main class used to implement the exception handling system
> > (EHS).  It plays two distinct roles:  that of the exception, and that of
> > the exception handler.  More specifically, it implements the bulk of the
> > protocols laid out in the ANSI specification - those protocol names are
> > reflected in the message categories.
> >
> >   Exception is an abstract class.  Instances should neither be created nor
> > trapped.  In most cases, subclasses should inherit from Error or
> > Notification rather than directly from Exception.
> >
> >   In implementing this EHS, The Fourth Estate Inc. incorporated some ideas
> > and code from Craig Latta's EHS.  His insights were crucial in allowing us
> > to implement BlockContext>>valueUninterruptably (and by extension, #ensure:
> > and #ifCurtailed:), and we imported the following methods with little or no
> > modification:
> >
> >   ContextPart>>terminateTo:
> >   ContextPart>>terminate
> >   MethodContext>>receiver:
> >   MethodContext>>answer:
> >
> >   Thanks, Craig!!!
> >
> > Item was removed:
> > - ----- Method: Exception>>privHandlerContext: (in category 'priv
> > handling') -----
> > - privHandlerContext: aContextTag
> > -
> > -       handlerContext := aContextTag!
> >
> > Item was added:
> > + ----- Method: Exception>>privHandlerContext:from: (in category 'priv
> > handling') -----
> > + privHandlerContext: aContextTag from: anotherContextTag
> > +
> > +       handlerContext := aContextTag.
> > +       interceptorContext := anotherContextTag!
> >
> > Item was changed:
> >   ----- Method: Exception>>rearmHandlerDuring: (in category 'handling')
> > -----
> >   rearmHandlerDuring: aBlock
> >   "Make the current error handler re-entrant while it is running aBlock.
> > Only works in a closure-enabled image"
> >
> > +       ^ interceptorContext rearmHandler: handlerContext during: aBlock!
> > -       ^ handlerContext rearmHandlerDuring: aBlock!
> >
> >
> >

>


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-nice.857.mcz

Nicolas Cellier


2014-06-06 4:48 GMT+02:00 David T. Lewis <[hidden email]>:
The change behaves as described, fixing testHandlerFromAction and introducing
the (now expected) failure into testHandlerRentrancy.

I am not qualified to offer any more useful critique other than to say that
is clear that Nicolas has put some serious thought into this issue and I
hope that others will take a close look at it.

Dave


Thanks David,
Added wisdom: googling about usage of #rearmHandlerDuring: it seems required by Island mechanism in Tweak see
https://code.google.com/p/pharo/issues/detail?id=2519

Maybe there is another implementation possible in Tweak, but that's definitely the main usage of #rearmHandlerDuring:


I'll try and give a try to above Eliot's snippets



On Fri, Jun 06, 2014 at 04:13:39AM +0200, Nicolas Cellier wrote:
> Though I'm pretty happy with this solution, this late coding definitely
> needs more eyes, more tests and advice for rearmHandlerDuring:
>
>
> 2014-06-06 3:57 GMT+02:00 <[hidden email]>:
>
> > A new version of Kernel was added to project The Inbox:
> > http://source.squeak.org/inbox/Kernel-nice.857.mcz
> >
> > ==================== Summary ====================
> >
> > Name: Kernel-nice.857
> > Author: nice
> > Time: 6 June 2014, 3:56:46.41 am
> > UUID: 58fb31c3-1c03-4cf6-80d3-1f51f41f6d95
> > Ancestors: Kernel-cmm.855
> >
> > Resolve ExceptionTests>>testHandlerFromAction.
> >
> > Implementation details:
> >
> > Do so by intercepting any exception raised by the handle block in the
> > context which is invoking this block (see #handleSignal:) and by allways
> > propagating this new exception to a previously recorded outerHandler (temp
> > at: 3). Since this context is above the handlers (on:do:) on the stack, it
> > just has to be marked as a fake handler (<primitive: 199>) to do its
> > interceptor/propagator job.
> >
> > Do not use (temp at: 3) of handler (on:do:) as a marker of enablement
> > (true) or temporary disablement (false) during handle block execution,
> > since this is completely superseded by above mechanism. We should rather
> > rename the (temp at: 3) as outerHandler and set it to nil.
> > Since load order counts, and since some #on:do: context down the processes
> > stacks are not going to die soon, we have equipped #handleSignal: with a
> > workaround, so this change can wait.
> >
> > Attempt to preserve the possibility for an exception to
> > rearmHandlerDuring: aBlock by temporarily resetting the outerHandler temp
> > in the interceptorContext (which must be a new inst.var. recorded by the
> > Exception).
> >
> > Alas, the #testHandlerReentrancy is now failing, because it expects an
> > inner handler to be triggered by an exception raised from an outer handle
> > block, which is forbidden by above implementation. This test is hardly
> > compatible with testHandlerFromAction.
> >
> > I suggest adding an expected failure to document the change of behavior,
> > and creating a new test demonstrating how rearmHandlerDuring: could be used.
> > If we don't find a real interest in this message now that it cannot pass
> > then intercept, we can as well deprecate it.
> >
> > =============== Diff against Kernel-cmm.855 ===============
> >
> > Item was changed:
> >   ----- Method: ContextPart>>canHandleSignal: (in category
> > 'private-exceptions') -----
> >   canHandleSignal: exception
> >         "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then return true, otherwise forward this
> > message to the next handler context.  If none left, return false (see
> > nil>>canHandleSignal:)"
> >
> > +       ^ (((self tempAt: 1) handles: exception))
> > -       ^ (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> >                 or: [self nextHandlerContext canHandleSignal: exception].
> >   !
> >
> > Item was changed:
> >   ----- Method: ContextPart>>handleSignal: (in category
> > 'private-exceptions') -----
> >   handleSignal: exception
> > +       "Sent to handler (on:do:) and propagator (handleSignal:) contexts
> > only.
> > +       If I am a handler (on:do:), and if my exception class (first arg)
> > handles exception then execute my handle block (second arg), otherwise
> > forward this message to the next handler context.  If none left, execute
> > exception's defaultAction (see nil>>handleSignal:).
> > +       If I am a propagator (handleSignal:) I just forward the handling
> > to the outerHandler (temp at: 3). The propagator is allways above the
> > handlers on the stack. If an exception occurs while executing the handle
> > block, it will be intercepted by the propagator and redirected to the
> > previously recorded outer handler, effectively bypassing all inner and
> > current handler."
> > -       "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then execute my handle block (second arg),
> > otherwise forward this message to the next handler context.  If none left,
> > execute exception's defaultAction (see nil>>handleSignal:)."
> >
> > +       | val outerHandler |
> > +       <primitive: 199>
> > +       (self tempAt: 3) == true ifTrue: [self tempAt: 3 put: nil].
> > "preserve compatibility with old handlerContext"
> > +       ((self tempAt: 1) handles: exception) ifFalse: [
> > +               ^((self tempAt: 3) ifNil: [self nextHandlerContext])
> > handleSignal: exception].
> > +
> > +       exception privHandlerContext: self contextTag from: thisContext
> > contextTag.
> > +       outerHandler := self nextHandlerContext.  "disable self and all
> > inner handlers while executing handle block"
> > +       val := (self tempAt: 2) cull: exception.
> > -       | val |
> > -       (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> > ifFalse: [
> > -               ^ self nextHandlerContext handleSignal: exception].
> > -
> > -       exception privHandlerContext: self contextTag.
> > -       self tempAt: 3 put: false.  "disable self while executing handle
> > block"
> > -       val := [(self tempAt: 2) cull: exception ]
> > -               ensure: [self tempAt: 3 put: true].
> >         self return: val.  "return from self if not otherwise directed in
> > handle block"
> >   !
> >
> > Item was added:
> > + ----- Method: ContextPart>>rearmHandler:during: (in category
> > 'private-exceptions') -----
> > + rearmHandler: aHandlerContext during: aBlock
> > +       "Sent to interceptor (handleSignal:) contexts only, with a handler
> > context (on:do:) parameter.
> > +       Makes aHandlerContext re-entrant for the duration of aBlock. Only
> > works in a closure-enabled image"
> > +
> > +       | oldHandler |
> > +       oldHandler := self tempAt: 3.
> > +       ^ [self tempAt: 3 put: aHandlerContext. aBlock value]
> > +               ensure: [self tempAt: 3 put: oldHandler]!
> >
> > Item was removed:
> > - ----- Method: ContextPart>>rearmHandlerDuring: (in category
> > 'private-exceptions') -----
> > - rearmHandlerDuring: aBlock
> > -       "Sent to handler (on:do:) contexts only. Makes me re-entrant for
> > the duration of aBlock. Only works in a closure-enabled image"
> > -
> > -       ^ [self tempAt: 3 put: true. aBlock value]
> > -               ensure: [self tempAt: 3 put: false]!
> >
> > Item was changed:
> >   Object subclass: #Exception
> > +       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext interceptorContext'
> > -       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext'
> >         classVariableNames: ''
> >         poolDictionaries: ''
> >         category: 'Kernel-Exceptions-Kernel'!
> >
> >   !Exception commentStamp: '<historical>' prior: 0!
> >   This is the main class used to implement the exception handling system
> > (EHS).  It plays two distinct roles:  that of the exception, and that of
> > the exception handler.  More specifically, it implements the bulk of the
> > protocols laid out in the ANSI specification - those protocol names are
> > reflected in the message categories.
> >
> >   Exception is an abstract class.  Instances should neither be created nor
> > trapped.  In most cases, subclasses should inherit from Error or
> > Notification rather than directly from Exception.
> >
> >   In implementing this EHS, The Fourth Estate Inc. incorporated some ideas
> > and code from Craig Latta's EHS.  His insights were crucial in allowing us
> > to implement BlockContext>>valueUninterruptably (and by extension, #ensure:
> > and #ifCurtailed:), and we imported the following methods with little or no
> > modification:
> >
> >   ContextPart>>terminateTo:
> >   ContextPart>>terminate
> >   MethodContext>>receiver:
> >   MethodContext>>answer:
> >
> >   Thanks, Craig!!!
> >
> > Item was removed:
> > - ----- Method: Exception>>privHandlerContext: (in category 'priv
> > handling') -----
> > - privHandlerContext: aContextTag
> > -
> > -       handlerContext := aContextTag!
> >
> > Item was added:
> > + ----- Method: Exception>>privHandlerContext:from: (in category 'priv
> > handling') -----
> > + privHandlerContext: aContextTag from: anotherContextTag
> > +
> > +       handlerContext := aContextTag.
> > +       interceptorContext := anotherContextTag!
> >
> > Item was changed:
> >   ----- Method: Exception>>rearmHandlerDuring: (in category 'handling')
> > -----
> >   rearmHandlerDuring: aBlock
> >   "Make the current error handler re-entrant while it is running aBlock.
> > Only works in a closure-enabled image"
> >
> > +       ^ interceptorContext rearmHandler: handlerContext during: aBlock!
> > -       ^ handlerContext rearmHandlerDuring: aBlock!
> >
> >
> >

>





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-nice.857.mcz

Nicolas Cellier



2014-06-06 12:43 GMT+02:00 Nicolas Cellier <[hidden email]>:


2014-06-06 4:48 GMT+02:00 David T. Lewis <[hidden email]>:

The change behaves as described, fixing testHandlerFromAction and introducing
the (now expected) failure into testHandlerRentrancy.

I am not qualified to offer any more useful critique other than to say that
is clear that Nicolas has put some serious thought into this issue and I
hope that others will take a close look at it.

Dave


Thanks David,
Added wisdom: googling about usage of #rearmHandlerDuring: it seems required by Island mechanism in Tweak see
https://code.google.com/p/pharo/issues/detail?id=2519

Maybe there is another implementation possible in Tweak, but that's definitely the main usage of #rearmHandlerDuring:


I'll try and give a try to above Eliot's snippets



And good news, the new implementation of rearmHandlerDuring: is still work on Eliot's snippets (#caught is correctly transcripted 5 times).
Though the count of second snippet is still 1.


On Fri, Jun 06, 2014 at 04:13:39AM +0200, Nicolas Cellier wrote:
> Though I'm pretty happy with this solution, this late coding definitely
> needs more eyes, more tests and advice for rearmHandlerDuring:
>
>
> 2014-06-06 3:57 GMT+02:00 <[hidden email]>:
>
> > A new version of Kernel was added to project The Inbox:
> > http://source.squeak.org/inbox/Kernel-nice.857.mcz
> >
> > ==================== Summary ====================
> >
> > Name: Kernel-nice.857
> > Author: nice
> > Time: 6 June 2014, 3:56:46.41 am
> > UUID: 58fb31c3-1c03-4cf6-80d3-1f51f41f6d95
> > Ancestors: Kernel-cmm.855
> >
> > Resolve ExceptionTests>>testHandlerFromAction.
> >
> > Implementation details:
> >
> > Do so by intercepting any exception raised by the handle block in the
> > context which is invoking this block (see #handleSignal:) and by allways
> > propagating this new exception to a previously recorded outerHandler (temp
> > at: 3). Since this context is above the handlers (on:do:) on the stack, it
> > just has to be marked as a fake handler (<primitive: 199>) to do its
> > interceptor/propagator job.
> >
> > Do not use (temp at: 3) of handler (on:do:) as a marker of enablement
> > (true) or temporary disablement (false) during handle block execution,
> > since this is completely superseded by above mechanism. We should rather
> > rename the (temp at: 3) as outerHandler and set it to nil.
> > Since load order counts, and since some #on:do: context down the processes
> > stacks are not going to die soon, we have equipped #handleSignal: with a
> > workaround, so this change can wait.
> >
> > Attempt to preserve the possibility for an exception to
> > rearmHandlerDuring: aBlock by temporarily resetting the outerHandler temp
> > in the interceptorContext (which must be a new inst.var. recorded by the
> > Exception).
> >
> > Alas, the #testHandlerReentrancy is now failing, because it expects an
> > inner handler to be triggered by an exception raised from an outer handle
> > block, which is forbidden by above implementation. This test is hardly
> > compatible with testHandlerFromAction.
> >
> > I suggest adding an expected failure to document the change of behavior,
> > and creating a new test demonstrating how rearmHandlerDuring: could be used.
> > If we don't find a real interest in this message now that it cannot pass
> > then intercept, we can as well deprecate it.
> >
> > =============== Diff against Kernel-cmm.855 ===============
> >
> > Item was changed:
> >   ----- Method: ContextPart>>canHandleSignal: (in category
> > 'private-exceptions') -----
> >   canHandleSignal: exception
> >         "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then return true, otherwise forward this
> > message to the next handler context.  If none left, return false (see
> > nil>>canHandleSignal:)"
> >
> > +       ^ (((self tempAt: 1) handles: exception))
> > -       ^ (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> >                 or: [self nextHandlerContext canHandleSignal: exception].
> >   !
> >
> > Item was changed:
> >   ----- Method: ContextPart>>handleSignal: (in category
> > 'private-exceptions') -----
> >   handleSignal: exception
> > +       "Sent to handler (on:do:) and propagator (handleSignal:) contexts
> > only.
> > +       If I am a handler (on:do:), and if my exception class (first arg)
> > handles exception then execute my handle block (second arg), otherwise
> > forward this message to the next handler context.  If none left, execute
> > exception's defaultAction (see nil>>handleSignal:).
> > +       If I am a propagator (handleSignal:) I just forward the handling
> > to the outerHandler (temp at: 3). The propagator is allways above the
> > handlers on the stack. If an exception occurs while executing the handle
> > block, it will be intercepted by the propagator and redirected to the
> > previously recorded outer handler, effectively bypassing all inner and
> > current handler."
> > -       "Sent to handler (on:do:) contexts only.  If my exception class
> > (first arg) handles exception then execute my handle block (second arg),
> > otherwise forward this message to the next handler context.  If none left,
> > execute exception's defaultAction (see nil>>handleSignal:)."
> >
> > +       | val outerHandler |
> > +       <primitive: 199>
> > +       (self tempAt: 3) == true ifTrue: [self tempAt: 3 put: nil].
> > "preserve compatibility with old handlerContext"
> > +       ((self tempAt: 1) handles: exception) ifFalse: [
> > +               ^((self tempAt: 3) ifNil: [self nextHandlerContext])
> > handleSignal: exception].
> > +
> > +       exception privHandlerContext: self contextTag from: thisContext
> > contextTag.
> > +       outerHandler := self nextHandlerContext.  "disable self and all
> > inner handlers while executing handle block"
> > +       val := (self tempAt: 2) cull: exception.
> > -       | val |
> > -       (((self tempAt: 1) handles: exception) and: [self tempAt: 3])
> > ifFalse: [
> > -               ^ self nextHandlerContext handleSignal: exception].
> > -
> > -       exception privHandlerContext: self contextTag.
> > -       self tempAt: 3 put: false.  "disable self while executing handle
> > block"
> > -       val := [(self tempAt: 2) cull: exception ]
> > -               ensure: [self tempAt: 3 put: true].
> >         self return: val.  "return from self if not otherwise directed in
> > handle block"
> >   !
> >
> > Item was added:
> > + ----- Method: ContextPart>>rearmHandler:during: (in category
> > 'private-exceptions') -----
> > + rearmHandler: aHandlerContext during: aBlock
> > +       "Sent to interceptor (handleSignal:) contexts only, with a handler
> > context (on:do:) parameter.
> > +       Makes aHandlerContext re-entrant for the duration of aBlock. Only
> > works in a closure-enabled image"
> > +
> > +       | oldHandler |
> > +       oldHandler := self tempAt: 3.
> > +       ^ [self tempAt: 3 put: aHandlerContext. aBlock value]
> > +               ensure: [self tempAt: 3 put: oldHandler]!
> >
> > Item was removed:
> > - ----- Method: ContextPart>>rearmHandlerDuring: (in category
> > 'private-exceptions') -----
> > - rearmHandlerDuring: aBlock
> > -       "Sent to handler (on:do:) contexts only. Makes me re-entrant for
> > the duration of aBlock. Only works in a closure-enabled image"
> > -
> > -       ^ [self tempAt: 3 put: true. aBlock value]
> > -               ensure: [self tempAt: 3 put: false]!
> >
> > Item was changed:
> >   Object subclass: #Exception
> > +       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext interceptorContext'
> > -       instanceVariableNames: 'messageText tag signalContext
> > handlerContext outerContext'
> >         classVariableNames: ''
> >         poolDictionaries: ''
> >         category: 'Kernel-Exceptions-Kernel'!
> >
> >   !Exception commentStamp: '<historical>' prior: 0!
> >   This is the main class used to implement the exception handling system
> > (EHS).  It plays two distinct roles:  that of the exception, and that of
> > the exception handler.  More specifically, it implements the bulk of the
> > protocols laid out in the ANSI specification - those protocol names are
> > reflected in the message categories.
> >
> >   Exception is an abstract class.  Instances should neither be created nor
> > trapped.  In most cases, subclasses should inherit from Error or
> > Notification rather than directly from Exception.
> >
> >   In implementing this EHS, The Fourth Estate Inc. incorporated some ideas
> > and code from Craig Latta's EHS.  His insights were crucial in allowing us
> > to implement BlockContext>>valueUninterruptably (and by extension, #ensure:
> > and #ifCurtailed:), and we imported the following methods with little or no
> > modification:
> >
> >   ContextPart>>terminateTo:
> >   ContextPart>>terminate
> >   MethodContext>>receiver:
> >   MethodContext>>answer:
> >
> >   Thanks, Craig!!!
> >
> > Item was removed:
> > - ----- Method: Exception>>privHandlerContext: (in category 'priv
> > handling') -----
> > - privHandlerContext: aContextTag
> > -
> > -       handlerContext := aContextTag!
> >
> > Item was added:
> > + ----- Method: Exception>>privHandlerContext:from: (in category 'priv
> > handling') -----
> > + privHandlerContext: aContextTag from: anotherContextTag
> > +
> > +       handlerContext := aContextTag.
> > +       interceptorContext := anotherContextTag!
> >
> > Item was changed:
> >   ----- Method: Exception>>rearmHandlerDuring: (in category 'handling')
> > -----
> >   rearmHandlerDuring: aBlock
> >   "Make the current error handler re-entrant while it is running aBlock.
> > Only works in a closure-enabled image"
> >
> > +       ^ interceptorContext rearmHandler: handlerContext during: aBlock!
> > -       ^ handlerContext rearmHandlerDuring: aBlock!
> >
> >
> >

>






Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: Kernel-nice.857.mcz

Nicolas Cellier

2014-06-06 17:55 GMT+02:00 Nicolas Cellier <[hidden email]>:



2014-06-06 12:43 GMT+02:00 Nicolas Cellier <[hidden email]>:



2014-06-06 4:48 GMT+02:00 David T. Lewis <[hidden email]>:

The change behaves as described, fixing testHandlerFromAction and introducing
the (now expected) failure into testHandlerRentrancy.

I am not qualified to offer any more useful critique other than to say that
is clear that Nicolas has put some serious thought into this issue and I
hope that others will take a close look at it.

Dave


Thanks David,
Added wisdom: googling about usage of #rearmHandlerDuring: it seems required by Island mechanism in Tweak see
https://code.google.com/p/pharo/issues/detail?id=2519

Maybe there is another implementation possible in Tweak, but that's definitely the main usage of #rearmHandlerDuring:


I'll try and give a try to above Eliot's snippets



And good news, the new implementation of rearmHandlerDuring: is still work on Eliot's snippets (#caught is correctly transcripted 5 times).
Though the count of second snippet is still 1

Nah! The count is not 1 when the outer handler is rearmed.
The count is 6 (1 initial ProgressInitiationException + 5 inner Notification).
The return value is 1 (it is the value returned by 1 to: 5 do: ...), and that's what abused me.