~ubuntu-branches/ubuntu/precise/boinc/precise

« back to all changes in this revision

Viewing changes to client/app_control.cpp

Tags: 6.12.8+dfsg-1
* New upstream release.
* Simplified debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#ifdef _WIN32
23
23
#include "boinc_win.h"
24
24
#include "win_util.h"
 
25
#ifdef _MSC_VER
 
26
#define snprintf _snprintf
 
27
#endif
25
28
#ifndef STATUS_SUCCESS
26
29
#define STATUS_SUCCESS 0x0                 // may be in ntstatus.h
27
30
#endif
71
74
 
72
75
#include "app.h"
73
76
 
74
 
 
 
77
// Do periodic checks on running apps:
 
78
// - get latest CPU time and % done info
 
79
// - check if any has exited, and clean up
 
80
// - see if any has exceeded its CPU or disk space limits, and abort it
 
81
//
 
82
bool ACTIVE_TASK_SET::poll() {
 
83
    bool action;
 
84
    unsigned int i;
 
85
    static double last_time = 0;
 
86
    if (gstate.now - last_time < TASK_POLL_PERIOD) return false;
 
87
    last_time = gstate.now;
 
88
 
 
89
    action = check_app_exited();
 
90
    send_heartbeats();
 
91
    send_trickle_downs();
 
92
    graphics_poll();
 
93
    process_control_poll();
 
94
    action |= check_rsc_limits_exceeded();
 
95
    get_msgs();
 
96
    for (i=0; i<active_tasks.size(); i++) {
 
97
        ACTIVE_TASK* atp = active_tasks[i];
 
98
        if (atp->task_state() == PROCESS_ABORT_PENDING) {
 
99
            if (gstate.now > atp->abort_time + ABORT_TIMEOUT) {
 
100
                atp->kill_task(false);
 
101
            }
 
102
        }
 
103
        if (atp->task_state() == PROCESS_QUIT_PENDING) {
 
104
            if (gstate.now > atp->quit_time + QUIT_TIMEOUT) {
 
105
                atp->kill_task(true);
 
106
            }
 
107
        }
 
108
    }
 
109
 
 
110
    if (action) {
 
111
        gstate.set_client_state_dirty("ACTIVE_TASK_SET::poll");
 
112
    }
 
113
 
 
114
    return action;
 
115
}
 
116
 
 
117
#if 0
 
118
// deprecated; TerminateProcessById() doesn't work if
 
119
// the process is running as a different user
 
120
//
75
121
#ifdef _WIN32
76
122
bool ACTIVE_TASK::kill_all_children() {
77
123
        unsigned int i,j;
98
144
    return true;
99
145
}
100
146
#endif
 
147
#endif
101
148
 
