mirror of
https://git.lyx.org/repos/lyx.git
synced 2025-01-19 05:53:35 +00:00
Add support for asynchronous child processes on Windows.
git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/branches/BRANCH_1_3_X@9843 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
parent
8dc7d275c5
commit
906b29c383
@ -1,3 +1,27 @@
|
||||
2005-04-19 Angus Leeming <leeming@lyx.org>
|
||||
|
||||
* 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 <leeming@lyx.org>
|
||||
|
||||
* package.C (get_temp_dir): call GetLongPathName on Windows.
|
||||
|
@ -30,19 +30,27 @@
|
||||
#include "lyxlib.h"
|
||||
#include "filetools.h"
|
||||
#include "os.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "frontends/Timeout.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <cerrno>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define SIGHUP 1
|
||||
# define SIGKILL 9
|
||||
# include <process.h>
|
||||
# include <windows.h>
|
||||
|
||||
#else
|
||||
# include <cerrno>
|
||||
# include <csignal>
|
||||
# include <cstdlib>
|
||||
# include <unistd.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/wait.h>
|
||||
#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 << "</command>" << 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) {
|
||||
|
@ -19,25 +19,59 @@
|
||||
#include "lyxfunctional.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <sstream>
|
||||
# include <windows.h>
|
||||
|
||||
#else
|
||||
# include <cerrno>
|
||||
# include <cstdlib>
|
||||
# include <unistd.h>
|
||||
# include <sys/wait.h>
|
||||
|
||||
# ifndef CXX_GLOBAL_CSTD
|
||||
using std::strerror;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "frontends/Timeout.h"
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/wait.h>
|
||||
|
||||
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<pid_t> const ForkedcallsController::getPIDs() const
|
||||
{
|
||||
vector<pid_t> pids;
|
||||
|
||||
if (forkedCalls.empty())
|
||||
return pids;
|
||||
|
||||
pids.resize(forkedCalls.size());
|
||||
|
||||
vector<pid_t>::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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,9 +18,6 @@
|
||||
|
||||
#include "LString.h"
|
||||
|
||||
#include <boost/signals/signal0.hpp>
|
||||
#include <boost/signals/trackable.hpp>
|
||||
|
||||
#include <sys/types.h> // needed for pid_t
|
||||
|
||||
#include <list>
|
||||
@ -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<pid_t> 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<void> 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
|
||||
|
@ -5,7 +5,40 @@
|
||||
#include <sys/types.h>
|
||||
#include <csignal>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "debug.h"
|
||||
#include "os.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <errno.h>
|
||||
|
||||
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user