This page is under heavy revision. Please refer to
http://doc.desktopgrid.hu/
for up-to-date information and documentation.
Develop Your Application
One method for preparing an application to be run on a distributed environment is to utilize the client-server model. In this model the application is divided into two parts. A server part and a client part. The job of the server application is to imitate the running of a single-threaded application for the researcher. This means that the researcher feeds the whole input of the research to the server application, which processes the given input and as a result produces a single output for the researcher. The difference is that processing is not meant to be literally, but rather the server application breaks the input into small pieces and feeds them into the BOINC server. The BOINC server automatically handles the given pieces of input and feeds them to the client applications that run on separate PCs. The effective process of the input is carried out on client machines, by the client part of the application.

dcapi Assuming you already have an application that does your research on a 1 machine environment, a slight modification is needed to be carried out. For an application to be run on SZTAKI Desktop Grid the file handling methods of the application has to be platform independent on the client side. Also the server side of the application has to be prepared. To make this development part as easy as possible SZTAKI Desktop Grid provides a simple and intuitive API, namely DC-API to aid the developer in this process. In the next section application development with DC-API will be presented.

Client Side

First let's see how the application has to be prepared on the client side.
  • DC-API header files has to be included:
    #include <dc_client.h>
  • The application has to be prepared to read and write all of its input and output into files:
    static char sworkfile[] = "in.txt";
    static char soutfile[] = "out.txt";
    static char sckptfile[] = "ckpt.txt";
    static FILE* workfile;
    static FILE* outfile;
  • These files has to be opened platform independently, to support multiple platforms
    /* open input file */ 
    DC_ResolveFileName(DC_FILE_IN, sworkfile, infilename, 256);
    workfile = fopen(infilename, "rb");
    /* open output file */
    DC_resolveFileName(DC_FILE_OUT, outfile_logical, outfile_physical, 
    sizeof(outfile_physical));
    outfile = fopen(outfile_physical, outfile_openmode);
    
  • If the runtime of our application is significant ( > 10 mins) it has to be prepared to be able to stop and continue the process at a later time. For this the state of the application has to be resumable. Saving the program state generally means to write the information into a file that is needed for the application to be able to resume the process at the point where it has been interrupted.
    int do_ckpt(void)
    {
      FILE *ckpt;
      char ckptpath[256];
      DC_ResolveFileName(DC_CKPT_FILE, sckptfile, ckptpath, 256)
      ckpt = fopen(ckptpath, "w");
      if (ckpt == NULL){
    	fprintf(stderr, "Cannot create ckpt file %s: errno=%d, %s\n", 
    		sckptfile, errno, strerror(errno));
         return -1;
      }
      fprintf(ckpt, "%d %d", lines_in, lines_out);
      fclose(ckpt);
      fprintf(stderr, "Checkpoint has been made, processed lines=%d, 
      finished lines=%d\n", 
    		  lines_in, lines_out);
      return 0;
    }
    
    After a restart the application checks whether the checkpoint file exists, meaning that the process has to be resumed and not to be started from scratch.
    /* checking if chekpoint file exists */
      DC_ResolveFileName(DC_FILE_CKPT, sckptfile, ckptfilename, 256);
      ckptfile = fopen(ckptfilename, "r");
      if (ckptfile != NULL){
        /* if exists: read content, set variables */
        fscanf(ckptfile, "%d %d", &lines_in, &lines_out);
        fclose(ckptfile);
        fprintf(stderr, "Restart from checkpoint, %d lines has already been processed 
        and %d lines has been output\n", 
    		    lines_in, lines_out);
    
        /* Seek to the next line to be processed in the input file */
        for (i=0; i<lines_in; i++)
            fgets(s,250,workfile);
    
        /* output already exists, open it as r+ */
        outfile_openmode = "r+";
      }
    
    With a built-in timer we can guarantee the application specific checkpoint to execute periodically during the process.
    if (DC_TimeToCheckpoint())   /* if it is time to do a checkpoint and */
    if (!do_ckpt())              /* checkpointing succeeds then */
    	DC_CheckpointMade(); /* restart timer */
    
  • Before the application exits DC_Finish(int status) function has to be called, which notifies the BOINC client to upload the results to the server.
    int main(int argc, char *argv[])
    {
       init_files(); /* BOINC specific init of IO files */
    
       while(input) /* main loop */
       {
            process_input(); /* process segment of input */
            frac_report(); /* report status to BOINC client */
            if (DC_TimeToCheckpoint())   /* if it is time to do a checkpoint */
                  if (!do_ckpt())     /* checkpointing succeeds then */
                       DC_CheckpointMade(); /* restart timer */
       }
      close_files(); /* close IO files */
    
      DC_finish(0); /* DON’T FORGET to call before program exit */
    
      return 0;
    }
    
    

Master side

