// Handle a task that has finished. // Mark its output files as present, and delete scratch files. // Don't delete input files because they might be shared with other WUs. // Update state of result record. // int CLIENT_STATE::app_finished(ACTIVE_TASK& at) { RESULT* rp = at.result; bool had_error = false; #ifndef SIM FILE_INFO* fip; unsigned int i; char path[MAXPATHLEN]; int retval; double size; // scan the output files, check if missing or too big. // Don't bother doing this if result was aborted via GUI or by project // switch (rp->exit_status) { case EXIT_ABORTED_VIA_GUI: case EXIT_ABORTED_BY_PROJECT: break; default: for (i=0; i<rp->output_files.size(); i++) { FILE_REF& fref = rp->output_files[i]; fip = fref.file_info; if (fip->uploaded) continue; get_pathname(fip, path, sizeof(path)); retval = file_size(path, size); if (retval) { if (fref.optional) { fip->upload_urls.clear(); continue; } // an output file is unexpectedly absent. // fip->status = retval; had_error = true; msg_printf( rp->project, MSG_INFO, "Output file %s for task %s absent", fip->name, rp->name ); } else if (size > fip->max_nbytes) { // Note: this is only checked when the application finishes. // The total disk space is checked while the application is running. // msg_printf( rp->project, MSG_INFO, "Output file %s for task %s exceeds size limit.", fip->name, rp->name ); msg_printf( rp->project, MSG_INFO, "File size: %f bytes. Limit: %f bytes", size, fip->max_nbytes ); fip->delete_file(); fip->status = ERR_FILE_TOO_BIG; had_error = true; } else { if (!fip->uploadable() && !fip->sticky) { fip->delete_file(); // sets status to NOT_PRESENT } else { retval = 0; if (fip->gzip_when_done) { retval = fip->gzip(); } if (!retval) { retval = md5_file(path, fip->md5_cksum, fip->nbytes); } if (retval) { fip->status = retval; had_error = true; } else { fip->status = FILE_PRESENT; } } } } } #endif if (rp->exit_status != 0) { had_error = true; } if (had_error) { switch (rp->exit_status) { case EXIT_ABORTED_VIA_GUI: case EXIT_ABORTED_BY_PROJECT: rp->set_state(RESULT_ABORTED, "CS::app_finished"); break; default: rp->set_state(RESULT_COMPUTE_ERROR, "CS::app_finished"); } rp->project->njobs_error++; } else { #ifdef SIM rp->set_state(RESULT_FILES_UPLOADED, "CS::app_finished"); rp->set_ready_to_report(); rp->completed_time = now; #else rp->set_state(RESULT_FILES_UPLOADING, "CS::app_finished"); rp->append_log_record(); #endif rp->project->update_duration_correction_factor(&at); rp->project->njobs_success++; } double elapsed_time = now - rec_interval_start; work_fetch.accumulate_inst_sec(&at, elapsed_time); rp->project->pwf.request_if_idle_and_uploading = true; // set this to allow work fetch if idle instance, // even before upload finishes return 0; }
// scan FILE_INFOs and create PERS_FILE_XFERs as needed. // NOTE: this doesn't start the file transfers // scan PERS_FILE_XFERs and delete finished ones. // bool CLIENT_STATE::create_and_delete_pers_file_xfers() { unsigned int i; FILE_INFO* fip; PERS_FILE_XFER *pfx; bool action = false; int retval; static double last_time; if (!clock_change && now - last_time < PERS_FILE_XFER_START_PERIOD) return false; last_time = now; // Look for FILE_INFOs for which we should start a transfer, // and make PERS_FILE_XFERs for them // for (i=0; i<file_infos.size(); i++) { fip = file_infos[i]; pfx = fip->pers_file_xfer; if (pfx) continue; if (fip->downloadable() && fip->status == FILE_NOT_PRESENT) { pfx = new PERS_FILE_XFER; pfx->init(fip, false); fip->pers_file_xfer = pfx; pers_file_xfers->insert(fip->pers_file_xfer); action = true; } else if (fip->uploadable() && fip->status == FILE_PRESENT && !fip->uploaded) { pfx = new PERS_FILE_XFER; pfx->init(fip, true); fip->pers_file_xfer = pfx; pers_file_xfers->insert(fip->pers_file_xfer); action = true; } } // Scan existing PERS_FILE_XFERs, looking for those that are done, // and deleting them // vector<PERS_FILE_XFER*>::iterator iter; iter = pers_file_xfers->pers_file_xfers.begin(); while (iter != pers_file_xfers->pers_file_xfers.end()) { pfx = *iter; // If the transfer finished, remove the PERS_FILE_XFER object // from the set and delete it // if (pfx->pers_xfer_done) { fip = pfx->fip; if (pfx->is_upload) { // file has been uploaded - delete if not sticky // if (!fip->sticky) { fip->delete_file(); } fip->uploaded = true; active_tasks.upload_notify_app(fip); } else if (fip->status >= 0) { // file transfer did not fail (non-negative status) // If this was a compressed download, rename .gzt to .gz // if (fip->download_gzipped) { char path[MAXPATHLEN], from_path[MAXPATHLEN], to_path[MAXPATHLEN]; get_pathname(fip, path, sizeof(path)); snprintf(from_path, sizeof(from_path), "%s.gzt", path); snprintf(to_path, sizeof(to_path), "%s.gz", path); boinc_rename(from_path, to_path); } // verify the file with RSA or MD5, and change permissions // retval = fip->verify_file(true, true, true); if (retval == ERR_IN_PROGRESS) { // do nothing } else if (retval) { msg_printf(fip->project, MSG_INTERNAL_ERROR, "Checksum or signature error for %s", fip->name ); fip->status = retval; } else { // Set the appropriate permissions depending on whether // it's an executable or normal file // retval = fip->set_permissions(); fip->status = FILE_PRESENT; } // if it's a user file, tell running apps to reread prefs // if (fip->is_user_file) { active_tasks.request_reread_prefs(fip->project); } // if it's a project file, make a link in project dir // if (fip->is_project_file) { PROJECT* p = fip->project; p->write_symlink_for_project_file(fip); p->update_project_files_downloaded_time(); } } iter = pers_file_xfers->pers_file_xfers.erase(iter); delete pfx; action = true; // `delete pfx' should have set pfx->fip->pfx to NULL assert (fip == NULL || fip->pers_file_xfer == NULL); } else { ++iter; } } return action; }