How to await nongeneric Task using Async workflow? [duplicate] - asynchronous

I'm trying to consume a C# library in F#. The library makes heavy use of async/await. I want to use within an async { ... } workflow in F#.
I see we can Async.AwaitTask on async C# methods returning Task<T>, but what about those returning plain Task?
Perhaps, is there a helper to convert these to Async<unit> or to convert Task to Task<unit> so it will work with Async.AwaitTask?

You can use ContinueWith:
let awaitTask (t: Task) = t.ContinueWith (fun t -> ()) |> Async.AwaitTask
Or AwaitIAsyncResult with infinite timeout:
let awaitTask (t: Task) = t |> Async.AwaitIAsyncResult |> Async.Ignore

Update:
The FSharp.Core library for F# 4.0 now includes an Async.AwaitTask overload that accepts a plain Task. If you're using F# 4.0 then you should use this core function instead of the code below.
Original answer:
If your task could throw an exception then you probably also want to check for this. e.g.
let awaitTask (task : Task) =
async {
do! task |> Async.AwaitIAsyncResult |> Async.Ignore
if task.IsFaulted then raise task.Exception
return ()
}

Update:
The FSharp.Core library for F# 4.0 now includes an Async.AwaitTask overload that accepts a plain Task. If you're using F# 4.0 then you should use this core function instead of the code below.
Original answer:
I really liked Ashley's suggestion using function composition. Additionally, you can extend the Async module like this:
module Async =
let AwaitTaskVoid : (Task -> Async<unit>) =
Async.AwaitIAsyncResult >> Async.Ignore
Then it appears in Intellisense along with Async.AwaitTask. It can be used like this:
do! Task.Delay delay |> Async.AwaitTaskVoid
Any suggestions for a better name?

To properly propagate both exceptions and cancellation properly, I think you need something like this (partially based on deleted answer by Tomáš Petříček):
module Async =
let AwaitVoidTask (task : Task) : Async<unit> =
Async.FromContinuations(fun (cont, econt, ccont) ->
task.ContinueWith(fun task ->
if task.IsFaulted then econt task.Exception
elif task.IsCanceled then ccont (OperationCanceledException())
else cont ()) |> ignore)

Related

Testing Async Functions

I am using Wasm-Pack and I need to write a unit test for a asynchronous function that references a JavaScript Library. I tried using the futures::executor::block_on in order to get the asynchronous function to return so I could make an assert. However, blocking is not supported in the wasm build target. I can't test in a different target because the asynchronous function I am testing is referencing a JavaScript library. I also don't think I can spawn a new thread and handle the future there, because it need to return to the assert statement in the original thread. What is the best way to go about testing this asynchronous function?
Code being tested in src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub async fn func_to_test() -> bool {
return some_long_running_fuction().await;
}
Testing code in tests/web.rs
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use test_crate;
use futures::executor::block_on;
#[wasm_bindgen_test]
fn can_return_from_async(){
let ret = block_on(test_crate::func_to_test());
assert!(ret);
}
How do I test an async function if I can't use any blocking?
Rust can handle tests that are async functions themselves. Just change the test fuction to be async and throw in an await.
#[wasm_bindgen_test]
async fn can_return_from_async(){
let ret = test_crate::func_to_test().await
assert!(ret);
}

F# run blocking call on another thread, use in async workflow

I have a blocking call blockingFoo() that I would like to use in an async context. I would like to run it on another thread, so as to not block the async.
Here is my solution:
let asyncFoo =
async {
blockingFoo() |> ignore
}
|> Async.StartAsTask
|> Async.AwaitTask
Is this the correct way to do this?
Will this work as expected?
I think you're a bit lost. Async.StartAsTask followed by Async.AwaitTask effectively cancel each other, with the side-effect that the Task created in the process actually triggers evaluation of the async block containing blockingFoo on the thread pool. So it works, but in a way that betrays expectations.
If you want to trigger evaluation of asyncFoo from within another async block, a more natural way to do it would be to use Async.Start if you don't want to await its completion, or Async.StartChild if you do.
let asyncFoo =
async {
blockingFoo() |> ignore
}
async {
// "fire and forget"
asyncFoo |> Async.Start
// trigger the computation
let! comp = Async.StartChild asyncFoo
// do other work here while comp is executing
// await the results of comp
do! comp
}

Why Threading.Timer can't work in async block?

