dlangide/src/ddebug/common/nodebug.d

161 lines
4.2 KiB
D

module ddebug.common.nodebug;
import ddebug.common.execution;
import core.thread;
import std.process;
import dlangui.core.logger;
class ProgramExecutionNoDebug : Thread, ProgramExecution {
// parameters
/// provides _executableFile, _executableArgs, _executableWorkingDir, _executableEnvVars parameters and setter function setExecutableParams
mixin ExecutableParams;
/// provides _terminalExecutable and setTerminalExecutable setter
mixin TerminalParams;
protected ProgramExecutionStatusListener _listener;
void setProgramExecutionStatusListener(ProgramExecutionStatusListener listener) {
_listener = listener;
}
// status
protected Pid _pid;
protected ExecutionStatus _status = ExecutionStatus.NotStarted;
protected int _exitCode = 0;
/// initialize but do not run
this() {
super(&threadFunc);
}
~this() {
stop();
}
private bool isProcessActive() {
if (_pid is null)
return false;
auto res = tryWait(_pid);
if (res.terminated) {
Log.d("Process ", _executableFile, " is stopped");
_exitCode = wait(_pid);
_pid = Pid.init;
return false;
} else {
return true;
}
}
private void killProcess() {
if (_pid is null)
return;
try {
Log.d("Trying to kill process", _executableFile);
kill(_pid, 9);
Log.d("Waiting for process termination");
_exitCode = wait(_pid);
_pid = Pid.init;
Log.d("Killed");
} catch (Exception e) {
Log.d("Exception while killing process " ~ _executableFile, e);
_pid = Pid.init;
}
}
private void threadFunc() {
import std.stdio;
import std.array: empty;
// prepare parameter list
string[] params;
params ~= _executableFile;
params ~= _executableArgs;
// external console support
if (!_terminalExecutable.empty) {
string cmdline = escapeShellCommand(params);
params.length = 0;
params ~= _terminalExecutable;
params ~= "-e";
params ~= cmdline;
}
File newstdin;
File newstdout;
File newstderr;
version (Windows) {
} else {
newstdin = stdin;
newstdout = stdout;
newstderr = stderr;
}
try {
_pid = spawnProcess(params, newstdin, newstdout, newstderr, null, Config.none, _executableWorkingDir);
} catch (Exception e) {
Log.e("ProgramExecutionNoDebug: Failed to spawn process: ", e);
killProcess();
_status = ExecutionStatus.Error;
}
if (_status != ExecutionStatus.Error) {
// thread loop: poll process status
while (!_stopRequested) {
Thread.sleep(dur!"msecs"(50));
if (!isProcessActive()) {
_status = ExecutionStatus.Finished;
break;
}
}
if (_stopRequested) {
killProcess();
_status = ExecutionStatus.Killed;
}
}
// finished
Log.d("ProgramExecutionNoDebug: finished, execution status: ", _status);
_listener.onProgramExecutionStatus(this, _status, _exitCode);
}
// implement ProgramExecution interface
/// returns true if it's debugger
@property bool isDebugger() { return false; }
/// executable file
@property string executableFile() { return _executableFile; }
/// returns execution status
@property ExecutionStatus status() { return _status; }
/// start execution
void run() {
if (_runRequested)
return; // already running
assert(_listener !is null);
_runRequested = true;
_threadStarted = true;
_status = ExecutionStatus.Running;
start();
}
/// stop execution (call from GUI thread)
void stop() {
if (!_runRequested)
return;
if (_stopRequested)
return;
_stopRequested = true;
if (_threadStarted && !_threadJoined) {
_threadJoined = true;
join();
}
}
protected bool _threadStarted;
protected bool _threadJoined;
protected bool _stopRequested;
protected bool _runRequested;
}