@@ -329,21 +329,13 @@ namespace Microsoft.FSharp.Control
329329
330330
331331
332- // Reify exceptional results as exceptions
332+ /// Reify exceptional results as exceptions
333333let commit res =
334334match reswith
335335| Ok res-> res
336336| Error edi-> edi.ThrowAny()
337337| Canceled exn-> raise exn
338338
339- // Reify exceptional results as exceptionsJIT 64 doesn't always take tailcalls correctly
340-
341- let commitWithPossibleTimeout res =
342- match reswith
343- | None-> raise( System.TimeoutException())
344- | Some res-> commit res
345-
346-
347339//----------------------------------
348340// PRIMITIVE ASYNC INVOCATION
349341
@@ -713,11 +705,15 @@ namespace Microsoft.FSharp.Control
713705[<AutoSerializable( false ) >]
714706type ResultCell < 'T >() =
715707let mutable result = None
708+
716709// The continuations for the result
717710let mutable savedConts : list < SuspendedAsync < 'T >> = []
711+
718712// The WaitHandle event for the result. Only created if needed, and set to null when disposed.
719713let mutable resEvent = null
714+
720715let mutable disposed = false
716+
721717// All writers of result are protected by lock on syncRoot.
722718let syncRoot = new Object()
723719
@@ -752,13 +748,11 @@ namespace Microsoft.FSharp.Control
752748interface IDisposablewith
753749member x.Dispose () = x.Close() // ; System.GC.SuppressFinalize(x)
754750
755-
756751member x.GrabResult () =
757752match resultwith
758753| Some res-> res
759754| None-> failwith" Unexpected no result"
760755
761-
762756/// Record the result in the ResultCell.
763757member x.RegisterResult ( res : 'T , reuseThread ) =
764758let grabbedConts =
@@ -795,7 +789,10 @@ namespace Microsoft.FSharp.Control
795789
796790member x.ResultAvailable = result.IsSome
797791
798- member x.AwaitResult =
792+ /// Await the result of a result cell, without a direct timeout or direct
793+ /// cancellation. That is, the underlying computation must fill the result
794+ /// if cancellation or timeout occurs.
795+ member x.AwaitResult_NoDirectCancelOrTimeout =
799796 unprotectedPrimitive( fun args ->
800797// Check if a result is available synchronously
801798let resOpt =
@@ -860,10 +857,10 @@ namespace Microsoft.FSharp.Control
860857// If timeout is provided, we govern the async by our own CTS, to cancel
861858// when execution times out. Otherwise, the user-supplied token governs the async.
862859match timeoutwith
863- | None-> token, None
864- | Some_ ->
865- let subSource = new LinkedSubSource( token)
866- subSource.Token, Some subSource
860+ | None-> token, None
861+ | Some_ ->
862+ let subSource = new LinkedSubSource( token)
863+ subSource.Token, Some subSource
867864
868865use resultCell= new ResultCell< AsyncImplResult<_>>()
869866 queueAsync
@@ -1252,7 +1249,8 @@ namespace Microsoft.FSharp.Control
12521249 aux.econt edi
12531250)
12541251
1255- static member AwaitWaitHandle ( waitHandle : WaitHandle ,? millisecondsTimeout : int ) =
1252+ /// Wait for a wait handle. Both timeout and cancellation are supported
1253+ static member AwaitWaitHandle ( waitHandle : WaitHandle , ? millisecondsTimeout : int ) =
12561254let millisecondsTimeout = defaultArg millisecondsTimeout Threading.Timeout.Infinite
12571255if millisecondsTimeout= 0 then
12581256 async.Delay( fun () ->
@@ -1312,61 +1310,61 @@ namespace Microsoft.FSharp.Control
13121310return ! Async.AwaitWaitHandle( iar.AsyncWaitHandle, ?millisecondsTimeout= millisecondsTimeout) }
13131311
13141312
1315- ///Await the result of a result cell without a timeout
1316- static member ReifyResult ( result : AsyncImplResult < 'T >) : Async < 'T > =
1313+ ///Bind the result of a result cell, calling the appropriate continuation.
1314+ static member BindResult ( result : AsyncImplResult < 'T >) : Async < 'T > =
13171315 unprotectedPrimitive( fun ({ aux = aux } as args ) ->
13181316( match resultwith
13191317| Ok v-> args.cont v
13201318| Error exn-> aux.econt exn
13211319| Canceled exn-> aux.ccont exn) )
13221320
1323- /// Await the result of a result cell without a timeout
1324- static member AwaitAndReifyResult ( resultCell : ResultCell < AsyncImplResult < 'T >>) : Async < 'T > =
1321+ /// Await and use the result of a result cell. The resulting async doesn't support cancellation
1322+ /// or timeout directly, rather the underlying computation must fill the result if cancellation
1323+ /// or timeout occurs.
1324+ static member AwaitAndBindResult_NoDirectCancelOrTimeout ( resultCell : ResultCell < AsyncImplResult < 'T >>) : Async < 'T > =
13251325async {
1326- let! result = resultCell.AwaitResult
1327- return ! Async.ReifyResult ( result)
1326+ let! result = resultCell.AwaitResult _ NoDirectCancelOrTimeout
1327+ return ! Async.BindResult ( result)
13281328}
1329-
1330-
13311329
1332- /// Await the result of a result cell without a timeout
1333- ///
1334- /// Always resyncs to the synchronization context if needed, by virtue of it being built
1335- /// from primitives which resync.
1336- static member AsyncWaitAsyncWithTimeout ( innerCTS : CancellationTokenSource , resultCell : ResultCell < AsyncImplResult < 'T >>, millisecondsTimeout ) : Async < 'T > =
1330+ /// Await the result of a result cell belonging to a child computation. The resulting async supports timeout and if
1331+ /// it happens the child computation will be cancelled. The resulting async doesn't support cancellation
1332+ /// directly, rather the underlying computation must fill the result if cancellation occurs.
1333+ static member AwaitAndBindChildResult ( innerCTS : CancellationTokenSource , resultCell : ResultCell < AsyncImplResult < 'T >>, millisecondsTimeout ) : Async < 'T > =
13371334match millisecondsTimeoutwith
13381335| None| Some- 1 ->
1339- resultCell|> Async.AwaitAndReifyResult
1336+ resultCell|> Async.AwaitAndBindResult _ NoDirectCancelOrTimeout
13401337
13411338| Some0 ->
13421339async { if resultCell.ResultAvailablethen
13431340return commit( resultCell.GrabResult())
13441341else
1345- return commitWithPossibleTimeout None }
1342+ return raise ( System.TimeoutException ()) }
13461343| _ ->
13471344async { try
13481345if resultCell.ResultAvailablethen
13491346return commit( resultCell.GrabResult())
13501347else
1351- let! ok = Async.AwaitWaitHandle( resultCell.GetWaitHandle(),? millisecondsTimeout= millisecondsTimeout)
1348+ let! ok = Async.AwaitWaitHandle( resultCell.GetWaitHandle(), ?millisecondsTimeout= millisecondsTimeout)
13521349if okthen
1353- return commitWithPossibleTimeout ( Some ( resultCell.GrabResult()))
1350+ return commit ( resultCell.GrabResult())
13541351else // timed out
13551352// issue cancellation signal
13561353 innerCTS.Cancel()
13571354// wait for computation to quiesce
13581355let! _ = Async.AwaitWaitHandle( resultCell.GetWaitHandle())
1359- return commitWithPossibleTimeout None
1356+ return raise ( System.TimeoutException ())
13601357finally
13611358 resultCell.Close() }
13621359
13631360
1364- static member FromBeginEnd ( beginAction , endAction ,? cancelAction ): Async < 'T > =
1361+ static member FromBeginEnd ( beginAction , endAction , ? cancelAction ): Async < 'T > =
13651362async { let! cancellationToken = getCancellationToken()
13661363let resultCell = new ResultCell<_>()
13671364
13681365let once = Once()
13691366let registration : CancellationTokenRegistration =
1367+
13701368let onCancel ( _ : obj ) =
13711369// Call the cancellation routine
13721370match cancelActionwith
@@ -1381,7 +1379,9 @@ namespace Microsoft.FSharp.Control
13811379// If we get an exception from a cooperative cancellation function
13821380// we assume the operation has already completed.
13831381try cancel() with _ -> ()
1382+
13841383 cancellationToken.Register( Action< obj>( onCancel), null )
1384+
13851385let callback =
13861386new System.AsyncCallback( fun iar ->
13871387if not iar.CompletedSynchronouslythen
@@ -1405,15 +1405,15 @@ namespace Microsoft.FSharp.Control
14051405// ResultCell allows a race and throws away whichever comes last.
14061406 resultCell.RegisterResult( res, reuseThread= true ) |> unfake
14071407else ())
1408-
1409-
14101408
14111409let ( iar : IAsyncResult ) = beginAction( callback,( null: obj ))
14121410if iar.CompletedSynchronouslythen
14131411 registration.Dispose()
14141412return endAction iar
14151413else
1416- return ! Async.AwaitAndReifyResult( resultCell) }
1414+ // Note: ok to use "NoDirectCancel" here because cancellation has been registered above
1415+ // Note: ok to use "NoDirectTimeout" here because no timeout parameter to this method
1416+ return ! Async.AwaitAndBindResult_ NoDirectCancelOrTimeout( resultCell) }
14171417
14181418
14191419static member FromBeginEnd ( arg , beginAction , endAction ,? cancelAction ): Async < 'T > =
@@ -1567,7 +1567,9 @@ namespace Microsoft.FSharp.Control
15671567 event.AddHandler( del)
15681568
15691569// Return the async computation that allows us to await the result
1570- return ! Async.AwaitAndReifyResult( resultCell) }
1570+ // Note: ok to use "NoDirectCancel" here because cancellation has been registered above
1571+ // Note: ok to use "NoDirectTimeout" here because no timeout parameter to this method
1572+ return ! Async.AwaitAndBindResult_ NoDirectCancelOrTimeout( resultCell) }
15711573
15721574type Async with
15731575static member Ignore ( computation : Async < 'T >) = bindA computation( fun _ -> doneA)
@@ -1597,7 +1599,7 @@ namespace Microsoft.FSharp.Control
15971599 computation
15981600|> unfake
15991601
1600- return Async.AsyncWaitAsyncWithTimeout ( innerCTS, resultCell, millisecondsTimeout) }
1602+ return Async.AwaitAndBindChildResult ( innerCTS, resultCell, millisecondsTimeout) }
16011603
16021604static member SwitchToContext syncContext =
16031605async { match syncContextwith
@@ -1681,10 +1683,6 @@ namespace Microsoft.FSharp.Control
16811683 Async.FromBeginEnd( buffer, offset, count, stream.BeginWrite, stream.EndWrite)
16821684#endif
16831685
1684- type System.Threading.WaitHandle with
1685- member waitHandle.AsyncWaitOne (? millisecondsTimeout : int ) = // only used internally, not a public API
1686- Async.AwaitWaitHandle( waitHandle,? millisecondsTimeout= millisecondsTimeout)
1687-
16881686type IObservable < 'Args > with
16891687
16901688[<CompiledName( " AddToObservable" ) >] // give the extension member a 'nice', unmangled compiled name, unique within this module
@@ -1715,7 +1713,7 @@ namespace Microsoft.FSharp.Control
17151713| :? System.Net.WebExceptionas webExn
17161714when webExn.Status= System.Net.WebExceptionStatus.RequestCanceled&& ! canceled->
17171715
1718- Async.ReifyResult ( AsyncImplResult.Canceled( OperationCanceledException webExn.Message))
1716+ Async.BindResult ( AsyncImplResult.Canceled( OperationCanceledException webExn.Message))
17191717| _ ->
17201718 edi.ThrowAny())
17211719
@@ -1791,7 +1789,10 @@ namespace Microsoft.FSharp.Control
17911789)
17921790 start a1 Choice1Of2
17931791 start a2 Choice2Of2
1794- let! result = c.AwaitResult
1792+ // Note: It is ok to use "NoDirectCancel" here because the started computations use the same
1793+ // cancellation token and will register a cancelled result if cancellation occurs.
1794+ // Note: It is ok to use "NoDirectTimeout" here because there is no specific timeout log to this routine.
1795+ let! result = c.AwaitResult_ NoDirectCancelOrTimeout
17951796return ! reify result
17961797}
17971798let timeout msec cancellationToken =
@@ -1805,7 +1806,10 @@ namespace Microsoft.FSharp.Control
18051806 exceptionContinuation= ignore,
18061807 cancellationContinuation= ignore,
18071808 cancellationToken= cancellationToken)
1808- c.AwaitResult
1809+ // Note: It is ok to use "NoDirectCancel" here because the started computations use the same
1810+ // cancellation token and will register a cancelled result if cancellation occurs.
1811+ // Note: It is ok to use "NoDirectTimeout" here because the child compuation above looks after the timeout.
1812+ c.AwaitResult_ NoDirectCancelOrTimeout
18091813
18101814[<Sealed>]
18111815[<AutoSerializable( false ) >]
@@ -1854,7 +1858,7 @@ namespace Microsoft.FSharp.Control
18541858 failwith" multiple waiting reader continuations for mailbox" )
18551859
18561860let waitOneWithCancellation ( timeout ) =
1857- ensurePulse() .AsyncWaitOne ( millisecondsTimeout= timeout)
1861+ Async.AwaitWaitHandle ( ensurePulse(), millisecondsTimeout= timeout)
18581862
18591863let waitOne ( timeout ) =
18601864if timeout< 0 && not cancellationSupportedthen
@@ -2125,36 +2129,34 @@ namespace Microsoft.FSharp.Control
21252129let msg = buildMessage( new AsyncReplyChannel<_>( fun reply ->
21262130// Note the ResultCell may have been disposed if the operation
21272131// timed out. In this case RegisterResult drops the result on the floor.
2128- resultCell.RegisterResult( reply, reuseThread= false ) |> unfake))
2132+ resultCell.RegisterResult( reply, reuseThread= false ) |> unfake))
21292133 mailbox.Post( msg)
21302134match timeoutwith
2131- | Threading.Timeout.Infinite->
2132- async { let! result = resultCell.AwaitResult
2133- return Some( result)
2134- }
2135+ | Threading.Timeout.Infinitewhen not cancellationSupported->
2136+ async { let! result = resultCell.AwaitResult_ NoDirectCancelOrTimeout
2137+ return Some result}
21352138
2136- | _ ->
2137- async { use _disposeCell= resultCell
2138- let! ok = resultCell.GetWaitHandle() .AsyncWaitOne ( millisecondsTimeout= timeout)
2139- let res = ( if okthen Some( resultCell.GrabResult()) else None)
2140- return res}
2139+ | _ ->
2140+ async { use _disposeCell= resultCell
2141+ let! ok = Async.AwaitWaitHandle ( resultCell.GetWaitHandle(), millisecondsTimeout= timeout)
2142+ let res = ( if okthen Some( resultCell.GrabResult()) else None)
2143+ return res}
21412144
21422145member x.PostAndAsyncReply ( buildMessage , ? timeout : int ) =
21432146let timeout = defaultArg timeout defaultTimeout
21442147match timeoutwith
2145- | Threading.Timeout.Infinite->
2146- // Nothing to dispose, no wait handles used
2147- let resultCell = new ResultCell<_>()
2148- let msg = buildMessage( new AsyncReplyChannel<_>( fun reply -> resultCell.RegisterResult( reply, reuseThread= false ) |> unfake))
2149- mailbox.Post( msg)
2150- resultCell.AwaitResult
2151- | _ ->
2152- let asyncReply = x.PostAndTryAsyncReply( buildMessage, timeout= timeout)
2153- async { let! res = asyncReply
2154- match reswith
2155- | None-> return ! raise( TimeoutException( SR.GetString( SR.mailboxProcessorPostAndAsyncReplyTimedOut)))
2156- | Some res-> return res
2157- }
2148+ | Threading.Timeout.Infinitewhen not cancellationSupported->
2149+ // Nothing to dispose, no wait handles used
2150+ let resultCell = new ResultCell<_>()
2151+ let msg = buildMessage( new AsyncReplyChannel<_>( fun reply -> resultCell.RegisterResult( reply, reuseThread= false ) |> unfake))
2152+ mailbox.Post( msg)
2153+ resultCell.AwaitResult_ NoDirectCancelOrTimeout
2154+ | _ ->
2155+ let asyncReply = x.PostAndTryAsyncReply( buildMessage, timeout= timeout)
2156+ async { let! res = asyncReply
2157+ match reswith
2158+ | None-> return ! raise( TimeoutException( SR.GetString( SR.mailboxProcessorPostAndAsyncReplyTimedOut)))
2159+ | Some res-> return res}
21582160
21592161member x.Receive (? timeout ) = mailbox.Receive( timeout= defaultArg timeout defaultTimeout)
21602162member x.TryReceive (? timeout ) = mailbox.TryReceive( timeout= defaultArg timeout defaultTimeout)