diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 94a279bffc..a435455dd6 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -81,7 +81,7 @@ #include "support/debug.h" #include "support/FileFilterList.h" #include "support/filetools.h" -#include "support/Forkedcall.h" +#include "support/ForkedCalls.h" #include "support/gettext.h" #include "support/gzstream.h" #include "support/lstrings.h" diff --git a/src/ISpell.cpp b/src/ISpell.cpp index 947a6f4d61..daeaabab46 100644 --- a/src/ISpell.cpp +++ b/src/ISpell.cpp @@ -15,14 +15,14 @@ #include "ISpell.h" #include "BufferParams.h" -#include "support/debug.h" #include "Encoding.h" -#include "support/gettext.h" #include "Language.h" #include "LyXRC.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/unicode.h" diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index cdc639515a..428336cb7a 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -18,10 +18,8 @@ #include "BufferView.h" #include "CoordCache.h" #include "Cursor.h" -#include "support/debug.h" #include "Font.h" #include "FuncRequest.h" -#include "support/gettext.h" #include "GuiApplication.h" #include "GuiKeySymbol.h" #include "GuiPainter.h" @@ -37,8 +35,10 @@ #include "graphics/GraphicsImage.h" #include "graphics/GraphicsLoader.h" +#include "support/debug.h" +#include "support/gettext.h" #include "support/FileName.h" -#include "support/ForkedcallsController.h" +#include "support/ForkedCalls.h" #include "frontends/Application.h" #include "frontends/FontMetrics.h" @@ -79,7 +79,7 @@ using std::string; namespace lyx { using support::FileName; -using support::ForkedcallsController; +using support::ForkedCallsController; /// 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 // have finished but are waiting to communicate this fact // to the rest of LyX. - ForkedcallsController & fcc = ForkedcallsController::get(); + ForkedCallsController & fcc = ForkedCallsController::get(); fcc.handleCompletedProcesses(); } diff --git a/src/graphics/GraphicsConverter.cpp b/src/graphics/GraphicsConverter.cpp index 1dd8be961e..81ac3c64d3 100644 --- a/src/graphics/GraphicsConverter.cpp +++ b/src/graphics/GraphicsConverter.cpp @@ -13,12 +13,12 @@ #include "GraphicsConverter.h" #include "Converter.h" -#include "support/debug.h" #include "Format.h" -#include "support/filetools.h" -#include "support/ForkedCallQueue.h" #include "support/convert.h" +#include "support/debug.h" +#include "support/filetools.h" +#include "support/ForkedCalls.h" #include "support/lstrings.h" #include "support/lyxlib.h" #include "support/os.h" @@ -33,7 +33,7 @@ namespace support = lyx::support; using support::addExtension; using support::changeExtension; using support::FileName; -using support::Forkedcall; +using support::ForkedCall; using support::ForkedCallQueue; using support::getExtension; using support::libScriptSearch; @@ -193,7 +193,7 @@ void Converter::Impl::startConversion() return; } - Forkedcall::SignalTypePtr + ForkedCall::SignalTypePtr ptr = ForkedCallQueue::get().add(script_command_); ptr->connect(boost::bind(&Impl::converted, this, _1, _2)); diff --git a/src/graphics/PreviewLoader.cpp b/src/graphics/PreviewLoader.cpp index 1d9c5628f4..e5c200d6b6 100644 --- a/src/graphics/PreviewLoader.cpp +++ b/src/graphics/PreviewLoader.cpp @@ -17,7 +17,6 @@ #include "Buffer.h" #include "BufferParams.h" #include "Converter.h" -#include "support/debug.h" #include "Encoding.h" #include "Format.h" #include "InsetIterator.h" @@ -31,12 +30,12 @@ #include "insets/Inset.h" +#include "support/convert.h" +#include "support/debug.h" #include "support/filetools.h" -#include "support/Forkedcall.h" -#include "support/ForkedcallsController.h" +#include "support/ForkedCalls.h" #include "support/lstrings.h" #include "support/lyxlib.h" -#include "support/convert.h" #include @@ -229,7 +228,7 @@ public: Buffer const & buffer() const { return buffer_; } 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 dumpPreamble(odocstream &) const; @@ -384,7 +383,7 @@ InProgress::InProgress(string const & filename_base, void InProgress::stop() const { if (pid) - lyx::support::ForkedcallsController::get().kill(pid, 0); + lyx::support::ForkedCallsController::get().kill(pid, 0); if (!metrics_file.empty()) metrics_file.removeFile(); @@ -608,12 +607,12 @@ void PreviewLoader::Impl::startLoading() string const command = support::libScriptSearch(cs.str()); // Initiate the conversion from LaTeX to bitmap images files. - support::Forkedcall::SignalTypePtr - convert_ptr(new support::Forkedcall::SignalType); + support::ForkedCall::SignalTypePtr + convert_ptr(new support::ForkedCall::SignalType); convert_ptr->connect(bind(&Impl::finishedGenerating, this, _1, _2)); - support::Forkedcall call; - int ret = call.startscript(command, convert_ptr); + support::ForkedCall call; + int ret = call.startScript(command, convert_ptr); if (ret != 0) { LYXERR(Debug::GRAPHICS, "PreviewLoader::startLoading()\n" diff --git a/src/insets/ExternalSupport.cpp b/src/insets/ExternalSupport.cpp index 19819f9328..2bb9665951 100644 --- a/src/insets/ExternalSupport.cpp +++ b/src/insets/ExternalSupport.cpp @@ -18,8 +18,6 @@ #include "Buffer.h" #include "Converter.h" -#include "support/debug.h" -#include "support/gettext.h" #include "ErrorList.h" #include "Exporter.h" #include "Format.h" @@ -27,8 +25,9 @@ #include "frontends/alert.h" +#include "support/debug.h" #include "support/filetools.h" -#include "support/Forkedcall.h" +#include "support/gettext.h" #include "support/lstrings.h" #include "support/lyxalgo.h" #include "support/lyxlib.h" diff --git a/src/support/ForkedCallQueue.cpp b/src/support/ForkedCallQueue.cpp deleted file mode 100644 index 003f0ef304..0000000000 --- a/src/support/ForkedCallQueue.cpp +++ /dev/null @@ -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 - -#include "support/ForkedCallQueue.h" - -#include "support/debug.h" - -#include - - -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 diff --git a/src/support/ForkedCallQueue.h b/src/support/ForkedCallQueue.h deleted file mode 100644 index dae48dcbd6..0000000000 --- a/src/support/ForkedCallQueue.h +++ /dev/null @@ -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 -#include - - -namespace lyx { -namespace support { - -class ForkedCallQueue { -public: - /// A process in the queue - typedef std::pair 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 callQueue_; - /// - bool running_; - /// - void callNext(); - /// - void startCaller(); - /// - void stopCaller(); - /// - void callback(pid_t, int); -}; - -} // namespace support -} // namespace lyx - -#endif // FORKEDCALLQUEUE_H diff --git a/src/support/Forkedcall.cpp b/src/support/ForkedCalls.cpp similarity index 53% rename from src/support/Forkedcall.cpp rename to src/support/ForkedCalls.cpp index 50b89b98ea..90ce2993a8 100644 --- a/src/support/Forkedcall.cpp +++ b/src/support/ForkedCalls.cpp @@ -1,34 +1,21 @@ /** - * \file Forkedcall.cpp + * \file ForkedCalls.cpp * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Asger Alstrup - * - * Interface cleaned up by * \author Angus Leeming + * \author Alfredo Braunstein * * 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 -#include "support/Forkedcall.h" +#include "support/ForkedCalls.h" #include "support/debug.h" #include "support/filetools.h" -#include "support/ForkedcallsController.h" #include "support/lstrings.h" #include "support/lyxlib.h" #include "support/os.h" @@ -38,13 +25,13 @@ #include #include +#include #ifdef _WIN32 # define SIGHUP 1 # define SIGKILL 9 -# include # include - +# include #else # include # include @@ -52,22 +39,32 @@ # include # endif # include +# ifndef CXX_GLOBAL_CSTD + using std::signal; + using std::strerror; +# endif #endif +using boost::bind; + using std::endl; +using std::equal_to; +using std::find_if; using std::string; using std::vector; -#ifndef CXX_GLOBAL_CSTD -using std::strerror; -#endif namespace lyx { namespace support { - namespace { +///////////////////////////////////////////////////////////////////// +// +// Murder +// +///////////////////////////////////////////////////////////////////// + class Murder : public boost::signals::trackable { public: // @@ -83,9 +80,8 @@ public: // void kill() { - if (pid_ != 0) { + if (pid_ != 0) support::kill(pid_, SIGKILL); - } lyxerr << "Killed " << pid_ << std::endl; delete this; } @@ -114,6 +110,12 @@ private: } // namespace anon +///////////////////////////////////////////////////////////////////// +// +// ForkedProcess +// +///////////////////////////////////////////////////////////////////// + ForkedProcess::ForkedProcess() : pid_(0), retval_(0) {} @@ -143,7 +145,7 @@ int ForkedProcess::run(Starttype type) break; case DontWait: { // Integrate into the Controller - ForkedcallsController & contr = ForkedcallsController::get(); + ForkedCallsController & contr = ForkedCallsController::get(); contr.addCall(*this); 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) { - retval_ = startscript(what, SignalTypePtr()); + retval_ = startScript(what, SignalTypePtr()); 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; signal_ = signal; @@ -284,7 +293,7 @@ int Forkedcall::startscript(string const & what, SignalTypePtr signal) // generate child in background -int Forkedcall::generateChild() +int ForkedCall::generateChild() { string line = trim(command_); if (line.empty()) @@ -383,5 +392,258 @@ int Forkedcall::generateChild() 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(), + 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 diff --git a/src/support/Forkedcall.h b/src/support/ForkedCalls.h similarity index 54% rename from src/support/Forkedcall.h rename to src/support/ForkedCalls.h index 32233fafd2..7d26bf42f3 100644 --- a/src/support/Forkedcall.h +++ b/src/support/ForkedCalls.h @@ -1,30 +1,18 @@ // -*- C++ -*- /** - * \file Forkedcall.h + * \file ForkedCalls.h * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Asger Alstrup - * - * Interface cleaned up by * \author Angus Leeming + * \author Alfredo Braunstein * * 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 -#define FORKEDCALL_H +#ifndef FORKEDCALLS_H +#define FORKEDCALLS_H #include #include @@ -33,6 +21,12 @@ # include #endif +#include +#include +#include +#include +#include + namespace lyx { 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: /// virtual boost::shared_ptr clone() const { - return boost::shared_ptr(new Forkedcall(*this)); + return boost::shared_ptr(new ForkedCall(*this)); } /** Start the child process. * * 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 command. This makes sense only for a command executed * in the background, ie DontWait. @@ -153,17 +160,107 @@ public: * The other startscript command can be executed either blocking * 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: /// 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 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 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 ForkedProcessPtr; + typedef std::list 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 // FORKEDCALL_H +#endif // FORKEDCALLS_H diff --git a/src/support/ForkedcallsController.cpp b/src/support/ForkedcallsController.cpp deleted file mode 100644 index f825f91736..0000000000 --- a/src/support/ForkedcallsController.cpp +++ /dev/null @@ -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 - -#include "support/ForkedcallsController.h" - -#include "support/debug.h" -#include "support/Forkedcall.h" - -#ifdef _WIN32 -# include -# include - -#else -# include -# include -# include -# ifdef HAVE_UNISTD_H -# include -# endif -# include - -# ifndef CXX_GLOBAL_CSTD - using std::signal; - using std::strerror; -# endif -#endif - -#include - -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(), - 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 diff --git a/src/support/ForkedcallsController.h b/src/support/ForkedcallsController.h deleted file mode 100644 index 796ff2ec9d..0000000000 --- a/src/support/ForkedcallsController.h +++ /dev/null @@ -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 - -#ifdef HAVE_SYS_TYPES_H -# include -#endif - -#include -#include -#include - - -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 ForkedProcessPtr; - typedef std::list 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 diff --git a/src/support/Makefile.am b/src/support/Makefile.am index 0846d55a7c..c689469f3c 100644 --- a/src/support/Makefile.am +++ b/src/support/Makefile.am @@ -44,12 +44,8 @@ liblyxsupport_la_SOURCES = \ FileName.h \ filetools.cpp \ filetools.h \ - Forkedcall.cpp \ - Forkedcall.h \ - ForkedCallQueue.cpp \ - ForkedCallQueue.h \ - ForkedcallsController.cpp \ - ForkedcallsController.h \ + ForkedCalls.cpp \ + ForkedCalls.h \ gettext.cpp \ gettext.h \ getcwd.cpp \