]> git.lyx.org Git - lyx.git/blobdiff - src/support/forkedcontr.C
* src/text2.C: deleteEmptyParagraphMechanism(): fix a crash in
[lyx.git] / src / support / forkedcontr.C
index 741d0ed52df2d0ae64be30f9e81fc320582865f8..708509aa6c27fd63198de8ef1ed0fa98bb1d2b4c 100644 (file)
 
 #include <config.h>
 
-#include "forkedcontr.h"
-#include "forkedcall.h"
-#include "lyxfunctional.h"
+#include "support/forkedcontr.h"
+#include "support/forkedcall.h"
+
 #include "debug.h"
 
-#include "frontends/Timeout.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>
 
-#include <cerrno>
-#include <cstdlib>
-#include <unistd.h>
-#include <sys/wait.h>
+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 {
 
+#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()
 {
@@ -50,53 +89,64 @@ ForkedcallsController & ForkedcallsController::get()
 
 
 ForkedcallsController::ForkedcallsController()
-{
-       timeout_ = new Timeout(100, Timeout::ONETIME);
-
-       timeout_->timeout
-               .connect(boost::bind(&ForkedcallsController::timer, this));
-}
+{}
 
 
 // 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()
-{
-       for (ListType::iterator it = forkedCalls.begin();
-            it != forkedCalls.end(); ++it) {
-               delete *it;
-       }
-
-       delete timeout_;
-}
+{}
 
 
 void ForkedcallsController::addCall(ForkedProcess const & newcall)
 {
-       if (!timeout_->running())
-               timeout_->start();
-
        forkedCalls.push_back(newcall.clone());
-       childrenChanged();
 }
 
 
-// Timer-call
-// Check the list and, if there is a stopped child, emit the signal.
-void ForkedcallsController::timer()
+// Check the list of dead children and emit any associated signals.
+void ForkedcallsController::handleCompletedProcesses()
 {
-       ListType::size_type start_size = forkedCalls.size();
-
        ListType::iterator it  = forkedCalls.begin();
        ListType::iterator end = forkedCalls.end();
        while (it != end) {
-               ForkedProcess * actCall = *it;
+               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);
-               bool remove_it = false;
 
                if (waitrpid == -1) {
                        lyxerr << "LyX: Error waiting for child: "
@@ -133,12 +183,11 @@ void ForkedcallsController::timer()
                        actCall->setRetValue(1);
                        remove_it = true;
                }
+#endif
 
                if (remove_it) {
                        forkedCalls.erase(it);
-
                        actCall->emitSignal();
-                       delete actCall;
 
                        /* start all over: emiting the signal can result
                         * in changing the list (Ab)
@@ -148,48 +197,16 @@ void ForkedcallsController::timer()
                        ++it;
                }
        }
-
-       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
+ForkedcallsController::iterator
+ForkedcallsController::find_pid(pid_t pid)
 {
-       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();
+       return find_if(forkedCalls.begin(), forkedCalls.end(),
+                      bind(equal_to<pid_t>(),
+                           bind(&Forkedcall::pid, _1),
+                           pid));
 }
 
 
@@ -197,19 +214,12 @@ string const ForkedcallsController::getCommand(pid_t pid) const
 // within tolerance secs
 void ForkedcallsController::kill(pid_t pid, int tolerance)
 {
-       ListType::iterator it =
-               find_if(forkedCalls.begin(), forkedCalls.end(),
-                       lyx::compare_memfun(&Forkedcall::pid, pid));
-
+       ListType::iterator it = find_pid(pid);
        if (it == forkedCalls.end())
                return;
 
        (*it)->kill(tolerance);
        forkedCalls.erase(it);
-
-       if (forkedCalls.empty()) {
-               timeout_->stop();
-       }
 }
 
 } // namespace support