4 people like it.

F# Future

Similar to an async { } block but captures the result for future consumption. This structure can be very useful for performing multiple result-returning operations in parallel when the results aren't needed immediately. For example, performing several read/transform operations or pre-populating a cache with yet-to-be computed values. Microsoft's Task Parallel Library in .NET 4.0 includes a Future implementation so this version is only needed on earlier .NET versions. Comments, suggestions, and improvements are always welcome.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
35: 
36: 
37: 
38: 
39: 
40: 
41: 
42: 
43: 
44: 
45: 
46: 
47: 
48: 
49: 
50: 
51: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
62: 
63: 
64: 
65: 
66: 
67: 
68: 
69: 
70: 
71: 
72: 
73: 
74: 
open System
open System.ComponentModel
open System.Threading

/// Executes a computation in a background worker and synchronizes on the result return.  The
/// computation is started immediately and calling 'Value' blocks until the result is ready.
type Future<'t>(f : unit -> 't) =
    /// Result of the computation on normal exit.
    let mutable result :'t option = None
    
    /// Result if an exception was thrown.
    let mutable ext : Exception option = None
    
    let syncRoot = new Object()
    
    /// Pulse object used to wait until a result is ready.  ensurePulse() is used so we
    /// don't have to create the object if the result is done before it's needed.
    let mutable pulse : ManualResetEvent = null
    let ensurePulse() = 
        lock syncRoot (fun () ->
            match pulse with 
            | null -> 
                pulse <- new ManualResetEvent(false);
            | _ -> 
                ()
            pulse)
        
    /// WARNING: Call once a lock on syncRoot is already held.  Pulses the wait notifier.  Safe if
    /// called after 'pulse' is created but before WaitOne is called.
    let notifyWaiters() = if pulse <> null then pulse.Set() |> ignore
    
    let work = new BackgroundWorker()

    /// On RunWorkerAsync(), run specified function and store result.  All exceptions must be
    /// trapped.      
    do work.DoWork.Add( fun args ->
                            try 
                                result <- Some( f() )
                            with e ->
                                ext <- Some e
                            lock syncRoot ( fun () -> notifyWaiters()) )

    /// Start immediately / automatically.
    do work.RunWorkerAsync()
    
    /// Returns the value of the computation, blocking if the result isn't ready yet.
    member t.Value =
        // If available, we can return it right away.
        match result with
        | Some x -> x
        | None when ext.IsSome -> raise (Option.get ext)
        | None ->
            let p = ensurePulse()
            
            // Check again in case it changed while we were gettting the wait object.
            match result with
            | Some x -> x
            | None ->
                // Lock-free is ok because if the pulse.Set() method is called between when we
                // checked 'result' and call WaitOne here, WaitOne will return immediately.
                p.WaitOne(1000000000) |> ignore
                match result with
                | Some x -> x
                | None ->
                    if ext.IsSome then raise (Option.get ext)
                    else failwith "Future computation failed."

    /// Returns true if the computation is finished, false if not.
    member t.IsComplete =
        match result with
        | Some x -> true
        | None when Option.isSome ext -> true
        | None -> false
    
