Some further clarification.
The exception you probably see right now (in latest released F#, be it a latest Oct 2009 CTP or VS2010 Beta2) is a manifestation of a bug in Async.StartChild, since fixed.
On our current internal bits you will see an exception which gives you much more clue as to what’s going on:
And here is the reason for that: as others noted on this thread,
you call waitForMsg() (i.e. inbox.Receive()) twice simultaneously in two different threads. This is not allowed – access to mailbox should be serialized in our implementation.
If you want to parallelize your replies, by all means do so but just keep waiting for messages on a single thread:
Incidentally, using Async.StartChild only make sense if you actually want to wait on child completion, because Async.StartChild returns the wait async.
Since you are ignoring the return value, you will be better off just using an Async.Start:
This also has the advantage of working in released F# versions J
Feel free to follow-up offline at dmitry(dot)lomov(at)microsoft(dot)com
The exception you probably see right now (in latest released F#, be it a latest Oct 2009 CTP or VS2010 Beta2) is a manifestation of a bug in Async.StartChild, since fixed.
On our current internal bits you will see an exception which gives you much more clue as to what’s going on:
> System.Exception: multiple waiting reader continuations for mailbox at <StartupCode$FSharp-Core>.$Control.-ctor@1856-4.Invoke(AsyncParams`1 _arg10)
And here is the reason for that: as others noted on this thread,
let rec waitForMsg () =
async {let! msg = inbox.Receive()
let! _ =
async{
match msg with
| CallMsg (replyChannel) ->
replyChannel.Reply("strResult")
| _ -> failwith "Invalid Message"
return! waitForMsg()
} |> Async.StartChild
return! waitForMsg()
}
you call waitForMsg() (i.e. inbox.Receive()) twice simultaneously in two different threads. This is not allowed – access to mailbox should be serialized in our implementation.
If you want to parallelize your replies, by all means do so but just keep waiting for messages on a single thread:
let rec waitForMsg () =
async {let! msg = inbox.Receive()
let! _ =
async{
match msg with
| CallMsg (replyChannel) ->
replyChannel.Reply("strResult")
| _ -> failwith "Invalid Message"
return()
} |> Async.StartChild
return! waitForMsg()
}
Incidentally, using Async.StartChild only make sense if you actually want to wait on child completion, because Async.StartChild returns the wait async.
Since you are ignoring the return value, you will be better off just using an Async.Start:
let rec waitForMsg () =
async { let! msg = inbox.Receive()
async{
match msg with
| CallMsg (replyChannel) ->
replyChannel.Reply("strResult")
| _ -> failwith "Invalid Message"
return()
} |> Async.Start
return! waitForMsg()
}
This also has the advantage of working in released F# versions J
Feel free to follow-up offline at dmitry(dot)lomov(at)microsoft(dot)com
Oops I jumped the gun a bit - you are not hitting the bug in Async.StartChild, you are hitting the genuine diagnostics - and you say it right there in the title :)
The rest of my comment stands.
The rest of my comment stands.
The spawn child looked fishy to me, and I think it was causing the problem. The ``let!'' should rotate to an available thread.
The code below will always get the exception on the second loop for me. But, removing the spawn child doesn't crash.
I don't know if it was from removing the irrevant parts. But, you could reduce runpipe to something like this:
The code below will always get the exception on the second loop for me. But, removing the spawn child doesn't crash.
module messageReplyTest
open System
open System.Threading
open System.Diagnostics
open Microsoft.FSharp.Control
type PipelineTestMsg =
| CallMsg of string AsyncReplyChannel
| Empty
type TestPipelineHandler1 (runPipeline : IDisposable -> string ) =
let rec runPipe =
MailboxProcessor.Start(fun inbox ->
let rec waitForMsg () =
async {let! msg = inbox.Receive()
let! _ =
async{
match msg with
| CallMsg (replyChannel) ->
replyChannel.Reply("strResult")
| _ -> failwith "Invalid Message"
return! waitForMsg()
} |> Async.StartChild
return! waitForMsg()
}
waitForMsg ())
member x.ProcessPageWithReply () =
// Console.WriteLine "Prepare to Call"
let res = runPipe.PostAndReply
(fun replyChannel -> CallMsg(replyChannel))
// Console.WriteLine "Returned from invocation"
()
type TestPipelineHandler2 (runPipeline : IDisposable -> string ) =
let rec runPipe =
MailboxProcessor.Start(fun inbox ->
let rec waitForMsg () =
async {let! msg = inbox.Receive()
let! _ =
async{
match msg with
| CallMsg (replyChannel) ->
replyChannel.Reply("strResult")
| _ -> failwith "Invalid Message"
return! waitForMsg()
}
return! waitForMsg()
}
waitForMsg ())
member x.ProcessPageWithReply () =
// Console.WriteLine "Prepare to Call"
let res = runPipe.PostAndReply
(fun replyChannel -> CallMsg(replyChannel))
// Console.WriteLine "Returned from invocation"
()
while true do
let foo (x : IDisposable) =
"result"
let handler = new TestPipelineHandler2(foo)
let bar = handler.ProcessPageWithReply ()
Console.WriteLine "Wait for exception"
Thread.Sleep 3000
Console.WriteLine "Return from Sleep"
I don't know if it was from removing the irrevant parts. But, you could reduce runpipe to something like this:
let rec runPipe () =
MailboxProcessor.Start(fun inbox ->
let rec waitForMsg () =
async {
let! msg = inbox.Receive()
match msg with
| CallMsg (replyChannel) -> replyChannel.Reply("strResult")
return! waitForMsg()
}
waitForMsg ())
Thanks, good observation.
The extra async you saw wrapped a semaphore access which I removed in building the test case. I'll take another look and see if it is really necessary. At the time, I thought it was, but maybe I was having a brain freeze.
I'm still not clear on why my code doesn't work, but right now, getting something working is a higher priority.
Thx,
--Don
The extra async you saw wrapped a semaphore access which I removed in building the test case. I'll take another look and see if it is really necessary. At the time, I thought it was, but maybe I was having a brain freeze.
I'm still not clear on why my code doesn't work, but right now, getting something working is a higher priority.
Thx,
--Don
The extra async is fine, if it is necessary. It looks like the Async.StartChild is forking the execution. (So, if you put a break point at
2 threads is not bad in itself. But, both execute
So removing the
let! _ before you have 1 worker thread running, after you have 2.2 threads is not bad in itself. But, both execute
waitForMsg(). Then you have two threads simultaneously accessing let! msg = inbox.Receive() and you get the exception.So removing the
Async.StartChild, you get only one path through the let! _ code. I don't think the trailing return! in the outer async is ever hit without the startchild.Very good - thanks.
--Don
--Don
Topic tags
- f# × 3660
- compiler × 263
- functional × 199
- c# × 119
- websharper × 114
- classes × 96
- web × 94
- book × 84
- .net × 82
- async × 72
- parallel × 43
- server × 43
- parsing × 41
- testing × 41
- asynchronous × 30
- monad × 28
- ocaml × 26
- tutorial × 26
- haskell × 25
- workflows × 22
- html × 21
- linq × 21
- introduction × 19
- silverlight × 19
- wpf × 19
- fpish × 18
- collections × 14
- pipeline × 14
- templates × 12
- monads × 11
- opinion × 10
- reactive × 10
- plugin × 9
- scheme × 9
- sitelets × 9
- solid × 9
- basics × 8
- concurrent × 8
- deployment × 8
- how-to × 8
- python × 8
- complexity × 7
- javascript × 6
- jquery × 6
- lisp × 6
- real-world × 6
- workshop × 6
- xaml × 6
- conference × 5
- dsl × 5
- java × 5
- metaprogramming × 5
- ml × 5
- scala × 5
- visual studio × 5
- formlets × 4
- fsi × 4
- lift × 4
- sql × 4
- teaching × 4
- alt.net × 3
- aml × 3
- enhancement × 3
- list × 3
- reflection × 3
- blog × 2
- compilation × 2
- computation expressions × 2
- corporate × 2
- courses × 2
- cufp × 2
- enterprise × 2
- entity framework × 2
- erlang × 2
- events × 2
- f# interactive × 2
- fsc × 2
- google maps × 2
- html5 × 2
- http × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- keynote × 2
- mvc × 2
- numeric × 2
- obfuscation × 2
- oop × 2
- packaging × 2
- pattern matching × 2
- pipelines × 2
- rx × 2
- script × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- type × 2
- type provider × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- addin × 1
- agents × 1
- agile × 1
- android × 1
- anonymous object × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net mvc × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- b-tree × 1
- bistro × 1
- bug × 1
- camtasia studio × 1
- canvas × 1
- class × 1
- client × 1
- clojure × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- combinator × 1
- confirm × 1
- constructor × 1
- continuation-passing style × 1
- coords × 1
- coursera × 1
- csla × 1
- css × 1
- data × 1
- database × 1
- declarative × 1
- delete × 1
- dhtmlx × 1
- discriminated union × 1
- distance × 1
- docs × 1
- documentation × 1
- dol × 1
- domain × 1
- du × 1
- duf-101 × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- error × 1
- etw × 1
- euclidean × 1
- event × 1
- example × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- fantomas × 1
- fear × 1
- float × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- function × 1
- functional style × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- google × 1
- group × 1
- hash × 1
- history × 1
- hosting × 1
- httpcontext × 1
- https × 1
- hubfs × 1
- ie 8 × 1
- if-doc × 1
- inheritance × 1
- installer × 1
- interpreter × 1
- io × 1
- ios × 1
- ipad × 1
- kendo × 1
- learning × 1
- licensing × 1
- macro × 1
- macros × 1
- maps × 1
- markup × 1
- marshal × 1
- math × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- multidimensional × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- nested × 1
- nested loops × 1
- node × 1
- object relation mapper × 1
- object-oriented × 1
- offline × 1
- option × 1
- orm × 1
- osx × 1
- owin × 1
- paper × 1
- parameter × 1
- performance × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- programming × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- ptvs × 1
- quant × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- real-time × 1
- reference × 1
- restful × 1
- round table × 1
- runtime × 1
- scriptcs × 1
- scripting × 1
- service × 1
- session-state × 1
- sitelet × 1
- stickynotes × 1
- stress × 1
- strong name × 1
- structures × 1
- tdd × 1
- template × 1
- tracing × 1
- tsunamiide × 1
- type inference × 1
- type providers × 1
- upload × 1
- vb × 1
- vb.net × 1
- vector × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio shell × 1
- visualstudio × 1
- web api × 1
- webapi × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- xml × 1
|
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us |
Built with WebSharper |
Can someone see what I'm doing wrong?
Thanks much,
--Don
module messageReplyTest open System open System.Threading open System.Diagnostics open Microsoft.FSharp.Control open NUnit.Framework open NUnit.Framework.Constraints type PipelineTestMsg = | CallMsg of string AsyncReplyChannel | Empty type TestPipelineHandler (runPipeline : IDisposable -> string ) = let rec runPipe = MailboxProcessor.Start(fun inbox -> let rec waitForMsg () = async {let! msg = inbox.Receive() let! _ = async{ match msg with | CallMsg (replyChannel) -> replyChannel.Reply("strResult") | _ -> failwith "Invalid Message" return! waitForMsg() } |> Async.StartChild return! waitForMsg() } waitForMsg ()) member x.ProcessPageWithReply () = // Console.WriteLine "Prepare to Call" let res = runPipe.PostAndReply (fun replyChannel -> CallMsg(replyChannel)) // Console.WriteLine "Returned from invocation" () [<TestFixture>] type Tests() = [<Test>] member x.TestMessageReply () = let foo (x : IDisposable) = "result" let handler = new TestPipelineHandler(foo) let bar = handler.ProcessPageWithReply () Console.WriteLine "Wait for exception" Thread.Sleep 3000 Console.WriteLine "Return from Sleep"