In this section the master-side of application development will be introduced through the original 'hello world' application of BOINC namely uppercase. The task is to take a text file and convert the letters of it to uppercase via dividing the file into smaller parts and creating workunits of these files to be processed in a desktopgrid environment.
  • First create the input date of the workunits
    /* split the major input file into MAX_N_OF_WUS minor input files */
    static void CreateInputFiles(void)
    {
    	...
    
    	/* Count how many lines the major input file has, 
    	and how many the minor files will get */
    	minor_lines_num = div(major_lines_num, max_wus);
    
    	/* Create the minor input files */
    	for (i = 0; i < max_wus; i++){
    		snprintf(minor_filename, sizeof(minor_filename), "master_input_%d.txt", i);
    		minor_input = fopen(minor_filename, "w");
    		if (minor_input == NULL){
    			fprintf(stderr, "Cannot open %s file.\n", minor_filename);
    			exit(1);
    		}
    		for(j = 0; j < minor_lines_num.quot; j++){
    			snprintf(buf, sizeof(buf), "");
    			fgets(buf, sizeof(buf), major_input);
    			fprintf(minor_input, buf);
    		}
    		if (i < minor_lines_num.rem){
    			fgets(buf, sizeof(buf), major_input);
    			fprintf(minor_input, buf);
    		}
    		fclose(minor_input);
    	}
    }
    
  • Submit the created workunits in the desktopgrid environment for processing
    static void CreateWork(void)
    {
    	int i;
    	char minor_filename[256];
    
    	CreateInputFiles();
    
    	for (i = 0; i < max_wus; i++){
    		wus[i] = DC_createWU("uppercase", ""); /* WU adatstruktúra definiálása */
    		if (wus[i] == -1)	{
    			fprintf(stderr, "Cannot create more workunit!\n");
    			exit(1);
    		}
    		snprintf(minor_filename, sizeof(minor_filename), "master_input_%d.txt", i);
    		DC_setInput(wus[i], minor_filename, "in.txt"); /* Input fájl hozzárendelése a WU-hoz */
    		DC_submitWU(wus[i]);
    	}
    }
    
  • After the workunits have been submitted all we have to do is to wait for any returning results.
    /* Waiting for the results */
    static void WaitForResults(void)
    {
    	while(assimilatedWUs < max_wus){
    		DC_checkForResult(0, cb_assimilate_result);
      }
    }
    
  • If all of the submitted workunits have been successfully returned, the only task remaining is to concatenate the separate results into one final text file, which virtually emulates the output as if it has been processed by a single computer.
    /* Copies the output files into the master working directory */
    static void cb_assimilate_result(DC_Result result)
    {
    	char syscmd[256];
    	int i;
    	fprintf(stdout, "Num of output files: %d\n", result->noutfiles);
    	fprintf(stdout, "result_dir: %s\n", result->outfiles_dir);
    	fprintf(stdout, "outputfile: %s\n", result->outfiles[0]);
    	if (result->noutfiles != 1){
    		fprintf(stderr, "Master: Error: the number of output files :%d\n", result->noutfiles);
    		exit(1);
    	}
    	for (i = 0; i < max_wus; i++){
    		if (result->wu == wus[i]){
    			snprintf(syscmd, 256, "cp %s/%s master_output_%d.txt", result->outfiles_dir, result->outfiles[0], i);
    			system(syscmd);
    			assimilatedWUs++;
    		}
    	}
    	DC_destroyWU(result->wu);
    }
    

Compiling the application

After the application has been prepared it can be compiled. On a linux environment the BOINC and DC-API developer packages has to be installed.
dpkg <package name>
E.g.: 
	dpkg boinc-dev_5.3.1-5_i386.deb
	dpkg libdcapi-dev_0.2-3_i386.deb
The DC-API library uses the pkg-config system to help the building of applications. For every backend, two pkg-config modules are available: dcapi-<backend>-client for building client applications and dcapi-<backend>-master for building master applications. During compilation, the pkg-config --cflags <module> command gives the list of compiler directives (like include file locations) that have to be passed to the compiler whenever one of the DC-API headers is referenced in a source file. During linking, the pkg-config --libs ≶module> command will return the list of libraries and linker options required to link the application with DC-API. If you have built DC-API from source and have installed it at a non-standard location (i.e. not under /usr or /usr/local), you may have to set the PKG_CONFIG_PATH environment variable to point to the directory where the .pc files are located in order to pkg-config to find them.

Validation

If you utilize desktopgrid computing in a local environment there is no need to use validation, because your slave computers are all trusted. Just set 'Redundancy' to 1 in the configuration file of the master application and install the dummy validator which accepts all results and is part of the BOINC distribution with the name 'sample_trivial_validator'. To install the validator simply insert the followings in the <daemons> section of the config.xml file of your project.
<daemon>
 <cmd>
	sample_trivial_validator -app <app_name>
 </cmd>
</daemon>

However, if you would like to set up a public BOINC server, you would have to write an application-dependent validator program, that is able to check whether the output of a program instance is right or not and also capable of comparing outputs of the application. You can find help on this topic at the main BOINC site.