namespace System
namespace System.ComponentModel
namespace System.Threading
Multiple items
type Future<'t> =
  new : f:(unit -> 't) -> Future<'t>
  member IsComplete : bool
  member Value : 't

Full name: Script.Future<_>


 Executes a computation in a background worker and synchronizes on the result return. The
 computation is started immediately and calling 'Value' blocks until the result is ready.


--------------------
new : f:(unit -> 't) -> Future<'t>
val f : (unit -> 't)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val mutable result : 't option


 Result of the computation on normal exit.
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
union case Option.None: Option<'T>
val mutable ext : Exception option


 Result if an exception was thrown.
Multiple items
type Exception =
  new : unit -> Exception + 2 overloads
  member Data : IDictionary
  member GetBaseException : unit -> Exception
  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit
  member GetType : unit -> Type
  member HelpLink : string with get, set
  member InnerException : Exception
  member Message : string
  member Source : string with get, set
  member StackTrace : string
  ...

Full name: System.Exception

--------------------
Exception() : unit
Exception(message: string) : unit
Exception(message: string, innerException: exn) : unit
val syncRoot : Object
Multiple items
type Object =
  new : unit -> obj
  member Equals : obj:obj -> bool
  member GetHashCode : unit -> int
  member GetType : unit -> Type
  member ToString : unit -> string
  static member Equals : objA:obj * objB:obj -> bool
  static member ReferenceEquals : objA:obj * objB:obj -> bool

Full name: System.Object

--------------------
Object() : unit
val mutable pulse : ManualResetEvent


 Pulse object used to wait until a result is ready. ensurePulse() is used so we
 don't have to create the object if the result is done before it's needed.
Multiple items
type ManualResetEvent =
  inherit EventWaitHandle
  new : initialState:bool -> ManualResetEvent

Full name: System.Threading.ManualResetEvent

--------------------
ManualResetEvent(initialState: bool) : unit
val ensurePulse : (unit -> ManualResetEvent)
val lock : lockObject:'Lock -> action:(unit -> 'T) -> 'T (requires reference type)

Full name: Microsoft.FSharp.Core.Operators.lock
val notifyWaiters : (unit -> unit)


 WARNING: Call once a lock on syncRoot is already held. Pulses the wait notifier. Safe if
 called after 'pulse' is created but before WaitOne is called.
EventWaitHandle.Set() : bool
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val work : BackgroundWorker
Multiple items
type BackgroundWorker =
  inherit Component
  new : unit -> BackgroundWorker
  member CancelAsync : unit -> unit
  member CancellationPending : bool
  member IsBusy : bool
  member ReportProgress : percentProgress:int -> unit + 1 overload
  member RunWorkerAsync : unit -> unit + 1 overload
  member WorkerReportsProgress : bool with get, set
  member WorkerSupportsCancellation : bool with get, set
  event DoWork : DoWorkEventHandler
  event ProgressChanged : ProgressChangedEventHandler
  ...

Full name: System.ComponentModel.BackgroundWorker

--------------------
BackgroundWorker() : unit
event BackgroundWorker.DoWork: IEvent<DoWorkEventHandler,DoWorkEventArgs>
member IObservable.Add : callback:('T -> unit) -> unit
val args : DoWorkEventArgs
union case Option.Some: Value: 'T -> Option<'T>
val e : exn
BackgroundWorker.RunWorkerAsync() : unit
BackgroundWorker.RunWorkerAsync(argument: obj) : unit
val t : Future<'t>
member Future.Value : 't

Full name: Script.Future`1.Value


 On RunWorkerAsync(), run specified function and store result. All exceptions must be
 trapped.
 Start immediately / automatically.
 Returns the value of the computation, blocking if the result isn't ready yet.
val x : 't
property Option.IsSome: bool
val raise : exn:Exception -> 'T

Full name: Microsoft.FSharp.Core.Operators.raise
module Option

from Microsoft.FSharp.Core
val get : option:'T option -> 'T

Full name: Microsoft.FSharp.Core.Option.get
val p : ManualResetEvent
WaitHandle.WaitOne() : bool
WaitHandle.WaitOne(timeout: TimeSpan) : bool
WaitHandle.WaitOne(millisecondsTimeout: int) : bool
WaitHandle.WaitOne(timeout: TimeSpan, exitContext: bool) : bool
WaitHandle.WaitOne(millisecondsTimeout: int, exitContext: bool) : bool
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
member Future.IsComplete : bool

Full name: Script.Future`1.IsComplete


 Returns true if the computation is finished, false if not.
val isSome : option:'T option -> bool

Full name: Microsoft.FSharp.Core.Option.isSome
Raw view Test code New version

More information

Link:http://fssnip.net/5T
Posted:13 years ago
Author:Jason McCampbell
Tags: async , parallel , future , thread