wxPython Threading problem - possible solution

I've been thinking about the threading and assert problems.

Here is my assessment of the problems and possible solutions.

I've implement some simple changes with success. However I
know I took short cuts in the implementation and cover only
the simple case I can test - start from mainloop().

    Barry

Problems:

  wxWindows only works if called on a single thread.
  Python users call wxWindows, via wxPython, from any
  thread. wxWindows asserts.

  If the wxWindows thread exits then a new thread must
  be able of becoming the wxWindows thread.

  wxWindows asserts when users make simple errors in
  using the app.

Requirement:
  Make wxPython work when called from any python thread.
  At least raise exceptions if called from the wrong thread.

  Never assert when called from Python for user errors,
  detecting developer bugs is fine.

Solution 1 - have wxWindows manage its own thread:

  Have wxWindows create a thread of its only that all
  wxWindows work is executed on. Any call from wxPython
  is first marshalled to the wxWindows thread and executed
  there, results are mashelled back.

  Call backs out of wxWindows to the python code would run
  on the wxWindows thread, python will need to be told
  about the wxWindows thread or python will assert.

Solution 2 - have mainloop choose the wx thread

  Change mainloop() so that it sets gs_idMainThread when it
  starts and clears gs_idMainThread before it returns.

solution 3 - signal errors:

  In every wxPython wrapper function call a function to
  check the preconditions.

  Precondition 1: Check that the thread is the wxWindows
  thread. If its wrong raise a RuntimeError exception.

  The following works for mainloop(). In wx.cpp line 1330
  in _wrap_wxPyApp_MainLoop:

  if( !wxThread::IsMain() )
    {
    PyErr_SetString(PyExc_RuntimeError,"wxPyApp_MainLoop must be called on the wxWindows main thread.");
    return NULL;
    }

  Running my demo (attached) I get the following output:

import wx_demo;wx_demo.main()
Creating GuiThread
Thread is running...
Exception in thread Thread-1:
Traceback (most recent call last):
  File "c:\python20\lib\threading.py", line 376, in __bootstrap
    self.run()
  File "u:\users\barry\bemacs\wx_demo.py", line 80, in run
    app.MainLoop()
  File "C:\Python20\wxPython\wx.py", line 1701, in MainLoop
    wxPyApp.MainLoop(self)
  File "C:\Python20\wxPython\wx.py", line 85, in MainLoop
    val = apply(wxc.wxPyApp_MainLoop,(self,) + _args, _kwargs)
RuntimeError: wxPyApp_MainLoop must be called on the wxWindows main thread.

solution 4 - set the main thread on first use:

  Building on solution 3 and setting gs_idMainThread on first use.

  In thread.cpp at line 1100: comment out setting gs_idMainThread.
  Add to IsMain() setting gs_idMainThread:

bool wxThread::IsMain()
{
    if( gs_idMainThread == 0 )
  {
  gs_idMainThread = ::GetCurrentThreadId();
  return 1;
  }

    return ::GetCurrentThreadId() == gs_idMainThread;
}

  For safty add an assert to wxWakeUpMainThread at line 1210:

    wxASSERT_MSG( wxThread::IsMain(), _T("should only be called from the main thread") );

wx_demo.py (1.94 KB)