4 people like it.
Like the snippet!
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
More information