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