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)