merge the Forked* machinery into a single pair of files

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21863 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
André Pönitz 2007-11-29 19:19:39 +00:00
parent 28afd72c65
commit 1aafa6ff77
13 changed files with 437 additions and 556 deletions

View File

@ -81,7 +81,7 @@
#include "support/debug.h" #include "support/debug.h"
#include "support/FileFilterList.h" #include "support/FileFilterList.h"
#include "support/filetools.h" #include "support/filetools.h"
#include "support/Forkedcall.h" #include "support/ForkedCalls.h"
#include "support/gettext.h" #include "support/gettext.h"
#include "support/gzstream.h" #include "support/gzstream.h"
#include "support/lstrings.h" #include "support/lstrings.h"

View File

@ -15,14 +15,14 @@
#include "ISpell.h" #include "ISpell.h"
#include "BufferParams.h" #include "BufferParams.h"
#include "support/debug.h"
#include "Encoding.h" #include "Encoding.h"
#include "support/gettext.h"
#include "Language.h" #include "Language.h"
#include "LyXRC.h" #include "LyXRC.h"
#include "WordLangTuple.h" #include "WordLangTuple.h"
#include "support/Forkedcall.h" #include "support/debug.h"
#include "support/gettext.h"
#include "support/ForkedCalls.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/unicode.h" #include "support/unicode.h"

View File

@ -18,10 +18,8 @@
#include "BufferView.h" #include "BufferView.h"
#include "CoordCache.h" #include "CoordCache.h"
#include "Cursor.h" #include "Cursor.h"
#include "support/debug.h"
#include "Font.h" #include "Font.h"
#include "FuncRequest.h" #include "FuncRequest.h"
#include "support/gettext.h"
#include "GuiApplication.h" #include "GuiApplication.h"
#include "GuiKeySymbol.h" #include "GuiKeySymbol.h"
#include "GuiPainter.h" #include "GuiPainter.h"
@ -37,8 +35,10 @@
#include "graphics/GraphicsImage.h" #include "graphics/GraphicsImage.h"
#include "graphics/GraphicsLoader.h" #include "graphics/GraphicsLoader.h"
#include "support/debug.h"
#include "support/gettext.h"
#include "support/FileName.h" #include "support/FileName.h"
#include "support/ForkedcallsController.h" #include "support/ForkedCalls.h"
#include "frontends/Application.h" #include "frontends/Application.h"
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
@ -79,7 +79,7 @@ using std::string;
namespace lyx { namespace lyx {
using support::FileName; using support::FileName;
using support::ForkedcallsController; using support::ForkedCallsController;
/// return the LyX mouse button state from Qt's /// return the LyX mouse button state from Qt's
@ -458,7 +458,7 @@ void GuiWorkArea::toggleCursor()
// Use this opportunity to deal with any child processes that // Use this opportunity to deal with any child processes that
// have finished but are waiting to communicate this fact // have finished but are waiting to communicate this fact
// to the rest of LyX. // to the rest of LyX.
ForkedcallsController & fcc = ForkedcallsController::get(); ForkedCallsController & fcc = ForkedCallsController::get();
fcc.handleCompletedProcesses(); fcc.handleCompletedProcesses();
} }

View File

@ -13,12 +13,12 @@
#include "GraphicsConverter.h" #include "GraphicsConverter.h"
#include "Converter.h" #include "Converter.h"
#include "support/debug.h"
#include "Format.h" #include "Format.h"
#include "support/filetools.h"
#include "support/ForkedCallQueue.h"
#include "support/convert.h" #include "support/convert.h"
#include "support/debug.h"
#include "support/filetools.h"
#include "support/ForkedCalls.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h" #include "support/lyxlib.h"
#include "support/os.h" #include "support/os.h"
@ -33,7 +33,7 @@ namespace support = lyx::support;
using support::addExtension; using support::addExtension;
using support::changeExtension; using support::changeExtension;
using support::FileName; using support::FileName;
using support::Forkedcall; using support::ForkedCall;
using support::ForkedCallQueue; using support::ForkedCallQueue;
using support::getExtension; using support::getExtension;
using support::libScriptSearch; using support::libScriptSearch;
@ -193,7 +193,7 @@ void Converter::Impl::startConversion()
return; return;
} }
Forkedcall::SignalTypePtr ForkedCall::SignalTypePtr
ptr = ForkedCallQueue::get().add(script_command_); ptr = ForkedCallQueue::get().add(script_command_);
ptr->connect(boost::bind(&Impl::converted, this, _1, _2)); ptr->connect(boost::bind(&Impl::converted, this, _1, _2));

