Reputation: 855
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
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
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