Does Async.RunSynchronously method block? - f#

From the documentation it appears that the Async.RunSynchronously runs the async computation and awaits its result. I've also read that it's similar to await in C#. I'm curious if this blocks the thread until it's run to completion?

Yes, Async.RunSynchronously blocks. A simple illustration:
let work = async {
printfn "Async starting"
do! Async.Sleep(1000)
printfn "Async done" }
printfn "Main starting"
work |> Async.RunSynchronously
printfn "Main done"
This will print:
Main starting
Async starting
Async done
Main done
It is roughly similar to task.RunSynchronously in C# - although there might be some subtle differences (the F# workflow will be executed using a thread pool while the main thread is blocked and waits for the completion while the C# equivalent might actually run the work on the current thread which is more akin to StartImmediate in F# - which however does not wait).

Related

Using System.Reactive.Linq for polling at an interval

I've spent hours combing through documentation and tutorials, but can't figure out how to use ReactiveX to poll an external resource, or anything for that matter, every at an interval. Below is some code I wrote to get information from a REST API at an interval.
open System
open System.Reactive.Linq
module MyObservable =
let getResources =
async {
use client = new HttpClient()
let! response = client.GetStringAsync("http://localhost:8080/items") |> Async.AwaitTask
return response
} |> Async.StartAsTask
let getObservable (interval: TimeSpan) =
let f () = getResources.Result
Observable.Interval(interval)
|> Observable.map(fun _ -> f ())
To test this out, I tried subscribing to the Observable and waiting five seconds. It does receive something every second for five seconds, but the getResources is only called the first time and then the result is just used at each interval. How can I modify this to make the REST call at each interval instead of just the result of the first call being used over and over again?
let mutable res = Seq.empty
getObservable (new TimeSpan(0,0,1))
|> Observable.subscribe(fun (x: seq<string>) -> res <- res |> Seq.append x;)
|> ignore
Threading.Thread.Sleep(5000)
Don't use a Task. Tasks are what we call "hot", meaning that if you have a value of type Task in your hand, it means that the task is already running, and there is nothing you can do about it. In particular, this means you cannot restart it, or start a second instance of it. Once a Task is created, it's too late.
In your particular case it means that getResources is not "a way to start a task", but just "a task". Already started, already running.
If you want to start a new task every time, you have two alternatives:
First (the worse alternative), you could make getResources a function rather than a value, which you can do by giving it a parameter:
let getResources () =
async { ...
And then call it with that parameter:
let f () = getResources().Result
This will run the getResources function afresh every time you call f(), which will create a new Task every time and start it.
Second (a better option), don't use a Task at all. You're creating a perfectly good async computation and then turning it into a Task only to block on getting its result. Why? You can block on an async's result just as well!
let getResources = async { ... }
let getObservable interval =
let f () = getResources |> Async.RunSynchronously
...
This works, even though getResources is not a function, because asyncs, unlike Tasks, are what we call "cold". This means that, if you have an async in your hand, it doesn't mean that it's already running. async, unlike Task, represents not an "already running" computation, but rather "a way to start a computation". A corollary is that you can start it multiple times from the same async value.
One way to start it is via Async.RunSynchronously as I'm doing in my example above. This is not the best way, because it blocks the current thread until the computation is done, but it's equivalent to what you were doing with accessing the Task.Result property, which also blocks until the Task is done.

Why won't `Async.SwitchToContext` return?

I'm trying to scrape some websites that need to run their JavaScript before the document has all the data I'm interested in. I'm trying to open a WebBrowser and wait for the document to load, but I can't get the data when I try to switch back to the thread the WebBrowser is on. Trying to run it without switching back to the thread gives casting errors. = (
What's stopping the async from switching threads? How do I fix this problem?
Script
open System
open System.Windows.Forms
open System.Threading
let step a = do printfn "%A" a
let downloadWebSite (address : Uri) (cont : HtmlDocument -> 'a) =
let browser = new WebBrowser()
let ctx = SynchronizationContext.Current
browser.DocumentCompleted.Add (fun _ ->
printfn "Document Loaded" )
async {
do step 1
do browser.Navigate(address)
do step 2
let! _ = Async.AwaitEvent browser.DocumentCompleted
do step 3
do! Async.SwitchToContext ctx
do step 4
return cont browser.Document }
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.RunSynchronously
Output
>
1
2
Document Loaded
3
# It just hangs here. I have to manually interrupt fsi.
- Interrupt
>
4
The problem with your approach is that RunSynchronously blocks the thread that you are trying to use to run the rest of the asynchronous computation using Async.SwitchToContext ctx.
When using F# Interactive, there is one main thread which runs in the F# Interactive and handles the user interactions. This is the thread that can use Windows Forms controls, so you correctly create WebBrowser outside of async. The waiting for DocumentCompleted happens on a thread pool thread (which runs the async workflow), but when you try to switch back to the main thread, it is already blocked by Async.RunSynchronously.
You can avoid blocking the thread by running a loop that calls Application.DoEvents to process events on the main thread (which will also allow it to run the rest of your async). Your downloadWebSite stays the same, but now you wait using:
let test =
downloadWebSite (Uri "http://www.google.com") Some
|> Async.Ignore
|> Async.StartAsTask
while not test.IsCompleted do
System.Threading.Thread.Sleep(100)
System.Windows.Forms.Application.DoEvents()
This is a bit of a hack - and there might be a better way of structuring this if you do not really need to wait for the result (e.g. just return a task and wait before running the next command), but this should do the trick.

Why is Async.RunSynchronously hanging?

In my f# project, I'm calling a c# library that returns me a Task. When I try to do this in my f# project, nothing happens.
let str = getName() |> Async.AwaitTask |> Async.RunSynchronously
However, if I update my code to use an async workfolow, it doesn't hang anymore. What am I doing wrong when calling Async.RunSynchronously?
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
}
In your second example you just build async workflow, but don't actually run it.
It is designed that way so you could define a complex workflow without running every async part of it immediately, but instead run the whole thing when it's good and ready.
In order to run it you need to call Async.RunSynchronously or Async.StartAsTask:
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
} |> Async.RunSynchronously
Also, if you do need to work with Tasks, it's probably better to use TaskBuilder.fs instead of Async.

Running c# async method in f# workflow

I am trying to get the below code to work in a F# async workflow, but I am getting the error "Unexpected symbol '}' in expression". I am fairly new to both F# and async in general. What am I missing here.
let someFunction (req : HttpRequestMesssage) a b =
// code
async{
let! readToProvider =
req.Content.ReadAsMultipartAsync(provider)
|> Async.AwaitIAsyncResult
} |> Async.RunSynchronously
req.CreateResponse(HttpStatusCode.OK)
I worry that my previous answer wasn't quite what you want. What I supplied just got you through the compile error. But one thing about it, is that it does not run asynchronously. Task.Wait and Async.RunSynchronously will both block the running thread until the operation is complete.
If you want to actually be async, i.e. not blocking, you have to put the entire method, or at least the last part of it, into the async block, such that you're actually returning an async op to the caller. So the answer would be
let someFunction (req : HttpRequestMesssage) a b =
async {
let! readToProvider = (req.Content.ReadAsMultipartAsync provider) |> Async.AwaitIAsyncResult
return req.CreateResponse HttpStatusCode.OK
}
This option returns not the response, but an Async<Response>. So now the caller can decide how to run it, either blocking or truly asynchronously.
This way if you're using a web server that handles asynchronous requests, then you can simply connect this function to an endpoint (probably converting the Async to a Task at the point of connection, since most .net async web servers are written from C# perspective) and it'll run asynchronously without blocking a thread. Or if you're calling it from another async op you can do do! someFunction ... and it'll run asynchronously. But if the caller doesn't care and just wants to run synchronously, it can do someFunction ... |> Async.RunSynchronously. So you get more flexibility there. And you can always define let someFunctionSync ... = someFunction ... |> Async.RunSynchronously if that's the more common use case.
I'd recommend going this way unless you really want to enforce blocking.
You're doing it right. You're only getting the error because you're ending your async block with a let! expression. Change it to return!, or do! ... |> Async.Ignore and you'll be good.
Blocks in F# (neither workflows nor regular code blocks) should not end with let.
Of course if all you're really doing in the workflow is that one call, you don't need the workflow block at all (you never really should need to write a block for a single call). Just do
req.Content.ReadAsMultipartAsync provider
|> Async.AwaitIAsyncResult
|> Async.Ignore
|> Async.RunSynchronously
req.CreateResponse HttpStatusCode.OK
Or for that matter, just use the built in Tasks Wait, which does the same thing as Async.RunSynchronously:
(req.Content.ReadAsMultipartAsync provider).Wait()

F# How Async<'T> cancellation works?

I was pretty comfortable with how async cancellations where done in C# with the TPL, but I am a little bit confused in F#. Apparently by calling Async.CancelDefaultToken() is enough to cancel outgoing Async<'T> operations. But they are not cancelled as I expected, they just... vanishes... I cannot detect properly the cancellation and tear down the stack properly.
For example, I have this code that depends on a C# library that uses TPL:
type WebSocketListener with
member x.AsyncAcceptWebSocket = async {
let! client = Async.AwaitTask <| x.AcceptWebSocketAsync Async.DefaultCancellationToken
if(not(isNull client)) then
return Some client
else
return None
}
let rec AsyncAcceptClients(listener : WebSocketListener) =
async {
let! result = listener.AsyncAcceptWebSocket
match result with
| None -> printf "Stop accepting clients.\n"
| Some client ->
Async.Start <| AsyncAcceptMessages client
do! AsyncAcceptClients listener
}
When the CancellationToken passed to x.AcceptWebSocketAsync is cancelled, returns null, and then AsyncAcceptWebSocket method returns None. I can verify this with a breakpoint.
But, AsyncAcceptClients (the caller), never gets that None value, the method just ends, and "Stop accepting clients.\n" is never displayed on the console. If I wrap everything in a try\finally :
let rec AsyncAcceptClients(listener : WebSocketListener) =
async {
try
let! result = listener.AsyncAcceptWebSocket
match result with
| None -> printf "Stop accepting clients.\n"
| Some client ->
Async.Start <| AsyncAcceptMessages client
do! AsyncAcceptClients listener
finally
printf "This message is actually printed"
}
Then what I put in the finally gets executed when listener.AsyncAcceptWebSocket returns None, but the code I have in the match still doesn't. (Actually, it prints the message on the finally block once for each connected client, so maybe I should move to an iterative approach?)
However, if I use a custom CancellationToken rather than Async.DefaultCancellationToken, everything works as expected, and the "Stop accepting clients.\n" message is print on screen.
What is going on here?
There are two things about the question:
First, when a cancellation happens in F#, the AwaitTask does not return null, but instead, the task throws OperationCanceledException exception. So, you do not get back None value, but instead, you get an exception (and then F# also runs your finally block).
The confusing thing is that cancellation is a special kind of exception that cannot be handled in user code inside the async block - once your computation is cancelled, it cannot be un-cancelled and it will always stop (you can do cleanup in finally). You can workaround this (see this SO answer) but it might cause unexpected things.
Second, I would not use default cancellation token - that's shared by all async workflows and so it might do unexpected things. You can instead use Async.CancellationToken which gives you access to a current cancellation token (which F# automatically propagates for you - so you do not have to pass it around by hand as you do in C#).
EDIT: Clarified how F# async handles cancellation exceptions.

Resources