View File

@ -17,7 +17,6 @@
#include "Buffer.h" #include "Buffer.h"
#include "BufferParams.h" #include "BufferParams.h"
#include "Converter.h" #include "Converter.h"
#include "support/debug.h"
#include "Encoding.h" #include "Encoding.h"
#include "Format.h" #include "Format.h"
#include "InsetIterator.h" #include "InsetIterator.h"
@ -31,12 +30,12 @@
#include "insets/Inset.h" #include "insets/Inset.h"
#include "support/convert.h"
#include "support/debug.h"
#include "support/filetools.h" #include "support/filetools.h"
#include "support/Forkedcall.h" #include "support/ForkedCalls.h"
#include "support/ForkedcallsController.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h" #include "support/lyxlib.h"
#include "support/convert.h"
#include <boost/bind.hpp> #include <boost/bind.hpp>
@ -229,7 +228,7 @@ public:
Buffer const & buffer() const { return buffer_; } Buffer const & buffer() const { return buffer_; }
private: private:
/// Called by the Forkedcall process that generated the bitmap files. /// Called by the ForkedCall process that generated the bitmap files.
void finishedGenerating(pid_t, int); void finishedGenerating(pid_t, int);
/// ///
void dumpPreamble(odocstream &) const; void dumpPreamble(odocstream &) const;
@ -384,7 +383,7 @@ InProgress::InProgress(string const & filename_base,
void InProgress::stop() const void InProgress::stop() const
{ {
if (pid) if (pid)
lyx::support::ForkedcallsController::get().kill(pid, 0); lyx::support::ForkedCallsController::get().kill(pid, 0);
if (!metrics_file.empty()) if (!metrics_file.empty())
metrics_file.removeFile(); metrics_file.removeFile();
@ -608,12 +607,12 @@ void PreviewLoader::Impl::startLoading()
string const command = support::libScriptSearch(cs.str()); string const command = support::libScriptSearch(cs.str());
// Initiate the conversion from LaTeX to bitmap images files. // Initiate the conversion from LaTeX to bitmap images files.
support::Forkedcall::SignalTypePtr support::ForkedCall::SignalTypePtr
convert_ptr(new support::Forkedcall::SignalType); convert_ptr(new support::ForkedCall::SignalType);
convert_ptr->connect(bind(&Impl::finishedGenerating, this, _1, _2)); convert_ptr->connect(bind(&Impl::finishedGenerating, this, _1, _2));
support::Forkedcall call; support::ForkedCall call;
int ret = call.startscript(command, convert_ptr); int ret = call.startScript(command, convert_ptr);
if (ret != 0) { if (ret != 0) {
LYXERR(Debug::GRAPHICS, "PreviewLoader::startLoading()\n" LYXERR(Debug::GRAPHICS, "PreviewLoader::startLoading()\n"

View File

@ -18,8 +18,6 @@
#include "Buffer.h" #include "Buffer.h"
#include "Converter.h" #include "Converter.h"
#include "support/debug.h"
#include "support/gettext.h"
#include "ErrorList.h" #include "ErrorList.h"
#include "Exporter.h" #include "Exporter.h"
#include "Format.h" #include "Format.h"
@ -27,8 +25,9 @@
#include "frontends/alert.h" #include "frontends/alert.h"
#include "support/debug.h"
#include "support/filetools.h" #include "support/filetools.h"
#include "support/Forkedcall.h" #include "support/gettext.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxalgo.h" #include "support/lyxalgo.h"
#include "support/lyxlib.h" #include "support/lyxlib.h"

View File

@ -1,99 +0,0 @@
/**
* \file ForkedCallQueue.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Alfredo Braunstein (based on an idea from Angus Leeming)
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
#include "support/ForkedCallQueue.h"
#include "support/debug.h"
#include <boost/bind.hpp>
using std::string;
using std::endl;
namespace lyx {
namespace support {
ForkedCallQueue & ForkedCallQueue::get()
{
static ForkedCallQueue singleton;
return singleton;
}
Forkedcall::SignalTypePtr ForkedCallQueue::add(string const & process)
{
Forkedcall::SignalTypePtr ptr;
ptr.reset(new Forkedcall::SignalType);
callQueue_.push(Process(process, ptr));
if (!running_) {
startCaller();
}
return ptr;
}
void ForkedCallQueue::callNext()
{
if (callQueue_.empty())
return;
Process pro = callQueue_.front();
callQueue_.pop();
// Bind our chain caller
pro.second->connect(boost::bind(&ForkedCallQueue::callback,
this, _1, _2));
Forkedcall call;
// If we fail to fork the process, then emit the signal
// to tell the outside world that it failed.
if (call.startscript(pro.first, pro.second) > 0) {
pro.second->operator()(0,1);
}
}
void ForkedCallQueue::callback(pid_t, int)
{
if (callQueue_.empty())
stopCaller();
else
callNext();
}
ForkedCallQueue::ForkedCallQueue()
: running_(false)
{}
void ForkedCallQueue::startCaller()
{
LYXERR(Debug::GRAPHICS, "ForkedCallQueue: waking up");
running_ = true ;
callNext();
}
void ForkedCallQueue::stopCaller()
{
running_ = false ;
LYXERR(Debug::GRAPHICS, "ForkedCallQueue: I'm going to sleep");
}
bool ForkedCallQueue::running() const
{
return running_ ;
}
} // namespace support
} // namespace lyx

View File

@ -1,68 +0,0 @@
// -*- C++ -*-
/**
* \file ForkedCallQueue.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Alfredo Braunstein (based on an idea from Angus Leeming)
*
* Full author contact details are available in file CREDITS.
*
* This class implements a queue of forked processes. In order not to
* hose the system with multiple processes running simultaneously, you can
* request the addition of your process to this queue and it will be
* executed when its turn comes.
*
*/
#ifndef FORKEDCALLQUEUE_H
#define FORKEDCALLQUEUE_H
#include "support/Forkedcall.h"
#include <queue>
#include <utility>
namespace lyx {
namespace support {
class ForkedCallQueue {
public:
/// A process in the queue
typedef std::pair<std::string, Forkedcall::SignalTypePtr> Process;
/** Add a process to the queue. Processes are forked sequentially
* only one is running at a time.
* Connect to the returned signal and you'll be informed when
* the process has ended.
*/
Forkedcall::SignalTypePtr add(std::string const & process);
/// Query whether the queue is running a forked process now.
bool running() const;
/// Get the and only instance of the class
static ForkedCallQueue & get();
private:
/** this class is a singleton class... use
* ForkedCallQueue::get() instead
*/
ForkedCallQueue();
/// in-progress queue
std::queue<Process> callQueue_;
///
bool running_;
///
void callNext();
///
void startCaller();
///
void stopCaller();
///
void callback(pid_t, int);
};
} // namespace support
} // namespace lyx
#endif // FORKEDCALLQUEUE_H

View File

@ -1,34 +1,21 @@
/** /**
* \file Forkedcall.cpp * \file ForkedCalls.cpp
* This file is part of LyX, the document processor. * This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING. * Licence details can be found in the file COPYING.
* *
* \author Asger Alstrup * \author Asger Alstrup
*
* Interface cleaned up by
* \author Angus Leeming * \author Angus Leeming
* \author Alfredo Braunstein
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*
* An instance of Class Forkedcall represents a single child process.
*
* Class Forkedcall uses fork() and execvp() to lauch the child process.
*
* Once launched, control is returned immediately to the parent process
* but a Signal can be emitted upon completion of the child.
*
* The child process is not killed when the Forkedcall instance goes out of
* scope, but it can be killed by an explicit invocation of the kill() member
* function.
*/ */
#include <config.h> #include <config.h>
#include "support/Forkedcall.h" #include "support/ForkedCalls.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/filetools.h" #include "support/filetools.h"
#include "support/ForkedcallsController.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/lyxlib.h" #include "support/lyxlib.h"
#include "support/os.h" #include "support/os.h"
@ -38,13 +25,13 @@
#include <vector> #include <vector>
#include <cerrno> #include <cerrno>
#include <sstream>
#ifdef _WIN32 #ifdef _WIN32
# define SIGHUP 1 # define SIGHUP 1
# define SIGKILL 9 # define SIGKILL 9
# include <process.h>
# include <windows.h> # include <windows.h>
# include <process.h>
#else #else
# include <csignal> # include <csignal>
# include <cstdlib> # include <cstdlib>
@ -52,22 +39,32 @@
# include <unistd.h> # include <unistd.h>
# endif # endif
# include <sys/wait.h> # include <sys/wait.h>
# ifndef CXX_GLOBAL_CSTD
using std::signal;
using std::strerror;
# endif
#endif #endif
using boost::bind;
using std::endl; using std::endl;
using std::equal_to;
using std::find_if;
using std::string; using std::string;
using std::vector; using std::vector;
#ifndef CXX_GLOBAL_CSTD
using std::strerror;
#endif
namespace lyx { namespace lyx {
namespace support { namespace support {
namespace { namespace {
/////////////////////////////////////////////////////////////////////
//
// Murder
//
/////////////////////////////////////////////////////////////////////
class Murder : public boost::signals::trackable { class Murder : public boost::signals::trackable {
public: public:
// //
@ -83,9 +80,8 @@ public:
// //
void kill() void kill()
{ {
if (pid_ != 0) { if (pid_ != 0)
support::kill(pid_, SIGKILL); support::kill(pid_, SIGKILL);
}
lyxerr << "Killed " << pid_ << std::endl; lyxerr << "Killed " << pid_ << std::endl;
delete this; delete this;
} }
@ -114,6 +110,12 @@ private:
} // namespace anon } // namespace anon
/////////////////////////////////////////////////////////////////////
//
// ForkedProcess
//
/////////////////////////////////////////////////////////////////////
ForkedProcess::ForkedProcess() ForkedProcess::ForkedProcess()
: pid_(0), retval_(0) : pid_(0), retval_(0)
{} {}
@ -143,7 +145,7 @@ int ForkedProcess::run(Starttype type)
break; break;
case DontWait: { case DontWait: {
// Integrate into the Controller // Integrate into the Controller
ForkedcallsController & contr = ForkedcallsController::get(); ForkedCallsController & contr = ForkedCallsController::get();
contr.addCall(*this); contr.addCall(*this);
break; break;
} }
@ -261,10 +263,17 @@ int ForkedProcess::waitForChild()
} }
int Forkedcall::startscript(Starttype wait, string const & what) /////////////////////////////////////////////////////////////////////
//
// ForkedCall
//
/////////////////////////////////////////////////////////////////////
int ForkedCall::startScript(Starttype wait, string const & what)
{ {
if (wait != Wait) { if (wait != Wait) {
retval_ = startscript(what, SignalTypePtr()); retval_ = startScript(what, SignalTypePtr());
return retval_; return retval_;
} }
@ -274,7 +283,7 @@ int Forkedcall::startscript(Starttype wait, string const & what)
} }
int Forkedcall::startscript(string const & what, SignalTypePtr signal) int ForkedCall::startScript(string const & what, SignalTypePtr signal)
{ {
command_ = what; command_ = what;
signal_ = signal; signal_ = signal;
@ -284,7 +293,7 @@ int Forkedcall::startscript(string const & what, SignalTypePtr signal)
// generate child in background // generate child in background
int Forkedcall::generateChild() int ForkedCall::generateChild()
{ {
string line = trim(command_); string line = trim(command_);
if (line.empty()) if (line.empty())
@ -383,5 +392,258 @@ int Forkedcall::generateChild()
return cpid; return cpid;
} }
/////////////////////////////////////////////////////////////////////
//
// ForkedCallQueue
//
/////////////////////////////////////////////////////////////////////
ForkedCallQueue & ForkedCallQueue::get()
{
static ForkedCallQueue singleton;
return singleton;
}
ForkedCall::SignalTypePtr ForkedCallQueue::add(string const & process)
{
ForkedCall::SignalTypePtr ptr;
ptr.reset(new ForkedCall::SignalType);
callQueue_.push(Process(process, ptr));
if (!running_)
startCaller();
return ptr;
}
void ForkedCallQueue::callNext()
{
if (callQueue_.empty())
return;
Process pro = callQueue_.front();
callQueue_.pop();
// Bind our chain caller
pro.second->connect(boost::bind(&ForkedCallQueue::callback,
this, _1, _2));
ForkedCall call;
// If we fail to fork the process, then emit the signal
// to tell the outside world that it failed.
if (call.startScript(pro.first, pro.second) > 0)
pro.second->operator()(0,1);
}
void ForkedCallQueue::callback(pid_t, int)
{
if (callQueue_.empty())
stopCaller();
else
callNext();
}
ForkedCallQueue::ForkedCallQueue()
: running_(false)
{}
void ForkedCallQueue::startCaller()
{
LYXERR(Debug::GRAPHICS, "ForkedCallQueue: waking up");
running_ = true ;
callNext();
}
void ForkedCallQueue::stopCaller()
{
running_ = false ;
LYXERR(Debug::GRAPHICS, "ForkedCallQueue: I'm going to sleep");
}
bool ForkedCallQueue::running() const
{
return running_ ;
}
/////////////////////////////////////////////////////////////////////
//
// ForkedCallsController
//
/////////////////////////////////////////////////////////////////////
#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 ss.str();
}
#endif
// Ensure, that only one controller exists inside process
ForkedCallsController & ForkedCallsController::get()
{
static ForkedCallsController singleton;
return singleton;
}
ForkedCallsController::ForkedCallsController()
{}
// open question: should we stop childs here?
// Asger says no: I like to have my xdvi open after closing LyX. Maybe
// I want to print or something.
ForkedCallsController::~ForkedCallsController()
{}
void ForkedCallsController::addCall(ForkedProcess const & newcall)
{
forkedCalls.push_back(newcall.clone());
}
// Check the list of dead children and emit any associated signals.
void ForkedCallsController::handleCompletedProcesses()
{
ListType::iterator it = forkedCalls.begin();
ListType::iterator end = forkedCalls.end();
while (it != end) {
ForkedProcessPtr 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);
if (waitrpid == -1) {
lyxerr << "LyX: Error waiting for child: "
<< strerror(errno) << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (waitrpid == 0) {
// Still running. Move on to the next child.
} else if (WIFEXITED(stat_loc)) {
// Ok, the return value goes into retval.
actCall->setRetValue(WEXITSTATUS(stat_loc));
remove_it = true;
} else if (WIFSIGNALED(stat_loc)) {
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (WIFSTOPPED(stat_loc)) {
lyxerr << "LyX: Child (pid: " << pid
<< ") stopped on signal "
<< WSTOPSIG(stat_loc)
<< ". Waiting for child to finish." << endl;
} else {
lyxerr << "LyX: Something rotten happened while "
"waiting for child " << pid << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
}
#endif
if (remove_it) {
forkedCalls.erase(it);
actCall->emitSignal();
/* start all over: emiting the signal can result
* in changing the list (Ab)
*/
it = forkedCalls.begin();
} else {
++it;
}
}
}
ForkedCallsController::iterator ForkedCallsController::find_pid(pid_t pid)
{
return find_if(forkedCalls.begin(), forkedCalls.end(),
bind(equal_to<pid_t>(),
bind(&ForkedCall::pid, _1),
pid));
}
// Kill the process prematurely and remove it from the list
// within tolerance secs
void ForkedCallsController::kill(pid_t pid, int tolerance)
{
ListType::iterator it = find_pid(pid);
if (it == forkedCalls.end())
return;
(*it)->kill(tolerance);
forkedCalls.erase(it);
}
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx

View File

@ -1,30 +1,18 @@
// -*- C++ -*- // -*- C++ -*-
/** /**
* \file Forkedcall.h * \file ForkedCalls.h
* This file is part of LyX, the document processor. * This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING. * Licence details can be found in the file COPYING.
* *
* \author Asger Alstrup * \author Asger Alstrup
*
* Interface cleaned up by
* \author Angus Leeming * \author Angus Leeming
* \author Alfredo Braunstein
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*
* An instance of Class Forkedcall represents a single child process.
*
* Class Forkedcall uses fork() and execvp() to lauch the child process.
*
* Once launched, control is returned immediately to the parent process
* but a Signal can be emitted upon completion of the child.
*
* The child process is not killed when the Forkedcall instance goes out of
* scope, but it can be killed by an explicit invocation of the kill() member
* function.
*/ */
#ifndef FORKEDCALL_H #ifndef FORKEDCALLS_H
#define FORKEDCALL_H #define FORKEDCALLS_H
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <boost/signal.hpp> #include <boost/signal.hpp>
@ -33,6 +21,12 @@
# include <sys/types.h> # include <sys/types.h>
#endif #endif
#include <list>
#include <queue>
#include <string>
#include <utility>
#include <vector>
namespace lyx { namespace lyx {
namespace support { namespace support {
@ -134,18 +128,31 @@ private:
}; };
class Forkedcall : public ForkedProcess { /*
* An instance of class ForkedCall represents a single child process.
*
* Class ForkedCall uses fork() and execvp() to lauch the child process.
*
* Once launched, control is returned immediately to the parent process
* but a Signal can be emitted upon completion of the child.
*
* The child process is not killed when the ForkedCall instance goes out of
* scope, but it can be killed by an explicit invocation of the kill() member
* function.
*/
class ForkedCall : public ForkedProcess {
public: public:
/// ///
virtual boost::shared_ptr<ForkedProcess> clone() const { virtual boost::shared_ptr<ForkedProcess> clone() const {
return boost::shared_ptr<ForkedProcess>(new Forkedcall(*this)); return boost::shared_ptr<ForkedProcess>(new ForkedCall(*this));
} }
/** Start the child process. /** Start the child process.
* *
* The command "what" is passed to execvp() for execution. * The command "what" is passed to execvp() for execution.
* *
* There are two startscript commands available. They differ in that * There are two startScript commands available. They differ in that
* the second receives a signal that is executed on completion of * the second receives a signal that is executed on completion of
* the command. This makes sense only for a command executed * the command. This makes sense only for a command executed
* in the background, ie DontWait. * in the background, ie DontWait.
@ -153,17 +160,107 @@ public:
* The other startscript command can be executed either blocking * The other startscript command can be executed either blocking
* or non-blocking, but no signal will be emitted on finishing. * or non-blocking, but no signal will be emitted on finishing.
*/ */
int startscript(Starttype, std::string const & what); int startScript(Starttype, std::string const & what);
/// ///
int startscript(std::string const & what, SignalTypePtr); int startScript(std::string const & what, SignalTypePtr);
private: private:
/// ///
virtual int generateChild(); virtual int generateChild();
}; };
/**
* This class implements a queue of forked processes. In order not to
* hose the system with multiple processes running simultaneously, you can
* request the addition of your process to this queue and it will be
* executed when its turn comes.
*
*/
class ForkedCallQueue {
public:
/// A process in the queue
typedef std::pair<std::string, ForkedCall::SignalTypePtr> Process;
/** Add a process to the queue. Processes are forked sequentially
* only one is running at a time.
* Connect to the returned signal and you'll be informed when
* the process has ended.
*/
ForkedCall::SignalTypePtr add(std::string const & process);
/// Query whether the queue is running a forked process now.
bool running() const;
/// Get the and only instance of the class
static ForkedCallQueue & get();
private:
/** this class is a singleton class... use
* ForkedCallQueue::get() instead
*/
ForkedCallQueue();
/// in-progress queue
std::queue<Process> callQueue_;
///
bool running_;
///
void callNext();
///
void startCaller();
///
void stopCaller();
///
void callback(pid_t, int);
};
/**
* A class for the control of child processes launched using
* fork() and execvp().
*/
class ForkedCallsController {
public:
/// Get hold of the only controller that can exist inside the process.
static ForkedCallsController & get();
/// Add a new child process to the list of controlled processes.
void addCall(ForkedProcess const &);
/** Those child processes that are found to have finished are removed
* from the list and their callback function is passed the final
* return state.
*/
void handleCompletedProcesses();
/** 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);
private:
ForkedCallsController();
ForkedCallsController(ForkedCallsController const &);
~ForkedCallsController();
typedef boost::shared_ptr<ForkedProcess> ForkedProcessPtr;
typedef std::list<ForkedProcessPtr> ListType;
typedef ListType::iterator iterator;
iterator find_pid(pid_t);
/// The child processes
ListType forkedCalls;
};
#if defined(_WIN32)
// a wrapper for GetLastError() and FormatMessage().
std::string const getChildErrorMessage();
#endif
} // namespace support } // namespace support
} // namespace lyx } // namespace lyx
#endif // FORKEDCALL_H #endif // FORKEDCALLS_H

View File

@ -1,226 +0,0 @@
/**
* \file ForkedcallsController.cpp
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Asger Alstrup Nielsen
* \author Angus Leeming
*
* Full author contact details are available in file CREDITS.
*
* A class for the control of child processes launched using
* fork() and execvp().
*/
#include <config.h>
#include "support/ForkedcallsController.h"
#include "support/debug.h"
#include "support/Forkedcall.h"
#ifdef _WIN32
# include <sstream>
# include <windows.h>
#else
# include <cerrno>
# include <csignal>
# include <cstdlib>
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# include <sys/wait.h>
# ifndef CXX_GLOBAL_CSTD
using std::signal;
using std::strerror;
# endif
#endif
#include <boost/bind.hpp>
using boost::bind;
using std::endl;
using std::equal_to;
using std::find_if;
using std::string;
using std::vector;
namespace lyx {
namespace support {
#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 ss.str().c_str();
}
#endif
// Ensure, that only one controller exists inside process
ForkedcallsController & ForkedcallsController::get()
{
static ForkedcallsController singleton;
return singleton;
}
ForkedcallsController::ForkedcallsController()
{}
// open question: should we stop childs here?
// Asger says no: I like to have my xdvi open after closing LyX. Maybe
// I want to print or something.
ForkedcallsController::~ForkedcallsController()
{}
void ForkedcallsController::addCall(ForkedProcess const & newcall)
{
forkedCalls.push_back(newcall.clone());
}
// Check the list of dead children and emit any associated signals.
void ForkedcallsController::handleCompletedProcesses()
{
ListType::iterator it = forkedCalls.begin();
ListType::iterator end = forkedCalls.end();
while (it != end) {
ForkedProcessPtr 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);
if (waitrpid == -1) {
lyxerr << "LyX: Error waiting for child: "
<< strerror(errno) << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (waitrpid == 0) {
// Still running. Move on to the next child.
} else if (WIFEXITED(stat_loc)) {
// Ok, the return value goes into retval.
actCall->setRetValue(WEXITSTATUS(stat_loc));
remove_it = true;
} else if (WIFSIGNALED(stat_loc)) {
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
} else if (WIFSTOPPED(stat_loc)) {
lyxerr << "LyX: Child (pid: " << pid
<< ") stopped on signal "
<< WSTOPSIG(stat_loc)
<< ". Waiting for child to finish." << endl;
} else {
lyxerr << "LyX: Something rotten happened while "
"waiting for child " << pid << endl;
// Child died, so pretend it returned 1
actCall->setRetValue(1);
remove_it = true;
}
#endif
if (remove_it) {
forkedCalls.erase(it);
actCall->emitSignal();
/* start all over: emiting the signal can result
* in changing the list (Ab)
*/
it = forkedCalls.begin();
} else {
++it;
}
}
}
ForkedcallsController::iterator
ForkedcallsController::find_pid(pid_t pid)
{
return find_if(forkedCalls.begin(), forkedCalls.end(),
bind(equal_to<pid_t>(),
bind(&Forkedcall::pid, _1),
pid));
}
// Kill the process prematurely and remove it from the list
// within tolerance secs
void ForkedcallsController::kill(pid_t pid, int tolerance)
{
ListType::iterator it = find_pid(pid);
if (it == forkedCalls.end())
return;
(*it)->kill(tolerance);
forkedCalls.erase(it);
}
} // namespace support
} // namespace lyx

View File

@ -1,79 +0,0 @@
// -*- C++ -*-
/**
* \file ForkedcallsController.h
* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
* \author Asger Alstrup Nielsen
* \author Angus Leeming
*
* Full author contact details are available in file CREDITS.
*
* A class for the control of child processes launched using
* fork() and execvp().
*/
#ifndef FORKEDCALLSCONTROLLER_H
#define FORKEDCALLSCONTROLLER_H
#include <boost/shared_ptr.hpp>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <list>
#include <string>
#include <vector>
namespace lyx {
namespace support {
class ForkedProcess;
class ForkedcallsController {
public:
/// Get hold of the only controller that can exist inside the process.
static ForkedcallsController & get();
/// Add a new child process to the list of controlled processes.
void addCall(ForkedProcess const &);
/** Those child processes that are found to have finished are removed
* from the list and their callback function is passed the final
* return state.
*/
void handleCompletedProcesses();
/** 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);
private:
ForkedcallsController();
ForkedcallsController(ForkedcallsController const &);
~ForkedcallsController();
typedef boost::shared_ptr<ForkedProcess> ForkedProcessPtr;
typedef std::list<ForkedProcessPtr> ListType;
typedef ListType::iterator iterator;
iterator find_pid(pid_t);
/// The child processes
ListType forkedCalls;
};
#if defined(_WIN32)
// a wrapper for GetLastError() and FormatMessage().
std::string const getChildErrorMessage();
#endif
} // namespace support
} // namespace lyx
#endif // FORKEDCALLSCONTROLLER_H

View File

@ -44,12 +44,8 @@ liblyxsupport_la_SOURCES = \
FileName.h \ FileName.h \
filetools.cpp \ filetools.cpp \
filetools.h \ filetools.h \
Forkedcall.cpp \ ForkedCalls.cpp \
Forkedcall.h \ ForkedCalls.h \
ForkedCallQueue.cpp \
ForkedCallQueue.h \
ForkedcallsController.cpp \
ForkedcallsController.h \
gettext.cpp \ gettext.cpp \
gettext.h \ gettext.h \
getcwd.cpp \ getcwd.cpp \