revised API

svn path=/trunk/boinc/; revision=266
This commit is contained in:
David Anderson 2002-08-05 00:29:34 +00:00
parent ae9cfdebb0
commit ac1f13f2a8
29 changed files with 887 additions and 808 deletions

View File

@ -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

367
api/api.C
View File

@ -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 <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#include <io.h>
#include <sys/stat.h>
#include <windows.h>
#include <winuser.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#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,
"<graphics_xsize>%d</graphics_xsize>\n"
"<graphics_ysize>%d</graphics_ysize>\n"
"<graphics_refresh_period>%f</graphics_refresh_period>\n"
"<checkpoint_period>%f</checkpoint_period>\n"
"<poll_period>%f</poll_period>\n"
"<checkpoint_period>%f</checkpoint_period>\n"
"<cpu_time>%f</cpu_time>\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, "<app_specific_prefs>")) {
strcpy(ai.app_preferences, "");
while (fgets(buf, 256, f)) {
if (match_tag(buf, "</app_specific_prefs>")) break;
strcat(ai.app_preferences, buf);
}
continue;
}
else if (parse_int(buf, "<graphics_xsize>", ai.graphics.xsize)) continue;
else if (parse_int(buf, "<graphics_ysize>", ai.graphics.ysize)) continue;
else if (parse_double(buf, "<graphics_refresh_period>", ai.graphics.refresh_period)) continue;
else if (parse_double(buf, "<checkpoint_period>", ai.checkpoint_period)) continue;
else if (parse_double(buf, "<poll_period>", ai.poll_period)) continue;
else if (parse_double(buf, "<checkpoint_period>", ai.checkpoint_period)) continue;
else if (parse_double(buf, "<cpu_time>", 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,
"<percent_done>%f</percent_done>\n"
"<cpu_time_at_checkpoint>%f</cpu_time_at_checkpoint>\n",
ao.percent_done,
ao.cpu_time_at_checkpoint
);
if (ao.checkpointed) {
fprintf(f, "<checkpointed/>\n");
}
}
static void parse_app_file(FILE* f, APP_OUT& ao) {
char buf[256];
while (fgets(buf, 256, f)) {
if (parse_double(buf, "<percent_done>", ao.percent_done)) continue;
else if (parse_double(buf, "<cpu_time_at_checkpoint>",
ao.cpu_time_at_checkpoint)) continue;
else if (match_tag(buf, "<checkpointed/>")) 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, "<fdesc_dup_infile>%s</fdesc_dup_infile>\n", file_name );
fprintf( f, "<fdesc_dup_innum>%d</fdesc_dup_innum>\n", fdesc );
} else {
fprintf( f, "<fdesc_dup_outfile>%s</fdesc_dup_outfile>\n", file_name );
fprintf( f, "<fdesc_dup_outnum>%d</fdesc_dup_outnum>\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, "<fdesc_dup_infile>", filename)) {
if (fgets(buf, 256, f)) {
if (parse_int(buf, "<fdesc_dup_innum>", 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, "<fdesc_dup_outfile>", filename)) {
if (fgets(buf, 256, f)) {
if (parse_int(buf, "<fdesc_dup_outnum>", 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 <soft_link> XML tag, return it's value,
// otherwise, return the original file name
//
if( !parse_str( buf, "<soft_link>", 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;
}

117
api/api.h
View File

@ -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 <stdio.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#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

View File

@ -24,7 +24,7 @@
#include <sys/time.h>
#endif
#include "api.h"
#include "boinc_api.h"
int recover(char* file, unsigned long int* i);
int timer(int secs, int usecs);

View File

@ -22,7 +22,7 @@ Contributor(s): See ACKNOWLEDGEMENTS.
#include <stdlib.h>
#include "api.h"
#include "boinc_api.h"
int get_run_info(double& time, unsigned long int& counter);
void run_api_test(char* args);

373
api/boinc_api.C Normal file
View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#include <io.h>
#include <sys/stat.h>
#include <windows.h>
#include <winuser.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#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 <soft_link> XML tag, return its value,
// otherwise, return the original file name
//
parse_str( buf, "<soft_link>", 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, "<app_preferences>\n%s</app_preferences>\n", ai.app_preferences);
}
if (strlen(ai.team_name)) {
fprintf(f, "<team_name>\n%s</team_name>\n", ai.team_name);
}
if (strlen(ai.user_name)) {
fprintf(f, "<user_name>\n%s</user_name>\n", ai.user_name);
}
fprintf(f,
"<wu_cpu_time>%f</wu_cpu_time>\n"
"<total_cobblestones>%f</total_cobblestones>\n"
"<recent_avg_cobblestones>%f</recent_avg_cobblestones>\n"
"<checkpoint_period>%f</checkpoint_period>\n"
"<fraction_done_update_period>%f</fraction_done_update_period>\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, "<app_preferences>")) {
strcpy(ai.app_preferences, "");
while (fgets(buf, 256, f)) {
if (match_tag(buf, "</app_specific_prefs>")) break;
strcat(ai.app_preferences, buf);
}
continue;
}
else if (parse_str(buf, "<user_name>", ai.user_name)) continue;
else if (parse_str(buf, "<team_name>", ai.team_name)) continue;
else if (parse_double(buf, "<total_cobblestones>", ai.total_cobblestones)) continue;
else if (parse_double(buf, "<recent_avg_cobblestones>", ai.recent_avg_cobblestones)) continue;
else if (parse_double(buf, "<wu_cpu_time>", ai.wu_cpu_time)) continue;
else if (parse_double(buf, "<checkpoint_period>", ai.checkpoint_period)) continue;
else if (parse_double(buf, "<fraction_done_update_period>", 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, "<checkpoint_cpu_time>%f</checkpoint_cpu_time>\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_time>", checkpoint_cpu)) continue;
}
return 0;
}
int write_fraction_done_file(FILE* f, double pct, double cpu) {
fprintf(f,
"<fraction_done>%f</fraction_done>\n"
"<cpu_time>%f</cpu_time>\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, "<fraction_done>", pct)) continue;
else if (parse_double(buf, "<cpu_time>", 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, "<fdesc_dup_infile>%s</fdesc_dup_infile>\n", file_name);
fprintf(f, "<fdesc_dup_innum>%d</fdesc_dup_innum>\n", fdesc);
} else {
fprintf(f, "<fdesc_dup_outfile>%s</fdesc_dup_outfile>\n", file_name);
fprintf(f, "<fdesc_dup_outnum>%d</fdesc_dup_outnum>\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, "<fdesc_dup_infile>", filename)) {
if (fgets(buf, 256, f)) {
if (parse_int(buf, "<fdesc_dup_innum>", 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, "<fdesc_dup_outfile>", filename)) {
if (fgets(buf, 256, f)) {
if (parse_int(buf, "<fdesc_dup_outnum>", 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;
}

101
api/boinc_api.h Executable file
View File

@ -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 <stdio.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#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

23
api/graphics_api.C Executable file
View File

@ -0,0 +1,23 @@
void write_graphics_file(FILE* f, GRAPHICS_INFO& gi) {
fprintf(f,
"<graphics_xsize>%d</graphics_xsize>\n"
"<graphics_ysize>%d</graphics_ysize>\n"
"<graphics_refresh_period>%f</graphics_refresh_period>\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, "<graphics_info>")) return 0;
else if (parse_int(buf, "<graphics_xsize>", gi.graphics.xsize)) continue;
else if (parse_int(buf, "<graphics_ysize>", gi.graphics.ysize)) continue;
else if (parse_double(buf, "<graphics_refresh_period>", gi.graphics.refresh_period)) continue;
else fprintf(stderr, "parse_core_file: unrecognized %s", buf);
}
return -1;
}

9
api/graphics_api.h Executable file
View File

@ -0,0 +1,9 @@
struct APP_IN_GRAPHICS {
int xsize;
int ysize;
double refresh_period;
char shmem_seg_name[32];
};
struct APP_OUT_GRAPHICS {
};

64
api/mfile.C Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <malloc.h>
#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);
}

View File

@ -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@

View File

@ -23,7 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#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<argc; i++) {
fprintf(stderr, "APP: concat: argv[%d] is %s\n", i, argv[i]);
}
boinc_resolve_link( argv[argc-1], file_name );
boinc_resolve_filename( argv[argc-1], file_name );
fprintf( stderr, "res: %s\n", file_name );
out = fopen(file_name, "w");
if (!out) {
@ -55,7 +54,7 @@ int main(int argc, char** argv) {
exit(1);
}
for (i=1; i<argc-1; i++) {
boinc_resolve_link( argv[i], file_name );
boinc_resolve_filename( argv[i], file_name );
fprintf( stderr, "res: %s\n", file_name );
in = fopen(file_name, "r");
if (!in) {
@ -67,5 +66,6 @@ int main(int argc, char** argv) {
}
fclose(out);
fprintf(stderr, "APP: concat: done\n");
boinc_finish(0);
return 0;
}

View File

@ -24,7 +24,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#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<argc; i++) {
fprintf(stderr, "APP: concat: argv[%d] is %s\n", i, argv[i]);
}
boinc_resolve_link( CHECKPOINT_FILE, file_name );
boinc_resolve_filename( CHECKPOINT_FILE, file_name );
state = fopen( file_name, "r" );
if( state ) {
fscanf( state, "%d %d", &file_num, &nchars );
@ -110,7 +106,7 @@ int main(int argc, char** argv) {
nchars = 0;
mode = "w";
}
boinc_resolve_link( argv[argc-1], file_name );
boinc_resolve_filename( argv[argc-1], file_name );
fprintf( stderr, "res: %s\n", file_name );
retval = out.open(file_name, mode);
if (retval) {
@ -118,7 +114,7 @@ int main(int argc, char** argv) {
exit(1);
}
for (i=file_num; i<argc-1; i++) {
boinc_resolve_link( argv[i], file_name );
boinc_resolve_filename( argv[i], file_name );
fprintf( stderr, "res: %s\n", file_name );
in = fopen(file_name, "r");
if (!in) {
@ -131,5 +127,6 @@ int main(int argc, char** argv) {
}
out.close();
fprintf(stderr, "APP: concat: done\n");
boinc_finish(0);
return 0;
}

View File

@ -26,7 +26,7 @@
#include <unistd.h>
#include <ctype.h>
#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;
}

View File

@ -26,7 +26,7 @@
#include <unistd.h>
#include <ctype.h>
#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;
}

View File

@ -22,14 +22,12 @@
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#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;
}

View File

@ -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

View File

@ -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 \

View File

@ -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; i<app_version->app_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; i<active_tasks.size(); i++) {
atp = active_tasks[i];
if( GetExitCodeProcess( atp->pid_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; i<active_tasks.size(); i++) {
atp = active_tasks[i];
if (atp->pid == pid) return atp;
@ -549,7 +523,7 @@ void ACTIVE_TASK_SET::exit_tasks() {
for (i=0; i<active_tasks.size(); i++) {
atp = active_tasks[i];
atp->request_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<ACTIVE_TASK*>::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; i<active_tasks.size(); i++) {
atp = active_tasks[i];
updated |= atp->update_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,
"<active_task>\n"
" <project_master_url>%s</project_master_url>\n"
" <result_name>%s</result_name>\n"
" <app_version_num>%d</app_version_num>\n"
" <slot>%d</slot>\n"
" <prev_cpu_time>%f</prev_cpu_time>\n"
" <checkpoint_cpu_time>%f</checkpoint_cpu_time>\n"
"</active_task>\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>", project_master_url)) continue;
else if (parse_int(buf, "<app_version_num>", app_version_num)) continue;
else if (parse_int(buf, "<slot>", slot)) continue;
else if (parse_double(buf, "<prev_cpu_time>", prev_cpu_time)) continue;
else if (parse_double(buf, "<checkpoint_cpu_time>", 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, "<active_task_set>\n");
for (i=0; i<active_tasks.size(); i++) {
active_tasks[i]->write(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, "</active_task_set>")) return 0;
else if (match_tag(buf, "<active_task>")) {

View File

@ -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*);

View File

@ -300,7 +300,6 @@ int CLIENT_STATE::exit() {
}
int CLIENT_STATE::exit_tasks() {
int retval;
active_tasks.exit_tasks();
return 0;
}

View File

@ -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>", cpu_time)) continue;
else if (parse_double(buf, "<final_cpu_time>", final_cpu_time)) continue;
else if (parse_int(buf, "<exit_status>", exit_status)) continue;
else if (match_tag(buf, "<stderr_out>" )) {
while (fgets(buf, 256, in)) {
@ -589,10 +589,10 @@ int RESULT::write(FILE* out, bool to_server) {
"<result>\n"
" <name>%s</name>\n"
" <exit_status>%d</exit_status>\n"
" <cpu_time>%f</cpu_time>\n",
" <final_cpu_time>%f</final_cpu_time>\n",
name,
exit_status,
cpu_time
final_cpu_time
);
n = strlen(stderr_out);
if (n) {

View File

@ -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;

View File

@ -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;
}

View File

@ -57,11 +57,8 @@ double CLIENT_STATE::current_water_days() {
for (i=0; i<results.size(); i++) {
rp = results[i];
if (rp->is_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);
}

View File

@ -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;
}

View File

@ -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, "<soft_link>%s</soft_link>\n", existing );
fclose( fp );
fprintf(fp, "<soft_link>%s</soft_link>\n", existing);
fclose(fp);
return 0;
}
#include <dirent.h>
// 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;

View File

@ -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);

View File

@ -9,45 +9,45 @@ The graphics API is described <a href=graphics.html>separately</a>.
<h3>Initialization and termination</h3>
The application must call
<pre>
int boinc_init(bool is_main_program, char** init_data_xml)
int boinc_init();
</pre>
before calling other BOINC functions or doing I/O.
<tt>is_main_program</tt> indicates whether the program
is the application's main program (normally true - see below).
Various information is returned in the malloced <tt>init_data_xml</tt>,
an XML string that may contain the following elements:
It may call
<pre>
&lt;init_data>
&lt;app_preferences>...&lt;/app_preferences>
&lt;user_name>...&lt;/user_name>
&lt;team_name>...&lt;/team_name>
&lt;wu_cpu_time>...&lt;/wu_cpu_time>
&lt;total_cobblestones>...&lt;/total_cobblestones>
&lt;recent_avg_cobblestones>...&lt;/recent_avg_cobblestones>
&lt;/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&);
</pre>
to get the following information:
<ul>
<li><b>app_preferences</b>: arbitrary text (generally XML);
the participant's preferences for this project.
<li><b>user_name</b>: the user's account name for this project
<li><b>team_name</b>: the user's team name, if any, for this project
<li><b>wu_cpu_time</b>: the CPU time used so far for this work unit
<li><b>total_cobblestones</b>: the user's total credit
<li><b>recent_avg_cobblestones</b>: the user's recent average credit
<li> <b>project_preferences</b>: An XML string containing
the user's project-specific preferences.
<li> <b>user_name</b>: the user's "screen name" on this project.
<li> <b>team_name</b>: the user's team name, if any.
<li> <b>wu_cpu_time</b>: the CPU time spent on this WU so far
<li> <b>total_cobblestones</b>: the user's total work for this project.
<li> <b>recent_avg_cobblestones</b>: the recent average work per day.
</ul>
<p>
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
<pre>
double boinc_cpu_time();
</pre>
to get its current CPU time.
<p>
When the application has completed successfully it must call
When the application has completed it must call
<pre>
int boinc_finish();
</pre>
before exiting.
An application that encounters a fatal error must call
<pre>
void exit(int error);
int boinc_finish(int status);
</pre>
<tt>status</tt> is nonzero if an error was encountered.
<h3>Resolving file names</h3>
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
<pre>
void boinc_checkpoint_completed();
</pre>
A call to <tt>boinc_time_to_checkpoint()</tt> is extremely fast,
so there is little penalty in calling it frequently.
<h3>Fraction done</h3>
The core client GUI displays the percent done of workunits in progress.
To keep this display current, an application should
periodically call
<pre>
boinc_percent_done(double fraction_done);
boinc_fraction_done(double fraction_done);
</pre>
The <tt>fraction_done</tt> argument is a rough estimate of the
workunit fraction complete (0 to 1).
This function is extremely fast and can be called often.
<h3>Multi-program applications</h3>
Some applications consist of multiple programs:
a <b>main program</b> 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 <tt>boinc_init()</tt>.
Each program should use the BOINC API as described above.
<p>
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.
<p>
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.
<p>
The coordinator must call
<pre>
boinc_child_start();
void boinc_child_start();
</pre>
prior to forking a child process.
When the child is done, the coordinator
must get the child's CPU time, then call
<pre>
boinc_child_done(double total_CPU);
void boinc_child_done(double total_cpu);
</pre>
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.)
<p>
Communication between the core client and applications
@ -189,10 +190,11 @@ as well as the minimum checkpoint period.
<b>Files created by the API implementation, read by the core client:</b>
<ul>
<li>
<b>percent_done.xml</b>:
contains the WU percent done.
<b>fraction_done.xml</b>:
contains the WU fraction done and the current CPU time from start of WU.
Written by the timer routine as needed.
<li>
<b>checkpoint_cpu.xml</b>
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:
<ul>
<li> To tell the app when to checkpoint;
<li> To regenerate the percent done file
<li> To regenerate the fraction done file
<li> To refresh graphics
</ul>
<p>
<b>Exit status</b>
The core client does a wait() to get the status.
boinc_finish() ends with an exit(status);
<p>
<b>Accounting of CPU time</b>:
(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.
<p>
The core client maintains
<p>
<b>Timing of checkpoints</b>
<p>
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.
<p>
<b>Maintaining fraction done and current CPU</b>
<p>
These two quantities are transferred from the app library to
the core client in the file fraction_done.xml.
The parameter <tt>time_until_fraction_done_update</tt>,
passed in the initialization file,
determines how often this file is written.
It is written from the timer handler.
<p>
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.
<p>
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.