diff --git a/android/buildAndroidBOINC-CI.sh b/android/buildAndroidBOINC-CI.sh index 1b87458b836..ad924d33742 100755 --- a/android/buildAndroidBOINC-CI.sh +++ b/android/buildAndroidBOINC-CI.sh @@ -260,7 +260,7 @@ vcpkgDir() echo $vcpkg_dir } -list_apps_name="boinc_gahp uc2 ucn multi_thread sleeper worker wrapper wrappture_example fermi" +list_apps_name="boinc_gahp uc2 ucn multi_thread sleeper sporadic worker wrapper wrappture_example fermi" NeonTest() { @@ -327,6 +327,7 @@ RenameAllApps() ../samples/example_app/ ucn ../samples/multi_thread/ multi_thread ../samples/sleeper/ sleeper + ../samples/sporadic/ sporadic ../samples/worker/ worker ../samples/wrapper/ wrapper ../samples/wrappture/ wrappture_example diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp index ace3dba97d3..5cded0cd01f 100644 --- a/api/boinc_api.cpp +++ b/api/boinc_api.cpp @@ -194,6 +194,7 @@ char remote_desktop_addr[256]; bool send_remote_desktop_addr = false; int app_min_checkpoint_period = 0; // min checkpoint period requested by app +SPORADIC_AC_STATE ac_state; #define TIMER_PERIOD 0.1 // Sleep interval for timer thread; @@ -434,6 +435,10 @@ static bool update_app_progress(double cpu_t, double cp_cpu_t) { snprintf(buf, sizeof(buf), "%f\n", bytes_received); strlcat(msg_buf, buf, sizeof(msg_buf)); } + if (ac_state) { + sprintf(buf, "%d\n", ac_state); + strlcat(msg_buf, buf, sizeof(msg_buf)); + } #ifdef MSGS_FROM_FILE if (fout) { fputs(msg_buf, fout); @@ -450,6 +455,7 @@ static void handle_heartbeat_msg() { char buf[MSG_CHANNEL_SIZE]; double dtemp; bool btemp; + int i; if (!app_client_shm->shm->heartbeat.get_msg(buf)) { return; @@ -467,6 +473,9 @@ static void handle_heartbeat_msg() { if (parse_bool(buf, "suspend_network", btemp)) { boinc_status.network_suspended = btemp; } + if (parse_int(buf, "", i)) { + boinc_status.ca_state = (SPORADIC_CA_STATE)i; + } } // called in timer thread @@ -714,6 +723,7 @@ int boinc_init_options_general(BOINC_OPTIONS& opt) { } int boinc_get_status(BOINC_STATUS *s) { + // can just do a struct copy?? s->no_heartbeat = boinc_status.no_heartbeat; s->suspended = boinc_status.suspended; s->quit_request = boinc_status.quit_request; @@ -722,6 +732,7 @@ int boinc_get_status(BOINC_STATUS *s) { s->working_set_size = boinc_status.working_set_size; s->max_working_set_size = boinc_status.max_working_set_size; s->network_suspended = boinc_status.network_suspended; + s->ca_state = boinc_status.ca_state; return 0; } @@ -1697,3 +1708,12 @@ void boinc_remote_desktop_addr(char* addr) { strlcpy(remote_desktop_addr, addr, sizeof(remote_desktop_addr)); send_remote_desktop_addr = true; } + +void boinc_sporadic_set_ac_state(SPORADIC_AC_STATE a) { + ac_state = a; +} + +SPORADIC_CA_STATE boinc_sporadic_get_ca_state() { + return boinc_status.ca_state; +} + diff --git a/api/boinc_api.h b/api/boinc_api.h index 3924bad1bfc..1218de36642 100644 --- a/api/boinc_api.h +++ b/api/boinc_api.h @@ -63,6 +63,8 @@ typedef struct BOINC_OPTIONS { // set this if application creates subprocesses. } BOINC_OPTIONS; +// info passed from client to app in heartbeat message +// typedef struct BOINC_STATUS { int no_heartbeat; int suspended; @@ -72,6 +74,7 @@ typedef struct BOINC_STATUS { double working_set_size; double max_working_set_size; int network_suspended; + SPORADIC_CA_STATE ca_state; } BOINC_STATUS; extern volatile BOINC_STATUS boinc_status; @@ -143,6 +146,8 @@ extern int boinc_temporary_exit( extern int boinc_finish_message( int status, const char* message, bool is_notice ); +extern void boinc_sporadic_set_ac_state(SPORADIC_AC_STATE); +extern SPORADIC_CA_STATE boinc_sporadic_get_ca_state(); /////////// API ENDS HERE diff --git a/client/Makefile.am b/client/Makefile.am index e583aa24ad1..f6cb71469d2 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -58,6 +58,7 @@ boinc_client_SOURCES = \ cs_prefs.cpp \ cs_proxy.cpp \ cs_scheduler.cpp \ + cs_sporadic.cpp \ cs_statefile.cpp \ cs_trickle.cpp \ current_version.cpp \ diff --git a/client/Makefile.linux b/client/Makefile.linux index 7f0461d74e6..bfac0c26e31 100644 --- a/client/Makefile.linux +++ b/client/Makefile.linux @@ -38,6 +38,7 @@ BOINC_OBJ = \ cs_prefs.o \ cs_proxy.o \ cs_scheduler.o \ + cs_sporadic.o \ cs_statefile.o \ cs_trickle.o \ current_version.o \ @@ -98,6 +99,7 @@ SRC = \ cs_proxy.cpp \ cs_scheduler.cpp \ cs_statefile.cpp \ + cs_sporadic.cpp \ cs_trickle.cpp \ current_version.cpp \ dhrystone.cpp \ diff --git a/client/app.cpp b/client/app.cpp index 28184ba3589..a0254c277f3 100644 --- a/client/app.cpp +++ b/client/app.cpp @@ -150,6 +150,9 @@ ACTIVE_TASK::ACTIVE_TASK() { safe_strcpy(remote_desktop_addr, ""); async_copy = NULL; finish_file_time = 0; + sporadic_ca_state = CA_NONE; + sporadic_ac_state = AC_NONE; + sporadic_ignore_until = 0; } bool ACTIVE_TASK::process_exists() { diff --git a/client/app.h b/client/app.h index e8f881794d0..1999acaeb11 100644 --- a/client/app.h +++ b/client/app.h @@ -28,6 +28,7 @@ #include "procinfo.h" #include "client_types.h" +#include "result.h" // values for preempt_type (see ACTIVE_TASK::preempt()) // @@ -184,11 +185,26 @@ struct ACTIVE_TASK { // Used to kill apps that hang after writing finished file int graphics_pid; // PID of running graphics app (Mac) + SPORADIC_CA_STATE sporadic_ca_state; + SPORADIC_AC_STATE sporadic_ac_state; + double sporadic_ignore_until; void set_task_state(int, const char*); inline int task_state() { return _task_state; } + inline bool sporadic() { + return wup->app->sporadic; + } + inline bool non_cpu_intensive() { + return result->app->non_cpu_intensive; + } + inline bool always_run() { + return sporadic() || non_cpu_intensive(); + } + inline bool dont_throttle() { + return result->dont_throttle(); + } int request_reread_prefs(); int request_reread_app_info(); int link_user_files(); diff --git a/client/app_control.cpp b/client/app_control.cpp index 6d6d6861796..99b9349d619 100644 --- a/client/app_control.cpp +++ b/client/app_control.cpp @@ -727,6 +727,13 @@ void ACTIVE_TASK_SET::send_heartbeats() { if (gstate.network_suspended) { safe_strcat(buf, ""); } + if (atp->sporadic_ca_state != CA_NONE) { + char buf2[256]; + sprintf(buf2, "%d", + atp->sporadic_ca_state + ); + safe_strcat(buf, buf2); + } bool sent = atp->app_client_shm.shm->heartbeat.send_msg(buf); if (log_flags.heartbeat_debug) { if (sent) { @@ -889,7 +896,7 @@ bool ACTIVE_TASK_SET::check_rsc_limits_exceeded() { for (i=0; itask_state() != PROCESS_EXECUTING) continue; - if (!atp->result->non_cpu_intensive() && (atp->elapsed_time > atp->max_elapsed_time)) { + if (!atp->always_run() && (atp->elapsed_time > atp->max_elapsed_time)) { snprintf(buf, sizeof(buf), "exceeded elapsed time limit %.2f (%.2fG/%.2fG)", atp->max_elapsed_time, atp->result->wup->rsc_fpops_bound/1e9, @@ -943,7 +950,7 @@ bool ACTIVE_TASK_SET::check_rsc_limits_exceeded() { // don't count RAM usage of non-CPU-intensive jobs // - if (!atp->result->non_cpu_intensive()) { + if (!atp->non_cpu_intensive()) { ram_left -= atp->procinfo.working_set_size_smoothed; } } @@ -1206,7 +1213,7 @@ void ACTIVE_TASK_SET::suspend_all(int reason) { // special cases for non-CPU-intensive apps // - if (atp->result->non_cpu_intensive()) { + if (atp->non_cpu_intensive()) { if (cc_config.dont_suspend_nci) { continue; } @@ -1218,7 +1225,7 @@ void ACTIVE_TASK_SET::suspend_all(int reason) { // handle CPU throttling separately // if (reason == SUSPEND_REASON_CPU_THROTTLE) { - if (atp->result->dont_throttle()) continue; + if (atp->dont_throttle()) continue; atp->preempt(REMOVE_NEVER, reason); continue; } @@ -1246,7 +1253,7 @@ void ACTIVE_TASK_SET::suspend_all(int reason) { // which uses a lot of CPU. // Avoid going into a preemption loop. // - if (atp->result->non_cpu_intensive()) break; + if (atp->always_run()) break; atp->preempt(REMOVE_NEVER); break; case SUSPEND_REASON_BATTERY_OVERHEATED: @@ -1390,7 +1397,7 @@ void ACTIVE_TASK::send_network_available() { bool ACTIVE_TASK::get_app_status_msg() { char msg_buf[MSG_CHANNEL_SIZE]; double fd; - int other_pid; + int other_pid, i; double dtemp; static double last_msg_time=0; @@ -1460,6 +1467,9 @@ bool ACTIVE_TASK::get_app_status_msg() { other_pids.clear(); other_pids.push_back(other_pid); } + if (parse_int(msg_buf, "", i)) { + sporadic_ac_state = (SPORADIC_AC_STATE)i; + } if (current_cpu_time < 0) { msg_printf(result->project, MSG_INFO, "app reporting negative CPU: %f", current_cpu_time diff --git a/client/app_start.cpp b/client/app_start.cpp index b0c34a601ee..d6b9073b35d 100644 --- a/client/app_start.cpp +++ b/client/app_start.cpp @@ -556,6 +556,12 @@ int ACTIVE_TASK::start(bool test) { return 0; } + // use special slot for test app + // + if (wup->project->app_test) { + strcpy(slot_dir, "slots/app_test"); + } + // run it at above idle priority if it // - uses coprocs // - uses less than one CPU @@ -691,14 +697,12 @@ int ACTIVE_TASK::start(bool test) { exit(0); } - // use special slot and exec path for test app + // use special exec path for test app // if (wup->project->app_test) { - strcpy(slot_dir, "slots/app_test"); strcpy(exec_path, gstate.app_test_file.c_str()); } - #ifdef _WIN32 PROCESS_INFORMATION process_info; STARTUPINFO startup_info; diff --git a/client/app_test.cpp b/client/app_test.cpp index 5c899ee6cb8..e63723730c9 100644 --- a/client/app_test.cpp +++ b/client/app_test.cpp @@ -31,12 +31,16 @@ void CLIENT_STATE::app_test_init() { strcpy(proj->master_url, "test_project_url"); strcpy(proj->_project_dir, "."); proj->app_test = true; + proj->non_cpu_intensive = false; projects.push_back(proj); APP *app = new APP; strcpy(app->name, "test app"); strcpy(app->user_friendly_name, "test app"); app->project = proj; + // can put other stuff here like + app->sporadic = true; + have_sporadic_app = true; apps.push_back(app); FILE_INFO *fip = new FILE_INFO; @@ -56,6 +60,12 @@ void CLIENT_STATE::app_test_init() { av->app = app; av->project = proj; av->app_files.push_back(*fref); + // can put other stuff here like + av->avg_ncpus = 1; + av->flops = 1e9; + av->gpu_ram = 1e7; + av->gpu_usage.rsc_type = PROC_TYPE_NVIDIA_GPU; + av->gpu_usage.usage = 1; app_versions.push_back(av); WORKUNIT *wu = new WORKUNIT; diff --git a/client/client_state.cpp b/client/client_state.cpp index 0a32d53941e..3b80bc1b0e8 100644 --- a/client/client_state.cpp +++ b/client/client_state.cpp @@ -186,6 +186,7 @@ CLIENT_STATE::CLIENT_STATE() #ifdef _WIN32 have_sysmon_msg = false; #endif + have_sporadic_app = false; } void CLIENT_STATE::show_host_info() { @@ -858,6 +859,8 @@ int CLIENT_STATE::init() { client_thread_mutex.lock(); throttle_thread.run(throttler, NULL); + sporadic_init(); + initialized = true; return 0; } @@ -949,6 +952,7 @@ void CLIENT_STATE::do_io_or_sleep(double max_time) { // possibly triggering state transitions. // Returns true if something happened // (in which case should call this again immediately) +// Called every POLL_INTERVAL (1 sec) // bool CLIENT_STATE::poll_slow_events() { int actions = 0, retval; @@ -1162,6 +1166,9 @@ bool CLIENT_STATE::poll_slow_events() { if (!network_suspended) { POLL_ACTION(scheduler_rpc , scheduler_rpc_poll ); } + if (have_sporadic_app) { + sporadic_poll(); + } retval = write_state_file_if_needed(); if (retval) { msg_printf(NULL, MSG_INTERNAL_ERROR, @@ -1269,6 +1276,9 @@ FILE_INFO* CLIENT_STATE::lookup_file_info(PROJECT* p, const char* name) { int CLIENT_STATE::link_app(PROJECT* p, APP* app) { if (lookup_app(p, app->name)) return ERR_NOT_UNIQUE; app->project = p; + if (!app->non_cpu_intensive) { + p->non_cpu_intensive = false; + } return 0; } diff --git a/client/client_state.h b/client/client_state.h index 44f62df8d75..c1bbb2b9e91 100644 --- a/client/client_state.h +++ b/client/client_state.h @@ -460,6 +460,11 @@ struct CLIENT_STATE { bool had_or_requested_work; bool scheduler_rpc_poll(); +// --------------- cs_sporadic.cpp: + bool have_sporadic_app; + void sporadic_poll(); + void sporadic_init(); + // --------------- cs_statefile.cpp: void set_client_state_dirty(const char*); int parse_state_file(); diff --git a/client/client_types.cpp b/client/client_types.cpp index afe9aa9d143..2bac026ccf7 100644 --- a/client/client_types.cpp +++ b/client/client_types.cpp @@ -142,6 +142,7 @@ int APP::parse(XML_PARSER& xp) { safe_strcpy(user_friendly_name, ""); project = NULL; non_cpu_intensive = false; + sporadic = false; while (!xp.get_tag()) { if (xp.match_tag("/app")) { if (!strlen(user_friendly_name)) { @@ -152,7 +153,12 @@ int APP::parse(XML_PARSER& xp) { if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_str("user_friendly_name", user_friendly_name, sizeof(user_friendly_name))) continue; if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) continue; + if (xp.parse_bool("sporadic", sporadic)) continue; if (xp.parse_bool("fraction_done_exact", fraction_done_exact)) continue; + if (xp.parse_bool("sporadic", sporadic)) { + if (sporadic) gstate.have_sporadic_app = true; + continue; + } #ifdef SIM if (xp.parse_double("latency_bound", latency_bound)) continue; if (xp.parse_double("fpops_est", fpops_est)) continue; diff --git a/client/client_types.h b/client/client_types.h index 567618178c6..7a444078170 100644 --- a/client/client_types.h +++ b/client/client_types.h @@ -268,6 +268,7 @@ struct APP { char name[256]; char user_friendly_name[256]; bool non_cpu_intensive; + bool sporadic; bool fraction_done_exact; PROJECT* project; bool report_results_immediately; diff --git a/client/coproc_sched.cpp b/client/coproc_sched.cpp index ec33283c633..1f1c65356bf 100644 --- a/client/coproc_sched.cpp +++ b/client/coproc_sched.cpp @@ -45,7 +45,7 @@ using std::vector; // COPROC::pending_usage[]: for each instance, its usage by running jobs // Note: "running" includes jobs suspended due to CPU throttling. // That's the only kind of suspended GPU job. -// CORPOC::usage[]: for each instance, its usage +// COPROC::usage[]: for each instance, its usage // // enforce_run_list() calls assign_coprocs(), // which assigns coproc instances to scheduled jobs, diff --git a/client/coproc_sched.h b/client/coproc_sched.h index 4525558cb9b..d15a7ca54a3 100644 --- a/client/coproc_sched.h +++ b/client/coproc_sched.h @@ -1,6 +1,6 @@ // This file is part of BOINC. // http://boinc.berkeley.edu -// Copyright (C) 2014 University of California +// Copyright (C) 2023 University of California // // BOINC is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License @@ -15,8 +15,87 @@ // You should have received a copy of the GNU Lesser General Public License // along with BOINC. If not, see . +// stuff related to scheduling coprocessors + #include struct RESULT; extern void assign_coprocs(std::vector& jobs); + +// The resources (proc and mem) used by running sporadic jobs. +// We recompute these on each poll, since e.g. WSS can change. +// +// Note: this is similar to (but simpler than) +// the PROC_RESOURCES struct in cpu_sched.cpp. +// Perhaps the two could be merged; for now, easier to keep them separate. +// +struct SPORADIC_RESOURCES { + double ncpus_used, ncpus_max; + double mem_used, mem_max; + COPROCS sr_coprocs; + + // clear reservations; called on each poll + void init_poll() { + ncpus_used= 0; + mem_used = 0; + sr_coprocs.clear_usage(); + } + + // called once at start + void init() { + sr_coprocs.clone(coprocs, false); + init_poll(); + } + + // are there enough free resources to run the task? + bool enough(ACTIVE_TASK *atp) { + if (mem_used + atp->procinfo.working_set_size_smoothed > mem_max) { + return false; + } + RESULT *rp = atp->result; + APP_VERSION *avp = rp->avp; + if (ncpus_used + avp->avg_ncpus > ncpus_max) { + return false; + } + int rt = avp->gpu_usage.rsc_type; + bool found = false; + if (rt) { + double u = avp->gpu_usage.usage; + COPROC& cp = sr_coprocs.coprocs[rt]; + for (int i=0; iapp, cp, i)) continue; + if (u + cp.usage[i] <= 1) { + found = true; + break; + } + } + if (!found) return false; + } + return true; + } + + // reserve resources for the task + void reserve(ACTIVE_TASK *atp) { + RESULT *rp = atp->result; + APP_VERSION *avp = rp->avp; + mem_used += atp->procinfo.working_set_size_smoothed; + ncpus_used+= avp->avg_ncpus; + int rt = avp->gpu_usage.rsc_type; + if (rt) { + double u = avp->gpu_usage.usage; + COPROC& cp = sr_coprocs.coprocs[rt]; + for (int i=0; iapp, cp, i)) continue; + if (u + cp.usage[i] <= 1) { + cp.usage[i] += u; + break; + } + } + } + } + + void print(); +}; + +extern SPORADIC_RESOURCES sporadic_resources; diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp index 8fcacee969c..d844cda29ef 100644 --- a/client/cpu_sched.cpp +++ b/client/cpu_sched.cpp @@ -346,6 +346,7 @@ void CLIENT_STATE::assign_results_to_projects() { // for (i=0; ialways_run()) continue; if (!atp->runnable()) continue; rp = atp->result; if (rp->already_selected) continue; @@ -410,7 +411,6 @@ RESULT* CLIENT_STATE::highest_prio_project_best_result() { for (i=0; inext_runnable_result) continue; - if (p->non_cpu_intensive) continue; if (first || p->sched_priority > best_prio) { first = false; best_project = p; @@ -447,7 +447,7 @@ RESULT* first_coproc_result(int rsc_type) { //msg_printf(rp->project, MSG_INFO, "not runnable: %s", rp->name); continue; } - if (rp->non_cpu_intensive()) continue; + if (rp->always_run()) continue; if (rp->already_selected) continue; prio = rp->project->sched_priority; if (!best) { @@ -500,7 +500,7 @@ static RESULT* earliest_deadline_result(int rsc_type) { if (rp->resource_type() != rsc_type) continue; if (rp->already_selected) continue; if (!rp->runnable()) continue; - if (rp->non_cpu_intensive()) continue; + if (rp->always_run()) continue; PROJECT* p = rp->project; // Skip this job if the project's deadline-miss count is zero. @@ -740,8 +740,7 @@ void CLIENT_STATE::adjust_rec() { for (i=0; ischeduler_state != CPU_SCHED_SCHEDULED) continue; - PROJECT* p = atp->result->project; - if (p->non_cpu_intensive) continue; + if (atp->non_cpu_intensive()) continue; work_fetch.accumulate_inst_sec(atp, elapsed_time); } @@ -883,6 +882,20 @@ void CLIENT_STATE::make_run_list(vector& run_list) { proc_rsc.init(); + // if there are sporadic apps, + // subtract the resource usage of those that are computing + // + if (have_sporadic_app) { + proc_rsc.ncpus -= sporadic_resources.ncpus_used; + for (int rt=1; rt &run_list) { atp->overdue_checkpoint = false; if (!atp->result->runnable()) continue; if (atp->result->uses_gpu() && gpu_suspend_reason) continue; - if (atp->result->non_cpu_intensive()) continue; + if (atp->result->always_run()) continue; if (atp->scheduler_state != CPU_SCHED_SCHEDULED) continue; if (finished_time_slice(atp)) continue; atp->result->unfinished_time_slice = true; @@ -1203,6 +1216,9 @@ bool CLIENT_STATE::enforce_run_list(vector& run_list) { } double ram_left = available_ram(); + if (have_sporadic_app) { + ram_left -= sporadic_resources.mem_used; + } double swap_left = (global_prefs.vm_max_used_frac)*host_info.m_swap; if (log_flags.mem_usage_debug) { @@ -1212,11 +1228,11 @@ bool CLIENT_STATE::enforce_run_list(vector& run_list) { ); } - // schedule non-CPU-intensive tasks, + // schedule non-CPU-intensive and sporadic tasks // for (i=0; inon_cpu_intensive() && rp->runnable()) { + if (rp->always_run() && rp->runnable()) { atp = get_task(rp); if (!atp) { msg_printf(rp->project, MSG_INTERNAL_ERROR, diff --git a/client/cs_scheduler.cpp b/client/cs_scheduler.cpp index 0cb894a4371..22429489453 100644 --- a/client/cs_scheduler.cpp +++ b/client/cs_scheduler.cpp @@ -800,6 +800,7 @@ int CLIENT_STATE::handle_scheduler_reply( // safe_strcpy(app->user_friendly_name, checked_app.user_friendly_name); app->non_cpu_intensive = checked_app.non_cpu_intensive; + app->sporadic = checked_app.sporadic; app->fraction_done_exact = checked_app.fraction_done_exact; } else { app = new APP; diff --git a/client/cs_sporadic.cpp b/client/cs_sporadic.cpp new file mode 100644 index 00000000000..939b9a8916e --- /dev/null +++ b/client/cs_sporadic.cpp @@ -0,0 +1,245 @@ +// This file is part of BOINC. +// http://boinc.berkeley.edu +// Copyright (C) 2023 University of California +// +// BOINC is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// BOINC is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with BOINC. If not, see . + +// logic for handling sporadic jobs +// +// Currently sporadic jobs have priority over others. +// In particular, they can preempt jobs that +// - are in danger of missing their deadline +// - have done a lot of computing and haven't checkpointed +// - are from projects with a resource share debt +// At some point we should fix this. + +// Apps can be +// regular: jobs compute when running +// sporadic: jobs run all the time but compute only part of the time +// non-CPU-intensive (NCI): jobs run all the time but don't compute +// +// Projects can have any or all of these, and this can change over time. +// A project is flagged as NCI if it has only NCI apps; +// in that case it's omitted from resource share calculations. + +// Note: the client and app communicate via 1-way streams +// that are polled once/sec. +// This introduces potential uncertainty: +// if we send the app a message, +// once second later we don't know if it received the message and responded. +// To avoid this problem, when we send a message to an app +// we ignore its messages for the next 2.5 seconds. +// Perhaps a better approach would be to use sequence numbers and acks. +// +// states and transitions: +// CA_DONT_COMPUTE +// computing is suspended, or insufficient resources +// transitions: +// to COULD_COMPUTE when these no longer hold +// CA_COULD_COMPUTE +// not computing, but could +// transitions: +// to CA_DONT_COMPUTE if computing suspended or insufficient resources +// to CA_COMPUTING if get AC_WANT_COMPUTE +// CA_COMPUTING +// job can compute (and is, as far as we know) +// transitions: +// to CA_DONT_COMPUTE if computing suspended or insufficient resources +// to CA_DONT_COMPUTE if get AC_DONT_WANT_COMPUTE or AC_NONE +// (after timeout - see above) +// +// Interaction with the batch scheduler: +// If we make a transition that changes resource usage, +// request a reschedule to start/stop batch jobs +// The batch scheduler subtracts resources used by sporadic jobs +// Coprocs: +// If batch jobs are using GPUs, it may take them a few seconds to exit. +// Sporadic jobs that use GPUs should delay for a few seconds at start, +// and retry failed VRAM allocations. +// + +#include "coproc.h" + +#include "client_state.h" +#include "client_msgs.h" +#include "coproc_sched.h" +#include "result.h" +#include "app.h" + +#define SPORADIC_MSG_DELAY 2.5 + +SPORADIC_RESOURCES sporadic_resources; + +void SPORADIC_RESOURCES::print() { + msg_printf(NULL, MSG_INFO, "Sporadic resources:"); + msg_printf(NULL, MSG_INFO, " %f CPUs", ncpus_used); + msg_printf(NULL, MSG_INFO, " %f MB RAM", mem_used/MEGA); + for (int i=1; i 0) { + msg_printf(NULL, MSG_INFO, " GPU %s instance %d: %f\n", + cp.type, j, cp.usage[j] + ); + } + } + } +} + +// is computing suspended for this job? +// +static bool computing_suspended(ACTIVE_TASK *atp) { + if (gstate.suspend_reason) return true; + if (atp->result->uses_gpu() && gpu_suspend_reason) return true; + return false; +} + +// polling routine, called once/sec +void CLIENT_STATE::sporadic_poll() { + sporadic_resources.init_poll(); + sporadic_resources.mem_max = available_ram(); + sporadic_resources.ncpus_max = n_usable_cpus; + + bool changed_active = false; + // whether we need to reschedule regular jobs + + // find jobs that are active but shouldn't be + // (CA_COMPUTING -> CA_NONE transitions) + // + for (ACTIVE_TASK *atp: active_tasks.active_tasks) { + if (!atp->sporadic()) continue; + if (atp->sporadic_ca_state != CA_COMPUTING) continue; + + // the job is in state CA_COMPUTING + + // see if the job needs to stop computing + if (computing_suspended(atp)) { + atp->sporadic_ca_state = CA_NONE; + changed_active = true; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] preempting %s: computing suspended", + atp->result->name + ); + } + } else if (!sporadic_resources.enough(atp)) { + // this could happen if user prefs change + atp->sporadic_ca_state = CA_NONE; + changed_active = true; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] preempting %s: insufficient resources", + atp->result->name + ); + } + } else if (atp->sporadic_ac_state != AC_WANT_COMPUTE) { + if (now > atp->sporadic_ignore_until) { + atp->sporadic_ca_state = CA_NONE; + changed_active = true; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s: app is done computing", + atp->result->name + ); + } + } + } + // the job can keep computing - reserve its resources + if (atp->sporadic_ca_state == CA_COMPUTING) { + sporadic_resources.reserve(atp); + } + } + + // activate jobs as needed + // (CA_COULD_COMPUTE -> CA_COMPUTING transitions) + // + for (ACTIVE_TASK *atp: active_tasks.active_tasks) { + if (!atp->sporadic()) continue; + if (atp->sporadic_ca_state != CA_COULD_COMPUTE) continue; + if (computing_suspended(atp)) { + atp->sporadic_ca_state = CA_DONT_COMPUTE; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s can no longer compute: suspended", + atp->result->name + ); + } + } else if (!sporadic_resources.enough(atp)) { + atp->sporadic_ca_state = CA_DONT_COMPUTE; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s can no longer compute: insufficient resources", + atp->result->name + ); + } + } else if (atp->sporadic_ac_state == AC_WANT_COMPUTE) { + if (now > atp->sporadic_ignore_until) { + atp->sporadic_ca_state = CA_COMPUTING; + atp->sporadic_ignore_until = now + SPORADIC_MSG_DELAY; + sporadic_resources.reserve(atp); + changed_active = true; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] starting %s", + atp->result->name + ); + } + } + } + } + + // assign states to initial, preempted, and done jobs + // + for (ACTIVE_TASK *atp: active_tasks.active_tasks) { + if (!atp->sporadic()) continue; + if (atp->sporadic_ca_state != CA_NONE) continue; + if (computing_suspended(atp)) { + atp->sporadic_ca_state = CA_DONT_COMPUTE; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s can't compute: suspended", + atp->result->name + ); + } + } else if (!sporadic_resources.enough(atp)) { + atp->sporadic_ca_state = CA_DONT_COMPUTE; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s can't compute: insufficient resources", + atp->result->name + ); + } + } else { + atp->sporadic_ca_state = CA_COULD_COMPUTE; + if (log_flags.sporadic_debug) { + msg_printf(atp->result->project, MSG_INFO, + "[sporadic] %s can compute", + atp->result->name + ); + } + } + } + + if (changed_active) { + request_schedule_cpus("sporadic apps changed state"); + } + + if (log_flags.sporadic_debug) { + sporadic_resources.print(); + } +} + +void CLIENT_STATE::sporadic_init() { + sporadic_resources.init(); +} diff --git a/client/log_flags.cpp b/client/log_flags.cpp index 842d6a4f283..11cc2712fcd 100644 --- a/client/log_flags.cpp +++ b/client/log_flags.cpp @@ -96,6 +96,7 @@ void LOG_FLAGS::show() { show_flag(buf, sizeof(buf), sched_op_debug, "sched_op_debug"); show_flag(buf, sizeof(buf), scrsave_debug, "scrsave_debug"); show_flag(buf, sizeof(buf), slot_debug, "slot_debug"); + show_flag(buf, sizeof(buf), sporadic_debug, "sporadic_debug"); show_flag(buf, sizeof(buf), state_debug, "state_debug"); show_flag(buf, sizeof(buf), statefile_debug, "statefile_debug"); show_flag(buf, sizeof(buf), task_debug, "task_debug"); diff --git a/client/project.cpp b/client/project.cpp index e83727e77de..5000d5813f4 100644 --- a/client/project.cpp +++ b/client/project.cpp @@ -86,7 +86,7 @@ void PROJECT::init() { disk_usage = 0.0; disk_share = 0.0; anonymous_platform = false; - non_cpu_intensive = false; + non_cpu_intensive = true; // true until link a non-NCI app report_results_immediately = false; pwf.reset(this); send_time_stats_log = 0; @@ -245,7 +245,6 @@ int PROJECT::parse_state(XML_PARSER& xp) { if (xp.parse_int("send_job_log", send_job_log)) continue; if (xp.parse_bool("send_full_workload", send_full_workload)) continue; if (xp.parse_bool("dont_use_dcf", dont_use_dcf)) continue; - if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) continue; if (xp.parse_bool("suspended_via_gui", suspended_via_gui)) continue; if (xp.parse_bool("dont_request_more_work", dont_request_more_work)) continue; if (xp.parse_bool("detach_when_done", detach_when_done)) continue; @@ -422,7 +421,7 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) { " %d\n" " %f\n" " %f\n" - "%s%s%s%s%s%s%s%s%s%s%s%s%s", + "%s%s%s%s%s%s%s%s%s%s%s%s", master_url, project_name, symstore, @@ -465,7 +464,6 @@ int PROJECT::write_state(MIOFILE& out, bool gui_rpc) { trickle_up_pending?" \n":"", send_full_workload?" \n":"", dont_use_dcf?" \n":"", - non_cpu_intensive?" \n":"", suspended_via_gui?" \n":"", dont_request_more_work?" \n":"", detach_when_done?" \n":"", @@ -606,7 +604,6 @@ void PROJECT::copy_state_fields(PROJECT& p) { dont_use_dcf = p.dont_use_dcf; send_time_stats_log = p.send_time_stats_log; send_job_log = p.send_job_log; - non_cpu_intensive = p.non_cpu_intensive; suspended_via_gui = p.suspended_via_gui; dont_request_more_work = p.dont_request_more_work; detach_when_done = p.detach_when_done; diff --git a/client/project.h b/client/project.h index 9b8bc3c5503..bfe9d982f9b 100644 --- a/client/project.h +++ b/client/project.h @@ -169,7 +169,7 @@ struct PROJECT : PROJ_AM { // use those apps rather then getting from server bool non_cpu_intensive; // All this project's apps are non-CPU-intensive. - // Apps can also be individually marked as NCI + // (determined dynamically) bool use_symlinks; bool report_results_immediately; bool sched_req_no_work[MAX_RSC]; diff --git a/client/result.cpp b/client/result.cpp index c5f4bd57291..0a780872d4f 100644 --- a/client/result.cpp +++ b/client/result.cpp @@ -651,7 +651,8 @@ double RESULT::estimated_runtime() { double RESULT::estimated_runtime_remaining() { if (computing_done()) return 0; ACTIVE_TASK* atp = gstate.lookup_active_task_by_result(this); - if (app->non_cpu_intensive) { + if (non_cpu_intensive()) { + // the following is questionable if (atp && atp->fraction_done>0) { double est_dur = atp->fraction_done_elapsed_time / atp->fraction_done; double x = est_dur - atp->elapsed_time; @@ -660,6 +661,7 @@ double RESULT::estimated_runtime_remaining() { } return 0; } + if (sporadic()) return 0; if (atp) { #ifdef SIM diff --git a/client/result.h b/client/result.h index 632394b35fd..c712a2bcaee 100644 --- a/client/result.h +++ b/client/result.h @@ -156,9 +156,13 @@ struct RESULT { return avp->gpu_usage.rsc_type; } inline bool non_cpu_intensive() { - if (project->non_cpu_intensive) return true; - if (app->non_cpu_intensive) return true; - return false; + return app->non_cpu_intensive; + } + inline bool sporadic() { + return app->sporadic; + } + inline bool always_run() { + return non_cpu_intensive() || sporadic(); } inline bool dont_throttle() { if (non_cpu_intensive()) return true; diff --git a/client/rr_sim.cpp b/client/rr_sim.cpp index a7fe512cf13..80d8549052f 100644 --- a/client/rr_sim.cpp +++ b/client/rr_sim.cpp @@ -192,7 +192,7 @@ void RR_SIM::init_pending_lists() { rp->already_selected = false; if (!rp->nearly_runnable()) continue; if (rp->some_download_stalled()) continue; - if (rp->project->non_cpu_intensive) continue; + if (rp->always_run()) continue; rp->rrsim_flops_left = rp->estimated_flops_remaining(); //if (rp->rrsim_flops_left <= 0) continue; diff --git a/client/scheduler_op.cpp b/client/scheduler_op.cpp index 657c470b148..be87864e730 100644 --- a/client/scheduler_op.cpp +++ b/client/scheduler_op.cpp @@ -590,7 +590,6 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) { MIOFILE mf; XML_PARSER xp(&mf); string delete_file_name; - bool non_cpu_intensive = false; bool ended = false; mf.init_file(in); @@ -652,7 +651,6 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) { // boolean project attributes. // If the scheduler reply didn't specify them, they're not set. // - project->non_cpu_intensive = non_cpu_intensive; project->ended = ended; return 0; } @@ -857,8 +855,6 @@ int SCHEDULER_REPLY::parse(FILE* in, PROJECT* project) { ); } continue; - } else if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) { - continue; } else if (xp.parse_bool("ended", ended)) { continue; } else if (xp.parse_bool("no_cpu_apps", btemp)) { diff --git a/deploy/prepare_deployment.py b/deploy/prepare_deployment.py index 9d9a8a147b7..a8d0a12d5aa 100644 --- a/deploy/prepare_deployment.py +++ b/deploy/prepare_deployment.py @@ -42,7 +42,8 @@ './samples/wrapper/wrapper', './samples/openclapp/openclapp', './samples/wrappture/wrappture_example', - './samples/wrappture/fermi' + './samples/wrappture/fermi', + './samples/sporadic/sporadic' ] linux_manager_list = [ @@ -67,7 +68,8 @@ './samples/worker/worker.exe', './samples/wrapper/wrapper.exe', './samples/wrappture/wrappture_example.exe', - './samples/wrappture/fermi.exe' + './samples/wrappture/fermi.exe', + './samples/sporadic/sporadic.exe' ] android_manager_generic_list = [ @@ -139,7 +141,13 @@ './samples/wrappture/android_arm_fermi', './samples/wrappture/android_arm64_fermi', './samples/wrappture/android_x86_fermi', - './samples/wrappture/android_x86_64_fermi' + './samples/wrappture/android_x86_64_fermi', + # sporadic + './samples/sporadic/android_armv6_sporadic', + './samples/sporadic/android_arm_sporadic', + './samples/sporadic/android_arm64_sporadic', + './samples/sporadic/android_x86_sporadic', + './samples/sporadic/android_x86_64_sporadic' ] windows_apps_list = [ diff --git a/lib/Makefile.linux b/lib/Makefile.linux index 6f65599813a..8d6881cade2 100644 --- a/lib/Makefile.linux +++ b/lib/Makefile.linux @@ -1,6 +1,7 @@ # make libraries for Linux client and boinccmd -CC = g++ -O4 -Wall -I ../ +//CC = g++ -O4 -Wall -I ../ +CC = g++ -g -Wall -I ../ all: boinc.a boinc_cmd.a diff --git a/lib/app_ipc.h b/lib/app_ipc.h index 54b10dc3c05..2ead2168f75 100644 --- a/lib/app_ipc.h +++ b/lib/app_ipc.h @@ -46,7 +46,7 @@ // Shared memory is a set of MSG_CHANNELs. // First byte of a channel is nonzero if -// the channel contains an unread data. +// the channel contains unread data. // This is set by the sender and cleared by the receiver. // The sender doesn't write if the flag is set. // Remaining 1023 bytes contain data. @@ -55,49 +55,41 @@ struct MSG_CHANNEL { char buf[MSG_CHANNEL_SIZE]; - bool get_msg(char*); // returns a message and clears pending flag + bool get_msg(char*); + // returns a message and clears pending flag inline bool has_msg() { return buf[0]?true:false; } - bool send_msg(const char*); // if there is not a message in the segment, - // writes specified message and sets pending flag + bool send_msg(const char*); + // if there is not a message in the segment, + // writes specified message and sets pending flag void send_msg_overwrite(const char*); - // write message, overwriting any msg already there + // write message, overwriting any msg already there }; struct SHARED_MEM { + // don't change the order of these! + // See https://github.com/BOINC/boinc/wiki/API-Implementation + // for message formats and details + // MSG_CHANNEL process_control_request; // core->app - // - // - // MSG_CHANNEL process_control_reply; // app->core + // not used MSG_CHANNEL graphics_request; // core->app // not currently used MSG_CHANNEL graphics_reply; // app->core - // - // MSG_CHANNEL heartbeat; // core->app - // sent every second, even while app is suspended - // app's current working set size - // max working set size MSG_CHANNEL app_status; // app->core - // status message every second, of the form - // ... - // ... - // ... - // ... MSG_CHANNEL trickle_up; // app->core - // MSG_CHANNEL trickle_down; // core->app - // }; // MSG_QUEUE provides a queuing mechanism for shared-mem messages diff --git a/lib/cc_config.cpp b/lib/cc_config.cpp index 38d5b6e7998..7be77f398aa 100644 --- a/lib/cc_config.cpp +++ b/lib/cc_config.cpp @@ -88,6 +88,7 @@ int LOG_FLAGS::parse(XML_PARSER& xp) { if (xp.parse_bool("sched_op_debug", sched_op_debug)) continue; if (xp.parse_bool("scrsave_debug", scrsave_debug)) continue; if (xp.parse_bool("slot_debug", slot_debug)) continue; + if (xp.parse_bool("sporadic_debug", sporadic_debug)) continue; if (xp.parse_bool("state_debug", state_debug)) continue; if (xp.parse_bool("statefile_debug", statefile_debug)) continue; if (xp.parse_bool("suspend_debug", suspend_debug)) continue; @@ -138,6 +139,7 @@ int LOG_FLAGS::write(MIOFILE& out) { " %d\n" " %d\n" " %d\n" + " %d\n" " %d\n" " %d\n" " %d\n" @@ -181,6 +183,7 @@ int LOG_FLAGS::write(MIOFILE& out) { sched_op_debug ? 1 : 0, scrsave_debug ? 1 : 0, slot_debug ? 1 : 0, + sporadic_debug ? 1 : 0, state_debug ? 1 : 0, statefile_debug ? 1 : 0, suspend_debug ? 1 : 0, diff --git a/lib/cc_config.h b/lib/cc_config.h index 88c62d50e53..9979c628bea 100644 --- a/lib/cc_config.h +++ b/lib/cc_config.h @@ -102,6 +102,7 @@ struct LOG_FLAGS { bool scrsave_debug; bool slot_debug; // allocation of slots + bool sporadic_debug; bool state_debug; // print textual summary of CLIENT_STATE initially // and after each scheduler RPC and garbage collect diff --git a/lib/common_defs.h b/lib/common_defs.h index f93dc42ea75..2bcce7c5845 100644 --- a/lib/common_defs.h +++ b/lib/common_defs.h @@ -170,6 +170,26 @@ enum BATTERY_STATE { BATTERY_STATE_OVERHEATED }; +// states for sporadic apps +// +// client state +enum SPORADIC_CA_STATE { + CA_NONE = 0, + CA_DONT_COMPUTE = 1, + // computing suspended (CPU and perhaps GPU) or other project have priority + CA_COULD_COMPUTE = 2, + // not computing, but could + CA_COMPUTING = 3 + // go ahead and compute +}; + +// app state +enum SPORADIC_AC_STATE { + AC_NONE = 0, + AC_DONT_WANT_COMPUTE = 1, + AC_WANT_COMPUTE = 2 +}; + // Values of RESULT::state in client. // THESE MUST BE IN NUMERICAL ORDER // (because of the > comparison in RESULT::computing_done()) diff --git a/mac_build/boinc.xcodeproj/project.pbxproj b/mac_build/boinc.xcodeproj/project.pbxproj index e326653c56e..7ee5bc2f349 100644 --- a/mac_build/boinc.xcodeproj/project.pbxproj +++ b/mac_build/boinc.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ DD0052F910CA6F1D0067570C /* cs_proxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD0052F710CA6F1D0067570C /* cs_proxy.cpp */; }; DD00F554176C081500850424 /* MacInstaller.icns in Resources */ = {isa = PBXBuildFile; fileRef = DD531BC50C193D3800742E50 /* MacInstaller.icns */; }; DD01B6790C16723C0023A806 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; + DD0428462ACD7D3F00229B6D /* cs_sporadic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD0428452ACD7D3F00229B6D /* cs_sporadic.cpp */; }; DD08648D19E0BE6D00994039 /* ProjectWelcomePage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD08648B19E0BE6D00994039 /* ProjectWelcomePage.cpp */; }; DD0925B51FB05490000902DF /* project_list.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD0925B31FB05490000902DF /* project_list.cpp */; }; DD0A06F50869A2D2007CD86E /* prefs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DD344BE407C5B1670043025C /* prefs.cpp */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; @@ -912,6 +913,7 @@ DD000D7324D0244C0083DE77 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; DD0052F710CA6F1D0067570C /* cs_proxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cs_proxy.cpp; path = ../client/cs_proxy.cpp; sourceTree = SOURCE_ROOT; }; DD0052F810CA6F1D0067570C /* cs_proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cs_proxy.h; path = ../client/cs_proxy.h; sourceTree = SOURCE_ROOT; }; + DD0428452ACD7D3F00229B6D /* cs_sporadic.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cs_sporadic.cpp; sourceTree = ""; }; DD04BE1A0EDD836A006D5603 /* TermsOfUsePage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TermsOfUsePage.h; path = ../clientgui/TermsOfUsePage.h; sourceTree = SOURCE_ROOT; }; DD08648B19E0BE6D00994039 /* ProjectWelcomePage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectWelcomePage.cpp; sourceTree = ""; }; DD08648C19E0BE6D00994039 /* ProjectWelcomePage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectWelcomePage.h; sourceTree = ""; }; @@ -2132,6 +2134,7 @@ DD0052F710CA6F1D0067570C /* cs_proxy.cpp */, DD0052F810CA6F1D0067570C /* cs_proxy.h */, F54B8FC902AC0A0C01FB7237 /* cs_scheduler.cpp */, + DD0428452ACD7D3F00229B6D /* cs_sporadic.cpp */, DD344B9307C5AE2E0043025C /* cs_statefile.cpp */, DD344B9407C5AE2E0043025C /* cs_trickle.cpp */, B13E2D162655655000D5C977 /* detect_rosetta_cpu.cpp */, @@ -4016,6 +4019,7 @@ DDD74D9707CF48F30065AC9D /* cs_files.cpp in Sources */, DDD74D9807CF48F40065AC9D /* cs_prefs.cpp in Sources */, DDD74D9907CF48F50065AC9D /* cs_scheduler.cpp in Sources */, + DD0428462ACD7D3F00229B6D /* cs_sporadic.cpp in Sources */, DD0925B51FB05490000902DF /* project_list.cpp in Sources */, DDD74D9A07CF48F60065AC9D /* cs_statefile.cpp in Sources */, DDD74D9B07CF48F90065AC9D /* cs_trickle.cpp in Sources */, diff --git a/samples/Makefile.am b/samples/Makefile.am index e2cd86a4521..ca89532b4f8 100644 --- a/samples/Makefile.am +++ b/samples/Makefile.am @@ -16,7 +16,7 @@ endif ## list the apps that should be build ## NOTE: nvcuda and wrappture need external libraries -SUBDIRS = condor example_app multi_thread sleeper worker wrapper +SUBDIRS = condor example_app multi_thread sleeper sporadic worker wrapper if BUILD_WITH_VBOX SUBDIRS += vboxmonitor vboxwrapper diff --git a/samples/sporadic/Makefile b/samples/sporadic/Makefile new file mode 100644 index 00000000000..7a9be28f283 --- /dev/null +++ b/samples/sporadic/Makefile @@ -0,0 +1,57 @@ + +BOINC_DIR = ../.. +BOINC_SOURCE_API_DIR = $(BOINC_DIR)/api +BOINC_SOURCE_LIB_DIR = $(BOINC_DIR)/lib +BOINC_SOURCE_ZIP_DIR = $(BOINC_DIR)/zip + +ifdef ANDROID + BOINC_API_DIR = $(TCINCLUDES)/lib + BOINC_LIB_DIR = $(TCINCLUDES)/lib + BOINC_ZIP_DIR = $(TCINCLUDES)/lib + + MAKEFILE_LDFLAGS = + MAKEFILE_STDLIB = +else + BOINC_API_DIR = $(BOINC_SOURCE_API_DIR) + BOINC_LIB_DIR = $(BOINC_SOURCE_LIB_DIR) + BOINC_ZIP_DIR = $(BOINC_SOURCE_ZIP_DIR) + + MAKEFILE_LDFLAGS = -lpthread libstdc++.a + MAKEFILE_STDLIB = libstdc++.a +endif + +CXXFLAGS += -g \ + -Wall -W -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -fno-common \ + -I$(BOINC_DIR) \ + -I$(BOINC_SOURCE_API_DIR) \ + -I$(BOINC_SOURCE_LIB_DIR) \ + -I$(BOINC_SOURCE_ZIP_DIR) \ + -L$(BOINC_API_DIR) \ + -L$(BOINC_LIB_DIR) \ + -L. + +ifdef BUILD_WITH_VCPKG + BUILD_DIR = $(BOINC_DIR)/3rdParty/linux + VCPKG_DIR ?= $(BUILD_DIR)/vcpkg/installed/x64-linux + + CXXFLAGS += \ + -I$(VCPKG_DIR)/include \ + -L$(VCPKG_DIR)/lib +endif + +all: sporadic + +libstdc++.a: + ln -s `$(CXX) -print-file-name=libstdc++.a` + +clean: distclean + +distclean: + /bin/rm -f sporadic $(addsuffix .exe, sporadic) *.o libstdc++.a + +install: sporadic + +sporadic: sporadic.o $(MAKEFILE_STDLIB) $(BOINC_API_DIR)/libboinc_api.a $(BOINC_LIB_DIR)/libboinc.a + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -o sporadic sporadic.o \ + -lboinc_api -lboinc $(MAKEFILE_LDFLAGS) \ + $(STDCPPTC) diff --git a/samples/sporadic/sporadic.cpp b/samples/sporadic/sporadic.cpp new file mode 100644 index 00000000000..294fc22a0e3 --- /dev/null +++ b/samples/sporadic/sporadic.cpp @@ -0,0 +1,79 @@ +// test sporadic app +// +// loop +// wait for NWAIT secs +// wait for could compute +// ask to compute (simulate getting a request) +// when OK, compute for NCOMP secs +// suspend as needed +// +// computing is embedded in loop. +// in a real app you'd want to use threads + +#define NWAIT 10 +#define NCOMP 10 + +#include "boinc_api.h" +#include "util.h" +#include "common_defs.h" + +void boinc_sporadic_set_ac_state(SPORADIC_AC_STATE); +SPORADIC_CA_STATE boinc_sporadic_get_ca_state(); + +void compute_one_sec() { + double start = dtime(); + while (1) { + double x = 0; + for (int i=0; i<1e8; i++) { + x += 1; + } + if (dtime() > start+1) break; + } +} + +int main(int, char**) { + boinc_init(); + SPORADIC_CA_STATE ca_state; + SPORADIC_AC_STATE ac_state; + fprintf(stderr, "starting\n"); + while (true) { + // wait for a bit + ac_state = AC_DONT_WANT_COMPUTE; + boinc_sporadic_set_ac_state(ac_state); + for (int i=0; i + diff --git a/win_build/sim.vcxproj b/win_build/sim.vcxproj index 3932994a842..79b686746d8 100644 --- a/win_build/sim.vcxproj +++ b/win_build/sim.vcxproj @@ -306,6 +306,7 @@ + diff --git a/win_build/sporadic.vcxproj b/win_build/sporadic.vcxproj new file mode 100644 index 00000000000..be517991772 --- /dev/null +++ b/win_build/sporadic.vcxproj @@ -0,0 +1,278 @@ + + + + + + Debug + ARM64 + + + Debug + x64 + + + Release + ARM64 + + + Release + x64 + + + + example_app_sporadic + {0740E6DC-ADE7-4272-BAB0-52DE62325D3E} + + + + Application + MultiByte + v143 + + + Application + MultiByte + v143 + + + Application + MultiByte + v143 + + + Application + MultiByte + v143 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\$(ProjectName)\obj\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\$(ProjectName)\obj\ + + + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\$(ProjectName)\obj\ + $(SolutionDir)Build\$(Platform)\$(Configuration)\$(ProjectName)\obj\ + + + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + X64 + + + + + + + MaxSpeed + OnlyExplicitInline + Speed + .;..;../api;../lib;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + true + + + boinc_win.h + $(IntDir)boinc_win.pch + + + CompileAsCpp + boinc_win.h;%(ForcedIncludeFiles) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + libcmt.lib;libcpmt.lib;%(AdditionalDependencies) + true + false + %(DelayLoadDLLs) + .\Build\$(Platform)\$(Configuration)\$(ProjectName).pdb + Console + MachineX64 + $(OutDir) + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + + + + + + + MaxSpeed + OnlyExplicitInline + Speed + .;..;../api;../lib;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + true + + + boinc_win.h + $(IntDir)boinc_win.pch + + + CompileAsCpp + boinc_win.h;%(ForcedIncludeFiles) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + libcmt.lib;libcpmt.lib;%(AdditionalDependencies) + true + false + %(DelayLoadDLLs) + .\Build\$(Platform)\$(Configuration)\$(ProjectName).pdb + Console + $(OutDir) + + + + + %(PreprocessorDefinitions) + true + true + X64 + + + + + + + Disabled + .;..;../api;../lib;%(AdditionalIncludeDirectories) + WIN32;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + boinc_win.h + $(IntDir)boinc_win.pch + + + CompileAsCpp + %(ForcedIncludeFiles) + + + %(PreprocessorDefinitions) + 0x0409 + + + libcmtd.lib;libcpmtd.lib;kernel32.lib;user32.lib;delayimp.lib;%(AdditionalDependencies) + true + false + %(DelayLoadDLLs) + .\Build\$(Platform)\$(Configuration)\$(ProjectName).pdb + Console + MachineX64 + $(OutDir) + + + + + %(PreprocessorDefinitions) + true + true + + + + + + + Disabled + .;..;../api;../lib;%(AdditionalIncludeDirectories) + WIN32;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + boinc_win.h + $(IntDir)boinc_win.pch + + + CompileAsCpp + %(ForcedIncludeFiles) + + + %(PreprocessorDefinitions) + 0x0409 + + + libcmtd.lib;libcpmtd.lib;kernel32.lib;user32.lib;delayimp.lib;%(AdditionalDependencies) + true + false + %(DelayLoadDLLs) + .\Build\$(Platform)\$(Configuration)\$(ProjectName).pdb + Console + $(OutDir) + + + + + + + + {07bda8f7-4aaf-4a3b-b96e-ea72a143c5ae} + false + + + {e8f6bd7e-461a-4733-b7d8-37b09a099ed8} + + + + + + + \ No newline at end of file