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 commits@source.squeak.org:
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!
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 commits@source.squeak.org:
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!
2014-06-06 4:48 GMT+02:00 David T. Lewis lewis@mail.msen.com:
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 also see this hack in VMMaker: http://forum.world.st/VM-Maker-VMMaker-oscog-eem-180-mcz-td4637135.html found after this thread: http://web.archiveorange.com/archive/v/kLJIkmCV2lbVUoHLbp3R
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 commits@source.squeak.org:
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!
2014-06-06 12:43 GMT+02:00 Nicolas Cellier < nicolas.cellier.aka.nice@gmail.com>:
2014-06-06 4:48 GMT+02:00 David T. Lewis lewis@mail.msen.com:
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 also see this hack in VMMaker: http://forum.world.st/VM-Maker-VMMaker-oscog-eem-180-mcz-td4637135.html found after this thread: http://web.archiveorange.com/archive/v/kLJIkmCV2lbVUoHLbp3R
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 commits@source.squeak.org:
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!
2014-06-06 17:55 GMT+02:00 Nicolas Cellier < nicolas.cellier.aka.nice@gmail.com>:
2014-06-06 12:43 GMT+02:00 Nicolas Cellier < nicolas.cellier.aka.nice@gmail.com>:
2014-06-06 4:48 GMT+02:00 David T. Lewis lewis@mail.msen.com:
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 also see this hack in VMMaker: http://forum.world.st/VM-Maker-VMMaker-oscog-eem-180-mcz-td4637135.html found after this thread: http://web.archiveorange.com/archive/v/kLJIkmCV2lbVUoHLbp3R
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.
squeak-dev@lists.squeakfoundation.org