RB> Once I get this working, I would like to provide a patch. To be safe, I
RB> will make this new method of detecting process termination only enabled
RB> with a new flag to wxExecute(). Without this new flag present, we will
RB> default to the old method of using the dummy pipe.
I'd love to have a patch fixing this but I really don't think we should
use 2 different methods. Either the patch will work, in which case it
should be always used, or it won't, in which case it shouldn't be applied
at all. I'd strongly prefer to remove the existing pipe-based end detection
stuff and use just SIGCHLD instead.
Why not call waitpid(-1, &status, WNOHANG) as I proposed?
RB> However, I think a more robust solution would to also add the call
RB> to wxCheckChildProcessTermination() inside of wxAppBase::ProcessIdle().
RB> Then we are constantly checking the opened pids for termination. What do
RB> you think of this idea?
This really shouldn't be necessary and could result in noticeable slow
down in the program if it launched many child processes.
On Sat, 5 Jan 2013 15:34:13 -0500 Rob Bresalier wrote:
RB> Currently, the console wait loop for synchronous execution is this:
RB>
RB> while ( !endHandler.Terminated() )
RB> {
RB> disp.Dispatch();
RB> }
RB>
RB> The disp.Dispatch() makes a blocking call to select() with no timeout. I
RB> think it would impossible to get my signal handler in there,
If we use the self-pipe trick it would be possible, and even easy, though:
we'd just need to register the pipe that is written to by the signal
handler with the dispatcher. And this is really the only real solution,
anything else would be just a dirty hack :-(
RB> Therefore I think that I should call Dispatch() with a timeout, and then
RB> check for terminated processes after the timeout. I was thinking 10ms, do
RB> you think that is a good value, or I should choose another one?
There is really no good value, 10ms seems a bit on the short side but is
probably fine for the relatively short waits. Avoiding polling completely
would still be much better though...
RB> Also, about the sync wait loop in GUI applications. If we put the signal
RB> handler in the idle function, are we sure it would get called in wxYield()?
RB> As a reminder, the sync wait loop in GUI applications is this:
This loop is quite horrible too, of course. I have no idea why don't we
use a local event loop with the same self-pipe approach to handle IO. More
I look at this code, more I realize just how completely broken it is :-(
RB> Again, I need a little reassurance that this should not be necessary. For
RB> example, I see a possible race condition. I need to store the PID, but I
RB> don't know the PID until after the call to fork(). So what happens if we
RB> get SIGCHLD after fork() but before I stored the PID?
This shouldn't be a problem because we don't do anything immediately when
we get the signal but only process it later, when we return to the event
loop. And by this time the PID should be already stored. Or am I missing
something here?
On Sun, 6 Jan 2013 11:09:30 -0500 Rob Bresalier wrote:
RB> One more question: I would like to call SetSignalHandler() at the start of
RB> the program to establish the SIGCHLD signal handler. Where would be a good
RB> place to put this function call at the start of a program?
I actually don't think it needs to be done at the start of the program.
Why not do it only when wxExecute() is called for the first time?
RB> Additionally, I see many cases in the wx library where it is possible that
RB> wxTheApp may not be defined. I was able to make a situation like this by
RB> using the console sample and commenting out the instantiation of
RB> wxInitializer.
This is wrong. The library _must_ be initialized. IOW the absence of
wxTheApp by the time wxExecute() is called should result in an assertion
failure and error return.
RB> I'm looking into the self-pipe trick. I'll need to move the definition
RB> of PipeIOHandler out of evtloopunix.cpp into a header file so I can reuse
RB> it. Do you have a good suggestion for which header file to use?
Probably a new file under include/wx/unix/private. As for the name of the
file... selfpipe.h perhaps?
RB> I'm also trying to think of a way to use it without the lock.
I think the simplest would be to have a thread-unsafe base class and a
derived class which would add a lock around the calls to the base class
methods. E.g.
class MTSafePipeIOHandler : public PipeIOHandler {
public:
virtual void WakeUp() {
wxCriticalSectionLocker lock(m_pipeLock);
PipeIOHandler::WakeUp();
}
virtual void OnReadWaiting() {
wxCriticalSectionLocker lock(m_pipeLock);
PipeIOHandler::OnReadWaiting();
}
private:
wxCriticalSection m_pipeLock;
};
should be all we need AFAICS.
RB> Yes, I have seen sometimes that when we call some wx functions or
RB> instantiate objects, that we may encounter a temporary event loop or
RB> execute a GTK call that may trigger handling of something (like processing
RB> our dummy pipe handler).
The only function that could result in it would be wxYield() -- which is
why it's so dangerous. But it really shouldn't happen otherwise (of course,
there are some functions/classes that use wxYield internally, e.g.
wxBusyInfo, but there are very few of them and usually it's not a surprise
that they do it because they work a bit like wxYield).
BTW, about following: could you please upload the patch when it's ready to
review.bakefile.org? This would make it simpler to comment on it. TIA!
wxBusyCursor does call gdk_display_flush() but I don't think it's supposed
to handle any wx events internally...
RB> What should I use as a trigger to exit the temporary event loops? I mean,
RB> I will need to find a place in the wx code where it has been decided that
RB> the application is going to terminate, so I will need to tell any temporary
RB> event loops running for child processes that they need to stop, even though
RB> the child process has not terminated yet. So where to insert this code?
Sorry, I just don't remember what was the problem with the temporary event
loops. Where are they started from and why?
RB> I'm not sure how well the existing code (polling) is handling this scenario.
It just doesn't do anything and I think it's good enough...
I've created a new patch that addresses all of the issues raised by the code review of the first patch located here: http://review.bakefile.org/r/471/ In this second patch, all changes to wxFile are removed, and only a Detach() function has been added to wxFileInputStream and wxInputStream. (All other changes to wxFileInputStream and wxInputStream from the first patch have been removed). All trivial accessor functions have been changed to 'const', with a few important exceptions (because of compiler errors), and are noted in comments in the code. All 'void' pointers that were present in the first patch have been removed and replaced with the appropriate types. All style issues have been corrected (spaces added where needed, trailing whitespace removed). I believe I caught every place where there was a style issue, but if I missed any then please comment. The patch is against the 2.9.4 release as I had issues compiling a recent trunk on Linux. The patch has been tested on OSX, GTK, and Windows. The test suite has been run and no new errors were introduced vs the 2.9.4 baseline. I also added 'exec.cpp' to the GUI test suite in tests.bkl (in addition to already being in the console test suite), and I added a few more tests as well (see exec.cpp). Looking forward to the comments from the second review. An "execution roadmap" has been created to make this review easier. If you perform the review in the order specified by the "Execution Roadmap", the review will be easier to follow and understand. Execution Roadmap: ================== Notes: When you see a 'see below' comment as follows: // See below It means the code is also reused in other places and its call tree will be shown later in the roadmap. wxAppConsole::wxAppConsole() // appunix.cpp { Initialize m_wakeupPipe to NULL } // End wxAppConsole::wxAppConsole() wxExecute() // utilsunx.cpp { wxApp::SetSignalHandler() // appunix.cpp WakeUpPipe::WakeUpPipe() // executepipe.h WakeUpPipe::Create() // utilsunx.cpp // *** Register the callback function for child termination *** // *** Same code reused in stdout/stderr callback registration. *** wxAppTraits::RegisterProcessCallback(wxExecuteCallbackPipe *data, int fd) // utilsunx.cpp { // AddProcessCallback is platform specific, one of: (console) wxAppTraits::AddProcessCallback() // utilsunx.cpp (OSX) wxGUIAppTraits::AddProcessCallback() // utilsexc_cf.cpp (GTK2) wxGUIAppTraits::AddProcessCallback() // src/gtk/utilsgtk.cpp (GTK1) wxGUIAppTraits::AddProcessCallback() // src/gtk1/utilsgtk.cpp (Motif) wxGUIAppTraits::AddProcessCallback() // utils.cpp } // end RegisterProcessCallback() wxUpdateListOfOpenChildProcesses() // utilsunx.cpp wxEndProcessData::wxEndProcessData() // execute.h wxProcess::SetEndProcDataPtr() // include/wx/process.h wxProcess::Redirect(true) // include/wx/process.h wxProcess::SetBufOut() // include/wx/process.h wxProcess::SetBufErr() // include/wx/process.h wxStreamTempInputBuffer::Init() // execcmn.cpp wxProcess::IsNotifyEnabled() // include/wx/process.h wxExecuteIOHandler::Init() // executepipe.h // *** Register the callback function for stdout/stderr *** // *** Same code reused in child process termination callback registration *** wxAppTraits::RegisterProcessCallback(wxExecuteCallbackPipe *data, int fd) // utilsunx.cpp { // AddProcessCallback is platform specific (console) wxAppTraits::AddProcessCallback() // utilsunx.cpp (OSX) wxGUIAppTraits::AddProcessCallback() // utilsexc_cf.cpp (GTK2) wxGUIAppTraits::AddProcessCallback() // src/gtk/utilsgtk.cpp (GTK1) wxGUIAppTraits::AddProcessCallback() // src/gtk1/utilsgtk.cpp (Motif) wxGUIAppTraits::AddProcessCallback() // utils.cpp } (Console app) wxAppTraits::WaitForChild(wxExecuteData& execData) // utilsunx.cpp { wxAppTraits::WaitForChildSync() // See below - common with GUI } (GUI app) wxGUIAppTraits::WaitForChild(wxExecuteData& execData) // apptraits.cpp { wxAppTraits::WaitForChildSync() // See below - common with Console } // WaitForChildSync is common to GUI and Console wxAppTraits::WaitForChildSync(wxExecuteData& execData) // utilsunx.cpp { // **** EVENT LOOP FOR SYNCHRONOUS WXEXECUTE RUNS HERE *** Event Loop { // SIGCHLD Signal Interrupt Handling (SIGCHLD) wxAppConsole::HandleSignal() // appunix.cpp { wxAppConsole::WakeUp() // appunix.cpp { WakeUpPipe::WakeUpNoLock() // utilsunx.cpp } } // Platform Specific Callback Handlers: // Console callbacks on stdout/stderr/termination pipe reception (Console-child termination) WakeUpPipe::OnReadWaiting() // See below (Console-stdout/stderr avail) wxExecuteIOHandler::OnReadWaiting() // See below // GUI callbacks on stdout/stderr/termination pipe reception (OSX-GUI) WXCF_OnReadWaiting() // utilsexc_cf.cpp (GTK-GUI) GTK_OnReadWaiting() // src/gtk1/utilsgtk.cpp (GTK1-GUI) GTK1_EndProcessDetector() // src/gtk1/utilsgtk.cpp (Motif-GUI) xt_OnReadWaiting() // utils.cpp // wxOnReadWaiting() is called by all GUI callbacks (and not by console callbacks) wxOnReadWaiting() // appunix.cpp { wxAppTraits::IsFdCallbackEnabled() // utilsunx.cpp wxAppTraits::GetFdExecutePipe() // utilsunx.cpp) (child termination) WakeUpPipe::OnReadWaiting() // see below (stdout/stderr) wxExecuteIOHandler::OnReadWaiting() // see below } // End of wxOnReadWaiting() // stdout/stderr handling - Common for GUI and Console wxExecuteIOHandler::OnReadWaiting() // utilsunx.cpp { wxProcess::GetEndProcDataPtr() // include/wx/process.h wxStreamTempInputBuffer::Eof() // execcmn.cpp wxProcess::OnInputAvailable() // include/wx/process.h - async only wxProcess::OnErrorAvailable() // include/wx/process.h - async only wxExecuteIOHandler::DisableCallback() // utilsunx.cpp { wxExecuteCallbackPipe::IsShutDownFlagSet() // executepipe.h wxAppTraits::UnRegisterProcessCallback() // utilsunx.cpp { // Platform Specific RemoveProcesscallback(): (console app) wxAppTraits::RemoveProcesscallback() // utilsunx.cpp (OSX) wxGUIAppTraits::RemoveProcesscallback() // utilsexc_cf.cpp (GTK2) wxGUIAppTraits::RemoveProcesscallback() // src/gtk/utilsgtk.cpp (GTK1) wxGUIAppTraits::RemoveProcesscallback() // src/gtk1/utilsgtk.cpp (Motif) wxGUIAppTraits::RemoveProcesscallback() // utils.cpp } wxFileInputStream::Detach() // wfstream.h, stream.h wxExecuteCallbackPipe::SetShutDownFlag() // executepipe.h } wxEndProcessData::CheckHandleTermination() // see below, common with child termination } // wxExecuteIOHandler::OnReadWaiting() // Child Termination handling - Common for GUI and console WakeUpPipe::OnReadWaiting() // utilsunx.cpp { wxAppConsole::OnWakeUp() // appunix.cpp { wxAppConsole::CheckSignal() // appunix.cpp { wxCheckChildProcessTermination() // utilsunx.cpp { DoWaitForChild() // utilsunx.cpp { // CheckHandleTermination is common with stdout/stderr wxEndProcessData::CheckHandleTermination() // see below } // DoWaitForChild() } // wxCheckChildProcessTermination() } // wxAppConsole::CheckSignal() } // wxAppConsole::OnWakeUp() } // WakeUpPipe::OnReadWaiting() // CheckHandleTermination() is called at pipe closure for each stdout/stderr/termination pipe wxEndProcessData::CheckHandleTermination() // utilsunx.cpp { wxExecuteCallbackPipe::IsShutDownFlagSet() // executepipe.h wxHandleProcessTermination() // utilsunx.cpp { wxProcess::OnTerminate() // async processes only *** Event Loop is told to stop here *** } // wxHandleProcessTermination } // CheckHandleTermination() } // End of Event loop } // end wxAppTraits::WaitForChildSync(wxExecuteData& execData) // utilsunx.cpp Allocate wxMemoryStream from temp buffers m_bufOut/m_bufErr wxProcess::ReplaceInputStream() // utilsunx.cpp wxProcess::ReplaceErrorStream() // utilsunx.cpp wxStreamTempInputBuffer::~wxStreamTempInputBuffer() // Windows only - execcmn.cpp { wxFileInputStream::Ungetch() // Windows only, NOT Unix. } } // End of wxExecute() wxProcess::~wxProcess() // process.cpp { delete m_stdoutHandler and m_stderrHandler delete m_bufOut, m_bufErr, { wxStreamTempInputBuffer::~wxStreamTempInputBuffer() } } wxAppConsole::~wxAppConsole() { delete m_wakeupPipe } Tests { test.bkl: Added exec.cpp for GUI also exec.cpp: Added some new tests for wxExecute-sync-stderr and wxExecute-sync }