diff --git a/api/Makefile.in b/api/Makefile.in index 391e160b54..fa659d3297 100644 --- a/api/Makefile.in +++ b/api/Makefile.in @@ -15,15 +15,17 @@ CC = @CC@ $(CFLAGS) PROGS = api_app api_test -all: $(PROGS) +#all: $(PROGS) +all: mfile.o boinc_api.o APP_OBJS = \ api_app.o \ - api.o \ + mfile.o \ + boinc_api.o \ ../lib/parse.o TEST_OBJS = \ - api.o \ + boinc_api.o \ api_test.o \ ../lib/parse.o diff --git a/api/api.C b/api/api.C deleted file mode 100644 index 9f35f1debb..0000000000 --- a/api/api.C +++ /dev/null @@ -1,367 +0,0 @@ -// The contents of this file are subject to the Mozilla Public License -// Version 1.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://www.mozilla.org/MPL/ -// -// Software distributed under the License is distributed on an "AS IS" -// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -// License for the specific language governing rights and limitations -// under the License. -// -// The Original Code is the Berkeley Open Infrastructure for Network Computing. -// -// The Initial Developer of the Original Code is the SETI@home project. -// Portions created by the SETI@home project are Copyright (C) 2002 -// University of California at Berkeley. All Rights Reserved. -// -// Contributor(s): -// - -#include "windows_cpp.h" - -#include -#include -#include -#include -#ifdef WIN32 -#include -#include -#include -#include -#endif -#if HAVE_UNISTD_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include - -#include -#include -#include "parse.h" -#include "api.h" -#include "error_numbers.h" - -int MFILE::open(char* path, char* mode) { - buf = 0; - len = 0; - f = fopen(path, mode); - if (!f) return -1; - return 0; -} - -int MFILE::printf(char* format, ...) { - va_list ap; - char buf2[4096]; - int n, k; - - va_start(ap, format); - k = vsprintf(buf2, format, ap); - va_end(ap); - n = strlen(buf2); - buf = (char*)realloc(buf, len+n); - strncpy(buf+len, buf2, n); - len += n; - return k; -} - -size_t MFILE::write(const void *ptr,size_t size,size_t nitems) { - buf = (char *)realloc( buf, len+(size*nitems) ); - memcpy( buf+len, ptr, size*nitems ); - len += size*nitems; - return nitems; -} - -int MFILE::_putchar(char c) { - buf = (char*)realloc(buf, len+1); - buf[len] = c; - len++; - return c; -} - -int MFILE::puts(char* p) { - int n = strlen(p); - buf = (char*)realloc(buf, len+n); - strncpy(buf+len, p, n); - len += n; - return 0; -} - -int MFILE::close() { - fwrite(buf, 1, len, f); - free(buf); - buf = 0; - return fclose(f); -} - -int MFILE::flush() { - fwrite(buf, 1, len, f); - len = 0; - return fflush(f); -} - -static void write_core_file(FILE* f, APP_IN& ai) { - fprintf(f, - "%d\n" - "%d\n" - "%f\n" - "%f\n" - "%f\n" - "%f\n" - "%f\n", - ai.graphics.xsize, - ai.graphics.ysize, - ai.graphics.refresh_period, - ai.checkpoint_period, - ai.poll_period, - ai.checkpoint_period, - ai.cpu_time - ); -} - -static void parse_core_file(FILE* f, APP_IN& ai) { - char buf[256]; - while (fgets(buf, 256, f)) { - if (match_tag(buf, "")) { - strcpy(ai.app_preferences, ""); - while (fgets(buf, 256, f)) { - if (match_tag(buf, "")) break; - strcat(ai.app_preferences, buf); - } - continue; - } - else if (parse_int(buf, "", ai.graphics.xsize)) continue; - else if (parse_int(buf, "", ai.graphics.ysize)) continue; - else if (parse_double(buf, "", ai.graphics.refresh_period)) continue; - else if (parse_double(buf, "", ai.checkpoint_period)) continue; - else if (parse_double(buf, "", ai.poll_period)) continue; - else if (parse_double(buf, "", ai.checkpoint_period)) continue; - else if (parse_double(buf, "", ai.cpu_time)) continue; - else fprintf(stderr, "parse_core_file: unrecognized %s", buf); - } -} - -static void write_app_file(FILE* f, APP_OUT& ao) { - fprintf(f, - "%f\n" - "%f\n", - ao.percent_done, - ao.cpu_time_at_checkpoint - ); - if (ao.checkpointed) { - fprintf(f, "\n"); - } -} - -static void parse_app_file(FILE* f, APP_OUT& ao) { - char buf[256]; - while (fgets(buf, 256, f)) { - if (parse_double(buf, "", ao.percent_done)) continue; - else if (parse_double(buf, "", - ao.cpu_time_at_checkpoint)) continue; - else if (match_tag(buf, "")) ao.checkpointed = true; - else fprintf(stderr, "parse_app_file: unrecognized %s", buf); - } -} - -static void write_init_file(FILE* f, char *file_name, int fdesc, int input_file ) { - if( input_file ) { - fprintf( f, "%s\n", file_name ); - fprintf( f, "%d\n", fdesc ); - } else { - fprintf( f, "%s\n", file_name ); - fprintf( f, "%d\n", fdesc ); - } -} - -void parse_init_file(FILE* f) { - char buf[256],filename[256]; - int filedesc,fd,retval; - while (fgets(buf, 256, f)) { - if (parse_str(buf, "", filename)) { - if (fgets(buf, 256, f)) { - if (parse_int(buf, "", filedesc)) { - fd = open(filename, O_RDONLY); - if (fd != filedesc) { - retval = dup2(fd, filedesc); - if (retval < 0) { - fprintf(stderr, "dup2 %d %d returned %d\n", fd, filedesc, retval); - exit(retval); - } - close(fd); - } - } - } - continue; - } - else if (parse_str(buf, "", filename)) { - if (fgets(buf, 256, f)) { - if (parse_int(buf, "", filedesc)) { - fd = open(filename, O_WRONLY|O_CREAT, 0660); - if (fd != filedesc) { - retval = dup2(fd, filedesc); - if (retval < 0) { - fprintf(stderr, "dup2 %d %d returned %d\n", fd, filedesc, retval); - exit(retval); - } - close(fd); - } - } - } - continue; - } - else fprintf(stderr, "parse_init_file: unrecognized %s", buf); - } -} - - -// read the BOINC_INIT_FILE and CORE_TO_APP_FILE files -// and performs the necessary initialization -// -void boinc_init(APP_IN& ai) { - FILE* f; - - memset(&ai, 0, sizeof(ai)); - f = fopen(CORE_TO_APP_FILE, "r"); - if (f) { - parse_core_file(f, ai); - unlink(CORE_TO_APP_FILE); - } - - f = fopen( BOINC_INIT_FILE, "r" ); - if (f) { - parse_init_file(f); - unlink(BOINC_INIT_FILE); - } - set_timer(ai.checkpoint_period); -} - -void boinc_poll(APP_IN& ai, APP_OUT& ao) { - FILE* f; - - f = fopen("_app_temp", "w"); - write_app_file(f, ao); - rename("_app_temp", APP_TO_CORE_FILE); -} - -// resolve XML soft links -// -int boinc_resolve_link(char *file_name, char *resolved_name) { - FILE *fp; - char buf[512]; - - // Open the file and load the first line - fp = fopen( file_name, "r" ); - if (!fp) { - strcpy( resolved_name, file_name ); - return -1; - } - rewind( fp ); - fgets(buf, 512, fp); - fclose( fp ); - - // If it's the XML tag, return it's value, - // otherwise, return the original file name - // - if( !parse_str( buf, "", resolved_name ) ) { - strcpy( resolved_name, file_name ); - } - - return 0; -} - -bool _checkpoint = false; - -double get_cpu_time() { -#ifdef HAVE_SYS_RESOURCE_H - int retval, pid = getpid(); - struct rusage ru; - retval = getrusage(RUSAGE_SELF, &ru); - if(retval) fprintf(stderr, "error: could not get cpu time for %d\n", pid); - return (double)ru.ru_utime.tv_sec + ( - ((double)ru.ru_utime.tv_usec) / ((double)1000000.0) - ); -#else - return 0; -#endif -} - -int checkpoint_completed(APP_OUT &ao) { - int retval; - FILE *f = fopen(APP_TO_CORE_FILE, "w"); - ao.cpu_time_at_checkpoint = get_cpu_time(); - write_app_file(f, ao); - retval = fflush(f); - if(retval) { - fprintf(stderr,"error: could not flush %s\n", APP_TO_CORE_FILE); - return retval; - } - retval = fclose(f); - if(retval) { - fprintf(stderr, "error: could not close %s\n", APP_TO_CORE_FILE); - return retval; - } - _checkpoint = false; - return 0; -} - -int app_completed(APP_OUT& ao) { - int retval; - FILE* f=fopen(APP_TO_CORE_FILE, "w"); - ao.cpu_time_at_checkpoint = get_cpu_time(); - write_app_file(f, ao); - retval=fflush(f); - if(retval) { - fprintf(stderr, "error: could not flush %s\n", APP_TO_CORE_FILE); - return retval; - } - retval=fclose(f); - if(retval) { - fprintf(stderr, "error: could not close %s\n", APP_TO_CORE_FILE); - return retval; - } - _checkpoint = false; - return 0; -} - -#ifdef _WIN32 - -void CALLBACK on_timer( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) { - _checkpoint = true; -} - -#else - -void on_timer(int a) { - _checkpoint = true; -} - -#endif - -int set_timer(double period) { - int retval=0; - if(period<0) { - fprintf(stderr, "error: set_timer: negative period\n"); - return ERR_NEG; - } -#ifdef _WIN32 - retval = SetTimer( NULL, 0, (int)(period*1000), on_timer ); -#endif - -#if HAVE_SIGNAL_H -#if HAVE_SYS_TIME_H - struct sigaction sa; - sa.sa_handler = on_timer; - sa.sa_flags = 0; - sigaction(SIGVTALRM, &sa, NULL); - itimerval value; - value.it_value.tv_sec = (int)period; - value.it_value.tv_usec = ((int)(period*1000000))%1000000; - value.it_interval = value.it_value; - retval = setitimer(ITIMER_VIRTUAL, &value, NULL); -#endif -#endif - return retval; -} - diff --git a/api/api.h b/api/api.h deleted file mode 100644 index 808753c2ff..0000000000 --- a/api/api.h +++ /dev/null @@ -1,117 +0,0 @@ -// The contents of this file are subject to the Mozilla Public License -// Version 1.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at -// http://www.mozilla.org/MPL/ -// -// Software distributed under the License is distributed on an "AS IS" -// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the -// License for the specific language governing rights and limitations -// under the License. -// -// The Original Code is the Berkeley Open Infrastructure for Network Computing. -// -// The Initial Developer of the Original Code is the SETI@home project. -// Portions created by the SETI@home project are Copyright (C) 2002 -// University of California at Berkeley. All Rights Reserved. -// -// Contributor(s): -// - -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_SYS_RESOURCE_H -#include -#endif - -#ifndef BOINC_API -#define BOINC_API - -// MFILE supports a primitive form of checkpointing. -// Write all your output (and restart file) to MFILEs. -// The output is buffered in memory. -// Then close all the MFILEs; -// all the buffers will be flushed to disk, almost atomically. - -class MFILE { - char* buf; - int len; - FILE* f; -public: - int open(char* path, char* mode); - int _putchar(char); - int puts(char*); - int printf(char* format, ...); - size_t write(const void *,size_t,size_t); - int close(); - int flush(); -}; - -// An application that wants to be well-behaved should do the following: -// -// - call boinc_init(APP_IN&) at startup -// - call time_to_checkpoint() often. -// This is cheap - it returns true if time to checkpoint. -// - checkpoint if time_to_checkpoint() returns true -// - call checkpoint_completed() when checkpoint is complete -// The only member of APP_OUT that needs to be filled in is percent_done -// - boinc_poll(): -// Call this as often as requested by core -// - call app_completed() when the application is completed -// The only member of APP_OUT that needs to be filled in is percent_done - -struct APP_IN_GRAPHICS { - int xsize; - int ysize; - double refresh_period; - char shmem_seg_name[32]; -}; - -struct APP_OUT_GRAPHICS { -}; - -struct APP_IN { - char app_preferences[4096]; - APP_IN_GRAPHICS graphics; - double checkpoint_period; // recommended checkpoint period - double poll_period; // recommended poll period - double cpu_time; // cpu time from previous sessions -}; - -struct APP_OUT { - double percent_done; //percent of work unit completed - double cpu_time_at_checkpoint; //cpu time of current process - bool checkpointed; // true iff checkpointed since last call -}; - -//void write_core_file(FILE* f, APP_IN &ai); -//void parse_core_file(FILE* f, APP_IN&); -//void write_init_file(FILE* f, char *file_name, int fdesc, int input_file ); -//void parse_init_file(FILE* f); -void boinc_init(APP_IN&); -//void parse_app_file(FILE* f, APP_OUT&); -//void write_app_file(FILE* f, APP_OUT&); -void boinc_poll(APP_IN&, APP_OUT&); -double boinc_cpu_time(); -int boinc_resolve_link(char *file_name, char *resolved_name); - -#define CORE_TO_APP_FILE "core_to_app.xml" -#define APP_TO_CORE_FILE "app_to_core.xml" -#define BOINC_INIT_FILE "boinc_init.xml" - -//the following are provided for implementation of the checkpoint system -int checkpoint_completed(APP_OUT& ao); //call this when checkpoint is completed -int app_completed(APP_OUT& ao); //call this when app is completed - -extern bool _checkpoint; -#define time_to_checkpoint() _checkpoint -int set_timer(double period); //period is seconds spent in process -#ifdef _WIN32 -void CALLBACK on_timer( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ); -#else -void on_timer(int not_used); //sets _checkpoint to true -#endif -double get_cpu_time(); //return cpu time for this process - -#endif diff --git a/api/api_app.C b/api/api_app.C index c536884813..c7befa15c0 100644 --- a/api/api_app.C +++ b/api/api_app.C @@ -24,7 +24,7 @@ #include #endif -#include "api.h" +#include "boinc_api.h" int recover(char* file, unsigned long int* i); int timer(int secs, int usecs); diff --git a/api/api_test.C b/api/api_test.C index 220f383a73..4d06e15e75 100644 --- a/api/api_test.C +++ b/api/api_test.C @@ -22,7 +22,7 @@ Contributor(s): See ACKNOWLEDGEMENTS. #include -#include "api.h" +#include "boinc_api.h" int get_run_info(double& time, unsigned long int& counter); void run_api_test(char* args); diff --git a/api/boinc_api.C b/api/boinc_api.C new file mode 100644 index 0000000000..6389276438 --- /dev/null +++ b/api/boinc_api.C @@ -0,0 +1,373 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations +// under the License. +// +// The Original Code is the Berkeley Open Infrastructure for Network Computing. +// +// The Initial Developer of the Original Code is the SETI@home project. +// Portions created by the SETI@home project are Copyright (C) 2002 +// University of California at Berkeley. All Rights Reserved. +// +// Contributor(s): +// + +#include "windows_cpp.h" + +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#include +#endif +#if HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include + +#include +#include + +#include "parse.h" +#include "error_numbers.h" + +#include "boinc_api.h" + +static APP_INIT_DATA aid; +static double timer_period = 0.1; +static double time_until_checkpoint; +static double time_until_fraction_done_update; +static double fraction_done; +static bool ready_to_checkpoint = false; +static bool this_process_active; + +// read the INIT_DATA and FD_INIT files +// +int boinc_init() { + FILE* f; + int retval; + + f = fopen(INIT_DATA_FILE, "r"); + if (!f) return ERR_FOPEN; + retval = parse_init_data_file(f, aid); + if (retval) return retval; + fclose(f); + + f = fopen(FD_INIT_FILE, "r"); + if (f) { + parse_fd_init_file(f); + fclose(f); + } + time_until_checkpoint = aid.checkpoint_period; + time_until_fraction_done_update = aid.fraction_done_update_period; + this_process_active = true; + set_timer(timer_period); + return 0; +} + +int boinc_finish(int status) { + write_checkpoint_cpu_file(boinc_cpu_time()); + exit(status); + return 0; +} + +// resolve XML soft links +// +int boinc_resolve_filename(char *virtual_name, char *physical_name) { + FILE *fp; + char buf[512]; + + strcpy( physical_name, virtual_name ); + + // Open the file and load the first line + fp = fopen(virtual_name, "r"); + if (!fp) return 0; + + fgets(buf, 512, fp); + fclose(fp); + + // If it's the XML tag, return its value, + // otherwise, return the original file name + // + parse_str( buf, "", physical_name); + return 0; +} + + +bool boinc_time_to_checkpoint() { + return ready_to_checkpoint; +} + +int boinc_checkpoint_completed() { + write_checkpoint_cpu_file(boinc_cpu_time()); + ready_to_checkpoint = false; + time_until_checkpoint = aid.checkpoint_period; + return 0; +} + +int boinc_fraction_done(double x) { + fraction_done = x; + return 0; +} + +int boinc_child_start() { + this_process_active = false; + return 0; +} + +int boinc_child_done(double cpu) { + this_process_active = true; + return 0; +} + +double boinc_cpu_time() { +#ifdef HAVE_SYS_RESOURCE_H + int retval, pid = getpid(); + struct rusage ru; + retval = getrusage(RUSAGE_SELF, &ru); + if(retval) fprintf(stderr, "error: could not get cpu time for %d\n", pid); + return (double)ru.ru_utime.tv_sec + ( + ((double)ru.ru_utime.tv_usec) / ((double)1000000.0) + ); +#else +#ifdef _WIN32 +#ifndef WINNT_CLOCK + return UtilGetCPUClock(); +#else + HANDLE hProcess; + FILETIME creationTime,exitTime,kernelTime,userTime; + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, GetCurrentProcessId()); + if (GetProcessTimes( + hProcess, &creationTime, &exitTime, &kernelTime, &userTime) + ){ + ULARGE_INTEGER tKernel, tUser; + LONGLONG totTime; + + CloseHandle(hProcess); + + tKernel.LowPart = kernelTime.dwLowDateTime; + tKernel.HighPart = kernelTime.dwHighDateTime; + tUser.LowPart = userTime.dwLowDateTime; + tUser.HighPart = userTime.dwHighDateTime; + + // Runtimes in 100-nanosecond units + totTime = tKernel.QuadPart + tUser.QuadPart; + + // Convert to seconds and return + return(totTime / 10000000.0); + } else { + CloseHandle(hProcess); + return ((double)clock())/CLOCKS_PER_SEC; + } +#endif // WINNT_CLOCK +#endif // _WIN32 +#endif + +#ifdef macintosh + return (double) GetCPUTime() / CLOCKS_PER_SEC; +#endif + + fprintf(stderr, "boinc_cpu_time(): not implemented\n"); +} + +#ifdef _WIN32 +void CALLBACK on_timer( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) { +#else +void on_timer(int a) { +#endif + + if (!ready_to_checkpoint) { + time_until_checkpoint -= timer_period; + if (time_until_checkpoint <= 0) { + ready_to_checkpoint = true; + } + } + + if (this_process_active) { + time_until_fraction_done_update -= timer_period; + if (time_until_fraction_done_update < 0) { + FILE* f = fopen(FRACTION_DONE_FILE, "w"); + write_fraction_done_file(f, boinc_cpu_time(), fraction_done); + fclose(f); + time_until_fraction_done_update = aid.fraction_done_update_period; + } + } +} + + +int set_timer(double period) { + int retval=0; +#ifdef _WIN32 + retval = SetTimer( NULL, 0, (int)(period*1000), on_timer ); +#endif + +#if HAVE_SIGNAL_H +#if HAVE_SYS_TIME_H + struct sigaction sa; + sa.sa_handler = on_timer; + sa.sa_flags = 0; + sigaction(SIGVTALRM, &sa, NULL); + itimerval value; + value.it_value.tv_sec = (int)period; + value.it_value.tv_usec = ((int)(period*1000000))%1000000; + value.it_interval = value.it_value; + retval = setitimer(ITIMER_VIRTUAL, &value, NULL); +#endif +#endif + return retval; +} + +int write_init_data_file(FILE* f, APP_INIT_DATA& ai) { + if (strlen(ai.app_preferences)) { + fprintf(f, "\n%s\n", ai.app_preferences); + } + if (strlen(ai.team_name)) { + fprintf(f, "\n%s\n", ai.team_name); + } + if (strlen(ai.user_name)) { + fprintf(f, "\n%s\n", ai.user_name); + } + fprintf(f, + "%f\n" + "%f\n" + "%f\n" + "%f\n" + "%f\n", + ai.wu_cpu_time, + ai.total_cobblestones, + ai.recent_avg_cobblestones, + ai.checkpoint_period, + ai.fraction_done_update_period + ); + return 0; +} + +int parse_init_data_file(FILE* f, APP_INIT_DATA& ai) { + char buf[256]; + memset(&ai, 0, sizeof(ai)); + while (fgets(buf, 256, f)) { + if (match_tag(buf, "")) { + strcpy(ai.app_preferences, ""); + while (fgets(buf, 256, f)) { + if (match_tag(buf, "")) break; + strcat(ai.app_preferences, buf); + } + continue; + } + else if (parse_str(buf, "", ai.user_name)) continue; + else if (parse_str(buf, "", ai.team_name)) continue; + else if (parse_double(buf, "", ai.total_cobblestones)) continue; + else if (parse_double(buf, "", ai.recent_avg_cobblestones)) continue; + else if (parse_double(buf, "", ai.wu_cpu_time)) continue; + else if (parse_double(buf, "", ai.checkpoint_period)) continue; + else if (parse_double(buf, "", ai.fraction_done_update_period)) continue; + else fprintf(stderr, "parse_init_data_file: unrecognized %s", buf); + } + return 0; +} + +int write_checkpoint_cpu_file(double cpu) { + FILE *f = fopen(CHECKPOINT_CPU_FILE, "w"); + if (!f) return ERR_FOPEN; + fprintf(f, "%f\n", cpu); + fclose(f); + return 0; +} + +int parse_checkpoint_cpu_file(FILE* f, double& checkpoint_cpu) { + char buf[256]; + while (fgets(buf, 256, f)) { + if (parse_double(buf, "", checkpoint_cpu)) continue; + } + return 0; +} + +int write_fraction_done_file(FILE* f, double pct, double cpu) { + fprintf(f, + "%f\n" + "%f\n", + pct, + cpu + ); + return 0; +} + +int parse_fraction_done_file(FILE* f, double& pct, double& cpu) { + char buf[256]; + while (fgets(buf, 256, f)) { + if (parse_double(buf, "", pct)) continue; + else if (parse_double(buf, "", cpu)) continue; + else fprintf(stderr, "parse_fraction_done_file: unrecognized %s", buf); + } + return 0; +} + +// TODO: this should handle arbitrarily many fd/filename pairs. +// Also, give the tags better names +int write_fd_init_file(FILE* f, char *file_name, int fdesc, int input_file ) { + if (input_file) { + fprintf(f, "%s\n", file_name); + fprintf(f, "%d\n", fdesc); + } else { + fprintf(f, "%s\n", file_name); + fprintf(f, "%d\n", fdesc); + } + return 0; +} + +// TODO: this should handle arbitrarily many fd/filename pairs. +// Also, this shouldn't be doing the actual duping! +// +int parse_fd_init_file(FILE* f) { + char buf[256],filename[256]; + int filedesc,fd,retval; + while (fgets(buf, 256, f)) { + if (parse_str(buf, "", filename)) { + if (fgets(buf, 256, f)) { + if (parse_int(buf, "", filedesc)) { + fd = open(filename, O_RDONLY); + if (fd != filedesc) { + retval = dup2(fd, filedesc); + if (retval < 0) { + fprintf(stderr, "dup2 %d %d returned %d\n", fd, filedesc, retval); + exit(retval); + } + close(fd); + } + } + } + continue; + } + else if (parse_str(buf, "", filename)) { + if (fgets(buf, 256, f)) { + if (parse_int(buf, "", filedesc)) { + fd = open(filename, O_WRONLY|O_CREAT, 0660); + if (fd != filedesc) { + retval = dup2(fd, filedesc); + if (retval < 0) { + fprintf(stderr, "dup2 %d %d returned %d\n", fd, filedesc, retval); + exit(retval); + } + close(fd); + } + } + } + continue; + } + else fprintf(stderr, "parse_fd_init_file: unrecognized %s", buf); + } + return 0; +} + diff --git a/api/boinc_api.h b/api/boinc_api.h new file mode 100755 index 0000000000..0f11d4f4e8 --- /dev/null +++ b/api/boinc_api.h @@ -0,0 +1,101 @@ +// The contents of this file are subject to the Mozilla Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations +// under the License. +// +// The Original Code is the Berkeley Open Infrastructure for Network Computing. +// +// The Initial Developer of the Original Code is the SETI@home project. +// Portions created by the SETI@home project are Copyright (C) 2002 +// University of California at Berkeley. All Rights Reserved. +// +// Contributor(s): +// + +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + +#ifndef _BOINC_API +#define _BOINC_API + +#define DEFAULT_FRACTION_DONE_UPDATE_PERIOD 10 +#define DEFAULT_CHECKPOINT_PERIOD 300 + +// MFILE supports a primitive form of checkpointing. +// Write all your output (and restart file) to MFILEs. +// The output is buffered in memory. +// Then close or flush all the MFILEs; +// all the buffers will be flushed to disk, almost atomically. + +class MFILE { + char* buf; + int len; + FILE* f; +public: + int open(char* path, char* mode); + int _putchar(char); + int puts(char*); + int printf(char* format, ...); + size_t write(const void *, size_t size, size_t nitems); + int close(); + int flush(); +}; + +struct APP_INIT_DATA { + char app_preferences[4096]; + char user_name[256]; + char team_name[256]; + double wu_cpu_time; // cpu time from previous sessions + double total_cobblestones; + double recent_avg_cobblestones; + double checkpoint_period; // recommended checkpoint period + double fraction_done_update_period; +}; + +extern int boinc_init(); +extern int boinc_get_init_data(APP_INIT_DATA&); +extern int boinc_finish(int); +extern int boinc_resolve_filename(char*, char*); +extern bool boinc_time_to_checkpoint(); +extern int boinc_checkpoint_completed(); +extern int boinc_fraction_done(); +extern int boinc_child_start(); +extern int boinc_child_done(double); +extern double boinc_cpu_time(); // CPU time for this process + +/////////// API ENDS HERE - IMPLEMENTATION STUFF FOLLOWS + +int write_init_data_file(FILE* f, APP_INIT_DATA&); +int parse_init_data_file(FILE* f, APP_INIT_DATA&); +int write_percent_done_file(FILE* f, double, double); +int parse_percent_done_file(FILE* f, double&, double&); +int write_fd_init_file(FILE*, char*, int, int); +int parse_fd_init_file(FILE*); +int write_checkpoint_cpu_file(double); +int parse_checkpoint_cpu_file(FILE*, double&); +int write_fraction_done_file(FILE*, double, double); +int parse_fraction_done_file(FILE*, double&, double&); + +#define INIT_DATA_FILE "init_data.xml" +#define FD_INIT_FILE "fd_init.xml" +#define CHECKPOINT_CPU_FILE "checkpoint_cpu.xml" +#define FRACTION_DONE_FILE "fraction_done.xml" + +int set_timer(double period); +#ifdef _WIN32 +void CALLBACK on_timer( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ); +#else +void on_timer(int not_used); //sets _checkpoint to true +#endif + +#endif diff --git a/api/graphics_api.C b/api/graphics_api.C new file mode 100755 index 0000000000..f20e681a39 --- /dev/null +++ b/api/graphics_api.C @@ -0,0 +1,23 @@ +void write_graphics_file(FILE* f, GRAPHICS_INFO& gi) { + fprintf(f, + "%d\n" + "%d\n" + "%f\n", + gi.graphics.xsize, + gi.graphics.ysize, + gi.graphics.refresh_period, + ); +} + +int parse_graphics_file(FILE* f, GRAPHICS_INFO& gi) { + char buf[256]; + while (fgets(buf, 256, f)) { + if (match_tag(buf, "")) return 0; + else if (parse_int(buf, "", gi.graphics.xsize)) continue; + else if (parse_int(buf, "", gi.graphics.ysize)) continue; + else if (parse_double(buf, "", gi.graphics.refresh_period)) continue; + else fprintf(stderr, "parse_core_file: unrecognized %s", buf); + } + return -1; +} + diff --git a/api/graphics_api.h b/api/graphics_api.h new file mode 100755 index 0000000000..a543d4730c --- /dev/null +++ b/api/graphics_api.h @@ -0,0 +1,9 @@ +struct APP_IN_GRAPHICS { + int xsize; + int ysize; + double refresh_period; + char shmem_seg_name[32]; +}; + +struct APP_OUT_GRAPHICS { +}; diff --git a/api/mfile.C b/api/mfile.C new file mode 100644 index 0000000000..b8029817f0 --- /dev/null +++ b/api/mfile.C @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +#include "boinc_api.h" + +int MFILE::open(char* path, char* mode) { + buf = 0; + len = 0; + f = fopen(path, mode); + if (!f) return -1; + return 0; +} + +int MFILE::printf(char* format, ...) { + va_list ap; + char buf2[4096]; + int n, k; + + va_start(ap, format); + k = vsprintf(buf2, format, ap); + va_end(ap); + n = strlen(buf2); + buf = (char*)realloc(buf, len+n); + strncpy(buf+len, buf2, n); + len += n; + return k; +} + +size_t MFILE::write(const void *ptr, size_t size, size_t nitems) { + buf = (char *)realloc( buf, len+(size*nitems) ); + memcpy( buf+len, ptr, size*nitems ); + len += size*nitems; + return nitems; +} + +int MFILE::_putchar(char c) { + buf = (char*)realloc(buf, len+1); + buf[len] = c; + len++; + return c; +} + +int MFILE::puts(char* p) { + int n = strlen(p); + buf = (char*)realloc(buf, len+n); + strncpy(buf+len, p, n); + len += n; + return 0; +} + +int MFILE::close() { + fwrite(buf, 1, len, f); + free(buf); + buf = 0; + return fclose(f); +} + +int MFILE::flush() { + fwrite(buf, 1, len, f); + len = 0; + return fflush(f); +} diff --git a/apps/Makefile.in b/apps/Makefile.in index 1c45987494..d666cfc2bc 100644 --- a/apps/Makefile.in +++ b/apps/Makefile.in @@ -13,7 +13,7 @@ CFLAGS = -g -Wall @DEFS@ \ CC = @CC@ $(CFLAGS) -I @top_srcdir@/api -LIBS = ../api/api.o ../lib/parse.o +LIBS = ../api/boinc_api.o ../api/mfile.o ../lib/parse.o CLIBS = @LIBS@ diff --git a/apps/concat.C b/apps/concat.C index 484472c94d..4498f24301 100644 --- a/apps/concat.C +++ b/apps/concat.C @@ -23,7 +23,7 @@ #include #include -#include "api.h" +#include "boinc_api.h" void file_append(FILE* in, FILE* out) { char buf[1024]; @@ -40,14 +40,13 @@ int main(int argc, char** argv) { FILE* in, *out; char file_name[512]; int i; - APP_IN ai; - boinc_init(ai); + boinc_init(); fprintf(stderr, "APP: concat: starting, argc %d\n", argc); for (i=0; i #include #include -#include "api.h" +#include "boinc_api.h" #define CHECKPOINT_FILE "concat_slow_state" @@ -32,7 +32,7 @@ int do_checkpoint(MFILE& mf, int filenum, int nchars ) { int retval; char resolved_name[512],res_name2[512]; - boinc_resolve_link( "temp", resolved_name ); + boinc_resolve_filename( "temp", resolved_name ); FILE* f = fopen(resolved_name, "w"); if (!f) return 1; fprintf(f, "%d %d", filenum, nchars); @@ -43,7 +43,7 @@ int do_checkpoint(MFILE& mf, int filenum, int nchars ) { // hopefully atomic part starts here retval = mf.flush(); if (retval) return retval; - boinc_resolve_link( CHECKPOINT_FILE, res_name2 ); + boinc_resolve_filename( CHECKPOINT_FILE, res_name2 ); retval = rename(resolved_name, res_name2); if (retval) return retval; // hopefully atomic part ends here @@ -54,7 +54,6 @@ int do_checkpoint(MFILE& mf, int filenum, int nchars ) { void file_append(FILE* in, MFILE &out, int skip, int filenum) { char buf[1]; int n,nread,retval; - APP_OUT ao; fseek( in, skip, SEEK_SET ); nread = skip; @@ -71,15 +70,14 @@ void file_append(FILE* in, MFILE &out, int skip, int filenum) { if( retval == 0 ) n = 1; - if( time_to_checkpoint() ) { + if( boinc_time_to_checkpoint() ) { fprintf( stderr, "Checkpoint.\n" ); retval = do_checkpoint( out, filenum, nread ); if( retval ) { fprintf( stderr, "APP: concat_slow checkpoint failed %d\n", retval ); exit(1); } - ao.percent_done = 1; - checkpoint_completed(ao); + boinc_checkpoint_completed(); } sleep(1); } @@ -91,16 +89,14 @@ int main(int argc, char** argv) { char file_name[512]; int i; int file_num,nchars,retval; - APP_IN ai; char *mode; - boinc_init(ai); - fprintf( stderr, "%f\n", ai.checkpoint_period ); + boinc_init(); fprintf(stderr, "APP: concat: starting, argc %d\n", argc); for (i=0; i #include -#include "api.h" +#include "boinc_api.h" #define CHECKPOINT_FILE "uc_slow_state" @@ -34,7 +34,7 @@ int do_checkpoint(MFILE& mf, int nchars) { int retval; char resolved_name[512],res_name2[512]; - boinc_resolve_link( "temp", resolved_name ); + boinc_resolve_filename( "temp", resolved_name ); FILE* f = fopen(resolved_name, "w"); if (!f) return 1; fprintf(f, "%d", nchars); @@ -45,7 +45,7 @@ int do_checkpoint(MFILE& mf, int nchars) { // hopefully atomic part starts here retval = mf.flush(); if (retval) return retval; - boinc_resolve_link( CHECKPOINT_FILE, res_name2 ); + boinc_resolve_filename( CHECKPOINT_FILE, res_name2 ); retval = rename(resolved_name, res_name2); if (retval) return retval; // hopefully atomic part ends here @@ -59,23 +59,21 @@ int main() { char resolved_name[512]; MFILE out, time_file; FILE* state, *in; - APP_IN ai; - APP_OUT ao; - boinc_init( ai ); + boinc_init(); - boinc_resolve_link( "in", resolved_name ); + boinc_resolve_filename( "in", resolved_name ); in = fopen(resolved_name, "r"); - boinc_resolve_link( CHECKPOINT_FILE, resolved_name ); + boinc_resolve_filename( CHECKPOINT_FILE, resolved_name ); state = fopen(resolved_name, "r"); if (state) { fscanf(state, "%d", &nchars); printf("nchars %d\n", nchars); fseek(in, nchars, SEEK_SET); - boinc_resolve_link( "out", resolved_name ); + boinc_resolve_filename( "out", resolved_name ); retval = out.open(resolved_name, "a"); } else { - boinc_resolve_link( "out", resolved_name ); + boinc_resolve_filename( "out", resolved_name ); retval = out.open(resolved_name, "w"); } fprintf(stderr, "APP: uc_slow starting\n"); @@ -99,13 +97,12 @@ int main() { j /= (float)n; } - if (time_to_checkpoint()) { + if (boinc_time_to_checkpoint()) { retval = do_checkpoint(out, nchars); if (retval) { fprintf(stderr, "APP: uc_slow checkpoint failed %d\n", retval); } - ao.percent_done = 1; - checkpoint_completed(ao); + boinc_checkpoint_completed(); } } retval = out.flush(); @@ -114,10 +111,9 @@ int main() { exit(1); } fprintf(stderr, "APP: uc_slow ending, wrote %d chars\n", nchars); - ao.percent_done = 1; - app_completed(ao); - time_file.printf("%f\n", ao.cpu_time_at_checkpoint); + time_file.printf("%f\n", boinc_cpu_time()); time_file.flush(); time_file.close(); + boinc_finish(0); return 0; } diff --git a/apps/uc_slow.C b/apps/uc_slow.C index 74b0c736b0..4dc11b6ce7 100644 --- a/apps/uc_slow.C +++ b/apps/uc_slow.C @@ -26,7 +26,7 @@ #include #include -#include "api.h" +#include "boinc_api.h" #define CHECKPOINT_FILE "uc_slow_state" @@ -34,7 +34,7 @@ int do_checkpoint(MFILE& mf, int nchars) { int retval; char resolved_name[512],res_name2[512]; - boinc_resolve_link( "temp", resolved_name ); + boinc_resolve_filename( "temp", resolved_name ); FILE* f = fopen(resolved_name, "w"); if (!f) return 1; fprintf(f, "%d", nchars); @@ -45,7 +45,7 @@ int do_checkpoint(MFILE& mf, int nchars) { // hopefully atomic part starts here retval = mf.flush(); if (retval) return retval; - boinc_resolve_link( CHECKPOINT_FILE, res_name2 ); + boinc_resolve_filename( CHECKPOINT_FILE, res_name2 ); retval = rename(resolved_name, res_name2); if (retval) return retval; // hopefully atomic part ends here @@ -58,23 +58,21 @@ int main() { char resolved_name[512]; MFILE out, time_file; FILE* state, *in; - APP_IN ai; - APP_OUT ao; - boinc_init( ai ); + boinc_init(); - boinc_resolve_link( "in", resolved_name ); + boinc_resolve_filename( "in", resolved_name ); in = fopen(resolved_name, "r"); - boinc_resolve_link( CHECKPOINT_FILE, resolved_name ); + boinc_resolve_filename( CHECKPOINT_FILE, resolved_name ); state = fopen(resolved_name, "r"); if (state) { fscanf(state, "%d", &nchars); printf("nchars %d\n", nchars); fseek(in, nchars, SEEK_SET); - boinc_resolve_link( "out", resolved_name ); + boinc_resolve_filename( "out", resolved_name ); retval = out.open(resolved_name, "a"); } else { - boinc_resolve_link( "out", resolved_name ); + boinc_resolve_filename( "out", resolved_name ); retval = out.open(resolved_name, "w"); } fprintf(stderr, "APP: uc_slow starting\n"); @@ -92,14 +90,13 @@ int main() { sleep(1); - if (time_to_checkpoint()) { + if (boinc_time_to_checkpoint()) { retval = do_checkpoint(out, nchars); if (retval) { fprintf(stderr, "APP: uc_slow checkpoint failed %d\n", retval); exit(1); } - ao.percent_done = 1; - checkpoint_completed(ao); + boinc_checkpoint_completed(); } } retval = out.flush(); @@ -108,10 +105,9 @@ int main() { exit(1); } fprintf(stderr, "APP: uc_slow ending, wrote %d chars\n", nchars); - ao.percent_done = 1; - app_completed(ao); - time_file.printf("%f\n", ao.cpu_time_at_checkpoint); + time_file.printf("%f\n", boinc_cpu_time()); time_file.flush(); time_file.close(); + boinc_finish(0); return 0; } diff --git a/apps/upper_case.C b/apps/upper_case.C index 0cada2ba1c..a2a449c3b9 100755 --- a/apps/upper_case.C +++ b/apps/upper_case.C @@ -22,14 +22,12 @@ #include #include #include -#include "api.h" +#include "boinc_api.h" int main() { int c, n=0; - APP_IN ai; - APP_OUT ao; - boinc_init(ai); + boinc_init(); fprintf(stderr, "APP: upper_case starting\n"); fflush(stderr); while (1) { @@ -38,12 +36,12 @@ int main() { c = toupper(c); putchar(c); n++; - if(time_to_checkpoint()) { + if (boinc_time_to_checkpoint()) { fflush(stdout); - ao.percent_done = 1; - checkpoint_completed(ao); + boinc_checkpoint_completed(); } } fprintf(stderr, "APP: upper_case ending, wrote %d chars\n", n); + boinc_finish(0); return 0; } diff --git a/checkin_notes b/checkin_notes index 4d366be442..1e1c8b570d 100755 --- a/checkin_notes +++ b/checkin_notes @@ -1382,3 +1382,59 @@ Eric Heien August 2, 2002 net_xfer.C net_xfer.h +David A. + Various changes to API: + - Added user, team names, credit info to APP_INIT_DATA + - separate call for getting init data + - separate call for returning fraction done + - separated out fraction_done_update_period + - moved graphics API to separate file + - moved MFILE implementation to separate file + - API timer is now 0.1 sec; use counters for various uses + (checkpoint, fraction done, graphics) + - changed name of API files to boinc_api.C,h + + - clarify distinction between + "current CPU time" + "CPU time at last checkpoint" + "CPU time at start of current run" + These are all kept in ACTIVE_TASK. + RESULT now only has "final CPU time". + + - fixed bug in dir scanning (ahem...) + + - Coding style notes: + - every fopen() MUST have a matching fclose() in same function + - every malloc() MUST have a matching free() in same function + - don't do a rewind() right after fopen() + - write function calls as + func(arg1, arg2); + NOTE: there's a space after every comma, everywhere + - write "if" statements as + if (condition) { + ... + } + - no explicit argument checking. do this at higher level + - functions in foo.C should be declared (as extern) in foo.h, + AND NOWHERE ELSE. + + api/ + Makefile.in + api.C,h (removed) + boinc_api.C,h (new) + graphics_api.C,h (new) + mfile.C (new) + apps/ + Makefile.in + *.C + client/ + Makefile.in + app.C,h + client_state.C,h + client_types.C,h + cs_apps.C + cs_scheduler.C + file_names.C + filesys.C,h + doc/ + api.html diff --git a/client/Makefile.in b/client/Makefile.in index 53fb02825f..e9b40177c2 100644 --- a/client/Makefile.in +++ b/client/Makefile.in @@ -51,7 +51,7 @@ OBJS = \ ../lib/md5.o \ ../lib/crypt.o \ ../RSAEuro/source/rsaeuro.a \ - ../api/api.o + ../api/boinc_api.o TEST_NET_XFER_OBJS = \ filesys.o \ diff --git a/client/app.C b/client/app.C index 07fc02571d..394f1f3ff3 100644 --- a/client/app.C +++ b/client/app.C @@ -67,11 +67,7 @@ #include "util.h" #include "app.h" -#include "api.h" - -extern void write_core_file(FILE *f, APP_IN& ai); -extern void write_init_file(FILE *f, char *file_name, int fdesc, int input_file); -extern void parse_app_file(FILE *f, APP_OUT& ao); +#include "boinc_api.h" // take a string containing some space separated words. // return an array of pointers to the null-terminated words. @@ -81,15 +77,6 @@ int parse_command_line(char* p, char** argv) { char** pp = argv; bool space = true; - if(p==NULL) { - fprintf(stderr, "error: parse_command_line: unexpected NULL pointer p\n"); - return ERR_NULL; - } - if(argv==NULL) { - fprintf(stderr, "error: parse_command_line: unexpected NULL pointer argv\n"); - return ERR_NULL; - } - while (*p) { if (isspace(*p)) { *p = 0; @@ -112,10 +99,6 @@ int parse_command_line(char* p, char** argv) { static int print_argv(char** argv) { int i; - if(argv==NULL) { - fprintf(stderr, "error: print_argv: unexpected NULL pointer argv\n"); - return ERR_NULL; - } for (i=0; argv[i]; i++) { fprintf(stderr, "argv[%d]: %s\n", i, argv[i]); } @@ -123,8 +106,6 @@ static int print_argv(char** argv) { return 0; } -// Initialize the ACTIVE_TASK object members to null -// ACTIVE_TASK::ACTIVE_TASK() { result = NULL; wup = NULL; @@ -135,17 +116,9 @@ ACTIVE_TASK::ACTIVE_TASK() { exit_status = 0; signal = 0; strcpy(dirname, ""); - prev_cpu_time = 0; } -// Initialize active task with a specific result requirement -// int ACTIVE_TASK::init(RESULT* rp) { - if(rp==NULL) { - fprintf(stderr, "error: ACTIVE_TASK.init: unexpected NULL pointer rp\n"); - return ERR_NULL; - } - result = rp; wup = rp->wup; app_version = wup->avp; @@ -154,57 +127,60 @@ int ACTIVE_TASK::init(RESULT* rp) { } // Start a task in a slot directory. This includes setting up soft links, -// passing preferences, and starting the actual process for computation +// passing preferences, and starting the process +// +// WHAT ARE ASSUMPTIONS ABOUT CURRENT DIR?? +// SHOULD REPLACE ../.. stuff // int ACTIVE_TASK::start(bool first_time) { - char exec_name[256], file_path[256], link_path[256],temp[256]; + char exec_name[256], file_path[256], link_path[256], temp[256]; char* argv[100]; unsigned int i; FILE_REF file_ref; FILE_INFO* fip; int retval; - char prefs_path[256],init_path[256]; + char prefs_path[256], init_path[256]; FILE *prefs_fd,*init_file; - APP_IN app_prefs; + APP_INIT_DATA aid; - prev_cpu_time = 0; - // These should be chosen in a better manner (user specifiable) - app_prefs.graphics.xsize = 640; - app_prefs.graphics.ysize = 480; - app_prefs.graphics.refresh_period = 5; - app_prefs.checkpoint_period = 5; - app_prefs.poll_period = 5; - if(!first_time) app_prefs.cpu_time = result->cpu_time; + if (first_time) { + checkpoint_cpu_time = 0; + } + current_cpu_time = checkpoint_cpu_time; + starting_cpu_time = checkpoint_cpu_time; + fraction_done = 0; - // Write out the app prefs. This has everything in the APP_IN - // struct, including graphics prefs and checkpoint/poll prefs - sprintf( prefs_path, "%s/%s", dirname, CORE_TO_APP_FILE ); - prefs_fd = fopen( prefs_path, "wb" ); - if( !prefs_fd ) { - if( log_flags.task_debug ) { - printf( "Failed to open core to app prefs file %s.\n", prefs_path ); + //app_prefs.graphics.xsize = 640; + //app_prefs.graphics.ysize = 480; + //app_prefs.graphics.refresh_period = 5; + + memset(&aid, 0, sizeof(aid)); + // TODO: fill in the app prefs, user name, team name, etc. + aid.checkpoint_period = DEFAULT_CHECKPOINT_PERIOD; + aid.fraction_done_update_period = DEFAULT_FRACTION_DONE_UPDATE_PERIOD; + aid.wu_cpu_time = checkpoint_cpu_time; + + sprintf(prefs_path, "%s/%s", dirname, INIT_DATA_FILE); + prefs_fd = fopen(prefs_path, "wb"); + if (!prefs_fd) { + if (log_flags.task_debug) { + printf("Failed to open core to app prefs file %s.\n", prefs_path); } return ERR_FOPEN; } - rewind( prefs_fd ); - write_core_file( prefs_fd,app_prefs ); + retval = write_init_data_file(prefs_fd, aid); fclose(prefs_fd); - // Open the init file. This file contains initialization - // information regarding tasks that can only be performed in - // the app, such as redirecting file descriptors - // - sprintf( init_path, "%s/%s", dirname, BOINC_INIT_FILE ); - init_file = fopen( init_path, "wb" ); - if( !init_file ) { - if( log_flags.task_debug ) { + sprintf(init_path, "%s/%s", dirname, FD_INIT_FILE); + init_file = fopen(init_path, "wb"); + if (!init_file) { + if(log_flags.task_debug) { printf( "Failed to open init file %s.\n", init_path ); } return ERR_FOPEN; } - rewind( init_file ); - // make a soft link to the executable(s) + // make soft links to the executable(s) // for (i=0; iapp_files.size(); i++) { fip = app_version->app_files[i].file_info; @@ -247,8 +223,8 @@ int ACTIVE_TASK::start(bool first_time) { } } } else { - sprintf( temp, "../../%s", file_path ); - write_init_file( init_file, temp, file_ref.fd, 1 ); + sprintf(temp, "../../%s", file_path); + write_fd_init_file(init_file, temp, file_ref.fd, 1); } } @@ -274,7 +250,7 @@ int ACTIVE_TASK::start(bool first_time) { } } else { sprintf( temp, "../../%s", file_path ); - write_init_file( init_file, temp, file_ref.fd, 0 ); + write_fd_init_file(init_file, temp, file_ref.fd, 0); } } @@ -304,7 +280,7 @@ int ACTIVE_TASK::start(bool first_time) { argv[0] = exec_name; parse_command_line(wup->command_line, argv+1); if (log_flags.task_debug) print_argv(argv); - boinc_resolve_link( exec_name, temp ); + boinc_resolve_filename(exec_name, temp); retval = execv(temp, argv); fprintf(stderr, "execv failed: %d\n", retval); perror("execv"); @@ -331,6 +307,7 @@ int ACTIVE_TASK::start(bool first_time) { // Need to condense argv into a single string //if (log_flags.task_debug) print_argv(argv); + // sprintf( temp, "%s/%s", dirname, exec_name ); boinc_resolve_link( temp, exec_name ); if( !CreateProcess( exec_name, @@ -394,10 +371,7 @@ void ACTIVE_TASK::request_exit(int seconds) { // int ACTIVE_TASK_SET::insert(ACTIVE_TASK* atp) { int retval; - if(atp==NULL) { - fprintf(stderr, "error: ACTIVE_TASK.insert: unexpected NULL pointer atp\n"); - return ERR_NULL; - } + get_slot_dir(atp->slot, atp->dirname); clean_out_dir(atp->dirname); retval = atp->start(true); @@ -423,9 +397,9 @@ bool ACTIVE_TASK_SET::poll() { for (i=0; ipid_handle,&exit_code ) ) { + if (GetExitCodeProcess(atp->pid_handle, &exit_code)) { // Get the elapsed CPU time - if( GetProcessTimes( atp->pid_handle, &creation_time, &exit_time, &kernel_time, &user_time ) ) { + if (GetProcessTimes(atp->pid_handle, &creation_time, &exit_time, &kernel_time, &user_time)) { tKernel.LowPart = kernel_time.dwLowDateTime; tKernel.HighPart = kernel_time.dwHighDateTime; @@ -440,7 +414,7 @@ bool ACTIVE_TASK_SET::poll() { // This probably isn't correct atp->result->cpu_time = ((double)clock())/CLOCKS_PER_SEC; } - if( exit_code != STILL_ACTIVE ) { + if (exit_code != STILL_ACTIVE) { // Not sure how to incorporate the other states (WAS_SIGNALED, etc) atp->state = PROCESS_EXITED; atp->exit_status = exit_code; @@ -470,15 +444,18 @@ bool ACTIVE_TASK_SET::poll() { fprintf(stderr, "ACTIVE_TASK_SET::poll(): pid %d not found\n", pid); return true; } - atp->result->cpu_time = rs.ru_utime.tv_sec + rs.ru_utime.tv_usec/1.e6; + double x = rs.ru_utime.tv_sec + rs.ru_utime.tv_usec/1.e6; + atp->result->final_cpu_time = atp->starting_cpu_time + x; if (WIFEXITED(stat)) { atp->state = PROCESS_EXITED; atp->exit_status = WEXITSTATUS(stat); atp->result->exit_status = atp->exit_status; + if (log_flags.task_debug) printf("process exited status%d\n", atp->exit_status); } else if (WIFSIGNALED(stat)) { atp->state = PROCESS_WAS_SIGNALED; atp->signal = WTERMSIG(stat); atp->result->exit_status = atp->signal; + if (log_flags.task_debug) printf("process was signaled %d\n", atp->signal); } else { atp->state = PROCESS_EXIT_UNKNOWN; atp->result->exit_status = -1; @@ -507,10 +484,7 @@ bool ACTIVE_TASK_SET::poll() { ACTIVE_TASK* ACTIVE_TASK_SET::lookup_pid(int pid) { unsigned int i; ACTIVE_TASK* atp; - if(pid<0) { - fprintf(stderr, "error: ACTIVE_TASK_SET.lookup_pid: negatvie pid\n"); - return NULL; - } + for (i=0; ipid == pid) return atp; @@ -549,7 +523,7 @@ void ACTIVE_TASK_SET::exit_tasks() { for (i=0; irequest_exit(0); - atp->update_time(); + atp->check_app_status_files(); } } @@ -586,10 +560,7 @@ void ACTIVE_TASK::unsuspend() { // int ACTIVE_TASK_SET::remove(ACTIVE_TASK* atp) { vector::iterator iter; - if(atp==NULL) { - fprintf(stderr, "error: ACTIVE_TASK_SET.remove: unexpected NULL pointer atp\n"); - return ERR_NULL; - } + iter = active_tasks.begin(); while (iter != active_tasks.end()) { if (*iter == atp) { @@ -627,22 +598,32 @@ int ACTIVE_TASK_SET::restart_tasks() { return 0; } -// Update the CPU time accounting based on the APP_TO_CORE_FILE passed to -// us by the application +// See if the app has generated new checkpoint CPU or fraction-done files. +// If so read them and return true. // -bool ACTIVE_TASK::update_time() { - FILE* app_fp; +bool ACTIVE_TASK::check_app_status_files() { + FILE* f; char app_path[256]; - APP_OUT ao; + bool found = false; - sprintf(app_path, "%s/%s", dirname, APP_TO_CORE_FILE); - app_fp = fopen(app_path, "r"); - if(!app_fp) return false; - parse_app_file(app_fp, ao); - if(!ao.checkpointed) return false; - result->cpu_time += ao.cpu_time_at_checkpoint - prev_cpu_time; - prev_cpu_time = ao.cpu_time_at_checkpoint; - return true; + sprintf(app_path, "%s/%s", dirname, CHECKPOINT_CPU_FILE); + f = fopen(app_path, "r"); + if (f) { + found = true; + parse_checkpoint_cpu_file(f, checkpoint_cpu_time); + fclose(f); + } + + sprintf(app_path, "%s/%s", dirname, FRACTION_DONE_FILE); + f = fopen(app_path, "r"); + if (f) { + found = true; + parse_fraction_done_file( + f, current_cpu_time, fraction_done + ); + fclose(f); + } + return found; } // Poll each of the currently running tasks and get their CPU time @@ -651,9 +632,10 @@ bool ACTIVE_TASK_SET::poll_time() { ACTIVE_TASK* atp; unsigned int i; bool updated; + for(i=0; iupdate_time(); + updated |= atp->check_app_status_files(); } return updated; } @@ -661,23 +643,19 @@ bool ACTIVE_TASK_SET::poll_time() { // Write XML data about this ACTIVE_TASK // int ACTIVE_TASK::write(FILE* fout) { - if(fout==NULL) { - fprintf(stderr, "error: ACTIVE_TASK.write: unexpected NULL pointer fout\n"); - return ERR_NULL; - } fprintf(fout, "\n" " %s\n" " %s\n" " %d\n" " %d\n" - " %f\n" + " %f\n" "\n", result->project->master_url, result->name, app_version->version_num, slot, - prev_cpu_time + checkpoint_cpu_time ); return 0; } @@ -688,14 +666,7 @@ int ACTIVE_TASK::parse(FILE* fin, CLIENT_STATE* cs) { char buf[256], result_name[256], project_master_url[256]; int app_version_num=0; PROJECT* project; - if(fin==NULL) { - fprintf(stderr, "error: ACTIVE_TASK.parse: unexpected NULL pointer fin\n"); - return ERR_NULL; - } - if(cs==NULL) { - fprintf(stderr, "error: ACTIVE_TASK.parse: unexpected NULL pointer cs\n"); - return ERR_NULL; - } + strcpy(result_name, ""); strcpy(project_master_url, ""); while (fgets(buf, 256, fin)) { @@ -727,7 +698,7 @@ int ACTIVE_TASK::parse(FILE* fin, CLIENT_STATE* cs) { else if (parse_str(buf, "", project_master_url)) continue; else if (parse_int(buf, "", app_version_num)) continue; else if (parse_int(buf, "", slot)) continue; - else if (parse_double(buf, "", prev_cpu_time)) continue; + else if (parse_double(buf, "", checkpoint_cpu_time)) continue; else fprintf(stderr, "ACTIVE_TASK::parse(): unrecognized %s\n", buf); } return -1; @@ -737,10 +708,7 @@ int ACTIVE_TASK::parse(FILE* fin, CLIENT_STATE* cs) { // int ACTIVE_TASK_SET::write(FILE* fout) { unsigned int i; - if(fout==NULL) { - fprintf(stderr, "error: ACTIVE_TASK_SET.write: unexpected NULL pointer fout\n"); - return ERR_NULL; - } + fprintf(fout, "\n"); for (i=0; iwrite(fout); @@ -755,14 +723,7 @@ int ACTIVE_TASK_SET::parse(FILE* fin, CLIENT_STATE* cs) { ACTIVE_TASK* atp; char buf[256]; int retval; - if(fin==NULL) { - fprintf(stderr, "error: ACTIVE_TASK_SET.parse: unexpected NULL pointer fin\n"); - return ERR_NULL; - } - if(cs==NULL) { - fprintf(stderr, "error: ACTIVE_TASK_SET.parse: unexpected NULL pointer cs\n"); - return ERR_NULL; - } + while (fgets(buf, 256, fin)) { if (match_tag(buf, "")) return 0; else if (match_tag(buf, "")) { diff --git a/client/app.h b/client/app.h index 25c897e285..a861bad813 100644 --- a/client/app.h +++ b/client/app.h @@ -63,8 +63,11 @@ public: int state; int exit_status; int signal; + double fraction_done; + double starting_cpu_time; + double checkpoint_cpu_time; + double current_cpu_time; char dirname[256]; // directory where process runs - double prev_cpu_time; ACTIVE_TASK(); int init(RESULT*); @@ -77,7 +80,7 @@ public: void suspend(); void unsuspend(); - bool update_time(); + bool check_app_status_files(); int write(FILE*); int parse(FILE*, CLIENT_STATE*); diff --git a/client/client_state.C b/client/client_state.C index 23ff714dbb..c0889c884d 100644 --- a/client/client_state.C +++ b/client/client_state.C @@ -300,7 +300,6 @@ int CLIENT_STATE::exit() { } int CLIENT_STATE::exit_tasks() { - int retval; active_tasks.exit_tasks(); return 0; } diff --git a/client/client_types.C b/client/client_types.C index 21e0483382..fb1b5d3af3 100644 --- a/client/client_types.C +++ b/client/client_types.C @@ -512,7 +512,7 @@ void RESULT::clear() { is_active = false; is_compute_done = false; is_server_ack = false; - cpu_time = 0; + final_cpu_time = 0; exit_status = 0; strcpy(stderr_out, ""); app = NULL; @@ -563,7 +563,7 @@ int RESULT::parse_state(FILE* in) { output_files.push_back(file_ref); continue; } - else if (parse_double(buf, "", cpu_time)) continue; + else if (parse_double(buf, "", final_cpu_time)) continue; else if (parse_int(buf, "", exit_status)) continue; else if (match_tag(buf, "" )) { while (fgets(buf, 256, in)) { @@ -589,10 +589,10 @@ int RESULT::write(FILE* out, bool to_server) { "\n" " %s\n" " %d\n" - " %f\n", + " %f\n", name, exit_status, - cpu_time + final_cpu_time ); n = strlen(stderr_out); if (n) { diff --git a/client/client_types.h b/client/client_types.h index 842e7f0178..dc9335a600 100644 --- a/client/client_types.h +++ b/client/client_types.h @@ -177,7 +177,7 @@ struct RESULT { bool is_active; // an app is currently running for this bool is_compute_done; // computation finished bool is_server_ack; // ack received from scheduling server - double cpu_time; // cpu time spent completing result + double final_cpu_time; int exit_status; char stderr_out[STDERR_MAX_LEN]; APP* app; diff --git a/client/cs_apps.C b/client/cs_apps.C index b42e72fac0..98e2b508ba 100644 --- a/client/cs_apps.C +++ b/client/cs_apps.C @@ -50,7 +50,7 @@ int CLIENT_STATE::app_finished(ACTIVE_TASK& at) { at.result->is_active = false; at.result->is_compute_done = true; update_avg_cpu(at.result->project); - at.result->project->exp_avg_cpu += at.result->cpu_time; + at.result->project->exp_avg_cpu += at.result->final_cpu_time; return 0; } diff --git a/client/cs_scheduler.C b/client/cs_scheduler.C index ad430504ab..7cfd1bd7e5 100644 --- a/client/cs_scheduler.C +++ b/client/cs_scheduler.C @@ -57,11 +57,8 @@ double CLIENT_STATE::current_water_days() { for (i=0; iis_compute_done) continue; - if (rp->cpu_time > 0) { - seconds_remaining += (rp->wup->seconds_to_complete - rp->cpu_time); - } else { - seconds_remaining += rp->wup->seconds_to_complete; - } + // TODO: subtract time already finished for WUs in progress + seconds_remaining += rp->wup->seconds_to_complete; } return (seconds_remaining * SECONDS_IN_DAY); } diff --git a/client/file_names.C b/client/file_names.C index fc6fe73d27..913a13b8dd 100644 --- a/client/file_names.C +++ b/client/file_names.C @@ -38,9 +38,6 @@ static void c2x(char *what) { char d1 = num / 16; char d2 = num % 16; int abase1, abase2; - if(what==NULL) { - fprintf(stderr, "error: c2x: unexpected NULL pointer what\n"); - } if (d1 < 10) abase1 = 48; else abase1 = 55; if (d2 < 10) abase2 = 48; @@ -57,12 +54,6 @@ static void c2x(char *what) { // static void escape_url(char *in, char* out) { int x, y; - if(in==NULL) { - fprintf(stderr, "error: escape_url: unexpected NULL pointer in\n"); - } - if(out==NULL) { - fprintf(stderr, "error: escape_url: unexpected NULL pointer out\n"); - } for (x=0, y=0; in[x]; ++x) { if (isalnum(in[x]) || in[x]=='.' || in[x]=='-' || in[x]=='_') { out[y] = in[x]; @@ -85,14 +76,9 @@ static void escape_url(char *in, char* out) { // Gets the pathname of a file // void get_pathname(FILE_INFO* fip, char* path) { - if(fip==NULL) { - fprintf(stderr, "error: get_pathname: unexpected NULL pointer fip\n"); - } - if(path==NULL) { - fprintf(stderr, "error: get_pathname: unexpected NULL pointer path\n"); - } PROJECT* p = fip->project; char buf[256]; + // for testing purposes, it's handy to allow a FILE_INFO without // an associated PROJECT. // @@ -107,12 +93,6 @@ void get_pathname(FILE_INFO* fip, char* path) { // Returns the location of a numbered slot directory // void get_slot_dir(int slot, char* path) { - if(path==NULL) { - fprintf(stderr, "error: get_slot_dir: unexpected NULL pointer path\n"); - } - if(slot<0) { - fprintf(stderr, "error: get_slot_dir: negative slot\n"); - } sprintf(path, "slots/%d", slot); } @@ -173,10 +153,6 @@ int make_slot_dir(int slot) { // Returns a filename used for prefs backup // int make_prefs_backup_name(PREFS& prefs, char* name) { - if(name==NULL) { - fprintf(stderr, "error: make_prefs_backup_name: unexpected NULL pointer name\n"); - return ERR_NULL; - } sprintf(name, "prefs_backup_%d", prefs.mod_time); return 0; } diff --git a/client/filesys.C b/client/filesys.C index e520b44117..0ccf6ddc38 100644 --- a/client/filesys.C +++ b/client/filesys.C @@ -70,13 +70,10 @@ char failed_file[256]; // routines for enumerating the entries in a directory // Open a directory +// DIR *dir_open(char* p) { DIR *dirp; - if(p==NULL) { - fprintf(stderr, "error: dir_open: unexpected NULL pointer p\n"); - return NULL; - } #ifdef HAVE_DIRENT_H dirp = opendir(p); if (!dirp) return NULL; @@ -97,22 +94,14 @@ DIR *dir_open(char* p) { // Scan through a directory and return the next file name in it // int dir_scan(char* p, DIR *dirp) { - if(p==NULL) { - fprintf(stderr, "error: dir_scan: unexpected NULL pointer p\n"); - return ERR_NULL; - } - if( !dirp ) - return -1; #ifdef HAVE_DIRENT_H while (1) { dirent* dp = readdir(dirp); if (dp) { if (dp->d_name[0] == '.') continue; - if (p) strncpy(p, dp->d_name, 255); + if (p) strcpy(p, dp->d_name); return 0; } else { - closedir(dirp); - dirp = 0; return -1; } } @@ -151,11 +140,10 @@ int dir_scan(char* p, DIR *dirp) { // Close a directory // -void dir_close( DIR* dirp ) { +void dir_close(DIR* dirp) { #ifdef HAVE_DIRENT_H if (dirp) { closedir(dirp); - dirp = 0; } #endif #ifdef _WIN32 @@ -165,7 +153,7 @@ void dir_close( DIR* dirp ) { } #endif #ifdef macintosh - SayErr("\pdir_close called (empty function)"); /* CAF Temp */ + SayErr("\pdir_close called (empty function)"); /* CAF Temp */ #endif } @@ -173,10 +161,7 @@ void dir_close( DIR* dirp ) { // int file_delete(char* path) { int retval,i; - if(path==NULL) { - fprintf(stderr, "error: file_delete: unexpected NULL pointer path\n"); - return ERR_NULL; - } + for (i=0; i<2; i++) { #ifdef HAVE_UNISTD_H retval = unlink(path); @@ -198,54 +183,41 @@ int file_delete(char* path) { int file_size(char* path, int& size) { struct stat sbuf; int retval; - if(path==NULL) { - fprintf(stderr, "error: file_size: unexpected NULL pointer path\n"); - return ERR_NULL; - } + retval = stat(path, &sbuf); if (retval) return retval; size = sbuf.st_size; return 0; } -/* boinc_link creates a file (new_link) which contains an XML - reference (soft link) to existing. */ - -int boinc_link( char *existing, char *new_link ) { +// create a file (new_link) which contains an XML +// reference to existing file. +// +int boinc_link(char *existing, char *new_link) { FILE *fp; - if(existing==NULL) { - fprintf(stderr, "error: boinc_link: unexpected NULL pointer existing\n"); - return ERR_NULL; - } - if(new_link==NULL) { - fprintf(stderr, "error: boinc_link: unexpected NULL pointer new_link\n"); - return ERR_NULL; - } - fp = fopen( new_link, "wb" ); + + fp = fopen(new_link, "wb"); if (!fp) return ERR_FOPEN; - rewind( fp ); - fprintf( fp, "%s\n", existing ); - fclose( fp ); + fprintf(fp, "%s\n", existing); + fclose(fp); return 0; } +#include + // Goes through directory specified by dirpath and removes all files from it // int clean_out_dir(char* dirpath) { char filename[256], path[256]; int retval; DIR *dirp; - if(dirpath==NULL) { - fprintf(stderr, "error: clean_out_dir: unexpected NULL pointer dirpath\n"); - return ERR_NULL; - } dirp = dir_open(dirpath); if (!dirp) return -1; while (1) { strcpy(filename,""); - retval = dir_scan(filename,dirp); + retval = dir_scan(filename, dirp); if (retval) break; sprintf(path, "%s/%s", dirpath, filename); retval = file_delete(path); @@ -262,25 +234,19 @@ int clean_out_dir(char* dirpath) { // total size of files in it and its subdirectories // double dir_size(char* dirpath) { - char filename[256], *path; + char filename[256], subdir[256]; int retval,temp; double cur_size = 0; DIR *dirp; - if(dirpath==NULL) { - fprintf(stderr, "error: dir_size: unexpected NULL pointer dirpath\n"); - return ERR_NULL; - } - - path = (char *)malloc( 256*sizeof( char ) ); dirp = dir_open(dirpath); if (!dirp) return -1; while (1) { - retval = dir_scan(filename,dirp); + retval = dir_scan(filename, dirp); if (retval) break; - sprintf(path, "%s/%s", dirpath, filename); - cur_size += dir_size( path ); - retval = file_size(path,temp); + sprintf(subdir, "%s/%s", dirpath, filename); + cur_size += dir_size(subdir); + retval = file_size(subdir, temp); if (retval) { dir_close(dirp); return cur_size; diff --git a/client/filesys.h b/client/filesys.h index c042fc41d3..401e1b37d5 100644 --- a/client/filesys.h +++ b/client/filesys.h @@ -25,6 +25,6 @@ extern int dir_scan(char*,DIR*); extern void dir_close(DIR*); extern int file_delete(char*); extern int file_size(char*, int&); -extern int boinc_link( char *existing, char *new_link ); +extern int boinc_link(char *existing, char *new_link); extern int clean_out_dir(char*); extern double dir_size(char* dirpath); diff --git a/doc/api.html b/doc/api.html index f8ebb1b3f6..654320ac0b 100644 --- a/doc/api.html +++ b/doc/api.html @@ -9,45 +9,45 @@ The graphics API is described separately.

