|
|
Stack Overflow running Concurrent Life
Last post 08-14-2006, 20:30 by dday51. 14 replies.
-
08-12-2006, 17:30 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Stack Overflow running Concurrent Life
I am new to F Sharp. I have installed the FSharp package and run the Life sample and every time when the life is running I eventually get a stack overrun error. Is this something that anyone else has seen?
|
|
-
08-12-2006, 17:46 |
-
dsyme
-
-
-
Joined on 02-15-2006
-
-
Posts 617
-
-
|
Re: Stack Overflow running Concurrent Life
Yes, this is a known bug - thanks fo reporting it.
The control trechnique used in worker.fs is based on "recursive functions to represent a state machine", but this relies on tailcalls being taken in all situations, but there is an issue that prevents this for this code. We'll have a modified version of the sample in the next release.
Instead you can use a more explicit representation of the state machine via a recursive data term or a data term where strings are used to name the states (i.e. the states are a "Dictionary<string,StateAction>"). Making this coding modification might be a good way to learn the language.
Don
|
|
-
08-12-2006, 19:17 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
It seems that mkWorker is called from the client and passed a grid size and a list of callbacks of which one is the function 'workerNotifyUpdates(born,died)' which is assigned to NotifyUpdates in the IWorkerCallbacks interface. This is called from the function oneStep in worker.fs which is called from the statemachine every time it enters the state 'stepThen'. What I can't figure out is why this function call never returns but stays on the stack. Is it because of being called through BeginInvoke? (the function remoteCall is defined as
form.BeginInvoke( new MethodInvoker(f))
The library docs state that MethodInvoker is not to be used except in the internals of the library??
dday51
|
|
-
08-13-2006, 4:30 |
-
dsyme
-
-
-
Joined on 02-15-2006
-
-
Posts 617
-
-
|
Re: Stack Overflow running Concurrent Life
Hi again,
I looked into this, and there is indeed a mistake in tailcall generation in 1.1.11.7/12, in particular for function calls to anonymous functions when the call returns "unit". FWIW tailcall generation of this kind is only rarely significant - indeed this is the only sample in the F# distribution where it is essential (of course taking direct tailcalls is much more significant!). We will of course fix the problem immediately and add further tests to our test suite.
If you change the obvious corresponding lines in worker.fs to the following the problem goes away:
type State<'a> = ('a -> 'a)
...
and Finish(s) = s
...
let start = ResetThen Running in
...
and Start() = start(Game.newEmptyGame) |> ignore
then all is OK.
I'm pretty sure MethodInvoker is not the issue - when you break you will often get one of these on the stack. The problem is the series of functions in the set of mutually recursive states represented by tail-calling functions, i.e. Running, CollectUserInputThen, StepThen, SleepThen, Paused, FinishEarly, Finish.
I looked at the MethodInvoker docs at http://msdn2.microsoft.com/en-us/library/system.windows.forms.methodinvoker.aspx and didn't see that it was for internal use. Could you send me a link to where you saw that? Thanks!
Cheers
Don
|
|
-
08-13-2006, 10:58 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
The info on Method Invoker was in the online help available from within Visual Studio here is the URL ms-help://MS_VSCC.v80/MS.MSDN.v80/NETDEVFX.v20.en/CPref0/html/T_Microsoft_JScript_MethodInvoker.htm.
I will erttainly try the changes you suggest. I spent a lartge part of last night makng a detailed state chart of the automotan in the worker.fs and building a function to implement it that did not invole recursion figuring this weould clear out the problem of leaving function calls on the stack. see code following.
I have to figure out howto use exceptions in F Sharp however as I need to throw an exception when invoking the Finish state to break out of the infinite loop in the RunStateMachine function
type AutomatonState =
Start | ResetFromStart| ResetFromPaused| Running| ReadInputRunning| ReadInputPaused
| TakeRunningStep| TakePausedStep| SleepWhileRunning| SleepWhilePaused| Paused| FinishEarly
| Finish
type Transitions = (AutoResetEvent * AutomatonState) list
let peekForOne (xs : Transitions)
(otherwise : AutomatonState)
(s ) =
let waithandles = waitHandlesOfTransitions xs in
let actions = actionsOfTransitions xs in
let i = WaitHandle.WaitAny(waithandles,0,false) in
if i = WaitHandle.WaitTimeout
then (otherwise, s)
else (actions.(i), s)
let waitForOne (xs : Transitions)
(s ) =
let waithandles = waitHandlesOfTransitions xs in
let actions = actionsOfTransitions xs in
let i = WaitHandle.WaitAny(waithandles) in
s
/// STATES ==>
// Start -> Reset
// ResetFromStart -> Running
// ResetFromPaused -> Paused
// Running resetSignal -> ResetFromStart
// stepSignal -> Running
// userInputReadySignal -> ReadingUserInputRunning
// exitSignal -> Finish
// ReadingUserInputRunning -> Running
// ReadingUserInputPaused -> Paused
// TakingStepRunning contSignal -> Running
// not contSignal -> FinishEarly
// TakingStepPaused contSignal -> Paused
// not contSignal -> FinishEarly
// SleepingWhileRunning -> Running
// SleepingWhilePaused -> Paused
// Paused stopSignal -> Paused
// stepSignal ->TakingStepPaused
// runSignal -> Running
// resetSignal -> ResetFromPaused
// userInputReadySignal -> ReadingUserInputPaused
// exitSignal -> Finish
// FinishEarly -> Finish
// Finish -> End
let currentState : State = { state = Start } in
let RunStateMachine s : Game.grid =
let (state, s) = (Start, s) in
try
while true do (state, s) = stepStateMachine (state, s) done in
catch Exception
let stepStateMachine (state, s) =
match state with
| Start -> startMachine (state, s)
| ResetFromStart -> restartFromStart (state, s)
| ResetFromPaused -> restartFromPaused (state, s)
| Running -> running (state, s)
| ReadInputRunning -> readInputRunning (state, s)
| ReadInputPaused -> readInputPaused (state, s)
| TakeRunningStep -> takeRunningStep state s
| TakePausedStep -> takePausedStep (state, s)
| SleepWhileRunning -> sleepWhileRunning (state, s)
| SleepWhilePaused -> sleepWhilePaused (state, s)
| Paused -> paused (state, s)
| FinishEarly -> finishEarly (state, s)
| Finish -> finish (state, s) in
let startMachine (state, s) = (ResetFromStart, s) in
let restartFromStart (state, s) =
let s = resetGame(s) in
(Running, s) in
let restartFromPaused (state, s) =
let s = resetGame(s) in
(Paused, s) in
let running (state, s) =
peekForOne
[ (stopSignal, Paused);
(stepSignal, Running);
(runSignal, Running);
(resetSignal, ResetFromStart);
(userInputReadySignal, ReadInputRunning);
(exitSignal, Finish) ]
TakeRunningStep
s in
let readInputRunning (state, s) =
let s = collectUserInput(s) in
(Running, s) in
let readInputPaused (state, s) =
let s = collectUserInput(s) in
(Paused, s) in
let takeRunningStep (state, s) =
let s,cont = oneStep(s) in
if cont
then (SleepWhileRunning, s)
else (FinishEarly, s) in
let takePausedStep (state, s) =
let s,cont = oneStep(s) in
if cont
then (SleepWhilePaused, s)
else (FinishEarly, s) in
let sleepWhileRunning (state, s) =
ignore(Thread.Sleep(20));
(Running, s) in
let sleepWhilePaused (state, s) =
ignore(Thread.Sleep(20));
(Paused, s) in
let paused (state, s) =
waitForOne
[ (stopSignal, Paused);
(stepSignal, TakePausedStep);
(runSignal, Running);
(resetSignal, ResetFromPaused );
(userInputReadySignal, ReadInputPaused);
(exitSignal,Finish) ] s in
let finishEarly (state, s) =
callbacks.NotifyFinishedEarly();
(Finish, s) in
let finish (state, s) = Throw Exception
This code doesn't actually work yet as should be obvious but represents a much more severe change to the architecture than what you suggest so I will try your suggestions first.
Thanks
dday51
|
|
-
08-13-2006, 11:19 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
I tried your suggested code changes buyt I am still getting the same problem. Is the approach I am taking in the previous post on the right track. does changing the recursive version of the State Machine to an infinite loop that steps from state to state work around the problem or will this also leave incomplete function calls on the stack. Plus exactly where do I find info in the Manual on using exceptions it doesn't seem to be discussed anywhere.
One of the problems that I discovered in tghe existing Automoton is that the state consists of two machines working in parrallel one machine going betweeen a state of Paused and Running and another machine going between the states Reset, UserInput, Step, Sleep, Start, Finish, and FinishEarly by breaking out the extra states into seperate functions I don't have to pass around what the next expected state should be in several cases. In other words each state knows precisely what the next state should be for each signal. IMHO I felt that this was easier to understand and more intuitive.
thanks
dday51
|
|
-
08-13-2006, 15:03 |
-
dsyme
-
-
-
Joined on 02-15-2006
-
-
Posts 617
-
-
|
Re: Stack Overflow running Concurrent Life
Hi DDay,
I checked the changes I sent worked - perhaps we should look more into that offline - if you like email me your modified code at dsyme AT microsoft DOT com. BTW are you using Mono and/or .NET 1.x? Or are you using VS 2005 and .NET 2.0?
There is only one state machine - it switches between two overlapping sets of modes based on paused (single stepping) and running based on the signals. Perhaps you should just delete everything to do with "Paused" states and collecting user input and thus start with something simpler?
You are doing the right thing to represent the states as datam, i.e. to represent the states using an enumeration alone you absolutely have to remove the coupld of "higher order" combinators that build new states (e.g. SleepThen). Those are really just there for fun.
I think it would be good to add a loop/data-based representation of states to the sample.
This is fairly advanced stuff for beginning F# coding - I hope you're finding it fun :-)
Don
|
|
-
08-13-2006, 16:29 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
I have woring versions of both the loop based state machine and the recursion based state machine. I found that the call to Application.DoEvents is the problem. It never returns it merely triggeres the state machine to go through another cycle. Now I have a problem with getting the application to respond to user events. I have added a callback to the IWorker interface to call the DoEvents function from within the state machine and have it fairly stable. Where could I put the modified code I have created where you could find it. Pasting it into this text box is very ugly. I have run up to a 300X300 grid at 2 times magnification for 90 minutes plus and had no problem so I think the stack overflow is gone. I am working on adding an options menu that allows you to set the grid size and magnification size and specify the rules to use. I would also like to set the forms windows style to
ControlStyles .OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint
To Optimize the window redraw but cannot figure out how to do this. In CHarp I would do this in the constructor for the derived form but I don't see how you create a derived form in F#.
I will try to see if I can place the loop based code on the files page and as soon as I move some of the fixes to application events over to the recursive based I will do that also.
dday51
|
|
-
08-13-2006, 17:01 |
-
dsyme
-
-
-
Joined on 02-15-2006
-
-
Posts 617
-
-
|
Re: Stack Overflow running Concurrent Life
Interesting - the call to Application.DoEvents is indeed pretty bogus, so it seems you're now doing the right thing. I'd be interested to know if you find out why the GUI goes unresponsive. Is it because too many paint events are being scheduled through the updates?
Calling Control.SetStyle reveals a slightly niggling feature of .NET and other object models - the use of protected methods forces the declaration of fairly bogus subclasses, just to give a new "family" of types that can access the protected members. (My main objection to this is that it just isn't orthogonal to the class system in the way that other features such as generics are). Anyway, here's the magic incarnation:
type SmoothForm = class inherit Form new() as x = { inherit Form(); } then x.SetStyle(Enum.combine [ControlStyles.AllPaintingInWmPaint; ControlStyles.Opaque], true); end
then use "new SmoothForm()" etc.
BTW we plan that F# will support the use of "|||" as replacement for Enum.combine ("|||" is the standard overloaded bitwise-or operator in F# - we will extend its interpretation to enum types).
Don
|
|
-
08-13-2006, 17:05 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
I just moved my changes to the Recursion Based version and I again got the stack overflow. It doesn't happen as often and takes longer but the DoEvents (even when called from within worker) is still triggering a new cycle of the state machine every now and then when the onPaint takes just a little too long such as when you have a 300X300 grid you are calculating. Thanks for the sample code I will try that. I have been unable to figure out how to upload a file to the files area.
dday51
|
|
-
08-14-2006, 8:43 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
I am attaching a zip file of my loop based concurrent life called concurrentlife2 for use by any one out there interested
dday51
|
|
-
08-14-2006, 9:36 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
Note unless you wish to wait an inbordinately long time for the simulation to start please change the grid size and the magnification size at the top of the client.fs source file. I suggest gridsize = 100 and magnification = 5
|
|
-
08-14-2006, 17:22 |
-
dsyme
-
-
-
Joined on 02-15-2006
-
-
Posts 617
-
-
|
Re: Stack Overflow running Concurrent Life
Nice job! I like this style - it is very clearer (in many ways clearer than the original, only uses constructs that beginners will be able to understand, and is operationally clearer as you have discovered).
Do you still have the doEvents problem in this version? I notice that DoEvents() is still being called from the worker thread.
Don
|
|
-
08-14-2006, 18:05 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
If you have a smaller grid you are calculating there are fewer problems I am looking at optimizing the algorithm so that it doesn't get overrun by events. Of course the best solution would be to set up a lock that stops the state machine thread whenever the algorithm is entered and then releases the thread for the state machine when the algorithm finishes. The key thing is that when you are hovering over a menu item you must wait until the item highlights before you click on it.
The reason I have been so intent on the life simulation is that it is a good model to use to program any type of complicated agent based system embedded in an environment for which you wish to display a real time picture of the simulation. I have a version of the Anticipatory Learning Classifier System the laerns a dynamically changing grid world that I would like to program in F# and this seemed like a good platform to start from.
I am glad you like the code I have learned in engineering that it is very important to keep thangs as simple as possible but as complicated as necessary to quote Einstein.
dday51
|
|
-
08-14-2006, 20:30 |
-
dday51
-
-
-
Joined on 08-13-2006
-
-
Posts 28
-
-
|
Re: Stack Overflow running Concurrent Life
Have implemented a locking mechanism around the DoEvents call which has fixed that problem the program runs cleanly and is extremely responsive to the user interface, also added an implementatio of form.Closing to cleanly shut doen the state machine and eliminate an exception I began to have when the state machine tried to paint to a non existent Graphics object.
I have begun work on an options dialog which should give me some exercise buillding GUIs in FSharp too bad you don't have hooks into the dialog editor.
dday51
|
|
|
|