12.17: Ensure's Implementation
- Page ID
- 45987
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Now we propose to have a look at the implementation of the method ensure:
.
First we need to understand how the unwind block is stored and how this information is found at run-time. Let’s look at the definition of the central method ensure:
defined on the class BlockClosure
.
ensure: aBlock "Evaluate a termination block after evaluating the receiver, regardless of whether the receiver's evaluation completes. N.B. This method is *not* implemented as a primitive. Primitive 198 always fails. The VM uses prim 198 in a context's method as the mark for an ensure:/ifCurtailed: activation." | complete returnValue | <primitive: 198> returnValue := self valueNoContextSwitch. complete ifNil: [ complete := true. aBlock value ]. ^ returnValue
The <primitive: 198 >
works the same way as the <primitive: 199 >
we saw in the previous section. It always fails, however, its presence marks the method in way that can easily be detected from the context activating this method. Moreover, the unwind block is stored the same way as the exception class and its associated handler. More explicitly, it is stored in the context of ensure:
method execution, that can be accessed from the block through thisContext sender tempAt: 1
.
In the case where the block does not fail and does not have a non-local return, the ensure:
message implementation executes the block, stores the result in the returnValue
variable, executes the argument block and lastly returns the result of the block previously stored. The complete
variable is here to prevent the argument block from being executed twice.
Ensuring a failing block. The ensure:
message will execute the argument block even if the block fails. In the following example, the ensureWithOnDo
message returns 2 and executes 1. In the subsequent section we will carefully look at where and what the block is actually returning and in which order the blocks are executed.
Bexp>>ensureWithOnDo ^[ [ Error signal ] ensure: [ 1 ]. ^3 ] on: Error do: [ 2 ]
Let’s have a quick example before looking at the implementation. We define 4 blocks and 1 method that ensure a failing Block.
Bexp>>mainBlock ^[ self traceCr: 'mainBlock start'. self failingBlock ensure: self ensureBlock. self traceCr: 'mainBlock end' ] Bexp>>failingBlock ^[ self traceCr: 'failingBlock start'. Error signal. self traceCr: 'failingBlock end' ] Bexp>>ensureBlock ^[ self traceCr: 'ensureBlock value'. #EnsureBlockValue ] Bexp>>exceptionHandlerBlock ^[ self traceCr: 'exceptionHandlerBlock value'. #ExceptionHandlerBlockValue ] Bexp>>start | res | self traceCr: 'start start'. res := self mainBlock on: Error do: self exceptionHandlerBlock. self traceCr: 'start end'. self traceCr: 'The result is : ', res, '.'. ^ res
Executing Bexp new start
prints the following (we added indentation to stress the calling flow).
start start mainBlock start failingBlock start exceptionHandlerBlock value ensureBlock value start end The result is: ExceptionHandlerBlockValue.
There are three important things to see. First, the failing block and the main block are not fully executed because of the signal
message. Secondly, the exception block is executed before the ensure block. Lastly, the start method will return the result of the exception handler block.
To understand how this works, we have to look at the end of the exception implementation. We finish the previous explanation on the handleSignal
method.
ContextPart»handleSignal: exception
"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:)."
| value |
((self exceptionClass handles: exception)
and: [self exceptionHandlerIsActive])
ifFalse: [ ^ self nextHandlerContext handleSignal: exception ].
exception privHandlerContext: self contextTag.
"disable self while executing handle block"
self exceptionHandlerIsActive: false.
value := [ self exceptionHandlerBlock cull: exception ]
ensure: [ self exceptionHandlerIsActive: true ].
"return from self if not otherwise directed in handle block"
self return: value.
In our example, Pharo will execute the failing block, then will look for the next handler context, marked with <primitive: 199 >
. As a regular exception, Pharo finds the exception handler context, and runs the exceptionHandlerBlock
. The method handleSignal
finishes with the return:
method. Let’s have a look into it.
ContextPart>>return: value "Unwind thisContext to self and return value to self's sender. Execute any unwind blocks while unwinding. ASSUMES self is a sender of thisContext" sender ifNil: [self cannotReturn: value to: sender]. sender resume: value
The return:
message will check if the context has a sender, and, if not, send a CannotReturn
Exception. Then the sender of this context will call the resume:
message.
resume: value "Unwind thisContext to self and resume with value as result of last send. Execute unwind blocks when unwinding. ASSUMES self is a sender of thisContext" self resume: value through: (thisContext findNextUnwindContextUpTo: self)
ContextPart>>resume: value through: firstUnwindContext "Unwind thisContext to self and resume with value as result of last send. Execute any unwind blocks while unwinding. ASSUMES self is a sender of thisContext." | context unwindBlock | self isDead ifTrue: [ self cannotReturn: value to: self ]. context := firstUnwindContext. [ context isNil ] whileFalse: [ context unwindComplete ifNil:[ context unwindComplete: true. unwindBlock := context unwindBlock. thisContext terminateTo: context. unwindBlock value]. context := context findNextUnwindContextUpTo: self]. thisContext terminateTo: self. ^value
This is the method where the argument block of ensure:
is executed. This method looks for all the unwind contexts between the context of the method resume:
and self, which is the sender of the on:do:
context (in our case the context of start
). When the method finds an unwound context, the unwound block is executed. Lastly, it triggers the terminateTo:
message.
ContextPart>>terminateTo: previousContext "Terminate all the Contexts between me and previousContext, if previousContext is on my Context stack. Make previousContext my sender." | currentContext sendingContext | <primitive: 196> (self hasSender: previousContext) ifTrue: [ currentContext := sender. [currentContext == previousContext] whileFalse: [ sendingContext := currentContext sender. currentContext terminate. currentContext := sendingContext]]. sender := previousContext
Basically, this method terminates all the contexts between thisContext
and self
, which is the sender of the on:do:
context (in our case the context of start
). Moreover, the sender of thisContext
will become self
, which is the sender of the on:do:
context (in our case the context of start
). It is implemented as a primitive for performance only, so the primitive is optional and the fallback code has the same behavior.
Let’s summarize what happens with Figure \(\PageIndex{1}\) which represents the execution of the method ensureWithOnDo
defined previously.


Ensuring a non local return. The method resume:through:
is also called when performing a non local return. In the case of non local return, the stack is unwound in a similar way than or exception. The virtual machine, while performing a non local return, send the message aboutToReturn:through:
to the active context. Therefore, if one has changed the implementation of exception in the language,