Initialization and termination

The application must call
-    int boinc_init(bool is_main_program, char** init_data_xml)
+    int boinc_init();
 
before calling other BOINC functions or doing I/O. -is_main_program indicates whether the program -is the application's main program (normally true - see below). -Various information is returned in the malloced init_data_xml, -an XML string that may contain the following elements: +It may call
-    <init_data>
-        <app_preferences>...</app_preferences>
-        <user_name>...</user_name>
-        <team_name>...</team_name>
-        <wu_cpu_time>...</wu_cpu_time>
-        <total_cobblestones>...</total_cobblestones>
-        <recent_avg_cobblestones>...</recent_avg_cobblestones>
-    </init_data>
+    struct APP_INIT_DATA {
+        char project_preferences[4096];
+        char user_name[256];
+        char team_name[256];
+        double wu_cpu_time;           // cpu time from previous sessions
+        double total_cobblestones;
+        double recent_avg_cobblestones;
+    };
+
+    int boinc_get_init_data(APP_INIT_DATA&);
 
+to get the following information:
    -
  • app_preferences: arbitrary text (generally XML); -the participant's preferences for this project. -
  • user_name: the user's account name for this project -
  • team_name: the user's team name, if any, for this project -
  • wu_cpu_time: the CPU time used so far for this work unit -
  • total_cobblestones: the user's total credit -
  • recent_avg_cobblestones: the user's recent average credit +
  • project_preferences: An XML string containing +the user's project-specific preferences. +
  • user_name: the user's "screen name" on this project. +
  • team_name: the user's team name, if any. +
  • wu_cpu_time: the CPU time spent on this WU so far +
  • total_cobblestones: the user's total work for this project. +
  • recent_avg_cobblestones: the recent average work per day.

