wxProcess::Redirect shutdown problem Topic is solved
-
- Knows some wx things
- Posts: 43
- Joined: Tue Jun 13, 2006 7:21 am
- Location: Denmark
- Contact:
wxProcess::Redirect shutdown problem
Hi.
I have a GUI application that launches a commandline application and captures its stdio using wxProcess::Redirect()
I cannot see a way to wait for input from the child process, at the same time as I'm waiting for input from the GUI. I'm searching for something similar to select() where I can wait on multiple data, or wait with a timeout.
I have tried to solve it by using a wxThread that reads from the redirected stdout. This frees my GUI thread, but I still have a problem when the client process does not send any output - the thread can only wait for one "signal" at a time, so it is impossible to interrupt it while it is blocking on wxInputStream::GetC()
Are there a cross-platform solution to this, or do I have to write platform specific code with a Windows WaitForMultipleObjects and a Unix select?
Regards
I have a GUI application that launches a commandline application and captures its stdio using wxProcess::Redirect()
I cannot see a way to wait for input from the child process, at the same time as I'm waiting for input from the GUI. I'm searching for something similar to select() where I can wait on multiple data, or wait with a timeout.
I have tried to solve it by using a wxThread that reads from the redirected stdout. This frees my GUI thread, but I still have a problem when the client process does not send any output - the thread can only wait for one "signal" at a time, so it is impossible to interrupt it while it is blocking on wxInputStream::GetC()
Are there a cross-platform solution to this, or do I have to write platform specific code with a Windows WaitForMultipleObjects and a Unix select?
Regards
Hi,
I quite likely don't adequately understand the question; if so I apologise in advance. But the 'exec' sample seems to cope with a similar situation by using idle time. It calls wxWakeUpIdle() from a timer, then handles EVT_IDLE to poll the process for more input. I've adapted this also to send the process any fresh data at the same time, and I don't see why other functions couldn't be called too.
Regards,
David
I quite likely don't adequately understand the question; if so I apologise in advance. But the 'exec' sample seems to cope with a similar situation by using idle time. It calls wxWakeUpIdle() from a timer, then handles EVT_IDLE to poll the process for more input. I've adapted this also to send the process any fresh data at the same time, and I don't see why other functions couldn't be called too.
Regards,
David
-
- Knows some wx things
- Posts: 43
- Joined: Tue Jun 13, 2006 7:21 am
- Location: Denmark
- Contact:
Hi David
I have not run the samples, so this is purely from reading the source. Originally I also thought that wxProcess::IsInputAvailable() would solve my problem, but unfortunately it does not.
To determine if data is ready, the sample function MyPipedProcess::HasInput() calls wxProcess::IsInputAvailable() which calls wxInputStream::CanRead()
From wxWidgets 2.6.3 source
Thus this is true if the child process has not yet closed, but unfortunetely also true if no data has been sent. This will block the thread that called IsInputAvailable, and make it impossible to Delete that thread until data has arrived from the child process.
If the behaviour is not as I have described it, please correct me. There are many corners in wxWidgets and it is difficult to know them all.
PS. I would have pressed "assist" but seem to have hit "solved" instead. Oh well.
I have not run the samples, so this is purely from reading the source. Originally I also thought that wxProcess::IsInputAvailable() would solve my problem, but unfortunately it does not.
To determine if data is ready, the sample function MyPipedProcess::HasInput() calls wxProcess::IsInputAvailable() which calls wxInputStream::CanRead()
From wxWidgets 2.6.3 source
Code: Select all
bool wxInputStream::CanRead() const
{
// we don't know if there is anything to read or not and by default we
// prefer to be optimistic and try to read data unless we know for sure
// there is no more of it
return m_lasterror != wxSTREAM_EOF;
}
If the behaviour is not as I have described it, please correct me. There are many corners in wxWidgets and it is difficult to know them all.
PS. I would have pressed "assist" but seem to have hit "solved" instead. Oh well.
-
- Knows some wx things
- Posts: 43
- Joined: Tue Jun 13, 2006 7:21 am
- Location: Denmark
- Contact:
My explanation may not be too clear, so here are the relevant code snippets from exec sample and wxMSW 2.6.3
First the idle loop in the sample
... this checks to see if the process has input ...
... which checks the wxProcess ...
... which checks the wxInputStream ...
... so the result is true and MyPipedProcess::HasInput() progress to ReadLine(), with no input available. This causes the thread to stop, waiting for input.
First the idle loop in the sample
Code: Select all
// input polling
void MyFrame::OnIdle(wxIdleEvent& event)
{
size_t count = m_running.GetCount();
for ( size_t n = 0; n < count; n++ )
{
if ( m_running[n]->HasInput() )
{
event.RequestMore();
}
}
}
Code: Select all
bool MyPipedProcess::HasInput()
{
bool hasInput = false;
if ( IsInputAvailable() )
{
wxTextInputStream tis(*GetInputStream());
// this assumes that the output is always line buffered
wxString msg;
msg << m_cmd << _T(" (stdout): ") << tis.ReadLine();
m_parent->GetLogListBox()->Append(msg);
hasInput = true;
}
// peso : deleted similar code for error stream
return hasInput;
}
Code: Select all
bool wxProcess::IsInputAvailable() const
{
return m_inputStream && m_inputStream->CanRead();
}
Code: Select all
bool wxInputStream::CanRead() const
{
// we don't know if there is anything to read or not and by default we
// prefer to be optimistic and try to read data unless we know for sure
// there is no more of it
return m_lasterror != wxSTREAM_EOF;
}
Must you use a thread at all? I have no problem with the following code, used in a (poor man's) terminal emulator. Note the first comment, though:
I didn't know there might be no input, so I didn't check for an empty string. Easy enough to add though.
Code: Select all
bool MyPipedProcess::HasInput()
{
char c;
bool hasInput = FALSE;
// The original used wxTextInputStream to read a line at a time. Fine, except when there was no \n, whereupon the thing would hang
while ( IsInputAvailable() ) // If there's std input
{ wxString line;
do
{ c = GetInputStream()->GetC(); // Get a char from the input
if ( GetInputStream()->Eof() ) break; // Check we've not just overrun
line += c; // Add it to line
if ( c==wxT('\n') ) break; // If \n, break to print the line
}
while ( IsInputAvailable() ); // Unless \n, loop to get another char
text->AddInput( line ); // Either there's a full line in 'line', or we've run out of input. Either way, print it
matches = true;
hasInput = TRUE;
}
while ( IsErrorAvailable() ) // Ditto with std error
...
{
That's OKPS. I would have pressed "assist" but seem to have hit "solved" instead.
-
- Knows some wx things
- Posts: 43
- Joined: Tue Jun 13, 2006 7:21 am
- Location: Denmark
- Contact:
I just compiled the exec sample, and given a client application that simply stops sending on stdout, it blocks the GUI. This is the behaviour I was seeking to avoid. Using a thread, prevents the GUI from locking up, but trades it for a shut-down problem.
Try with a client similar to the code below.
Try with a client similar to the code below.
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv) {
int i;
for (i=0; i<10; i++) {
printf("ping %d\n",i);
long sec;
sec = 400000000L; /* number of loops that correspond to 1 second */
long d;
/* wait a few seconds */
for (d=0; d<10*sec; d++) { i = i + d - d; }
}
return 0;
}
Hmm. I just ran your test program through the exec sample, and it didn't block the gui (that was running it async, of course; and I had to add a fflush(stdout); after the printf to make the input visible during running, rather that all at once at the end).
More importantly, I ran the test from my terminal emulator i.e. a 'real-life' situation, and everything behaved itself: the pings appeared one by one as expected, and the rest of my app stayed active and usable.
This is in wxGTK-2.6.3. Perhaps there's a platform difference?
More importantly, I ran the test from my terminal emulator i.e. a 'real-life' situation, and everything behaved itself: the pings appeared one by one as expected, and the rest of my app stayed active and usable.
This is in wxGTK-2.6.3. Perhaps there's a platform difference?
-
- Knows some wx things
- Posts: 43
- Joined: Tue Jun 13, 2006 7:21 am
- Location: Denmark
- Contact:
My mistake. I ran the exec sample on another computer using mingw and wxMSW-2.6.2 and the GUI did not lock up. I must have selected syncronous or something.
BTW, wxInputStream::CanRead() is virtual, and wxExecute creates a wxPipedInputStream, which uses the non-blocking PeekNamedPipe. My mistake again.
To sum up: It works if you poll CanRead(), instead of block on GetC().
I can use this as a workaround, but I would still like to know if I can get a signal or something when input is ready. If I can avoid it I would rather not spend cpu on polling.
BTW, wxInputStream::CanRead() is virtual, and wxExecute creates a wxPipedInputStream, which uses the non-blocking PeekNamedPipe. My mistake again.
To sum up: It works if you poll CanRead(), instead of block on GetC().
I can use this as a workaround, but I would still like to know if I can get a signal or something when input is ready. If I can avoid it I would rather not spend cpu on polling.