1 module ddebug.common.nodebug; 2 3 import ddebug.common.execution; 4 5 import core.thread; 6 import std.process; 7 import dlangui.core.logger; 8 9 class ProgramExecutionNoDebug : Thread, ProgramExecution { 10 11 // parameters 12 protected string _executableFile; 13 protected string[] _args; 14 protected string _workDir; 15 protected string _externalConsole; 16 protected ProgramExecutionStatusListener _listener; 17 18 19 // status 20 protected Pid _pid; 21 protected ExecutionStatus _status = ExecutionStatus.NotStarted; 22 protected int _exitCode = 0; 23 24 25 26 /// initialize but do not run 27 this(string executable, string[] args, string workDir, string externalConsole, ProgramExecutionStatusListener listener) { 28 super(&threadFunc); 29 _executableFile = executable; 30 _args = args; 31 _workDir = workDir; 32 _externalConsole = externalConsole; 33 _listener = listener; 34 assert(_listener !is null); 35 } 36 37 ~this() { 38 stop(); 39 } 40 41 private bool isProcessActive() { 42 if (_pid is null) 43 return false; 44 auto res = tryWait(_pid); 45 if (res.terminated) { 46 Log.d("Process ", _executableFile, " is stopped"); 47 _exitCode = wait(_pid); 48 _pid = Pid.init; 49 return false; 50 } else { 51 return true; 52 } 53 } 54 55 private void killProcess() { 56 if (_pid is null) 57 return; 58 try { 59 Log.d("Trying to kill process", _executableFile); 60 kill(_pid, 9); 61 Log.d("Waiting for process termination"); 62 _exitCode = wait(_pid); 63 _pid = Pid.init; 64 Log.d("Killed"); 65 } catch (Exception e) { 66 Log.d("Exception while killing process " ~ _executableFile, e); 67 _pid = Pid.init; 68 } 69 } 70 71 private void threadFunc() { 72 import std.stdio; 73 import std.array: empty; 74 75 // prepare parameter list 76 string[] params; 77 params ~= _executableFile; 78 params ~= _args; 79 80 // external console support 81 if (!_externalConsole.empty) { 82 string cmdline = escapeShellCommand(params); 83 params.length = 0; 84 params ~= _externalConsole; 85 params ~= "-e"; 86 params ~= cmdline; 87 } 88 89 File newstdin; 90 File newstdout; 91 File newstderr; 92 version (Windows) { 93 } else { 94 newstdin = stdin; 95 newstdout = stdout; 96 newstderr = stderr; 97 } 98 try { 99 _pid = spawnProcess(params, newstdin, newstdout, newstderr, null, Config.none, _workDir); 100 } catch (Exception e) { 101 Log.e("ProgramExecutionNoDebug: Failed to spawn process: ", e); 102 killProcess(); 103 _status = ExecutionStatus.Error; 104 } 105 106 if (_status != ExecutionStatus.Error) { 107 // thread loop: poll process status 108 while (!_stopRequested) { 109 Thread.sleep(dur!"msecs"(50)); 110 if (!isProcessActive()) { 111 _status = ExecutionStatus.Finished; 112 break; 113 } 114 } 115 if (_stopRequested) { 116 killProcess(); 117 _status = ExecutionStatus.Killed; 118 } 119 } 120 121 // finished 122 Log.d("ProgramExecutionNoDebug: finished, execution status: ", _status); 123 _listener.onProgramExecutionStatus(this, _status, _exitCode); 124 } 125 126 // implement ProgramExecution interface 127 128 /// returns true if it's debugger 129 @property bool isDebugger() { return false; } 130 131 /// executable file 132 @property string executableFile() { return _executableFile; } 133 134 /// returns execution status 135 @property ExecutionStatus status() { return _status; } 136 137 /// start execution 138 bool run() { 139 if (_runRequested) 140 return false; // already running 141 _runRequested = true; 142 _threadStarted = true; 143 _status = ExecutionStatus.Running; 144 start(); 145 return true; 146 } 147 148 /// stop execution (call from GUI thread) 149 bool stop() { 150 if (!_runRequested) 151 return false; 152 if (_stopRequested) 153 return true; 154 _stopRequested = true; 155 if (_threadStarted && !_threadJoined) { 156 _threadJoined = true; 157 join(); 158 } 159 return true; 160 } 161 162 protected bool _threadStarted; 163 protected bool _threadJoined; 164 protected bool _stopRequested; 165 protected bool _runRequested; 166 }