~teejee2008/timeshift/trunk

« back to all changes in this revision

Viewing changes to src/Utility/AsyncTask.vala

  • Committer: Tony George
  • Date: 2016-08-13 04:16:47 UTC
  • Revision ID: tony.george.kol@gmail.com-20160813041647-ivf2g6rszt00xco5
Updated project structure

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
using TeeJee.Logging;
 
2
using TeeJee.FileSystem;
 
3
using TeeJee.JsonHelper;
 
4
using TeeJee.ProcessHelper;
 
5
using TeeJee.System;
 
6
using TeeJee.Misc;
 
7
 
 
8
public abstract class AsyncTask : GLib.Object{
 
9
        
 
10
        private string err_line = "";
 
11
        private string out_line = "";
 
12
        private DataOutputStream dos_in;
 
13
        private DataInputStream dis_out;
 
14
        private DataInputStream dis_err;
 
15
        protected DataOutputStream dos_log;
 
16
        protected bool is_terminated = false;
 
17
 
 
18
        private bool stdout_is_open = false;
 
19
        private bool stderr_is_open = false;
 
20
        
 
21
        protected Pid child_pid;
 
22
        private int input_fd;
 
23
        private int output_fd;
 
24
        private int error_fd;
 
25
 
 
26
        protected string script_file = "";
 
27
        protected string working_dir = "";
 
28
        protected string log_file = "";
 
29
 
 
30
        public bool background_mode = false;
 
31
        
 
32
        // public
 
33
        public AppStatus status;
 
34
        public bool is_running = false;
 
35
        public string status_line = "";
 
36
        public int exit_code = 0;
 
37
        public string error_msg = "";
 
38
        public GLib.Timer timer;
 
39
        public double progress = 0.0;
 
40
        public double percent = 0.0;
 
41
        public int64 prg_count = 0;
 
42
        public int64 prg_count_total = 0;
 
43
        public int64 prg_bytes = 0;
 
44
        public int64 prg_bytes_total = 0;
 
45
        
 
46
        // signals
 
47
        public signal void stdout_line_read(string line);
 
48
        public signal void stderr_line_read(string line);
 
49
        public signal void task_complete();
 
50
        
 
51
        public void AsyncTask(string _script_file, string _working_dir, string _log_file){
 
52
                this.script_file = _script_file;
 
53
                this.working_dir = _working_dir;
 
54
                this.log_file = _log_file;
 
55
        }
 
56
 
 
57
        public bool begin(){
 
58
                bool has_started = true;
 
59
                is_terminated = false;
 
60
                
 
61
                prg_count = 0;
 
62
                prg_bytes = 0;
 
63
                error_msg = "";
 
64
                
 
65
                string[] spawn_args = new string[1];
 
66
                spawn_args[0] = script_file;
 
67
                
 
68
                string[] spawn_env = Environ.get();
 
69
                
 
70
                try {
 
71
                        // start timer
 
72
                        timer = new GLib.Timer();
 
73
                        timer.start();
 
74
 
 
75
                        is_running = true;
 
76
                        status = AppStatus.RUNNING;
 
77
                        
 
78
                        // execute script file
 
79
                        Process.spawn_async_with_pipes(
 
80
                            working_dir, // working dir
 
81
                            spawn_args,  // argv
 
82
                            spawn_env,   // environment
 
83
                            SpawnFlags.SEARCH_PATH,
 
84
                            null,        // child_setup
 
85
                            out child_pid,
 
86
                            out input_fd,
 
87
                            out output_fd,
 
88
                            out error_fd);
 
89
 
 
90
                        set_priority();
 
91
 
 
92
                        log_debug("AsyncTask: child_pid: %d".printf(child_pid));
 
93
                        
 
94
                        // create stream readers
 
95
                        UnixOutputStream uos_in = new UnixOutputStream(input_fd, false);
 
96
                        UnixInputStream uis_out = new UnixInputStream(output_fd, false);
 
97
                        UnixInputStream uis_err = new UnixInputStream(error_fd, false);
 
98
                        dos_in = new DataOutputStream(uos_in);
 
99
                        dis_out = new DataInputStream(uis_out);
 
100
                        dis_err = new DataInputStream(uis_err);
 
101
                        dis_out.newline_type = DataStreamNewlineType.ANY;
 
102
                        dis_err.newline_type = DataStreamNewlineType.ANY;
 
103
 
 
104
                        // create log file
 
105
                        if (log_file.length > 0){
 
106
                                var file = File.new_for_path(log_file);
 
107
                                if (file.query_exists()){
 
108
                                        file.delete();
 
109
                                }
 
110
                                var file_stream = file.create (FileCreateFlags.REPLACE_DESTINATION);
 
111
                                dos_log = new DataOutputStream (file_stream);
 
112
                        }
 
113
 
 
114
                        try {
 
115
                                //start thread for reading output stream
 
116
                                Thread.create<void> (read_stdout, true);
 
117
                        } catch (Error e) {
 
118
                                log_error ("AsyncTask.begin():create_thread:read_stdout()");
 
119
                                log_error (e.message);
 
120
                        }
 
121
 
 
122
                        try {
 
123
                                //start thread for reading error stream
 
124
                                Thread.create<void> (read_stderr, true);
 
125
                        } catch (Error e) {
 
126
                                log_error ("AsyncTask.begin():create_thread:read_stderr()");
 
127
                                log_error (e.message);
 
128
                        }
 
129
                }
 
130
                catch (Error e) {
 
131
                        log_error ("AsyncTask.begin()");
 
132
                        log_error(e.message);
 
133
                        is_running = false;
 
134
                        has_started = false;
 
135
                        //status = AppStatus.FINISHED;
 
136
                }
 
137
 
 
138
                return has_started;
 
139
        }
 
140
 
 
141
        private void read_stdout() {
 
142
                try {
 
143
                        stdout_is_open = true;
 
144
                        
 
145
                        out_line = dis_out.read_line (null);
 
146
                        while (out_line != null) {
 
147
                                //log_msg("O: " + out_line);
 
148
                                if (!is_terminated && (out_line.length > 0)){
 
149
                                        parse_stdout_line(out_line);
 
150
                                        stdout_line_read(out_line); //signal
 
151
                                }
 
152
                                out_line = dis_out.read_line (null); //read next
 
153
                        }
 
154
 
 
155
                        stdout_is_open = false;
 
156
 
 
157
                        // dispose stdout
 
158
                        if ((dis_out != null) && !dis_out.is_closed()){
 
159
                                dis_out.close();
 
160
                        }
 
161
                        //dis_out.close();
 
162
                        dis_out = null;
 
163
                        GLib.FileUtils.close(output_fd);
 
164
 
 
165
                        // check if complete
 
166
                        if (!stdout_is_open && !stderr_is_open){
 
167
                                finish();
 
168
                        }
 
169
                }
 
170
                catch (Error e) {
 
171
                        log_error ("AsyncTask.read_stdout()");
 
172
                        log_error (e.message);
 
173
                }
 
174
        }
 
175
        
 
176
        private void read_stderr() {
 
177
                try {
 
178
                        stderr_is_open = true;
 
179
                        
 
180
                        err_line = dis_err.read_line (null);
 
181
                        while (err_line != null) {
 
182
                                if (!is_terminated && (err_line.length > 0)){
 
183
                                        error_msg += "%s\n".printf(err_line);
 
184
                                        
 
185
                                        parse_stderr_line(err_line);
 
186
                                        stderr_line_read(err_line); //signal
 
187
                                }
 
188
                                err_line = dis_err.read_line (null); //read next
 
189
                        }
 
190
 
 
191
                        stderr_is_open = false;
 
192
 
 
193
                        // dispose stderr
 
194
                        if ((dis_err != null) && !dis_err.is_closed()){
 
195
                                dis_err.close(); 
 
196
                        }
 
197
                        //dis_err.close();
 
198
                        dis_err = null;
 
199
                        GLib.FileUtils.close(error_fd);
 
200
 
 
201
                        // check if complete
 
202
                        if (!stdout_is_open && !stderr_is_open){
 
203
                                finish();
 
204
                        }
 
205
                }
 
206
                catch (Error e) {
 
207
                        log_error ("AsyncTask.read_stderr()");
 
208
                        log_error (e.message);
 
209
                }
 
210
        }
 
211
 
 
212
        public void write_stdin(string line){
 
213
                try{
 
214
                        if (status == AppStatus.RUNNING){
 
215
                                dos_in.put_string(line + "\n");
 
216
                        }
 
217
                        else{
 
218
                                log_error ("AsyncTask.write_stdin(): NOT RUNNING");
 
219
                        }
 
220
                }
 
221
                catch(Error e){
 
222
                        log_error ("AsyncTask.write_stdin(): %s".printf(line));
 
223
                        log_error (e.message);
 
224
                }
 
225
        }
 
226
        
 
227
        protected abstract void parse_stdout_line(string out_line);
 
228
        
 
229
        protected abstract void parse_stderr_line(string err_line);
 
230
        
 
231
        private void finish(){
 
232
                // dispose stdin
 
233
                try{
 
234
                        dos_in.close();
 
235
                }
 
236
                catch(Error e){
 
237
                        log_error ("AsyncTask.finish(): dos_in.close()");
 
238
                        log_error (e.message);
 
239
                }
 
240
                
 
241
                dos_in = null;
 
242
                GLib.FileUtils.close(input_fd);
 
243
 
 
244
                // dispose child process
 
245
                Process.close_pid(child_pid); //required on Windows, doesn't do anything on Unix
 
246
 
 
247
                try{
 
248
                        // dispose log
 
249
                        if ((dos_log != null) && !dos_log.is_closed() && !dos_log.is_closing()){
 
250
                                dos_log.close();
 
251
                        }
 
252
                        dos_log = null;
 
253
                }
 
254
                catch (Error e) {
 
255
                        // error can be ignored
 
256
                        // dos_log is closed automatically when the last reference is set to null
 
257
                        // there may be pending operations which may throw an error
 
258
                }
 
259
 
 
260
                read_exit_code();
 
261
                
 
262
                is_running = false;
 
263
 
 
264
                if ((status != AppStatus.CANCELLED) && (status != AppStatus.PASSWORD_REQUIRED)) {
 
265
                        status = AppStatus.FINISHED;
 
266
                }
 
267
 
 
268
                timer.stop();
 
269
                
 
270
                finish_task();
 
271
                
 
272
                task_complete(); //signal
 
273
        }
 
274
 
 
275
        protected abstract void finish_task();
 
276
 
 
277
        protected int read_exit_code(){
 
278
                exit_code = -1;
 
279
                var path = file_parent(script_file) + "/status";
 
280
                if (file_exists(path)){
 
281
                        var txt = file_read(path);
 
282
                        exit_code = int.parse(txt);
 
283
                }
 
284
                log_debug("exit_code: %d".printf(exit_code));
 
285
                return exit_code;
 
286
        }
 
287
        
 
288
        // public actions --------------
 
289
 
 
290
        public void pause() {
 
291
                Pid sub_child_pid;
 
292
                foreach (long pid in get_process_children(child_pid)) {
 
293
                        sub_child_pid = (Pid) pid;
 
294
                        process_pause(sub_child_pid);
 
295
                }
 
296
 
 
297
                status = AppStatus.PAUSED;
 
298
        }
 
299
 
 
300
        public void resume() {
 
301
                Pid sub_child_pid;
 
302
                foreach (long pid in get_process_children(child_pid)) {
 
303
                        sub_child_pid = (Pid) pid;
 
304
                        process_resume(sub_child_pid);
 
305
                }
 
306
 
 
307
                status = AppStatus.RUNNING;
 
308
        }
 
309
 
 
310
        public void stop(AppStatus status_to_update) {
 
311
                // we need to un-freeze the processes before we kill them
 
312
                if (status == AppStatus.PAUSED) {
 
313
                        resume();
 
314
                }
 
315
 
 
316
                status = status_to_update;
 
317
                
 
318
                process_quit(child_pid);
 
319
                
 
320
                log_debug("process_quit: %d".printf(child_pid));
 
321
        }
 
322
 
 
323
        public void set_priority() {
 
324
                if (background_mode){
 
325
                        set_priority_value(5);
 
326
                }
 
327
                else{
 
328
                        set_priority_value(0);
 
329
                }
 
330
        }
 
331
 
 
332
        public void set_priority_value(int prio) {
 
333
                Pid app_pid = Posix.getpid();
 
334
                process_set_priority (app_pid, prio);
 
335
 
 
336
                if (status == AppStatus.RUNNING) {
 
337
                        process_set_priority (child_pid, prio);
 
338
 
 
339
                        Pid sub_child_pid;
 
340
                        foreach (long pid in get_process_children (child_pid)) {
 
341
                                sub_child_pid = (Pid) pid;
 
342
                                process_set_priority (sub_child_pid, prio);
 
343
                        }
 
344
                }
 
345
        }
 
346
 
 
347
        public string stat_time_elapsed{
 
348
                owned get{
 
349
                        long elapsed = (long) timer_elapsed(timer);
 
350
                        return format_duration(elapsed);
 
351
                }
 
352
        }
 
353
 
 
354
        public string stat_time_remaining{
 
355
                owned get{
 
356
                        if (progress > 0){
 
357
                                long elapsed = (long) timer_elapsed(timer);
 
358
                                long remaining = (long)((elapsed / progress) * (1.0 - progress));
 
359
                                return format_duration(remaining);
 
360
                        }
 
361
                        else{
 
362
                                return "???";
 
363
                        }
 
364
                }
 
365
        }
 
366
 
 
367
        public void print_app_status(){
 
368
                switch(status){
 
369
                case AppStatus.NOT_STARTED:
 
370
                        log_debug("status=%s".printf("NOT_STARTED"));
 
371
                        break;
 
372
                case AppStatus.RUNNING:
 
373
                        log_debug("status=%s".printf("RUNNING"));
 
374
                        break;
 
375
                case AppStatus.PAUSED:
 
376
                        log_debug("status=%s".printf("PAUSED"));
 
377
                        break;
 
378
                case AppStatus.FINISHED:
 
379
                        log_debug("status=%s".printf("FINISHED"));
 
380
                        break;
 
381
                case AppStatus.CANCELLED:
 
382
                        log_debug("status=%s".printf("CANCELLED"));
 
383
                        break;
 
384
                case AppStatus.PASSWORD_REQUIRED:
 
385
                        log_debug("status=%s".printf("PASSWORD_REQUIRED"));
 
386
                        break;
 
387
                }
 
388
        }
 
389
}
 
390
 
 
391
public enum AppStatus {
 
392
        NOT_STARTED,
 
393
        RUNNING,
 
394
        PAUSED,
 
395
        FINISHED,
 
396
        CANCELLED,
 
397
        PASSWORD_REQUIRED
 
398
}
 
399