Gakuo
Gakuo

Reputation: 855

Pharo differences with Smalltalk

I am trying to extend Pharo with promises/futures. I came across this website http://onsmalltalk.com/smalltalk-concurrency-playing-with-futures. It implements futures in Smalltalk. However, when I copy this part of the code onto Pharo, I get some errors:

value: aBlock 
promiseLock  := Semaphore new.

[ [ promiseValue := aBlock value ] 
    on: Error
    do: [ :err | promiseError  := err ]
    ensure: [ promiseLock signal ] ] forkBackground

These are the errors:

[forkBackground] Messages sent but not implemented 
[on:do:ensure:] Messages sent but not implemented

I was of the idea that Pharo is not different from Smalltalk, or is it possible that the website's solution does not also work with Smalltalk?

Upvotes: 7

Views: 524

Answers (2)

Tony Giaccone
Tony Giaccone

Reputation: 511

Late to the party here, but there's a ebook available that talks directly about this problem in Pharo. And gives an exact solution. I know it works because I've used it.

https://books.pharo.org/booklet-ConcurrentProgramming/

This solution is lifted right from that book. I didn't invent it.

The solution involves adding two message to the Class BlockClosure.

BlockClosure >> promise
^ self promiseAt: Processor activePriority

and:

BlockClosure >> promiseAt: aPriority
"Answer a promise that represents the result of the receiver
execution
at the given priority."
| promise |
promise := Promise new.
[ promise value: self value ] forkAt: aPriority.
^ promise

Then creating a Promise Class.

Object subclass: #Promise
instanceVariableNames: 'valueProtectingSemaphore value hasValue'
classVariableNames: ''
package: 'Promise'

Promise >> initialize
super initialize.
valueProtectingSemaphore := Semaphore new.
hasValue := false

Promise >> hasValue
^ hasValue

Promise >> value
"Wait for a value and once it is available returns it"
valueProtectingSemaphore wait.
valueProtectingSemaphore signal. "To allow multiple requests for
the value."
^ value

Promise >> value: resultValue
value := resultValue.
hasValue := true.
valueProtectingSemaphore signal

I used this when implementing an MQTT data reader. I would request the latest message from the Broker and return a promise. That allowed me to loop around the promise and not block all the other processes running in the image.

Works like a charm. And while it took me a little effort to wrap my head around how it works, it's not that bad.

You use it by putting your action in a block and sending it the promise method

myPromise := [broker readMessage ] promise.

And you can test it:

(myPromise hasValue) ifTrue: [ 
    myObject processMsg: (myPromise value)
]

You'll want to put your task in it's own thread to keep it from blocking the rest of your image.

Upvotes: 0

Leandro Caniglia
Leandro Caniglia

Reputation: 14858

Try the following:

promiseLock := Semaphore new.
[
  [[promiseValue := aBlock value] on: Error do: [:err | promiseError := err]]
    ensure: [promiseLock signal]] forkAt: Processor userBackgroundPriority

The idea is to ensure: that the promiseLock semaphore will receive a signal even if an Error curtails the evaluation of aBlock. The priority to forkAt: is debatable, but I would start somewhere, and adjust it as needed.

Upvotes: 6

Related Questions