future-2.0.0: Supposed to mimics and enhance proposed C++ "future" features



This Future module was written by Chris Kuklewicz to see if he understood the design and utility of the new C++ standard's future. In particular the ability to cleanly access either a resulting value or exception.

There a methods to poll (with check), to block (with wait or timedWait), and to block and retrieve the actual value or rethrow the exception in the accessing thread (with get or timedGet). Timeouts are in micro seconds, values less than or equal to zero use non-blocking check. The timeout should be detected reagarless of the blocking or FFI state of the worker thread.

On top of forkPromise is forkPromises, racePromises, and declarePromise.

One can also manage the threadBy calling abort, which may cause the promise to store the exception from the abort as well as killing the worker thread. The worker thread Id is a secret, this is needed to ensure the running of the continuations. The abort operation has the same synchronous behavior as killThread.

Note: There is no way for an outside thread to directly set the value of the promise to a non-exception value. Using abort (or throwTo with getPromiseThreadId) creates a race condition in setting the result of the promise. There is no way to change the result of promise once it has been set.

The extension to the C++ standard is in the continuation attachment. The addTodo command will, while the worker is running, add the todo continuation to an internal list. Immediately upon finishing the action the worker thread will always run through the queued continuations. Each todo will be run in its own forkIO thread (unblocked). If the addTodo command is issued after the promise value has been set then it simplify runs the todo in a new thread. Thus there is no way multiple continuations can interfere with each other, and there are no ordering guarantees between them. The todo action will not be able to distinguish whether it is being run from the stored queue or immediately.

The use of block and finally should ensure that no matter how the worker ends the stored continations are run. For instance: if abort is used then the continations might be run with that thread killing exception or with the custom Promise.abort exception if no other result is already present.

One use case for addTodo is to allow multiplexing. Several promises could be given a continuation to write the results to an MChan or MVar, allowing another process to block waiting for the first one to finish.



data Promise a Source


Eq (Promise a) 
Show (Promise a) 

forkPromise :: IO a -> IO (Promise a)Source

forkPromise take an action to run, and runs it in a new thread. This is run in an unblock context. If the action succeeds it will store its result as (Right {}). If the action throws an exception, or the

declarePromise :: IO (Promise a, IO a -> IO Bool)Source

declarePromise is built on top of forkPromise. It creates a promise and an function to fulfill the promise with an action. The first time the fulfull function is used it gives the action to the promise and returns True. All additional usages of the fulfill function will do nothing and return False. Note that the Promise may be aborted before the fulfill function is used, and in this case the fulfill function will appear to succeed but achieve nothing.

forkPromises :: [IO a] -> IO ([Promise a], Chan (PromiseResult a))Source

forkPromises is build on top of forkPromise. It converts a list of actions into a list of promises, and additionally collects the results, in completion order, into the returned Chan.

racePromises :: [IO a] -> IO (PromiseResult a)Source

racePromises is build on top of forkPromise. It runs a list of actions as promises and waits for the first result (which may be an exception). Once the result is found it asynchronously kills the threads.

check :: Promise a -> IO (Maybe (PromiseResult a))Source

check is a non-blocking read. Like timedWait with 0 delay.

wait :: Promise a -> IO (PromiseResult a)Source

wait is a blocking read.

get :: Promise a -> IO aSource

get is wait which rethrows a SomeException in the calling thread

timedWait :: Int -> Promise a -> IO (Maybe (PromiseResult a))Source

timedWait with a positive value in micro seconds is a blocking read with timeout.

timedGet :: Int -> Promise a -> IO (Maybe a)Source

timedGet is a timedWait which rethrows a SomeException in the calling thread

abort :: Promise t -> IO ()Source

If the abort occurs before the action has stored a result then the result is set to an exception. The first call to abort gets the threadId and performs the, possibly blocking, killThread. If it completes then the ThreadId is forgotten (so the thread can be garbage collected).

addTodo :: Promise a -> (PromiseResult a -> IO ()) -> IO ()Source

Post an action to perform in a new thread with the reasult of the promise. All are run unblocked in a fresh thread.