hubFS: THE place for F#

. . . are you on The Hub?
Welcome to hubFS: THE place for F# Sign in | Join | Help
in Search

Stack Overflow running Concurrent Life

Last post 08-14-2006, 20:30 by dday51. 14 replies.
Sort Posts: Previous Next
  •  08-12-2006, 17:30 479

    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 481 in reply to 479

    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 482 in reply to 481

    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 483 in reply to 482

    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 485 in reply to 483

    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 486 in reply to 485

    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 488 in reply to 486

    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 489 in reply to 488

    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 490 in reply to 489

    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 491 in reply to 490

    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 497 in reply to 491

    Re: Stack Overflow running Concurrent Life

    Attachment: ConcurrentLife2.zip

    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 498 in reply to 497

    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 501 in reply to 497

    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 502 in reply to 501

    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 503 in reply to 502

    Re: Stack Overflow running Concurrent Life

    Attachment: ConcurrentLife2.zip

    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

     

View as RSS news feed in XML
Powered by Community Server, by Telligent Systems