102
149
// Send a quit message.
103
150
//
126
173
//
127
174
int ACTIVE_TASK::kill_task(bool restart) {
128
175
#ifdef _WIN32
129
 
    TerminateProcessById(pid);
 
176
    TerminateProcess(process_handle, 1);
130
177
#else
131
178
#ifdef SANDBOX
132
179
    kill_via_switcher(pid);
138
185
    cleanup_task();
139
186
        if (restart) {
140
187
                set_task_state(PROCESS_UNINITIALIZED, "kill_task");
141
 
                gstate.request_enforce_schedule("Task restart");
 
188
        char buf[256];
 
189
        sprintf(buf, "restarting %s", result->name);
 
190
                gstate.request_schedule_cpus(buf);
142
191
        } else {
143
192
                set_task_state(PROCESS_ABORTED, "kill_task");
144
193
        }
156
205
 
157
206
#ifdef _WIN32
158
207
    unsigned long exit_code;
159
 
    if (GetExitCodeProcess(pid_handle, &exit_code)) {
 
208
    if (GetExitCodeProcess(process_handle, &exit_code)) {
160
209
        if (exit_code != STILL_ACTIVE) {
161
210
            exited = true;
162
211
        }
198
247
#endif
199
248
}
200
249
 
 
250
static void clear_backoffs(ACTIVE_TASK* atp) {
 
251
    int rt = atp->result->avp->rsc_type();
 
252
    if (rt == RSC_TYPE_CPU) return;
 
253
    for (unsigned int i=0; i<gstate.results.size(); i++) {
 
254
        RESULT* rp = gstate.results[i];
 
255
        if (rp->avp->rsc_type() == rt) {
 
256
            rp->schedule_backoff = 0;
 
257
        }
 
258
    }
 
259
}
 
260
 
201
261
// handle a task that exited prematurely (i.e. the job isn't done)
202
262
//
203
263
void ACTIVE_TASK::handle_premature_exit(bool& will_restart) {
239
299
 
240
300
    if (log_flags.task_debug) {
241
301
        msg_printf(result->project, MSG_INFO,
242
 
            "[task_debug] Process for %s exited",
 
302
            "[task] Process for %s exited",
243
303
            result->name
244
304
        );
245
305
    }
247
307
    get_app_status_msg();
248
308
    get_trickle_up_msg();
249
309
    result->final_cpu_time = current_cpu_time;
 
310
    result->final_elapsed_time = elapsed_time;
250
311
    if (task_state() == PROCESS_ABORT_PENDING) {
251
312
        set_task_state(PROCESS_ABORTED, "handle_exited_app");
252
313
    } else {
261
322
                set_task_state(PROCESS_EXITED, "handle_exited_app");
262
323
                break;
263
324
            }
 
325
            double x;
 
326
            if (temporary_exit_file_present(x)) {
 
327
                if (log_flags.task_debug) {
 
328
                    msg_printf(result->project, MSG_INFO,
 
329
                        "[task] task called temporary_exit(%f)", x
 
330
                    );
 
331
                }
 
332
                set_task_state(PROCESS_UNINITIALIZED, "temporary exit");
 
333
                will_restart = true;
 
334
                result->schedule_backoff = gstate.now + x;
 
335
                break;
 
336
            }
264
337
            handle_premature_exit(will_restart);
265
338
            break;
266
339
        case 0xc000013a:        // control-C??
285
358
            );
286
359
            if (log_flags.task_debug) {
287
360
                msg_printf(result->project, MSG_INFO,
288
 
                    "[task_debug] Process for %s exited",
 
361
                    "[task] Process for %s exited",
289
362
                    result->name
290
363
                );
291
364
                msg_printf(result->project, MSG_INFO,
292
 
                    "[task_debug] exit code %d (0x%x): %s",
 
365
                    "[task] exit code %d (0x%x): %s",
293
366
                    exit_code, exit_code,
294
367
                    windows_format_error_string(exit_code, szError, sizeof(szError))
295
368
                );
315
388
                    handle_premature_exit(will_restart);
316
389
                }
317
390
            }
318
 
            if (log_flags.task_debug) {
319
 
                msg_printf(result->project, MSG_INFO,
320
 
                    "[task_debug] exit status %d\n",
321
 
                    result->exit_status
322
 
                );
 
391
            double x;
 
392
            if (temporary_exit_file_present(x)) {
 
393
                if (log_flags.task_debug) {
 
394
                    msg_printf(result->project, MSG_INFO,
 
395
                        "[task] task called temporary_exit(%f)", x
 
396
                    );
 
397
                }
 
398
                set_task_state(PROCESS_UNINITIALIZED, "temporary exit");
 
399
                will_restart = true;
 
400
                result->schedule_backoff = gstate.now + x;
 
401
            } else {
 
402
                if (log_flags.task_debug) {
 
403
                    msg_printf(result->project, MSG_INFO,
 
404
                        "[task] process exited with status %d\n",
 
405
                        result->exit_status
 
406
                    );
 
407
                }
323
408
            }
324
409
        } else if (WIFSIGNALED(stat)) {
325
410
            int got_signal = WTERMSIG(stat);
326
411
 
327
412
            if (log_flags.task_debug) {
328
413
                msg_printf(result->project, MSG_INFO,
329
 
                    "[task_debug] process got signal %d", signal
 
414
                    "[task] process got signal %d", signal
330
415
                );
331
416
            }
332
417
 
364
449
 
365
450
    cleanup_task();
366
451
 
367
 
    if (gstate.exit_after_finish) {
 
452
    if (config.exit_after_finish) {
368
453
        exit(0);
369
454
    }
370
455
 
371
456
    if (!will_restart) {
372
457
        copy_output_files();
373
458
        read_stderr_file();
374
 
        client_clean_out_dir(slot_dir);
 
459
        client_clean_out_dir(slot_dir, "handle_exited_app()");
 
460
        clear_backoffs(this);   // clear scheduling backoffs of jobs waiting for GPU
375
461
    }
376
462
    gstate.request_schedule_cpus("application exited");
377
463
    gstate.request_work_fetch("application exited");
383
469
    return (boinc_file_exists(path) != 0);
384
470
}
385
471
 
 
472
bool ACTIVE_TASK::temporary_exit_file_present(double& x) {
 
473
    char path[256];
 
474
    sprintf(path, "%s/%s", slot_dir, TEMPORARY_EXIT_FILE);
 
475
    FILE* f = fopen(path, "r");
 
476
    if (!f) return false;
 
477
    int y;
 
478
    int n = fscanf(f, "%d", &y);
 
479
    fclose(f);
 
480
    if (n != 1 || y < 0 || y > 86400) {
 
481
        x = 300;
 
482
    } else {
 
483
        x = y;
 
484
    }
 
485
    return true;
 
486
}
 
487
 
386
488
void ACTIVE_TASK_SET::send_trickle_downs() {
387
489
    unsigned int i;
388
490
    ACTIVE_TASK* atp;
406
508
void ACTIVE_TASK_SET::send_heartbeats() {
407
509
    unsigned int i;
408
510
    ACTIVE_TASK* atp;
409
 
        char buf[256];
 
511
        char buf[1024];
410
512
        double ar = gstate.available_ram();
411
513
 
412
514
    for (i=0; i<active_tasks.size(); i++) {
413
515
        atp = active_tasks[i];
414
516
        if (!atp->process_exists()) continue;
415
517
        if (!atp->app_client_shm.shm) continue;
416
 
                sprintf(buf, "<heartbeat/>"
417
 
                        "<wss>%f</wss>"
418
 
                        "<max_wss>%f</max_wss>",
 
518
                snprintf(buf, sizeof(buf), "<heartbeat/>"
 
519
                        "<wss>%e</wss>"
 
520
                        "<max_wss>%e</max_wss>",
419
521
                        atp->procinfo.working_set_size, ar
420
522
                );
421
523
        bool sent = atp->app_client_shm.shm->heartbeat.send_msg(buf);
449
551
                //
450
552
                if (atp->process_control_queue.timeout(180)) {
451
553
            if (log_flags.task_debug) {
452
 
                msg_printf(NULL, MSG_INFO,
 
554
                msg_printf(atp->result->project, MSG_INFO,
453
555
                    "Restarting %s - message timeout", atp->result->name
454
556
                );
455
557
            }
475
577
    for (i=0; i<active_tasks.size(); i++) {
476
578
        atp = active_tasks[i];
477
579
        if (!atp->process_exists()) continue;
478
 
        if (GetExitCodeProcess(atp->pid_handle, &exit_code)) {
 
580
        if (GetExitCodeProcess(atp->process_handle, &exit_code)) {
479
581
            if (exit_code != STILL_ACTIVE) {
480
582
                found = true;
481
583
                atp->handle_exited_app(exit_code);
483
585
        } else {
484
586
            if (log_flags.task_debug) {
485
587
                char errmsg[1024];
486
 
                msg_printf(0, MSG_INFO,
487
 
                    "[task_debug] task %s GetExitCodeProcess() failed - %s GLE %d (0x%x)",
 
588
                msg_printf(atp->result->project, MSG_INFO,
 
589
                    "[task] task %s GetExitCodeProcess() failed - %s GLE %d (0x%x)",
488
590
                    atp->result->name,
489
591
                    windows_format_error_string(
490
592
                        GetLastError(), errmsg, sizeof(errmsg)
502
604
#else
503
605
    int pid, stat;
504
606
 
505
 
    if ((pid = waitpid(0, &stat, WNOHANG)) > 0) {
 
607
    if ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
506
608
        atp = lookup_pid(pid);
507
609
        if (!atp) {
508
610
            // if we're running benchmarks, exited process
509
611
            // is probably a benchmark process; don't show error
510
612
            //
511
613
            if (!gstate.are_cpu_benchmarks_running() && log_flags.task_debug) {
512
 
                msg_printf(NULL, MSG_INTERNAL_ERROR, "Process %d not found\n", pid);
 
614
                msg_printf(NULL, MSG_INTERNAL_ERROR,
 
615
                    "Process %d not found\n", pid
 
616
                );
513
617
            }
514
618
            return false;
515
619
        }
529
633
 
530
634
    retval = current_disk_usage(disk_usage);
531
635
    if (retval) {
532
 
        msg_printf(0, MSG_INTERNAL_ERROR,
 
636
        msg_printf(this->wup->project, MSG_INTERNAL_ERROR,
533
637
            "Can't get task disk usage: %s", boincerror(retval)
534
638
        );
535
639
    } else {
570
674
    for (i=0; i<active_tasks.size(); i++) {
571
675
        atp = active_tasks[i];
572
676
        if (atp->task_state() != PROCESS_EXECUTING) continue;
573
 
                if (atp->current_cpu_time > atp->max_cpu_time) {
 
677
                if (!atp->result->project->non_cpu_intensive && (atp->elapsed_time > atp->max_elapsed_time)) {
574
678
                        msg_printf(atp->result->project, MSG_INFO,
575
 
                                "Aborting task %s: exceeded CPU time limit %f\n",
576
 
                                atp->result->name, atp->max_cpu_time
 
679
                                "Aborting task %s: exceeded elapsed time limit %.2f (%.2fG/%.2fG)",
 
680
                                atp->result->name, atp->max_elapsed_time,
 
681
                atp->result->wup->rsc_fpops_bound/1e9,
 
682
                atp->result->avp->flops/1e9
577
683
                        );
578
 
                        atp->abort_task(ERR_RSC_LIMIT_EXCEEDED, "Maximum CPU time exceeded");
 
684
                        atp->abort_task(ERR_RSC_LIMIT_EXCEEDED, "Maximum elapsed time exceeded");
579
685
                        did_anything = true;
580
686
                        continue;
581
687
                }
636
742
    int max_len = 63*1024;
637
743
    sprintf(path, "%s/%s", slot_dir, STDERR_FILE);
638
744
    if (!boinc_file_exists(path)) return false;
639
 
    if (read_file_string(path, stderr_file, max_len, true)) return false;
 
745
    if (read_file_string(path, stderr_file, max_len, !config.stderr_head)) {
 
746
        return false;
 
747
    }
640
748
 
641
749
    result->stderr_out += "<stderr_txt>\n";
642
750
    result->stderr_out += stderr_file;
766
874
            task_iter++;
767
875
        }
768
876
    }
769
 
    project->long_term_debt = 0;
770
877
    return 0;
771
878
}
772
879
 
774
881
// called only from CLIENT_STATE::suspend_tasks(),
775
882
// e.g. because on batteries, time of day, benchmarking, CPU throttle, etc.
776
883
//
777
 
void ACTIVE_TASK_SET::suspend_all(bool cpu_throttle) {
778
 
    unsigned int i;
779
 
        bool leave_in_mem;
780
 
 
781
 
        if (cpu_throttle) {
782
 
                leave_in_mem = true;
783
 
        } else {
784
 
                leave_in_mem = gstate.global_prefs.leave_apps_in_memory;
785
 
        }
786
 
    for (i=0; i<active_tasks.size(); i++) {
 
884
void ACTIVE_TASK_SET::suspend_all(int reason) {
 
885
    for (unsigned int i=0; i<active_tasks.size(); i++) {
787
886
        ACTIVE_TASK* atp = active_tasks[i];
788
887
        if (atp->task_state() != PROCESS_EXECUTING) continue;
789
 
                if (cpu_throttle) {
 
888
        switch (reason) {
 
889
        case SUSPEND_REASON_CPU_THROTTLE:
790
890
                        // if we're doing CPU throttling, don't bother suspending apps
791
891
                        // that don't use a full CPU
792
892
                        //
793
893
                        if (atp->result->project->non_cpu_intensive) continue;
794
894
                        if (atp->app_version->avg_ncpus < 1) continue;
795
 
                }
796
 
        atp->preempt(!leave_in_mem);
 
895
            atp->preempt(REMOVE_NEVER);
 
896
            break;
 
897
        case SUSPEND_REASON_BENCHMARKS:
 
898
            atp->preempt(REMOVE_NEVER);
 
899
            break;
 
900
        case SUSPEND_REASON_CPU_USAGE:
 
901
            // If we're suspending because of non-BOINC CPU load,
 
902
            // don't remove from memory.
 
903
            // Some systems do a security check when apps are launched,
 
904
            // which uses a lot of CPU.
 
905
            // Avoid going into a preemption loop.
 
906
            //
 
907
            if (atp->result->project->non_cpu_intensive) break;
 
908
            atp->preempt(REMOVE_NEVER);
 
909
            break;
 
910
        default:
 
911
            atp->preempt(REMOVE_MAYBE_USER);
 
912
        }
797
913
    }
798
914
}
799
915
 
819
935
 
820
936
// Check to see if any tasks are running
821
937
// called if benchmarking and waiting for suspends to happen
 
938
// or the system needs to suspend itself so we are suspending
 
939
// the applications
822
940
//
823
941
bool ACTIVE_TASK_SET::is_task_executing() {
824
942
    unsigned int i;
933
1051
        return false;
934
1052
    }
935
1053
    if (log_flags.app_msg_receive) {
936
 
        msg_printf(NULL, MSG_INFO,
 
1054
        msg_printf(this->wup->project, MSG_INFO,
937
1055
            "[app_msg_receive] got msg from slot %d: %s", slot, msg_buf
938
1056
        );
939
1057
    }
990
1108
}
991
1109
 
992
1110
// check for msgs from active tasks.
993
 
// Return true if any of them has changed its checkpoint_cpu_time
994
 
// (since in that case we need to write state file)
995
1111
//
996
 
bool ACTIVE_TASK_SET::get_msgs() {
 
1112
void ACTIVE_TASK_SET::get_msgs() {
997
1113
    unsigned int i;
998
1114
    ACTIVE_TASK *atp;
999
1115
    double old_time;
1000
 
    bool action = false;
1001
1116
    static double last_time=0;
1002
1117
    double delta_t;
1003
1118
    if (last_time) {
1004
1119
        delta_t = gstate.now - last_time;
 
1120
 
 
1121
        // Normally this is called every second.
 
1122
        // If delta_t is > 10, we'll assume that a period of hibernation
 
1123
        // or suspension happened, and treat it as zero.
 
1124
        // If negative, must be clock reset.  Ignore.
 
1125
        //
 
1126
        if (delta_t > 10 || delta_t < 0) {
 
1127
            delta_t = 0;
 
1128
        }
1005
1129
    } else {
1006
1130
        delta_t = 0;
1007
1131
    }
1008
1132
    last_time = gstate.now;
1009
1133
 
1010
 
 
1011
1134
    for (i=0; i<active_tasks.size(); i++) {
1012
1135
        atp = active_tasks[i];
1013
1136
        if (!atp->process_exists()) continue;
1014
1137
        old_time = atp->checkpoint_cpu_time;
1015
 
        if (atp->scheduler_state == CPU_SCHED_SCHEDULED) {
 
1138
        if (atp->task_state() == PROCESS_EXECUTING) {
1016
1139
            atp->elapsed_time += delta_t;
1017
1140
        }
1018
1141
        if (atp->get_app_status_msg()) {
1019
1142
            if (old_time != atp->checkpoint_cpu_time) {
1020
 
                gstate.request_enforce_schedule("Checkpoint reached");
 
1143
                char buf[256];
 
1144
                sprintf(buf, "%s checkpointed", atp->result->name);
 
1145
                gstate.request_schedule_cpus(buf);
1021
1146
                atp->checkpoint_wall_time = gstate.now;
1022
1147
                atp->premature_exit_count = 0;
1023
1148
                atp->checkpoint_elapsed_time = atp->elapsed_time;
1024
 
                action = true;
1025
1149
                if (log_flags.task_debug) {
1026
1150
                    msg_printf(atp->wup->project, MSG_INFO,
1027
 
                        "[task_debug] result %s checkpointed",
 
1151
                        "[task] result %s checkpointed",
1028
1152
                        atp->result->name
1029
1153
                    );
1030
1154
                } else if (log_flags.checkpoint_debug) {
1031
1155
                    msg_printf(atp->wup->project, MSG_INFO,
1032
 
                        "[checkpoint_debug] result %s checkpointed",
 
1156
                        "[checkpoint] result %s checkpointed",
1033
1157
                        atp->result->name
1034
1158
                    );
1035
1159
                }
 
1160
                atp->write_task_state_file();
1036
1161
            }
1037
1162
        }
1038
1163
        atp->get_trickle_up_msg();
1039
1164
    }
1040
 
    return action;
1041
 
}
1042
 
 
1043
 
const char *BOINC_RCSID_10ca137461 = "$Id: app_control.cpp 16608 2008-12-03 18:35:17Z romw $";
 
1165
}
 
1166
 
 
1167
// write checkpoint state to a file in the slot dir
 
1168
// (this avoids rewriting the state file on each checkpoint)
 
1169
//
 
1170
void ACTIVE_TASK::write_task_state_file() {
 
1171
    char path[1024];
 
1172
    sprintf(path, "%s/%s", slot_dir, TASK_STATE_FILENAME);
 
1173
    FILE* f = fopen(path, "w");
 
1174
    if (!f) return;
 
1175
    fprintf(f,
 
1176
        "<active_task>\n"
 
1177
        "    <project_master_url>%s</project_master_url>\n"
 
1178
        "    <result_name>%s</result_name>\n"
 
1179
        "    <checkpoint_cpu_time>%f</checkpoint_cpu_time>\n"
 
1180
        "    <checkpoint_elapsed_time>%f</checkpoint_elapsed_time>\n"
 
1181
        "    <fraction_done>%f</fraction_done>\n"
 
1182
        "</active_task>\n",
 
1183
        result->project->master_url,
 
1184
        result->name,
 
1185
        checkpoint_cpu_time,
 
1186
        checkpoint_elapsed_time,
 
1187
        fraction_done
 
1188
    );
 
1189
    fclose(f);
 
1190
}
 
1191
 
 
1192
// called on startup; read the task state file in case it's more recent
 
1193
// then the main state file
 
1194
//
 
1195
void ACTIVE_TASK::read_task_state_file() {
 
1196
    char buf[4096], path[1024], s[1024];
 
1197
    sprintf(path, "%s/%s", slot_dir, TASK_STATE_FILENAME);
 
1198
    FILE* f = fopen(path, "r");
 
1199
    if (!f) return;
 
1200
    buf[0] = 0;
 
1201
    fread(buf, 1, 4096, f);
 
1202
    fclose(f);
 
1203
    buf[4095] = 0;
 
1204
    double x;
 
1205
    // sanity checks - project and result name must match
 
1206
    //
 
1207
    if (!parse_str(buf, "<project_master_url>", s, sizeof(s))) {
 
1208
        msg_printf(wup->project, MSG_INTERNAL_ERROR,
 
1209
            "no project URL in task state file"
 
1210
        );
 
1211
        return;
 
1212
    }
 
1213
    if (strcmp(s, result->project->master_url)) {
 
1214
        msg_printf(wup->project, MSG_INTERNAL_ERROR,
 
1215
            "wrong project URL in task state file"
 
1216
        );
 
1217
        return;
 
1218
    }
 
1219
    if (!parse_str(buf, "<result_name>", s, sizeof(s))) {
 
1220
        msg_printf(wup->project, MSG_INTERNAL_ERROR,
 
1221
            "no task name in task state file"
 
1222
        );
 
1223
        return;
 
1224
    }
 
1225
    if (strcmp(s, result->name)) {
 
1226
        msg_printf(wup->project, MSG_INTERNAL_ERROR,
 
1227
            "wrong task name in task state file"
 
1228
        );
 
1229
        return;
 
1230
    }
 
1231
    if (parse_double(buf, "<checkpoint_cpu_time>", x)) {
 
1232
        if (x > checkpoint_cpu_time) {
 
1233
            checkpoint_cpu_time = x;
 
1234
        }
 
1235
    }
 
1236
    if (parse_double(buf, "<checkpoint_elapsed_time>", x)) {
 
1237
        if (x > checkpoint_elapsed_time) {
 
1238
            checkpoint_elapsed_time = x;
 
1239
        }
 
1240
    }
 
1241
}
 
1242