future-1.1.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.

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 instnace: 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) 
Ord (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

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 act has stored a result then the result is set to (userError Promise.abort :: IOError), or the killThread exception.

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.