These items might be used by the application in its graphics. -They can be parsed using the functions in lib/parse.C. +At any time it may call +

+    double boinc_cpu_time();
+
+to get its current CPU time.

-When the application has completed successfully it must call +When the application has completed it must call

-    int boinc_finish();
-
-before exiting. -An application that encounters a fatal error must call -
-    void exit(int error);
+    int boinc_finish(int status);
 
+status is nonzero if an error was encountered.

Resolving file names

Applications that use named input or output files must call @@ -93,7 +93,7 @@ public: int _putchar(char); int puts(char*); int printf(char* format, ...); - size_t write(const void* buf, size_t count); + size_t write(const void* buf, size_t size, size_t nitems); int close(); int flush(); }; @@ -115,42 +115,44 @@ then call
     void boinc_checkpoint_completed();
 
+A call to boinc_time_to_checkpoint() is extremely fast, +so there is little penalty in calling it frequently.

Fraction done

The core client GUI displays the percent done of workunits in progress. To keep this display current, an application should periodically call
-   boinc_percent_done(double fraction_done);
+   boinc_fraction_done(double fraction_done);
 
The fraction_done argument is a rough estimate of the workunit fraction complete (0 to 1). +This function is extremely fast and can be called often.

Multi-program applications

Some applications consist of multiple programs: a main program that acts as coordinator, and one or more subsidiary programs. -Each program should use the BOINC API as described above, -using the appropriate argument to boinc_init(). +Each program should use the BOINC API as described above.

