]> git.lyx.org Git - features.git/commitdiff
Systemcall infrastructure for canceling background processes.
authorRichard Heck <rgheck@lyx.org>
Thu, 21 Dec 2017 23:45:52 +0000 (18:45 -0500)
committerRichard Heck <rgheck@lyx.org>
Thu, 21 Dec 2017 23:55:39 +0000 (18:55 -0500)
src/support/Systemcall.cpp
src/support/Systemcall.h
src/support/SystemcallPrivate.h

index 2dbbba7c83010bdd6abcfdee2dc3accff12c33bc..93166b5c461f25b5efa63ebb2c2e336c4b0f74d3 100644 (file)
@@ -23,6 +23,8 @@
 #include "support/os.h"
 #include "support/ProgressInterface.h"
 
+#include "frontends/Application.h"
+
 #include "LyX.h"
 #include "LyXRC.h"
 
@@ -251,6 +253,7 @@ int Systemcall::startscript(Starttype how, string const & what,
                        parsecmd(what_ss, infile, outfile, errfile).c_str());
 
        SystemcallPrivate d(infile, outfile, errfile);
+       bool do_events = process_events || how == WaitLoop;
 
 #ifdef Q_OS_WIN32
        // QProcess::startDetached cannot provide environment variables. When the
@@ -259,34 +262,48 @@ int Systemcall::startscript(Starttype how, string const & what,
        // time a viewer is started. To avoid this, we fall back on Windows to the
        // original implementation that creates a QProcess object.
        d.startProcess(cmd, path, lpath, false);
-       if (!d.waitWhile(SystemcallPrivate::Starting, process_events, -1)) {
-               LYXERR0("Systemcall: '" << cmd << "' did not start!");
-               LYXERR0("error " << d.errorMessage());
-               return 10;
+       if (!d.waitWhile(SystemcallPrivate::Starting, do_events, -1)) {
+               if (d.state == SystemcallPrivate::Error) {
+                       LYXERR0("Systemcall: '" << cmd << "' did not start!");
+                       LYXERR0("error " << d.errorMessage());
+                       return NOSTART;
+               } else if (d.state == SystemcallPrivate::Killed) {
+                       LYXERR0("Killed: " << cmd);
+                       return KILLED;
+               }
        }
        if (how == DontWait) {
                d.releaseProcess();
-               return 0;
+               return OK;
        }
 #else
        d.startProcess(cmd, path, lpath, how == DontWait);
        if (how == DontWait && d.state == SystemcallPrivate::Running)
-               return 0;
+               return OK;
 
        if (d.state == SystemcallPrivate::Error
-                       || !d.waitWhile(SystemcallPrivate::Starting, process_events, -1)) {
-               LYXERR0("Systemcall: '" << cmd << "' did not start!");
-               LYXERR0("error " << d.errorMessage());
-               return 10;
+                       || !d.waitWhile(SystemcallPrivate::Starting, do_events, -1)) {
+               if (d.state == SystemcallPrivate::Error) {
+                       LYXERR0("Systemcall: '" << cmd << "' did not start!");
+                       LYXERR0("error " << d.errorMessage());
+                       return NOSTART;
+               } else if (d.state == SystemcallPrivate::Killed) {
+                       LYXERR0("Killed: " << cmd);
+                       return KILLED;
+               }
        }
 #endif
 
-       if (!d.waitWhile(SystemcallPrivate::Running, process_events,
+       if (!d.waitWhile(SystemcallPrivate::Running, do_events,
                         os::timeout_min() * 60 * 1000)) {
+               if (d.state == SystemcallPrivate::Killed) {
+                       LYXERR0("Killed: " << cmd);
+                       return KILLED;
+               }
                LYXERR0("Systemcall: '" << cmd << "' did not finish!");
                LYXERR0("error " << d.errorMessage());
                LYXERR0("status " << d.exitStatusMessage());
-               return 20;
+               return TIMEOUT;
        }
 
        int const exit_code = d.exitCode();
@@ -383,18 +400,19 @@ void SystemcallPrivate::startProcess(QString const & cmd, string const & path,
 }
 
 
-void SystemcallPrivate::processEvents()
-{
-       if (process_events_) {
-               QCoreApplication::processEvents(/*QEventLoop::ExcludeUserInputEvents*/);
-       }
-}
-
-
-void SystemcallPrivate::waitAndProcessEvents()
+bool SystemcallPrivate::waitAndCheck()
 {
        Sleep::millisec(100);
-       processEvents();
+       if (theApp()->cancel_export) {
+               // is there a better place to reset this?
+               process_->kill();
+               state = Killed;
+               theApp()->cancel_export = false;
+               LYXERR0("Export Canceled!!");
+               return false;
+       }
+       QCoreApplication::processEvents(/*QEventLoop::ExcludeUserInputEvents*/);
+       return true;
 }
 
 
@@ -449,7 +467,9 @@ bool SystemcallPrivate::waitWhile(State waitwhile, bool process_events, int time
        // process events while waiting, no timeout
        if (timeout == -1) {
                while (state == waitwhile && state != Error) {
-                       waitAndProcessEvents();
+                       // check for cancellation of background process
+                       if (!waitAndCheck())
+                               return false;
                }
                return state != Error;
        }
@@ -458,7 +478,10 @@ bool SystemcallPrivate::waitWhile(State waitwhile, bool process_events, int time
        QTime timer;
        timer.start();
        while (state == waitwhile && state != Error && !timedout) {
-               waitAndProcessEvents();
+               // check for cancellation of background process
+               if (!waitAndCheck())
+                       return false;
+
                if (timer.elapsed() > timeout) {
                        bool stop = queryStopCommand(cmd_);
                        // The command may have finished in the meantime
index 876aa357b8b99aa79d5e0fc07d2897836dbd32ed..32f3a61dd1fe41aec202e5d1a0a36d2281656c44 100644 (file)
@@ -34,8 +34,18 @@ class Systemcall {
 public:
        /// whether to wait for completion
        enum Starttype {
-               Wait, //< wait for completion before returning from startscript()
-               DontWait //< don't wait for completion
+               Wait,     //< wait for completion before returning from startscript()
+               WaitLoop, //< wait, but check occasionally for cancellation
+               DontWait  //< don't wait for completion
+       };
+       // enum values chosen hopefully not to conflict with ordinary return values
+       enum ReturnValue {
+               OK = 0,
+               NOSTART = 1001,
+               ERROR = 2001,
+               TIMEOUT = 4001,
+               KILLED = 9001
        };
 
        /** Start child process.
index 329274008b8f844b67e3d4e80b4facc320bcf4e4..3f00f0c3ba79fbdaf5b1094df1ee3f1e9654618f 100644 (file)
@@ -40,7 +40,8 @@ public:
                Starting,
                Running,
                Finished,
-               Error
+               Error,
+               Killed
        };
        State state;
 
@@ -95,6 +96,9 @@ private:
        void processEvents();
        void killProcess();
 
+       /// returns false if we killed the process
+       /// actually returns Systemcall::ReturnValue
+       bool waitAndCheck();
 };
 
 } // namespace support