wiki:BasicApi

Version 31 (modified by davea, 14 years ago) (diff)

--

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.

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

BOINC applications may consist of several programs that are executed in sequence; these are called compound applications. See the compound application API.

Initialization and termination

Initialization must be done before calling other BOINC functions.

To initialize a single-thread program, call

int boinc_init();

To initialize a multi-thread program, call

int boinc_init_parallel();

Note that boinc_init_parallel will fork on Unix systems, so you must not create any thread or try to store the current PID before calling this function. If this function succeeds, the parent (original) process will run an internal loop, and will not return; code following the boinc_init_parallel call will run in the child process.

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.

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 = fopen(resolved_name.c_str(), "r");

boinc_resolve_filename() doesn't need to be used for temporary files; only for the input or output files specified in the job templates, or files 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

Computations that use a significant amount of time per work unit may want to periodically write the current state of the computation to disk. This is known as checkpointing. The state file must include everything required to start the computation again at the same place it was checkpointed. On startup, the application reads the state file to determine where to begin computation. If the BOINC client quits or exits, the computation can be restarted from the most recent checkpoint.

Frequency of checkpointing is a user preference (e.g. laptop users might want to checkpoint infrequently). An application must call

int boinc_time_to_checkpoint();

whenever it reaches a point where it is able 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).

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. 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 core client GUI displays the percent done of workunits 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. The sequence of arguments in successive calls should be non-decreasing. An application should never 'reset' and start over if an error occurs; it should exit with an error code.

Miscellaneous data

The following functions return miscellaneous data:

int boinc_get_init_data_p(APP_INIT_DATA*);
int boinc_get_init_data(APP_INIT_DATA&);

struct APP_INIT_DATA {
    int major_version;
    int minor_version;
    int release;
    int app_version;
    char app_name[256];
    char symstore[256];
    char acct_mgr_url[256];
    char* project_preferences;
    int hostid;
    char user_name[256];
    char team_name[256];
    char project_dir[256];
    char boinc_dir[256];
    char wu_name[256];
    char authenticator[256];
    int slot;
    double user_total_credit;
    double user_expavg_credit;
    double host_total_credit;
    double host_expavg_credit;
    double resource_share_fraction;
    HOST_INFO host_info;
    PROXY_INFO proxy_info;  // in case app wants to use network
    GLOBAL_PREFS global_prefs;
    double starting_elapsed_time;

    // info about the WU
    double rsc_fpops_est;
    double rsc_fpops_bound;
    double rsc_memory_bound;
    double rsc_disk_bound;

    // the following are used for compound apps,
    // where each stage of the computation is a fixed
    // fraction of the total.
    double fraction_done_start;
    double fraction_done_end;
};

to get the following information:

core versionThe version number of the core client
app_nameThe application name (from the server's DB)
project_preferencesAn XML string containing the user's project-specific preferences.
user_nameThe user's 'screen name' on this project.
team_nameThe user's team name, if any.
project_dirAbsolute path of project directory
boinc_dirAbsolute path of BOINC root directory
wu_nameName of workunit being processed
authenticatorUser's authenticator for this project
slotThe number of the app's 'slot'
user_total_creditUser's total work for this project.
user_expavg_creditUser's recent average work per day.
team_total_creditTeam's total work for this project.
team_expavg_creditTeam's recent average work per day.
host_infoA structure describing the host hardware and OS
starting_elapsed_timeElapsed time, counting previous episodes (provided only by 6.10 and later clients)

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 time 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.

Requesting network connection

If your application needs to do network communication and it appears that there is no physical network connection (e.g. gethostbyname() fails for a valid name) then:

  • Call boinc_need_network(). This will alert the user that a network connection is needed.
  • Periodically call boinc_network_poll() until it returns zero.
  • Do whatever communication is needed.
  • When done, call boinc_network_done(). This enables the hangup of a modem connection, if needed.
void boinc_need_network();
int boinc_network_poll();
void boinc_network_done();

Temporary exit

If a GPU application fails to allocate GPU RAM, it may be a temporary problem (non-BOINC programs have GPU RAM allocated). In this case they should call

int boinc_temporary_exit(int delay);

This will exit the application, and will 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).