I spent some time today tracking down the die-on-restart problem with
wxHaskell.


Here's a simple demo of the problem:

    import Graphics.UI.WX
    main = io >> io where io = start (frame [] >> return ())

(See
http://sourceforge.net/tracker/?func=detail&atid=536845&aid=1610984&group_id=73133.)

In practice, the die-on-restart problem happens commonly when using
wxHaskell in ghci.  This behavior is why I stopped using wxHaskell and
switched to gtk2hs, though I'd love to change back, since wxHaskell is a
more elegant library, and gtk2hs and open don't work together under native
os x (and the X-based macports version generates ugly GUIs).

I didn't get the the heart of the problem, though I may have gotten close.
This note records my journey, in the hope that together we can get further.



In Graphics.UI.WX (in the wx package):

    -- | 'start' runs the GUI.
    start :: IO a -> IO ()
    start io
      = run (unitIO io)

In Graphics.UI.WXCore.Types (wxcore):

    -- | Ignore the result of an 'IO' action.
    unitIO :: IO a -> IO ()
    unitIO io
      = do io; return ()

In Graphics.UI.WXCore (wxcore):

    -- | Start the event loop. Takes an initialisation action as argument.
    -- Except for 'run', the functions in the WXH library can only be called
    -- from this intialisation action or from event handlers, or otherwise
bad
    -- things will happen :-)
    run :: IO a -> IO ()
    run init
      = do appOnInit (do wxcAppInitAllImageHandlers
                         init
                         return ())
           performGC
           performGC

I guess the unitIO in start is unnecessary, and start == run.

Appartently, the fatal re-initialization happens in appOnInit or
wxcAppInitAllImageHandlers, or maybe both.

In Graphics.UI.WXCore.Events:


------------------------------------------------------------------------------------------
    -- Application startup

------------------------------------------------------------------------------------------
    -- | Installs an init handler and starts the event loop.
    -- Note: the closure is deleted when initialization is complete, and
than the Haskell init function
    -- is started.
    appOnInit :: IO () -> IO ()
    appOnInit init
      = do closure  <- createClosure (return () :: IO ()) onDelete (\ev ->
return ())   -- run init on destroy !
           progName <- getProgName
           args     <- getArgs
           argv     <- mapM newCWString (progName:args)
           let argc = length argv
           withArray (argv ++ [nullPtr]) $ \cargv -> wxcAppInitializeC
closure argc cargv
           mapM_ free argv
      where
        onDelete ownerDeleted
          = init

What's going on here?

wxcAppInitializeC sounds pretty hard core.  In
Graphics.UI.WXCore.WxcClassesMZ:

    -- | usage: (@wxcAppInitAllImageHandlers@).
    wxcAppInitAllImageHandlers ::  IO ()
    wxcAppInitAllImageHandlers
      = wx_ELJApp_InitAllImageHandlers
    foreign import ccall "ELJApp_InitAllImageHandlers"
wx_ELJApp_InitAllImageHandlers :: IO ()

    -- | usage: (@wxcAppInitializeC closure argc argv@).
    wxcAppInitializeC :: Closure  a -> Int -> Ptr (Ptr CWchar) ->  IO ()
    wxcAppInitializeC closure _argc _argv
      = withObjectPtr closure $ \cobj_closure ->
        wx_ELJApp_InitializeC cobj_closure  (toCInt _argc)  _argv
    foreign import ccall "ELJApp_InitializeC" wx_ELJApp_InitializeC :: Ptr
(TClosure a) -> CInt -> Ptr (Ptr CWchar) -> IO ()

In wxcore-0.11.1.2/wxc/src/ewxw_main.cpp:

    EWXWEXPORT(void, ELJApp_InitializeC) (wxClosure* closure, int _argc,
char** _argv)
    {
      char* args[] = { "wxc", NULL };

      initClosure = closure;
      if (_argv == NULL) {
        /* note: wxGTK crashes when argv == NULL */
        _argv = args;
        _argc = 1;
      }
      APPTerminating = 0;
      wxEntry(_argc,_argv);
      APPTerminating = 1;
      /* wxPendingEvents is deleted but not set to NULL -> disaster when
restarted from an interpreter */
    #if !defined(WXMAKINGDLL) && !defined(WXUSINGDLL)
      wxPendingEvents = NULL;
    #endif
    #if defined(wxUSE_ODBC) && (wxUSE_ODBC != 0)
      wxDbCloseConnections();
    #endif
    }

(There is also a version for Win32.)

That "disaster" warning sounds relevant.  I didn't know whether the
wxPendingEvents NULLing was in active code, so I added some non-compilable
code in a new #else branch.  Recompilation succeeded, so I gather that the
NULLing is happening.

Next, I added printfs before & after the wxEntry call in
ELJApp_InitializeC.  Sure enough, on the second start, wxEntry is called but
not returned from.

What's initClosure?  The only use I could found is in
wxcore-0.11.1.2/wxc/src/wrapper.cpp:


/*-----------------------------------------------------------------------------
        The main application

-----------------------------------------------------------------------------*/
    wxClosure* initClosure = NULL;

    bool ELJApp::OnInit (void)
    {
      wxInitAllImageHandlers();
      initIdleTimer();
      if (initClosure) {
        delete initClosure; /* special: init is only called once with a NULL
event */
        initClosure=NULL;
      }
      return true;
    }

I see the closure getting deleted but not getting invoked.  Oh, yeah!  The
whole user action has been stuffed into the delete portion of the closure.
The other parts of the closure are inert.


Loose end: there's also ELJApp_InitAllImageHandlers, called by wxHaskell's
'run' (and hence 'start'), defined in wxcore-0.11.1.2/wxc/src/wrapper.cpp:

    EWXWEXPORT(void,ELJApp_InitAllImageHandlers)()
    {
            wxInitAllImageHandlers();
    }

I added printfs before & after the wxInitAllImageHandlers call.  They both
showed up in both start attempts, so I think ELJApp_InitAllImageHandlers is
not a re-start problem.


Where can we go from here?  For instance, are there solutions for other
interpreted settings wxHaskell works, e.g. in Python, Ruby, Lisp or Scheme?

    - Conal
------------------------------------------------------------------------------
Come build with us! The BlackBerry&reg; Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay 
ahead of the curve. Join us from November 9&#45;12, 2009. Register now&#33;
http://p.sf.net/sfu/devconf
_______________________________________________
wxhaskell-users mailing list
wxhaskell-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wxhaskell-users

Reply via email to