9 BasicApi
David Anderson edited this page 2024-03-22 02:34:41 -07:00

The BOINC application programming interface (API)

The BOINC API is a set of C++ functions. Most of the functions have a C interface, so that they can be used from programs written in C and other languages. Unless otherwise specified, the functions return an integer error code; zero indicates success. To use the API include the header file:

#include "boinc_api.h"

BOINC applications may have an associate graphics program, which can act as a screensaver. The API for these graphics apps is here.

Initialization

Initialization must be done before calling other BOINC functions. For sequential (single-threaded) apps, call

boinc_init();

Parallel apps

If your app uses multiple threads or processes for parallelism, initialize using

BOINC_OPTIONS options;

boinc_options_defaults(options);
options.multi_thread = true;    // if your app's main process uses multiple threads
options.multi_process = true;   // if your app uses multiple processes

boinc_init_options(&options);

Do this before creating any threads or processes, or storing the PID.

GPU and coprocessor apps

If your app uses GPUs or coprocessors, initialize using

BOINC_OPTIONS options;

boinc_options_defaults(options);
options.normal_thread_priority = true;
boinc_init_options(&options);

On Windows, this causes the application to run at normal thread priority, so that the GPU will run at full speed even if the CPUs are loaded.

Termination

When the application has completed it must call

int boinc_finish(int status);

status is nonzero if an error was encountered. This call does not return.

Do not call exit(0). If you do, BOINC will restart the app, which is probably not what you want.

Alternatively, if you want to show a message to the user (e.g. because of an error condition that the user can remedy) use

boinc_finish_message(int status, const char* msg, bool is_notice);

If is_notice is true, the message will be shown as a notice in the GUI (works with 7.5+ clients; for others, no message will be shown).

Resolving file names

Applications that use named input or output files must call

int boinc_resolve_filename(char *logical_name, char *physical_name, int len);

or

int boinc_resolve_filename_s(char *logical_name, std::string& physical_name);

to convert logical file names to physical names. For example, instead of

f = fopen("my_file", "r");

the application might use

string resolved_name;
retval = boinc_resolve_filename_s("my_file", resolved_name);
if (retval) fail("can't resolve filename");
f = boinc_fopen(resolved_name.c_str(), "r");

Don't use boinc_resolve_filename() for files with the copy_file attribute, or for temporary files. It must be used for all other input or output files specified in the job templates, or files that are part of the application version.

I/O wrappers

Applications should replace fopen() calls with

boinc_fopen(char* path, char* mode);

This deals with platform-specific problems. On Windows, where security and indexing programs can briefly lock files, boinc_fopen() does several retries at 1-second intervals. On Unix, where signals can cause fopen() to fail with EINTR, boinc_fopen checks for this and does a few retries; it also sets the 'close-on-exec' flag.

Checkpointing

Long jobs may want to periodically write the current state of the computation to disk. This is known as checkpointing. The checkpoint file must include everything required to restart the computation at the same point. On startup, the application reads the checkpoint file to determine where to begin computation. If the BOINC client quits or exits, the computation can be restarted from the most recent checkpoint.

Most applications are able to checkpoint only at specific points, e.g. at the end the outer loop. Whan the application is at such a point, it must call

int boinc_time_to_checkpoint();

If this returns nonzero (True) then the application should checkpoint immediately (i.e., write the state file and flush all output files), then call

void boinc_checkpoint_completed();

boinc_time_to_checkpoint() is fast, so it can be called frequently (hundreds or thousands of times a second).

boinc_time_to_checkpoint returns true only when sufficient time has passed since the last checkpoint. This minimum interval is the maximum of:

  • A user preference (e.g. laptop users might want to checkpoint infrequently).
  • An optional application-supplied, specified by calling
boinc_set_min_checkpoint_period(int nsecs);

If you're using replication, make sure your application generates the same results regardless of where and how often it restarts. This requires:

  • In writing the checkpoint file, use conversion codes that don't lose precision; e.g., use %e for doubles.
  • If your app uses random numbers, save and restore the state of the RNG. If you use rand(), you can do this by surrounding every boinc_time_to_checkpoint() with the following:
int x = rand();
if (boinc_time_to_checkpoint()) {
   ...
}
srand(x);

Write x to the checkpoint file, and do a srand(x) when restarting from a checkpoint.

Critical sections

void boinc_begin_critical_section();
void boinc_end_critical_section();

Call these around code segments during which you don't want to be suspended or killed by the core client. Since r14694, critical sections are reentrant. This means that you can begin critical section multiple times, but each begin must have a matching end call.

NOTE: This is done automatically while checkpointing.

Atomic file update

To facilitate atomic checkpoint, an application can write to output and state files using the MFILE class.

class MFILE {
public:
    int open(char* path, char* mode);
    int _putchar(char);
    int puts(char*);
    int printf(char* format, ...);
    size_t write(const void* buf, size_t size, size_t nitems);
    int close();
    int flush();
};

MFILE buffers data in memory and writes to disk only on flush() or close(). This lets you write output files and state files more or less atomically.

Reporting progress

The BOINC Manager displays the percent done of tasks in progress. To keep this display current, an application should periodically call

boinc_fraction_done(double fraction_done);

The fraction_done argument is an estimate of the workunit fraction complete (from 0 to 1). This function is fast and can be called frequently (once per second or more). The sequence of arguments in successive calls should be non-decreasing. (An application should not 'reset' and start over if an error occurs; it should call boinc_finish() with a nonzero error code.)

Many applications can supply only an approximate fraction done. If your application can supply an accurate fraction done, set the "exact fraction done" attribute of the app.

Timing information

int boinc_wu_cpu_time(double &cpu_time);

gets the total CPU time (from the beginning of the work unit, not just since the last restart).

double boinc_elapsed_time();

returns the elapsed runtime (i.e. wall-clock time during which the job was not suspended) since the start of the current episode. The elapsed time from earlier episodes is in APP_INIT_DATA::starting_elapsed_time (only from 6.10+ clients).

Standalone mode

BOINC applications can be run in "standalone" mode for testing, or under the control of the BOINC client. You might want your application to behave differently in the two cases. For example you might want to output debugging information if the application is running standalone. To determine if the application is running in standalone mode or under the control of the BOINC client, call

int boinc_is_standalone(void);

This returns non-zero (True) if the application is running standalone, and zero (False) if the application is running under the control of the BOINC client.

Registering a timer handler

typedef void (*FUNC_PTR)();
void boinc_register_timer_callback(FUNC_PTR);

This registers a timer handler function, which will be called once per second.

Temporary exit

If an application is unable to run because of a transient condition, it should call

int boinc_temporary_exit(int delay, const char* reason=NULL, bool is_notice=false);

This will exit the application and tell the BOINC client to restart it again in at least delay seconds. (This works with 6.10.25+ client; on other clients, it will potentially restart immediately). Reason, if supplied, is shown to the user as the explanation for the deferral. If is_notice is true, it's shown as a notice (this should be used only for conditions that the user can fix).

Examples:

  • A GPU application fails to allocate GPU RAM because non-BOINC programs have GPU RAM allocated.
  • The Vboxwrapper sees that an incompatible version of VirtualBox is installed. In this case "is_notice" would be true, because the user can update VirtualBox.