Unification of tasks <T> and F # Processing mailbox handlers
When using Task <T>, an exception during task execution is raised during Task.Wait (); when using F # MailBoxProcessor, the exception is swallowed and should be explicitly addressed in accordance with this issue .
This difference makes it difficult to identify F # agents for C # code using a task. For example, this agent:
type internal IncrementMessage =
Increment of int * AsyncReplyChannel<int>
type IncrementAgent() =
let counter = Agent.Start(fun agent ->
let rec loop() = async { let! Increment(msg, replyChannel) = agent.Receive()
match msg with
| int.MaxValue -> return! failwith "Boom!"
| _ as i -> replyChannel.Reply (i + 1)
return! loop() }
loop())
member x.PostAndAsyncReply i =
Async.StartAsTask (counter.PostAndAsyncReply (fun channel -> Increment(i, channel)))
can be called from C #, but the exception does not return to C #:
[Test]
public void ExceptionHandling()
{
//
// TPL exception behaviour
//
var task = Task.Factory.StartNew<int>(() => { throw new Exception("Boom!"); });
try
{
task.Wait();
}
catch(AggregateException e)
{
// Exception available here
Console.WriteLine("Task failed with {0}", e.InnerException.Message);
}
//
// F# MailboxProcessor exception behaviour
//
var incAgent = new IncrementAgent();
task = incAgent.PostAndAsyncReply(int.MaxValue);
try
{
task.Wait(); // deadlock here
}
catch (AggregateException e)
{
Console.WriteLine("Agent failed with {0}", e.InnerException.Message);
}
}
Instead of getting an exception, C # code just hangs in task.Wait (). Is there a way to make the F # agent behave like a task? If not, there seems to be limited use when exposing F # agents to other .NET code.
+5
1
- DU . .
type internal IncrementResponse =
| Response of int
| Error of exn
type internal IncrementMessage =
| Increment of int * AsyncReplyChannel<IncrementResponse>
type IncrementAgent() =
let counter = Agent.Start(fun agent ->
let rec loop() =
async {
let! Increment(msg, replyChannel) = agent.Receive()
match msg with
| int.MaxValue -> replyChannel.Reply (Error (Failure "Boom!"))
| _ as i -> replyChannel.Reply (Response(i + 1))
return! loop()
}
loop())
member x.PostAndAsyncReply i =
Async.StartAsTask (
async {
let! res = counter.PostAndAsyncReply (fun channel -> Increment(i, channel))
match res with
| Response i -> return i
| Error e -> return (raise e)
}
)
+3