diff --git a/src/support/ChangeLog b/src/support/ChangeLog index ac48b99f54..273887d85a 100644 --- a/src/support/ChangeLog +++ b/src/support/ChangeLog @@ -1,3 +1,27 @@ +2005-04-19 Angus Leeming + + * forkedcall.C: protect system-specific headers with preprocessor + guards. + (running): don't call waitpid() on Windows. + (waitForChild): add Windows-specific code to wait for a child process + to finish. + (generateChild): add Windows-specific code to spawn the child in the + first place. + + * forkedcontr.[Ch]: remove cruft from the days of the dialog to kill + child processes. Class no longer derives from trackable, no longer + emits a global signal when a child process is reaped. + (getPIDs, getCommand): removed. + + * forkedcontr.C: protect system-specific headers with preprocessor + guards. Add Windows-specific code to wait for a child process + asynchronously. + (getChildErrorMessage): new function, wrapping the Windows native API. + (timer): use WaitForSingleObject and GetExitCodeProcess on Windows. + + * kill.C (kill): add Ruurd's Windows-specific code to terminate + a child process. + 2005-04-19 Angus Leeming * package.C (get_temp_dir): call GetLongPathName on Windows. @@ -40,7 +64,7 @@ * filetools.[Ch] (LibScriptSearch): backport the 1.4.x code so that the function now substitutes the $$s placeholder for the path to the appropriate LyX support directory. - + * forkedcall.C (generateChild): backport the 1.4.x code to split the input command up into an array of words and to strip any quotes. @@ -81,7 +105,7 @@ * FileInfo.h: * chdir.C: - * forkedcall.C: + * forkedcall.C: * forkedcontr.C: * getcwd.C: * lyxsum.C: @@ -191,7 +215,7 @@ file using mktemp and open. 2004-12-14 Angus Leeming - + * os.h, os_os2.C, os_unix.C, os_win32.C: (binpath, binname, getTmpDir): return a const reference rather than a copy of the data. @@ -213,7 +237,7 @@ 2004-12-14 Angus Leeming * os.C: Add _WIN32 to the #define. - + * systemcall.C (startscript): remove trailing '/n' from request to start the command in a minimized window under DOS. @@ -257,9 +281,9 @@ 2003-03-06 Alfredo Braunstein - * forkedcontr.C (timer): reworked the loop to allow running changes + * forkedcontr.C (timer): reworked the loop to allow running changes on the list. - + 2003-02-27 Ling Li * lyxalgo.h (eliminate_duplicates): re-written to avoid the initial diff --git a/src/support/forkedcall.C b/src/support/forkedcall.C index 3f823c8723..6054a02214 100644 --- a/src/support/forkedcall.C +++ b/src/support/forkedcall.C @@ -30,19 +30,27 @@ #include "lyxlib.h" #include "filetools.h" #include "os.h" + #include "debug.h" #include "frontends/Timeout.h" #include #include -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include + +#ifdef _WIN32 +# define SIGHUP 1 +# define SIGKILL 9 +# include +# include + +#else +# include +# include +# include +# include +# include +# include #endif using std::endl; @@ -153,10 +161,12 @@ bool ForkedProcess::running() const if (!pid()) return false; +#if !defined (_WIN32) // Un-UNIX like, but we don't have much use for // knowing if a zombie exists, so just reap it first. int waitstatus; waitpid(pid(), &waitstatus, WNOHANG); +#endif // Racy of course, but it will do. if (lyx::kill(pid(), 0) && errno == ESRCH) @@ -196,6 +206,29 @@ int ForkedProcess::waitForChild() { // We'll pretend that the child returns 1 on all error conditions. retval_ = 1; + +#if defined (_WIN32) + HANDLE const hProcess = HANDLE(pid_); + + DWORD const wait_status = ::WaitForSingleObject(hProcess, INFINITE); + + switch (wait_status) { + case WAIT_OBJECT_0: { + DWORD exit_code = 0; + if (!GetExitCodeProcess(hProcess, &exit_code)) { + lyxerr << "GetExitCodeProcess failed waiting for child\n" + << getChildErrorMessage() << std::endl; + } else + retval_ = exit_code; + break; + } + case WAIT_FAILED: + lyxerr << "WaitForSingleObject failed waiting for child\n" + << getChildErrorMessage() << std::endl; + break; + } + +#else int status; bool wait = true; while (wait) { @@ -224,6 +257,7 @@ int ForkedProcess::waitForChild() wait = false; } } +#endif return retval_; } @@ -327,7 +361,12 @@ int Forkedcall::generateChild() lyxerr << "" << std::endl; } -#ifndef __EMX__ +#if defined (__EMX__) + pid_t const cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND, + argv[0], &*argv.begin()); +#elif defined (_WIN32) + pid_t const cpid = spawnvp(_P_NOWAIT, argv[0], &*argv.begin()); +#else // POSIX pid_t const cpid = ::fork(); if (cpid == 0) { // Child @@ -338,9 +377,6 @@ int Forkedcall::generateChild() << strerror(errno) << endl; _exit(1); } -#else - pid_t const cpid = spawnvp(P_SESSION|P_DEFAULT|P_MINIMIZE|P_BACKGROUND, - argv[0], &*argv.begin()); #endif if (cpid < 0) { diff --git a/src/support/forkedcontr.C b/src/support/forkedcontr.C index a3caac5ec2..2ee8132710 100644 --- a/src/support/forkedcontr.C +++ b/src/support/forkedcontr.C @@ -19,25 +19,59 @@ #include "lyxfunctional.h" #include "debug.h" +#ifdef _WIN32 +# include +# include + +#else +# include +# include +# include +# include + +# ifndef CXX_GLOBAL_CSTD + using std::strerror; +# endif +#endif + #include "frontends/Timeout.h" #include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include using std::vector; using std::endl; using std::find_if; -#ifndef CXX_GLOBAL_CSTD -using std::strerror; + +#if defined(_WIN32) +string const getChildErrorMessage() +{ + DWORD const error_code = ::GetLastError(); + + HLOCAL t_message = 0; + bool const ok = ::FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + 0, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &t_message, 0, 0 + ) != 0; + + std::ostringstream ss; + ss << "LyX: Error waiting for child: " << error_code; + + if (ok) { + ss << ": " << (LPTSTR)t_message; + ::LocalFree(t_message); + } else + ss << ": Error unknown."; + + return STRCONV(ss.str()); +} #endif + // Ensure, that only one controller exists inside process ForkedcallsController & ForkedcallsController::get() { @@ -75,7 +109,6 @@ void ForkedcallsController::addCall(ForkedProcess const & newcall) timeout_->start(); forkedCalls.push_back(newcall.clone()); - childrenChanged(); } @@ -83,17 +116,45 @@ void ForkedcallsController::addCall(ForkedProcess const & newcall) // Check the list and, if there is a stopped child, emit the signal. void ForkedcallsController::timer() { - ListType::size_type start_size = forkedCalls.size(); - ListType::iterator it = forkedCalls.begin(); ListType::iterator end = forkedCalls.end(); while (it != end) { ForkedProcess * actCall = *it; + bool remove_it = false; +#if defined(_WIN32) + HANDLE const hProcess = HANDLE(actCall->pid()); + + DWORD const wait_status = ::WaitForSingleObject(hProcess, 0); + + switch (wait_status) { + case WAIT_TIMEOUT: + // Still running + break; + case WAIT_OBJECT_0: { + DWORD exit_code = 0; + if (!GetExitCodeProcess(hProcess, &exit_code)) { + lyxerr << "GetExitCodeProcess failed waiting for child\n" + << getChildErrorMessage() << std::endl; + // Child died, so pretend it returned 1 + actCall->setRetValue(1); + } else { + actCall->setRetValue(exit_code); + } + remove_it = true; + break; + } + case WAIT_FAILED: + lyxerr << "WaitForSingleObject failed waiting for child\n" + << getChildErrorMessage() << std::endl; + actCall->setRetValue(1); + remove_it = true; + break; + } +#else pid_t pid = actCall->pid(); int stat_loc; pid_t const waitrpid = waitpid(pid, &stat_loc, WNOHANG); - bool remove_it = false; if (waitrpid == -1) { lyxerr << "LyX: Error waiting for child: " @@ -130,6 +191,7 @@ void ForkedcallsController::timer() actCall->setRetValue(1); remove_it = true; } +#endif if (remove_it) { forkedCalls.erase(it); @@ -149,44 +211,6 @@ void ForkedcallsController::timer() if (!forkedCalls.empty() && !timeout_->running()) { timeout_->start(); } - - if (start_size != forkedCalls.size()) - childrenChanged(); -} - - -// Return a vector of the pids of all the controlled processes. -vector const ForkedcallsController::getPIDs() const -{ - vector pids; - - if (forkedCalls.empty()) - return pids; - - pids.resize(forkedCalls.size()); - - vector::iterator vit = pids.begin(); - for (ListType::const_iterator lit = forkedCalls.begin(); - lit != forkedCalls.end(); ++lit, ++vit) { - *vit = (*lit)->pid(); - } - - std::sort(pids.begin(), pids.end()); - return pids; -} - - -// Get the command string of the process. -string const ForkedcallsController::getCommand(pid_t pid) const -{ - ListType::const_iterator it = - find_if(forkedCalls.begin(), forkedCalls.end(), - lyx::compare_memfun(&Forkedcall::pid, pid)); - - if (it == forkedCalls.end()) - return string(); - - return (*it)->command(); } diff --git a/src/support/forkedcontr.h b/src/support/forkedcontr.h index 88ac92a2d1..937837cc42 100644 --- a/src/support/forkedcontr.h +++ b/src/support/forkedcontr.h @@ -18,9 +18,6 @@ #include "LString.h" -#include -#include - #include // needed for pid_t #include @@ -29,7 +26,7 @@ class ForkedProcess; class Timeout; -class ForkedcallsController : public boost::signals::trackable { +class ForkedcallsController { public: /// We need this to avoid warnings. ForkedcallsController(); @@ -51,21 +48,12 @@ public: */ void timer(); - /// Return a vector of the pids of all the controlled processes. - std::vector const getPIDs() const; - - /// Get the command string of the process. - string const getCommand(pid_t) const; - /** Kill this process prematurely and remove it from the list. * The process is killed within tolerance secs. * See forkedcall.[Ch] for details. */ void kill(pid_t, int tolerance = 5); - /// Signal emitted when the list of current child processes changes. - boost::signal0 childrenChanged; - private: /// ForkedcallsController(ForkedcallsController const &); @@ -81,4 +69,9 @@ private: Timeout * timeout_; }; +#if defined(_WIN32) +// a wrapper for GetLastError() and FormatMessage(). +string const getChildErrorMessage(); +#endif + #endif // FORKEDCONTR_H diff --git a/src/support/kill.C b/src/support/kill.C index 105ce29db6..6e1a3edba3 100644 --- a/src/support/kill.C +++ b/src/support/kill.C @@ -5,7 +5,40 @@ #include #include +#ifdef _WIN32 +#include "debug.h" +#include "os.h" + +#include +#include + +using std::endl; +#endif //_WIN32 + int lyx::kill(int pid, int sig) { +#ifdef _WIN32 && 0 + if (pid == (int)GetCurrentProcessId()) + return -(raise(sig)); + else{ + HANDLE hProcess; + if (!(hProcess = + OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid))) { + lyxerr << "kill OpenProcess failed!" << endl; + return -1; + } + else { + if (!TerminateProcess(hProcess, sig)){ + lyxerr << "kill process failed!" << endl; + CloseHandle(hProcess); + return -1; + } + CloseHandle(hProcess); + } + } + return 0; + +#else return ::kill(pid, sig); +#endif }