[[PageOutline]] = 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 [GraphicsApps here]. == Initialization == #init Initialization must be done before calling other BOINC functions. For sequential (single-threaded) apps, call {{{ boinc_init(); }}} === Parallel apps === If your 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 {{{ #!c++ 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 == #filenames Applications that use named input or output files must call {{{ #!c++ int boinc_resolve_filename(char *logical_name, char *physical_name, int len); }}} or {{{ #!c++ int boinc_resolve_filename_s(char *logical_name, std::string& physical_name); }}} to convert logical file names to physical names. For example, instead of {{{ #!c++ f = fopen("my_file", "r"); }}} the application might use {{{ #!c++ 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 == #fopen Applications should replace `fopen()` calls with {{{ #!c++ 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 == #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 {{{ #!c++ 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 {{{ #!c++ 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 == #critical_sections {{{ #!c++ 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. {{{ #!c++ 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 == #progress The BOINC Manager displays the percent done of tasks in progress. To keep this display current, an application should periodically call {{{ #!c++ 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 == {{{ #!c++ 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 == #standalone 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 {{{ #!c++ 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 == #timer {{{ #!c++ 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 {{{ #!c++ 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.