This program work fine:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
But when I put the same code in the async block when I want to deal with tcp client:
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
Console.WriteLine("a")
inc<-inc+1
if inc=3 then
autoEvent.Set()|>ignore
let listener=new TcpListener(IPAddress.Parse("127.0.0.1"),2000)
let private loop(client:TcpClient,sr:StreamReader,sw:StreamWriter)=
async{
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(a,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let private startLoop()=
while true do
let client=listener.AcceptTcpClient()
let stream=client.GetStream()
let sr=new StreamReader(stream)
let sw=new StreamWriter(stream)
sw.AutoFlush<-true
Async.Start(loop(client,sr,sw))|>ignore
listener.Start()
startLoop()
listener.Stop()
the timer function will not quit when it have run three times.I want to know why?Thanks
I first want to mention a few things, instead of using Console.WriteLine("a"), just use printfn "a". Secondly, the snippet of code you gave does not terminate, so if you try it in FSI, it will continue running after the main thread finishes. This is likely not an issue in a console app. To answer your question, it has to do with async workflow. If you like in this article: Async Programming, you'll notice that they spawn the async computation as a child and then perform an async sleep to give the child a chance to start. This has to do with the way tasks are scheduled. .NET Frameworks use a "work-first" policy. Continuations typically don't get executed until a blocking event forces the thread to give up the current task. This is how I got the timer event to run:
open System
open System.Threading
let mutable inc =0
let a(o:obj)=
let autoEvent=o :?> AutoResetEvent
printfn "a"
inc<-inc+1
if inc=3 then
printfn "hit 3!"
//autoEvent.Set()|>ignore
let private loop i =
async{
printfn "Started as child..."
let aWrap(o:obj) = // so that we can see which child prints
printfn "%d" i
let autoEvent=new AutoResetEvent(false)
let timer=new Timer(aWrap,autoEvent,0,2000)
autoEvent.WaitOne()|>ignore
}
let startLoopAsync() =
async {
let children =
[1..3]
|> List.map(fun i ->
Async.StartChild(loop i) // start as child
)
do! Async.Sleep 100 // give chance for children to start
children
|> List.iter (Async.RunSynchronously >> ignore) // wait for all children
}
startLoopAsync() |> (Async.RunSynchronously >> ignore) // wait for async loop start
Thread.Sleep(5000)
Note that I used StartChild. I recommend this because of the facts noted here: Async.Start vs. Async.StartChild. A child async task does not need to be given its own cancellation token. Instead it inherits from its parent. So, if I had assigned a cancellation token to the startLoopAsync(), I could cancel that task and all children would cancel as well. Lastly, I recommend keeping a handle on timer in case you ever need to stop that re-occurring event. Not keeping a handle would result in not being able to stop it without killing the process. That is what Thread.Sleep(5000) was for. To show that after the async tasks finish, the timers keep triggering events until the process dies (which requires killing FSI if you use that to test).
I hope this answers your question,
Cheers!

Generic reply from agent/mailboxprocessor?

I currently have an agent that does heavy data processing by constantly posting "work" messages to itself.
Sometimes clients to this agent wants to interrupt this processing to access the data in a safe manner.
For this I thought that posting an async to the agent that the agent can run whenever it's in a safe state would be nice. This works fine and the message looks like this:
type Message = |Sync of Async<unit>*AsyncReplyChannel<unit>
And the agent processing simply becomes:
match mailbox.Receive () with
| Sync (async, reply) -> async |> Async.RunSynchronously |> reply.Reply
This works great as long as clients don't need to return some value from the async as I've constrained the async/reply to be of type unit and I cannot use a generic type in the discriminated union.
My best attempts to solve this has involved wrapper asyncs and waithandles, but this seems messy and not as elegant as I've come to expect from F#. I'm also new to async workflows in F# so it's very possible that I've missed/misunderstood some concepts here.
So the question is; how can I return generic types in a agent response?
The thing that makes this difficult is that, in your current version, the agent would somehow have to calculate the value and then pass it to the channel, without knowing what is the type of the value. Doing that in a statically typed way in F# is tricky.
If you make the message generic, then it will work, but the agent will only be able to handle messages of one type (the type T in Message<T>).
An alternative is to simply pass Async<unit> to the agent and let the caller do the value passing for each specific type. So, you can write message & agent just like this:
type Message = | Sync of Async<unit>
let agent = MailboxProcessor.Start(fun inbox -> async {
while true do
let! msg = inbox.Receive ()
match msg with
| Sync (work) -> do! work })
When you use PostAndReply, you get access to the reply channel - rather than passing the channel to the agent, you can just use it in the local async block:
let num = agent.PostAndReply(fun chan -> Sync(async {
let ret = 42
chan.Reply(ret) }))
let str = agent.PostAndReply(fun chan -> Sync(async {
let ret = "hi"
chan.Reply(ret) }))

Timeout on WebRequest with F#-Style Asynchronous Workflows

For a broader context, here is my code, which downloads a list of URLs.
It seems to me that there is no good way to handle timeouts in F# when using use! response = request.AsyncGetResponse() style URL fetching. I have pretty much everything working as I'd like it too (error handling and asynchronous request and response downloading) save the problem that occurs when a website takes a long time to response. My current code just hangs indefinitely. I've tried it on a PHP script I wrote that waits 300 seconds. It waited the whole time.
I have found "solutions" of two sorts, both of which are undesirable.
AwaitIAsyncResult + BeginGetResponse
Like the answer by ildjarn on this other Stack Overflow question. The problem with this is that if you have queued many asynchronous requests, some are artificially blocked on AwaitIAsyncResult. In other words, the call to make the request has been made, but something behind the scenes is blocking the call. This causes the time-out on AwaitIAsyncResult to be triggered prematurely when many concurrent requests are made. My guess is a limit on the number of requests to a single domain or just a limit on total requests.
To support my suspicion I wrote little WPF application to draw a timeline of when the requests seem to be starting and ending. In my code linked above, notice the timer start and stops on lines 49 and 54 (calling line 10). Here is the resulting timeline image.
When I move the timer start to after the initial response (so I am only timing the downloading of the contents), the timeline looks a lot more realistic. Note, these are two separate runs, but no code change aside from where the timer is started. Instead of having the startTime measured directly before use! response = request.AsyncGetResponse(), I have it directly afterwards.
To further support my claim, I made a timeline with Fiddler2. Here is the resulting timeline. Clearly the requests aren't starting exactly when I tell them to.
GetResponseStream in a new thread
In other words, synchronous requests and download calls are made in a secondary thread. This does work, since GetResponseStream respects the Timeout property on the WebRequest object. But in the process, we lose all of the waiting time as the request is on the wire and the response hasn't come back yet. We might as well write it in C#... ;)
Questions
Is this a known problem?
Is there any good solution that takes advantage of F# asynchronous workflows and still allows timeouts and error handling?
If the problem is really that I am making too many requests at once, then would the best way to limit the number of request be to use a Semaphore(5, 5) or something like that?
Side Question: if you've looked at my code, can you see any stupid things I've done and could fix?
If there is anything you are confused about, please let me know.
AsyncGetResponse simply ignoring any timeout value posted... here's a solution we just cooked:
open System
open System.IO
open System.Net
type Request = Request of WebRequest * AsyncReplyChannel<WebResponse>
let requestAgent =
MailboxProcessor.Start <| fun inbox -> async {
while true do
let! (Request (req, port)) = inbox.Receive ()
async {
try
let! resp = req.AsyncGetResponse ()
port.Reply resp
with
| ex -> sprintf "Exception in child %s\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
} |> Async.Start
}
let getHTML url =
async {
try
let req = "http://" + url |> WebRequest.Create
try
use! resp = requestAgent.PostAndAsyncReply ((fun chan -> Request (req, chan)), 1000)
use str = resp.GetResponseStream ()
use rdr = new StreamReader (str)
return Some <| rdr.ReadToEnd ()
with
| :? System.TimeoutException ->
req.Abort()
Console.WriteLine "RequestAgent call timed out"
return None
with
| ex ->
sprintf "Exception in request %s\n\n%s" (ex.GetType().Name) ex.Message |> Console.WriteLine
return None
} |> Async.RunSynchronously;;
getHTML "www.grogogle.com"
i.e. We're delegating to another agent and calling it providing an async timeout... if we do not get a reply from the agent in the specified amount of time we abort the request and move on.
I see my other answer may fail to answer your particular question... here's another implementation for a task limiter that doesn't require the use of semaphore.
open System
type IParallelLimiter =
abstract GetToken : unit -> Async<IDisposable>
type Message=
| GetToken of AsyncReplyChannel<IDisposable>
| Release
let start count =
let agent =
MailboxProcessor.Start(fun inbox ->
let newToken () =
{ new IDisposable with
member x.Dispose () = inbox.Post Release }
let rec loop n = async {
let! msg = inbox.Scan <| function
| GetToken _ when n = 0 -> None
| msg -> async.Return msg |> Some
return!
match msg with
| Release ->
loop (n + 1)
| GetToken port ->
port.Reply <| newToken ()
loop (n - 1)
}
loop count)
{ new IParallelLimiter with
member x.GetToken () =
agent.PostAndAsyncReply GetToken}
let limiter = start 100;;
for _ in 0..1000 do
async {
use! token = limiter.GetToken ()
Console.WriteLine "Sleeping..."
do! Async.Sleep 3000
Console.WriteLine "Releasing..."
} |> Async.Start

Resources