everything is ok, when pipes are non blocking on parent's side and blocking on client's side, and select is not used (not necessary for non blocking IO)

master
Marc Wäckerlin 20 years ago
parent 1386c24c79
commit 7c07d63570
  1. 126
      mrw/exec.cpp

@ -9,6 +9,9 @@
@license LGPL, see file <a href="license.html">COPYING</a> @license LGPL, see file <a href="license.html">COPYING</a>
$Log$ $Log$
Revision 1.8 2004/12/18 21:00:09 marc
everything is ok, when pipes are non blocking on parent's side and blocking on client's side, and select is not used (not necessary for non blocking IO)
Revision 1.7 2004/12/17 17:38:57 marc Revision 1.7 2004/12/17 17:38:57 marc
it works perfectly with blocking pipes and buffer size of 1 it works perfectly with blocking pipes and buffer size of 1
@ -36,12 +39,10 @@
#include <mrw/unistd.hpp> #include <mrw/unistd.hpp>
#include <mrw/exception.hpp> #include <mrw/exception.hpp>
#include <mrw/stdext.hpp> // max #include <mrw/stdext.hpp> // max
#include <sys/wait.h> // waitpid #include <sys/wait.h> // waitpid#include <unistd.h> // fork, exec
#include <unistd.h> // fork, exec #include <string.h> // memcpy
#include <string.h> // memcpy
#include <assert.h> // assert #include <assert.h> // assert
#include <iostream>
mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w, mrw::ExecutionFailedExc::ExecutionFailedExc(const std::string& w,
const std::string& c) const std::string& c)
throw(std::bad_exception): throw(std::bad_exception):
@ -133,7 +134,7 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
program. */ program. */
_success = false; _success = false;
_res = _err = ""; _res = _err = "";
mrw::Pipe stdOut(true), stdErr(true); mrw::Pipe stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
if (!stdOut || !stdErr) if (!stdOut || !stdErr)
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd); throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
pid_t pid(fork()); pid_t pid(fork());
@ -144,46 +145,34 @@ mrw::Exec& mrw::Exec::execute(bool exc) throw(std::exception) {
stdErr.close_out(); stdErr.close_out();
if (!stdOut || !stdErr) if (!stdOut || !stdErr)
throw ExecutionFailedExc("cannot close pipe", *_cmd); throw ExecutionFailedExc("cannot close pipe", *_cmd);
ssize_t num1(-1), num2(-1); ssize_t num1(1), num2(1);
fd_set readfds; char buf[4096];
char buf[1]; // used to have larger buffer, but since non blocking fails...
int res(0), s(0); int res(0), s(0);
/** @bug It sometimes did not get the whole input since I /** @bug @b Solved: It sometimes did not get the whole input
changed to non blocking pipes. Now pipes are blocking since I changed to non blocking pipes. Now I have found
again and buffer size is 1, so I hope all problems are the solution to the problems: Non blocking IO does not
resolved. Please report any problems! */ work in conjunction with @c select! Also the pipe must
while (num1||num2) try { // not all end of file not be nonblocking on both ends, but only on one. */
while (num1||num2||!res) try { // not end of files or child terminated
if (!res && (res=waitpid(pid, &s, WNOHANG))) if (!res && (res=waitpid(pid, &s, WNOHANG)))
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
throw ExecutionFailedExc("execution failed", *_cmd); throw ExecutionFailedExc("execution failed", *_cmd);
// select... if (num1<=0&&num2<=0) usleep(10000); // nothing to read last time
FD_ZERO(&readfds);
if (num1) FD_SET(stdOut.istream(), &readfds);
if (num2) FD_SET(stdErr.istream(), &readfds);
int n(mrw::max((num1?stdOut.istream():0),
(num2?stdErr.istream():0)));
timeval tm = {0, 10000};
int num = select(n+1, &readfds, 0, 0, 0);
if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
if (num==0 && res==pid) break; // process ended, no data left
// check and handle stdout // check and handle stdout
if (num>0 && FD_ISSET(stdOut.istream(), &readfds)) if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
if ((num1=read(stdOut.istream(), buf, sizeof(buf)))>0) _res += std::string(buf, num1);
_res += std::string(buf, num1); else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN)) throw ExecutionFailedExc("readin stdout", *_cmd);
throw ExecutionFailedExc("readin stdout", *_cmd);
// check and handle stderr // check and handle stderr
if (num>0 && FD_ISSET(stdErr.istream(), &readfds)) if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
if ((num2=read(stdErr.istream(), buf, sizeof(buf)))>0) _err += std::string(buf, num2);
_err += std::string(buf, num2); else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN)) throw ExecutionFailedExc("readin stderr", *_cmd);
throw ExecutionFailedExc("readin stderr", *_cmd);
} catch (...) { } catch (...) {
if (exc) throw; if (exc) throw;
_success = false; _success = false;
return *this; return *this;
} }
std::cout<<"num1="<<num1<<", num2="<<num2<<std::endl;
} else { // child } else { // child
stdOut.close_in(); stdOut.close_in();
stdErr.close_in(); stdErr.close_in();
@ -209,8 +198,8 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
the program. */ the program. */
_success = false; _success = false;
_res = _err = ""; _res = _err = "";
mrw::Pipe stdIn(true), // child terminates if stdin is non-blocking mrw::Pipe stdIn(mrw::Pipe::block_input),
stdOut(true), stdErr(true); // non blocking is also a problem here stdOut(mrw::Pipe::block_output), stdErr(mrw::Pipe::block_output);
if (!stdIn || !stdOut || !stdErr) if (!stdIn || !stdOut || !stdErr)
throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd); throw mrw::ExecutionFailedExc("cannot create pipe", *_cmd);
pid_t pid(fork()); pid_t pid(fork());
@ -221,60 +210,43 @@ mrw::Exec& mrw::Exec::execute(const std::string& input, bool exc)
stdErr.close_out(); stdErr.close_out();
if (!stdIn || !stdOut || !stdErr) if (!stdIn || !stdOut || !stdErr)
throw ExecutionFailedExc("cannot close pipes", *_cmd); throw ExecutionFailedExc("cannot close pipes", *_cmd);
ssize_t num0(-1), num1(-1), num2(-1); ssize_t num0(1), num1(1), num2(1);
std::string in(input); std::string in(input);
fd_set writefds, readfds; char buf[4096];
char buf[1]; // used to have larger buffer, but since non blocking fails...
int res(0), s(0); int res(0), s(0);
/** @bug It sometimes did not get the whole input since I /** @bug @b Solved: It sometimes did not get the whole input
changed to non blocking pipes. Now pipes are blocking since I changed to non blocking pipes. Now I have found
again and buffer size is 1, so I hope all problems are the solution to the problems: Non blocking IO does not
resolved. Please report any problems! */ work in conjunction with @c select! Also the pipe must
while (num0||num1||num2) try { // not all end of file not be nonblocking on both ends, but only on one. */
while (num0||num1||num2||!res) try { // not end of files or child terminated
if (!res && (res=waitpid(pid, &s, WNOHANG))) if (!res && (res=waitpid(pid, &s, WNOHANG)))
if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0) if (res!=pid || WIFEXITED(s)!=0 && WEXITSTATUS(s)!=0)
throw ExecutionFailedExc("execution failed", *_cmd); throw ExecutionFailedExc("execution failed", *_cmd);
// select... if (num0<=0&&num1<=0&&num2<=0) usleep(10000); // no activity last time
FD_ZERO(&writefds);
FD_ZERO(&readfds);
if (num0) FD_SET(stdIn.ostream(), &writefds);
if (num1) FD_SET(stdOut.istream(), &readfds);
if (num2) FD_SET(stdErr.istream(), &readfds);
int n(mrw::max((num0?stdIn.ostream():0),
mrw::max((num1?stdOut.istream():0),
(num2?stdErr.istream():0))));
timeval tm = {0, 10000};
int num = select(n+1, &readfds, &writefds, 0, &tm);
if (num<0) throw ExecutionFailedExc("select failed", *_cmd);
if (num==0 && res==pid) break; // process ended, no data left
// check and handle stdin // check and handle stdin
if (num0 && num>0 if (num0 && (num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) {
&& FD_ISSET(stdIn.ostream(), &writefds)) if ((unsigned int)num0<in.size())
if ((num0=write(stdIn.ostream(), in.c_str(), in.size()))>0) { in = in.substr(num0);
if ((unsigned int)num0<in.size()) else if ((unsigned int)num0==in.size())
in = in.substr(num0); num0=0, stdIn.close_out();
else if ((unsigned int)num0==in.size()) } else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
num0=0, stdIn.close_out(); throw ExecutionFailedExc("writing stdin", *_cmd);
} else if (num0==-1 && (errno!=EINTR&&errno!=EAGAIN))
throw ExecutionFailedExc("writing stdin", *_cmd);
// check and handle stdout // check and handle stdout
if (num>0 && FD_ISSET(stdOut.istream(), &readfds)) if (num1 && (num1=read(stdOut.istream(), buf, sizeof(buf)))>0)
if ((num1=read(stdOut.istream(), buf, sizeof(buf)))>0) _res += std::string(buf, num1);
_res += std::string(buf, num1); else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN))
else if (num1==-1 && (errno!=EINTR&&errno!=EAGAIN)) throw ExecutionFailedExc("readin stdout", *_cmd);
throw ExecutionFailedExc("readin stdout", *_cmd);
// check and handle stderr // check and handle stderr
if (num>0 && FD_ISSET(stdErr.istream(), &readfds)) if (num2 && (num2=read(stdErr.istream(), buf, sizeof(buf)))>0)
if ((num2=read(stdErr.istream(), buf, sizeof(buf)))>0) _err += std::string(buf, num2);
_err += std::string(buf, num2); else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN))
else if (num2==-1 && (errno!=EINTR&&errno!=EAGAIN)) throw ExecutionFailedExc("readin stderr", *_cmd);
throw ExecutionFailedExc("readin stderr", *_cmd);
} catch (...) { } catch (...) {
if (exc) throw; if (exc) throw;
_success = false; _success = false;
return *this; return *this;
} }
std::cout<<"num0="<<num0<<", num1="<<num1<<", num2="<<num2<<std::endl;
} else { // child } else { // child
stdIn.close_out(); stdIn.close_out();
stdOut.close_in(); stdOut.close_in();

Loading…
Cancel
Save