Unifying Task<T> and F# MailboxProcessor exception handling -
when using task<t>, exception during execution of task thrown during task.wait(); when using f#'s mailboxprocessor, exception gets swallowed , needs explicitly dealt per this question.
this difference makes difficult expose f# agents c# code via task. example, 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 | int.maxvalue -> return! failwith "boom!" | _ -> replychannel.reply (i + 1) return! loop() } loop()) member x.postandasyncreply = async.startastask (counter.postandasyncreply (fun channel -> increment(i, channel))) can called c#, exception not returned 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 {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 {0}", e.innerexception.message); } } instead of getting exception, c# code hangs @ task.wait(). there way f# agent behave task? if not, seems there limited use in exposing f# agents other .net code.
one way handle have agent return du error case. raise exception outside agent.
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 | int.maxvalue -> replychannel.reply (error (failure "boom!")) | _ -> replychannel.reply (response(i + 1)) return! loop() } loop()) member x.postandasyncreply = async.startastask ( async { let! res = counter.postandasyncreply (fun channel -> increment(i, channel)) match res | response -> return | error e -> return (raise e) } )
Comments
Post a Comment