From f45fb58cabfd3d590be9be852489b2409a7fac1e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 24 Oct 2023 17:33:34 -0700 Subject: [PATCH 1/5] API and wrappers: allow wrapped applications to communicate sporadic state via files. This allows, for example, sporadic VM apps. The logic for reading and writing of files is in the API library, rather than in the wrappers. Also: wrappers show message and exit if bad command line option. Also: small code shuffle in vboxwrapper to parse cmdline before doing anything. --- api/boinc_api.cpp | 78 +++++++++++++++++++- api/boinc_api.h | 1 + samples/vboxwrapper/vboxwrapper.cpp | 107 +++++++++++++++++----------- samples/wrapper/wrapper.cpp | 27 ++++++- 4 files changed, 169 insertions(+), 44 deletions(-) diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp index 5cded0cd01f..bdc9a98eca1 100644 --- a/api/boinc_api.cpp +++ b/api/boinc_api.cpp @@ -93,6 +93,8 @@ #include #include #include +#include +#include #include #include #include @@ -144,6 +146,10 @@ using std::vector; // CPPFLAGS=-DGETRUSAGE_IN_TIMER_THREAD #endif +// Anything shared between the worker and timer thread +// must be declared volatile to ensure that writes in one thread +// are seen immediately by the other. + const char* api_version = "API_VERSION_" PACKAGE_VERSION; static APP_INIT_DATA aid; static FILE_LOCK file_lock; @@ -194,7 +200,9 @@ 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; +static volatile SPORADIC_AC_STATE ac_state; +static volatile int ac_fd, ca_fd; +static volatile bool do_sporadic_files; #define TIMER_PERIOD 0.1 // Sleep interval for timer thread; @@ -518,6 +526,46 @@ static bool client_dead() { return false; } +// called once/sec in timer thread. +// Copy sporadic app messages to/from files (for wrappers) +// +static void sporadic_files() { + static time_t last_ac_mod_time; + static SPORADIC_CA_STATE last_ca_state; + char buf[256]; + + // if C->A state has changed, write to file + // + if (last_ca_state != boinc_status.ca_state) { + sprintf(buf, "%d\n", boinc_status.ca_state); + lseek(ac_fd, 0, SEEK_SET); + if (write(ac_fd, buf, strlen(buf))){}; + // one way to avoid warnings + } + + // check if app has updated file with A->C state + // + struct stat sbuf; + int ret = fstat(ac_fd, &sbuf); + if (!ret) { + time_t t = sbuf.st_mtim.tv_sec; + if (t != last_ac_mod_time) { + lseek(ac_fd, 0, SEEK_SET); + ret = read(ac_fd, buf, 256); + if (!ret) { + int val; + int n = sscanf(buf, "%d", &val); + if (n == 1) { + ac_state = (SPORADIC_AC_STATE)val; + } else { + ac_state = AC_NONE; + } + last_ac_mod_time = t; + } + } + } +} + #ifndef _WIN32 // For multithread apps on Unix, the main process executes the following. // @@ -882,6 +930,26 @@ int boinc_is_standalone() { return 0; } +int boinc_sporadic_dir(const char* dir) { + char buf[MAXPATHLEN]; + + do_sporadic_files = true; + sprintf(buf, "%s/ac", dir); + ac_fd = open(buf, O_CREAT|O_RDONLY, 0666); + if (ac_fd < 0) { + fprintf(stderr, "can't open sporadic file %s\n", buf); + do_sporadic_files = false; + } + sprintf(buf, "%s/ca", dir); + ca_fd = open(buf, O_CREAT|O_WRONLY, 0666); + if (ca_fd < 0) { + fprintf(stderr, "can't open sporadic file %s\n", buf); + do_sporadic_files = false; + } + if (!do_sporadic_files) return ERR_FOPEN; + return 0; +} + // called from the timer thread if we need to exit, // e.g. quit message from client, or client has gone away // @@ -1058,7 +1126,7 @@ static int suspend_activities(bool called_from_worker) { suspend_or_resume_descendants(false); } // if called from worker thread, sleep until suspension is over - // if called from time thread, don't need to do anything; + // if called from timer thread, don't need to do anything; // suspension is done by signal handler in worker thread // if (called_from_worker) { @@ -1365,6 +1433,10 @@ static void timer_handler() { app_client_shm->shm->graphics_reply.send_msg(buf); send_remote_desktop_addr = false; } + + if (do_sporadic_files) { + sporadic_files(); + } } #ifdef _WIN32 @@ -1489,7 +1561,7 @@ int start_timer_thread() { // called in the worker thread. // set up a handler for SIGALRM. -// If Android, we'll get signals from the time thread. +// If Android, we'll get signals from the timer thread. // otherwise, set an interval timer to deliver signals // static int start_worker_signals() { diff --git a/api/boinc_api.h b/api/boinc_api.h index 1218de36642..acb65bb8452 100644 --- a/api/boinc_api.h +++ b/api/boinc_api.h @@ -148,6 +148,7 @@ extern int boinc_finish_message( ); extern void boinc_sporadic_set_ac_state(SPORADIC_AC_STATE); extern SPORADIC_CA_STATE boinc_sporadic_get_ca_state(); +extern int boinc_sporadic_dir(const char*); /////////// API ENDS HERE diff --git a/samples/vboxwrapper/vboxwrapper.cpp b/samples/vboxwrapper/vboxwrapper.cpp index 8fb83cc40bd..ca5c5f40528 100644 --- a/samples/vboxwrapper/vboxwrapper.cpp +++ b/samples/vboxwrapper/vboxwrapper.cpp @@ -367,6 +367,19 @@ void check_trickle_period(double& elapsed_time, double& trickle_period) { } } +void usage() { + vboxlog_msg( + "Options:\n" + " --trickle X\n" + " --nthreads N\n" + " --memory_size_mb X\n" + " --vmimage F\n" + " --register_only\n" + " --sporadic\n" + ); + boinc_finish(EXIT_INIT_FAILURE); +} + int main(int argc, char** argv) { int retval = 0; int loop_iteration = 0; @@ -406,11 +419,34 @@ int main(int argc, char** argv) { string message; string scratch_dir; char buf[256]; + bool is_sporadic = false; + bool register_only = false; // Initialize diagnostics system // boinc_init_diagnostics(BOINC_DIAG_DEFAULTS); + // Parse command line + // + for (int i=1; iinitialize(); - if (retval) { - vboxlog_msg("ERROR: VM initialization failed with return code: %s", retval); - //Chose not to postpone the task but rather just fail it. In the majority of cases - //if the hypervisor does not get initialized correctly the configuration is wrong - //and it will just keep failing to initialize. - // - boinc_finish(retval); - } - } - - // Parse command line parameters - // - for (int i=1; iregister_only = true; - } + pVM = (VBOX_VM*) new vboxmanage::VBOX_VM(); + retval = pVM->initialize(); + if (retval) { + vboxlog_msg("ERROR: VM initialization returned %d", retval); + // don't postpone the task but rather just fail it. + // If the hypervisor does not initialize correctly + // the configuration is probably wrong + // and it will just keep failing to initialize. + // + boinc_finish(retval); } + pVM->register_only= register_only; // Display trickle value if specified // if (trickle_period > 0.0) { vboxlog_msg( - "Feature: Enabling trickle-ups (Interval: %f)", trickle_period - ); + "Feature: Enabling trickle-ups (Interval: %f)", trickle_period + ); } // Check for architecture incompatibilities @@ -535,12 +553,13 @@ int main(int argc, char** argv) { if ((pVM->virtualbox_version_raw.find("4.2.6") != std::string::npos) || (pVM->virtualbox_version_raw.find("4.2.18") != std::string::npos) || - (pVM->virtualbox_version_raw.find("4.3.0") != std::string::npos) ) { + (pVM->virtualbox_version_raw.find("4.3.0") != std::string::npos) + ) { vboxlog_msg("Incompatible version of VirtualBox detected. Please upgrade to a later version."); boinc_temporary_exit(86400, - "Incompatible version of VirtualBox detected; please upgrade.", - true - ); + "Incompatible version of VirtualBox detected; please upgrade.", + true + ); } // Check to see if the system is in a state in which @@ -593,6 +612,14 @@ int main(int argc, char** argv) { } } + if (is_sporadic) { + retval = boinc_sporadic_dir("shared"); + if (retval) { + vboxlog_msg("ERROR: couldn't create sporadic files: %s.", boincerror(retval)); + exit(1); + } + } + // Copy files to the shared directory // if (pVM->enable_shared_directory && pVM->copy_to_shared.size()) { diff --git a/samples/wrapper/wrapper.cpp b/samples/wrapper/wrapper.cpp index 70e2bd2b9df..e7ca5255c4c 100644 --- a/samples/wrapper/wrapper.cpp +++ b/samples/wrapper/wrapper.cpp @@ -1161,6 +1161,18 @@ void check_executables() { check_execs(daemons); } +void usage() { + fprintf(stderr, + "Options:\n" + " --nthreads N\n" + " --device N\n" + " --sporadic\n" + " --trickle X\n" + " --version\n" + ); + boinc_finish(EXIT_INIT_FAILURE); +} + int main(int argc, char** argv) { BOINC_OPTIONS options; int retval, ntasks_completed; @@ -1169,6 +1181,7 @@ int main(int argc, char** argv) { double checkpoint_cpu_time; // total CPU time at last checkpoint char buf[256]; + bool is_sporadic = false; // Log banner // @@ -1186,6 +1199,8 @@ int main(int argc, char** argv) { nthreads = atoi(argv[++j]); } else if (!strcmp(argv[j], "--device")) { gpu_device_num = atoi(argv[++j]); + } else if (!strcmp(argv[j], "--sporadic")) { + is_sporadic = true; } else if (!strcmp(argv[j], "--trickle")) { trickle_period = atof(argv[++j]); #if !(defined(_WIN32) || defined(__APPLE__)) @@ -1193,8 +1208,10 @@ int main(int argc, char** argv) { fprintf(stderr, "%s\n", SVN_VERSION); boinc_finish(0); #endif + } else { + fprintf(stderr, "Unrecognized option %s\n", argv[j]); + usage(); } - } retval = parse_job_file(); @@ -1255,6 +1272,14 @@ int main(int argc, char** argv) { tasks[i].substitute_macros(); } + if (is_sporadic) { + retval = boinc_sporadic_dir("."); + if (retval) { + fprintf(stderr, "can't create sporadic files\n"); + boinc_finish(retval); + } + } + retval = start_daemons(argc, argv); if (retval) { fprintf(stderr, From b9f22201127745774c5f11a751d128383359071b Mon Sep 17 00:00:00 2001 From: davidpanderson Date: Tue, 24 Oct 2023 17:52:20 -0700 Subject: [PATCH 2/5] Win build fixes --- api/boinc_api.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp index bdc9a98eca1..749bd098e15 100644 --- a/api/boinc_api.cpp +++ b/api/boinc_api.cpp @@ -539,7 +539,7 @@ static void sporadic_files() { if (last_ca_state != boinc_status.ca_state) { sprintf(buf, "%d\n", boinc_status.ca_state); lseek(ac_fd, 0, SEEK_SET); - if (write(ac_fd, buf, strlen(buf))){}; + if (write(ac_fd, buf, sizeof(buf))) {} // one way to avoid warnings } @@ -548,10 +548,14 @@ static void sporadic_files() { struct stat sbuf; int ret = fstat(ac_fd, &sbuf); if (!ret) { +#ifdef _WIN32 + time_t t = sbuf.st_mtime; +#else time_t t = sbuf.st_mtim.tv_sec; +#endif if (t != last_ac_mod_time) { lseek(ac_fd, 0, SEEK_SET); - ret = read(ac_fd, buf, 256); + ret = read(ac_fd, buf, sizeof(buf)); if (!ret) { int val; int n = sscanf(buf, "%d", &val); From e31a1562f2706b48028846192d53cf745c064b07 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 27 Oct 2023 15:04:07 -0700 Subject: [PATCH 3/5] Debug sporadic wrapper feature API: debug sporadic stuff. Remove 2-second exit delay in standalone mode. client: if , show the heartbeat message don't create slot dir for --app_test job (it uses slots/app_test) don't show sporadic resoures used if there are none sporadic: add --wrapped option: don't use BOINC api; communicate via files wrapper: don't print start msg twice --- api/boinc_api.cpp | 32 ++++++++++----- client/app_control.cpp | 4 +- client/app_test.cpp | 3 ++ client/cpu_sched.cpp | 10 +++-- client/cs_cmdline.cpp | 1 + client/cs_sporadic.cpp | 1 + samples/sporadic/sporadic.cpp | 75 ++++++++++++++++++++++++++++++----- samples/wrapper/wrapper.cpp | 9 ----- 8 files changed, 102 insertions(+), 33 deletions(-) diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp index 749bd098e15..22899d20fb0 100644 --- a/api/boinc_api.cpp +++ b/api/boinc_api.cpp @@ -161,7 +161,7 @@ static volatile double last_checkpoint_cpu_time; static volatile bool ready_to_checkpoint = false; static volatile int in_critical_section = 0; static volatile double last_wu_cpu_time; -static volatile bool standalone = false; +static volatile bool standalone = true; static volatile double initial_wu_cpu_time; static volatile bool have_new_trickle_up = false; static volatile bool have_trickle_down = true; @@ -530,17 +530,18 @@ static bool client_dead() { // Copy sporadic app messages to/from files (for wrappers) // static void sporadic_files() { - static time_t last_ac_mod_time; - static SPORADIC_CA_STATE last_ca_state; + static time_t last_ac_mod_time = 0; + static SPORADIC_CA_STATE last_ca_state = CA_NONE; char buf[256]; // if C->A state has changed, write to file // if (last_ca_state != boinc_status.ca_state) { sprintf(buf, "%d\n", boinc_status.ca_state); - lseek(ac_fd, 0, SEEK_SET); - if (write(ac_fd, buf, sizeof(buf))) {} + lseek(ca_fd, 0, SEEK_SET); + if (write(ca_fd, buf, sizeof(buf))) {} // one way to avoid warnings + last_ca_state = boinc_status.ca_state; } // check if app has updated file with A->C state @@ -555,16 +556,20 @@ static void sporadic_files() { #endif if (t != last_ac_mod_time) { lseek(ac_fd, 0, SEEK_SET); - ret = read(ac_fd, buf, sizeof(buf)); - if (!ret) { + int nc = read(ac_fd, buf, sizeof(buf)); + if (nc>0) { int val; + buf[nc] = 0; int n = sscanf(buf, "%d", &val); if (n == 1) { ac_state = (SPORADIC_AC_STATE)val; } else { ac_state = AC_NONE; + fprintf(stderr, "API: error parsing AC state: %s\n", buf); } last_ac_mod_time = t; + } else { + fprintf(stderr, "API: error reading AC state: %d\n", nc); } } } @@ -739,6 +744,7 @@ int boinc_init_options_general(BOINC_OPTIONS& opt) { } } + standalone = false; retval = boinc_parse_init_data_file(); if (retval) { standalone = true; @@ -822,8 +828,10 @@ int boinc_finish_message(int status, const char* msg, bool is_notice) { boinc_msg_prefix(buf, sizeof(buf)), status ); finishing = true; - boinc_sleep(2.0); // let the timer thread send final messages - boinc_disable_timer_thread = true; // then disable it + if (!standalone) { + boinc_sleep(2.0); // let the timer thread send final messages + boinc_disable_timer_thread = true; // then disable it + } if (options.main_program) { FILE* f = fopen(BOINC_FINISH_CALLED_FILE, "w"); @@ -951,6 +959,8 @@ int boinc_sporadic_dir(const char* dir) { do_sporadic_files = false; } if (!do_sporadic_files) return ERR_FOPEN; + boinc_status.ca_state = CA_DONT_COMPUTE; + ac_state = AC_NONE; return 0; } @@ -1065,6 +1075,10 @@ int boinc_report_app_status_aux( snprintf(buf, sizeof(buf), "%f\n", _bytes_received); safe_strcat(msg_buf, 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); diff --git a/client/app_control.cpp b/client/app_control.cpp index 99b9349d619..b03a9875db1 100644 --- a/client/app_control.cpp +++ b/client/app_control.cpp @@ -738,8 +738,8 @@ void ACTIVE_TASK_SET::send_heartbeats() { if (log_flags.heartbeat_debug) { if (sent) { msg_printf(atp->result->project, MSG_INFO, - "[heartbeat] Heartbeat sent to task %s", - atp->result->name + "[heartbeat] Heartbeat sent to task %s: %s", + atp->result->name, buf ); } else { msg_printf(atp->result->project, MSG_INFO, diff --git a/client/app_test.cpp b/client/app_test.cpp index e63723730c9..d1a70070893 100644 --- a/client/app_test.cpp +++ b/client/app_test.cpp @@ -63,9 +63,11 @@ void CLIENT_STATE::app_test_init() { // can put other stuff here like av->avg_ncpus = 1; av->flops = 1e9; +#if 0 av->gpu_ram = 1e7; av->gpu_usage.rsc_type = PROC_TYPE_NVIDIA_GPU; av->gpu_usage.usage = 1; +#endif app_versions.push_back(av); WORKUNIT *wu = new WORKUNIT; @@ -77,6 +79,7 @@ void CLIENT_STATE::app_test_init() { wu->rsc_fpops_bound = 1e12; wu->rsc_memory_bound = 1e9; wu->rsc_disk_bound = 1e9; + wu->command_line = "--sporadic"; workunits.push_back(wu); RESULT *res = new RESULT; diff --git a/client/cpu_sched.cpp b/client/cpu_sched.cpp index d844cda29ef..cf178964a96 100644 --- a/client/cpu_sched.cpp +++ b/client/cpu_sched.cpp @@ -1626,10 +1626,12 @@ ACTIVE_TASK* CLIENT_STATE::get_task(RESULT* rp) { ACTIVE_TASK *atp = lookup_active_task_by_result(rp); if (!atp) { atp = new ACTIVE_TASK; - int retval = atp->get_free_slot(rp); - if (retval) { - delete atp; - return NULL; + if (!rp->project->app_test) { + int retval = atp->get_free_slot(rp); + if (retval) { + delete atp; + return NULL; + } } atp->init(rp); active_tasks.active_tasks.push_back(atp); diff --git a/client/cs_cmdline.cpp b/client/cs_cmdline.cpp index e7a624d6141..7cc616d381f 100644 --- a/client/cs_cmdline.cpp +++ b/client/cs_cmdline.cpp @@ -49,6 +49,7 @@ static void print_options(char* prog) { " --abort_jobs_on_exit when client exits, abort and report jobs\n" " --allow_remote_gui_rpc allow remote GUI RPC connections\n" " --allow_multiple_clients allow >1 instances per host\n" + " --app_test F run a simulated job with the given app\n" " --attach_project attach to a project\n" " --check_all_logins for idle detection, check remote logins too\n" " --daemon run as daemon (Unix)\n" diff --git a/client/cs_sporadic.cpp b/client/cs_sporadic.cpp index 939b9a8916e..33586a1df6f 100644 --- a/client/cs_sporadic.cpp +++ b/client/cs_sporadic.cpp @@ -82,6 +82,7 @@ SPORADIC_RESOURCES sporadic_resources; void SPORADIC_RESOURCES::print() { + if (!ncpus_used) return; 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); diff --git a/samples/sporadic/sporadic.cpp b/samples/sporadic/sporadic.cpp index 294fc22a0e3..cda264c7df5 100644 --- a/samples/sporadic/sporadic.cpp +++ b/samples/sporadic/sporadic.cpp @@ -7,18 +7,27 @@ // when OK, compute for NCOMP secs // suspend as needed // -// computing is embedded in loop. +// computing is embedded in the loop. // in a real app you'd want to use threads +// by default this uses the BOINC API for communicating sporadic state. +// --wrapped: use files instead (run under wrapper) + #define NWAIT 10 #define NCOMP 10 +#include +#include +#include +#include +#include + #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(); +bool wrapped = false; +int ac_fd, ca_fd; void compute_one_sec() { double start = dtime(); @@ -31,22 +40,70 @@ void compute_one_sec() { } } -int main(int, char**) { - boinc_init(); +void set_ac_state(SPORADIC_AC_STATE ac_state) { + static SPORADIC_AC_STATE last = AC_NONE; + if (wrapped) { + if (ac_state != last) { + char buf[256]; + sprintf(buf, "%d\n", ac_state); + lseek(ac_fd, 0, SEEK_SET); + write(ac_fd, buf, strlen(buf)); + } + last = ac_state; + } else { + boinc_sporadic_set_ac_state(ac_state); + } +} + +SPORADIC_CA_STATE get_ca_state() { + if (wrapped) { + // could check mod time; don't bother + char buf[256]; + lseek(ca_fd, 0, SEEK_SET); + read(ca_fd, buf, sizeof(buf)); + int s; + int n = sscanf(buf, "%d", &s); + if (n==1) return (SPORADIC_CA_STATE)s; + fprintf(stderr, "can't read CA state\n"); + exit(1); + } else { + return boinc_sporadic_get_ca_state(); + } +} + +int main(int argc, char** argv) { SPORADIC_CA_STATE ca_state; SPORADIC_AC_STATE ac_state; + + for (int i=1; i Date: Fri, 27 Oct 2023 15:52:46 -0700 Subject: [PATCH 4/5] Mac compile fix --- api/boinc_api.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/boinc_api.cpp b/api/boinc_api.cpp index 22899d20fb0..be7b345d3e9 100644 --- a/api/boinc_api.cpp +++ b/api/boinc_api.cpp @@ -551,6 +551,8 @@ static void sporadic_files() { if (!ret) { #ifdef _WIN32 time_t t = sbuf.st_mtime; +#elif defined(__APPLE__) + time_t t = sbuf.st_mtimespec.tv_sec; #else time_t t = sbuf.st_mtim.tv_sec; #endif From 43efa3a368a02336e3ee46f1baba6cbea5913884 Mon Sep 17 00:00:00 2001 From: davidpanderson Date: Fri, 27 Oct 2023 21:06:18 -0700 Subject: [PATCH 5/5] Win compile fix --- samples/sporadic/sporadic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/sporadic/sporadic.cpp b/samples/sporadic/sporadic.cpp index cda264c7df5..6f29dabee35 100644 --- a/samples/sporadic/sporadic.cpp +++ b/samples/sporadic/sporadic.cpp @@ -18,7 +18,9 @@ #include #include +#ifndef _WIN32 #include +#endif #include #include