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 }