API: my revisions to David Kim's Android version

This commit is contained in:
David Anderson 2017-04-07 16:46:45 -07:00
parent 35d40bbfc0
commit d726d1b0bc
1 changed files with 65 additions and 28 deletions

View File

@ -21,14 +21,31 @@
// 1) Thread structure: // 1) Thread structure:
// Sequential apps // Sequential apps
// Unix // Unix
// getting CPU time and suspend/resume have to be done // Suspend/resume have to be done in the worker thread,
// in the worker thread, so we use a SIGALRM signal handler. // so we use a 10 Hz SIGALRM signal handler.
// However, many library functions and system calls // Also get CPU time (getrusage()) in the signal handler.
// Note: many library functions and system calls
// are not "asynch signal safe": see, e.g. // are not "asynch signal safe": see, e.g.
// http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 // http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03
// (e.g. sprintf() in a signal handler hangs Mac OS X) // (e.g. sprintf() in a signal handler hangs Mac OS X).
// so we do as little as possible in the signal handler, // Can't do floating-point math because FP regs not saved.
// So we do as little as possible in the signal handler,
// and do the rest in a separate "timer thread". // and do the rest in a separate "timer thread".
// - send status and graphics messages to client
// - handle messages from client
// - set ready-to-checkpoint flag
// - check heartbeat
// - call app-defined timer callback function
// Mac: similar to Linux,
// but getrusage() in the worker signal handler causes crashes,
// so do it in the timer thread (GETRUSAGE_IN_TIMER_THREAD)
// TODO: why not do this on Linux too?
// Android: similar to Linux,
// but setitimer() causes crashes on some Android versions,
// so instead of using a periodic signal,
// have the timer thread send SIGALRM signals to the worker thread
// every .1 sec.
// TODO: for uniformity should we do this on Linux as well?
// Win // Win
// the timer thread does everything // the timer thread does everything
// Multi-thread apps: // Multi-thread apps:
@ -109,9 +126,12 @@
using std::vector; using std::vector;
//#define DEBUG_BOINC_API //#define DEBUG_BOINC_API
// enable a bunch of fprintfs to stderr
#ifdef __APPLE__ #ifdef __APPLE__
#include "mac_backtrace.h" #include "mac_backtrace.h"
#endif
#if defined(__APPLE__) || defined(ANDROID)
#define GETRUSAGE_IN_TIMER_THREAD #define GETRUSAGE_IN_TIMER_THREAD
// call getrusage() in the timer thread, // call getrusage() in the timer thread,
// rather than in the worker thread's signal handler // rather than in the worker thread's signal handler
@ -284,7 +304,7 @@ static int setup_shared_mem() {
} }
#else #else
if (aid.shmem_seg_name == -1) { if (aid.shmem_seg_name == -1) {
// Version 6 Unix/Linux/Mac client // Version 6 Unix/Linux/Mac client
if (attach_shmem_mmap(MMAPPED_FILE_NAME, (void**)&app_client_shm->shm)) { if (attach_shmem_mmap(MMAPPED_FILE_NAME, (void**)&app_client_shm->shm)) {
delete app_client_shm; delete app_client_shm;
app_client_shm = NULL; app_client_shm = NULL;
@ -686,8 +706,8 @@ static void send_trickle_up_msg() {
} }
} }
// NOTE: a non-zero status tells the client that we're exiting with // NOTE: a non-zero status tells the client that we're exiting with
// an "unrecoverable error", which will be reported back to server. // an "unrecoverable error", which will be reported back to server.
// A zero exit-status tells the client we've successfully finished the result. // A zero exit-status tells the client we've successfully finished the result.
// //
int boinc_finish_message(int status, const char* msg, bool is_notice) { int boinc_finish_message(int status, const char* msg, bool is_notice) {
@ -810,6 +830,9 @@ int boinc_is_standalone() {
return 0; return 0;
} }
// called from the timer thread if we need to exit,
// e.g. quit message from client, or client has gone away
//
static void exit_from_timer_thread(int status) { static void exit_from_timer_thread(int status) {
#ifdef DEBUG_BOINC_API #ifdef DEBUG_BOINC_API
char buf[256]; char buf[256];
@ -826,11 +849,22 @@ static void exit_from_timer_thread(int status) {
// //
boinc_exit(status); boinc_exit(status);
#else #else
// but on Unix there are synchronization problems; // but on Unix there are synchronization problems if we exit here;
// set a flag telling the worker thread to exit // set a flag telling the worker thread to exit
// //
worker_thread_exit_status = status; worker_thread_exit_status = status;
worker_thread_exit_flag = true; worker_thread_exit_flag = true;
#ifdef ANDROID
// trigger the worker signal handler, which will call boinc_exit()
//
pthread_kill(worker_thread_handle, SIGALRM);
// the exit should happen more or less instantly.
// But if we're still here after 1 sec, exit directly
//
sleep(1.0);
boinc_exit(status);
#endif
pthread_exit(NULL); pthread_exit(NULL);
#endif #endif
} }
@ -1093,6 +1127,9 @@ static void handle_process_control_msg() {
BOINCINFO("Received quit message"); BOINCINFO("Received quit message");
boinc_status.quit_request = true; boinc_status.quit_request = true;
if (!in_critical_section && options.direct_process_action) { if (!in_critical_section && options.direct_process_action) {
release_mutex();
// we hold mutex, and it's possible that worker
// is waiting on it, so release it
exit_from_timer_thread(0); exit_from_timer_thread(0);
} }
} }
@ -1121,7 +1158,7 @@ static void handle_process_control_msg() {
} }
} }
// timer handler; runs in the timer thread // timer handler; called every 0.1 sec in the timer thread
// //
static void timer_handler() { static void timer_handler() {
char buf[512]; char buf[512];
@ -1162,6 +1199,11 @@ static void timer_handler() {
handle_process_control_msg(); handle_process_control_msg();
} }
} }
#ifdef ANDROID
// Trigger call to worker_signal_handler() in the worker thread
//
pthread_kill(worker_thread_handle, SIGALRM);
#endif
if (interrupt_count % TIMERS_PER_SEC) return; if (interrupt_count % TIMERS_PER_SEC) return;
#ifdef DEBUG_BOINC_API #ifdef DEBUG_BOINC_API
@ -1201,7 +1243,7 @@ static void timer_handler() {
last_wu_cpu_time = cur_cpu + initial_wu_cpu_time; last_wu_cpu_time = cur_cpu + initial_wu_cpu_time;
update_app_progress(last_wu_cpu_time, last_checkpoint_cpu_time); update_app_progress(last_wu_cpu_time, last_checkpoint_cpu_time);
} }
if (have_new_trickle_up || have_new_upload_file) { if (have_new_trickle_up || have_new_upload_file) {
send_trickle_up_msg(); send_trickle_up_msg();
} }
@ -1232,7 +1274,6 @@ static void timer_handler() {
#ifdef _WIN32 #ifdef _WIN32
DWORD WINAPI timer_thread(void *) { DWORD WINAPI timer_thread(void *) {
while (1) { while (1) {
Sleep((int)(TIMER_PERIOD*1000)); Sleep((int)(TIMER_PERIOD*1000));
timer_handler(); timer_handler();
@ -1270,17 +1311,6 @@ static void worker_signal_handler(int) {
} }
if (options.direct_process_action) { if (options.direct_process_action) {
while (boinc_status.suspended && in_critical_section==0) { while (boinc_status.suspended && in_critical_section==0) {
#ifdef ANDROID
// per-thread signal masking doesn't work
// on old (pre-4.1) versions of Android.
// If we're handling this signal in the timer thread,
// send signal explicitly to worker thread.
//
if (pthread_self() == timer_thread_handle) {
pthread_kill(worker_thread_handle, SIGALRM);
return;
}
#endif
sleep(1); // don't use boinc_sleep() because it does FP math sleep(1); // don't use boinc_sleep() because it does FP math
} }
} }
@ -1317,7 +1347,7 @@ int start_timer_thread() {
); );
return errno; return errno;
} }
if (!options.normal_thread_priority) { if (!options.normal_thread_priority) {
// lower our (worker thread) priority // lower our (worker thread) priority
// //
@ -1341,28 +1371,35 @@ int start_timer_thread() {
} }
#ifndef _WIN32 #ifndef _WIN32
// set up a periodic SIGALRM, to be handled by the worker thread
// called in the worker thread.
// set up a handler for SIGALRM.
// If Android, we'll get signals from the time thread.
// otherwise, set an interval timer to deliver signals
// //
static int start_worker_signals() { static int start_worker_signals() {
int retval; int retval;
struct sigaction sa; struct sigaction sa;
itimerval value; memset(&sa, 0, sizeof(sa));
sa.sa_handler = worker_signal_handler; sa.sa_handler = worker_signal_handler;
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
retval = sigaction(SIGALRM, &sa, NULL); retval = sigaction(SIGALRM, &sa, NULL);
if (retval) { if (retval) {
perror("boinc start_timer_thread() sigaction"); perror("boinc start_worker_signals(): sigaction failed");
return retval; return retval;
} }
#ifndef ANDROID
itimerval value;
value.it_value.tv_sec = 0; value.it_value.tv_sec = 0;
value.it_value.tv_usec = (int)(TIMER_PERIOD*1e6); value.it_value.tv_usec = (int)(TIMER_PERIOD*1e6);
value.it_interval = value.it_value; value.it_interval = value.it_value;
retval = setitimer(ITIMER_REAL, &value, NULL); retval = setitimer(ITIMER_REAL, &value, NULL);
if (retval) { if (retval) {
perror("boinc start_timer_thread() setitimer"); perror("boinc start_worker_thread(): setitimer failed");
return retval; return retval;
} }
#endif
return 0; return 0;
} }
#endif #endif