Ray Doyle
Ray Doyle

Reputation: 849

Elegant way to retry blocks in Smalltalk

Smalltalk supports retry exceptions by using on: retry method defining the behavior between retries. I want to implement retry behaviors consisting of two additional operations: delayInterval, maxAttempts

[ operations ]
on: Exception
maxAttempts: 10
delayInterval: 5 seconds
do: [ : e | Transcript show: 'Trying...'; cr ]

Is there any elegant way to do this?

Upvotes: 2

Views: 152

Answers (2)

Leandro Caniglia
Leandro Caniglia

Reputation: 14868

Carlos's approach is fine, but the problem I see with it is that sends the #on:do: message a number of times. This is not necessary thanks to the fact that Exceptions understand the #retry message. So, instead of enclosing everything in a loop, we can loop inside the handling block, like this:

BlockClosure >> on: aClass do: aBlock maxAttempts: anInteger
  | counter |
  counter := anInteger.
  ^self
    on: aClass
    do: [:ex | | result |
      result := aBlock value: ex.
      counter := counter - 1.
      counter > 0 ifTrue:[ex retry].
      ex return: result]

Note that there is no "syntactic" loop in this code. The flow of execution, however, will evaluate the receiving block again in virtue of the ex retry message (without reaching the ex return: result line). Only if the counter reaches 0 the handler will "give up" and return the value of the exception block aBlock.

This same idea can now be used to introduce a timeout:

on: aClass
do: aBlock
maxAttempts: anInteger
timeout: anotherInteger
  | counter timeout |
  counter := anInteger.
  timeout := TimeStamp now asMilliseconds + anotherInteger "dialect dependent".
  ^self
    on: aClass
    do: [:ex | | result |
      result := aBlock value: ex.
      counter := counter - 1.
      (counter > 0 and: [TimeStamp now asMilliseconds < timeout])
         ifTrue: [ex retry].
      ex return: result]

The same considerations apply here. The only difference is that the retry condition also takes into account the timeout limit.

Upvotes: 5

Carlos E. Ferro
Carlos E. Ferro

Reputation: 948

on: exceptionClass maxAttempts: anInteger delayInterval: seconds do: aBlock

max := anInteger.
exception := false.
[ result := self on: exceptionClass do: [ :ex | aBlock value: ex. exception := true. max := max - 1 ].
exception and: [max > 0 ] ] whileTrue: [ exception := false ].
^result

"elegant" is arguable...

Upvotes: 1

Related Questions