-For checkpointing, each program has its own state file; +Each program should have its own state file; the state file of the coordinator program records which subsidiary program was last active.

-To correctly implement percent done, +To correctly implement fraction done, the main program should pass information to subsidiary programs -(perhaps as command-line arguments) the starting and ending +(perhaps as command-line arguments) indicating the starting and ending fractions for that program.

The coordinator must call

-    boinc_child_start();
+    void boinc_child_start();
 
prior to forking a child process. When the child is done, the coordinator must get the child's CPU time, then call
-    boinc_child_done(double total_CPU);
+    void boinc_child_done(double total_cpu);
 
before forking the next child process. @@ -164,9 +166,8 @@ Input and output files are kept outside the catbox. The mappings from virtual to physical filenames use "symbolic link" files in the catbox directory. The name of such a file is the virtual name, -and it contains an XML tag with the physical name. -This scheme is necessary because of the lack of filesystem links -in Windows. +and the file contains an XML tag with the physical name. +(This scheme is used because of the lack of filesystem links in Windows.)

Communication between the core client and applications @@ -189,10 +190,11 @@ as well as the minimum checkpoint period. Files created by the API implementation, read by the core client:

  • -percent_done.xml: -contains the WU percent done. +fraction_done.xml: +contains the WU fraction done and the current CPU time from start of WU. Written by the timer routine as needed. +
  • checkpoint_cpu.xml CPU time (from start of WU) at last checkpoint. Written by checkpoint_completed. @@ -204,6 +206,50 @@ the real-time clock is not available to applications. This timer is used for several purposes:
    • To tell the app when to checkpoint; -
    • To regenerate the percent done file +
    • To regenerate the fraction done file
    • To refresh graphics
    + +

    +Exit status +The core client does a wait() to get the status. +boinc_finish() ends with an exit(status); +

    +Accounting of CPU time: +(note: in Unix, a parent can't get the CPU time of a child +until the child exits. So we're forced to measure it in the child.) +The core passes the WU CPU time in init_data.xml. +boinc_checkpoint_done() and boinc_finish() compute the new WU CPU time, +and write it to checkpoint_cpu.xml. +The core deletes this after reading. +If on exit there is no checkpoint_cpu.xml, it means the app +called exit(0) rather than boinc_finish(). +In this case the core measures the child CPU itself. +

    +The core client maintains +

    +Timing of checkpoints +

    +The app library maintains time_until_checkpoint, +decremented from the timer handler. +boinc_time_to_checkpoint() returns true if this is zero or less. +boinc_checkpoint_done() resets it. + +

    +Maintaining fraction done and current CPU +

    +These two quantities are transferred from the app library to +the core client in the file fraction_done.xml. +The parameter time_until_fraction_done_update, +passed in the initialization file, +determines how often this file is written. +It is written from the timer handler. +

    +For multi-program applications, only the active application +must write the file. +The functions boinc_child_start() and boinc_child_done() +tell the app library to stop and start writing the file. +

    +TO DO: this creates disk traffic. +Either figure out a way of increasing the period for users who don't +want disk access, or don't use disk files.