mirror of https://github.com/BOINC/boinc.git
266 lines
7.6 KiB
C
266 lines
7.6 KiB
C
// The contents of this file are subject to the BOINC Public License
|
|
// Version 1.0 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://boinc.berkeley.edu/license_1.0.txt
|
|
//
|
|
// Software distributed under the License is distributed on an "AS IS"
|
|
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing rights and limitations
|
|
// under the License.
|
|
//
|
|
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
|
|
//
|
|
// The Initial Developer of the Original Code is the SETI@home project.
|
|
// Portions created by the SETI@home project are Copyright (C) 2002
|
|
// University of California at Berkeley. All Rights Reserved.
|
|
//
|
|
// Contributor(s):
|
|
//
|
|
|
|
// Untested and unsupported code for checkpointing multiple files.
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <cstdarg>
|
|
|
|
using namespace std;
|
|
|
|
// abstract interface - derive classes from this base class
|
|
class BoincCheckpointFile
|
|
{
|
|
public:
|
|
// these functions should set error on f on failure
|
|
|
|
// this is only called once each time an application is resumed
|
|
virtual void input(istream& f) = 0;
|
|
|
|
// this is called at every checkpoint
|
|
virtual void output(ostream& f) = 0;
|
|
};
|
|
|
|
// checkpoint class good for binary dump of struct as a state file
|
|
class BoincRawDataCheckpointFile : public BoincCheckpointFile {
|
|
void* data;
|
|
size_t length;
|
|
public:
|
|
BoincRawDataCheckpointFile(void* data_, size_t length_)
|
|
:data(data_), length(length_)
|
|
{
|
|
}
|
|
|
|
virtual void input(istream& f)
|
|
{
|
|
f.read((char*) data, length);
|
|
if (!f.eof())
|
|
f.clear(ios::badbit);
|
|
}
|
|
|
|
virtual void output(ostream& f)
|
|
{
|
|
f.write((char const*) data, length);
|
|
}
|
|
};
|
|
|
|
// Class that is good for writing or appending (text or binary) data.
|
|
// Use standard C++ iostream operators to output, or printf
|
|
class BoincStreamCheckpointFile : public BoincCheckpointFile, public stringstream
|
|
{
|
|
public:
|
|
virtual void input(istream& f)
|
|
{
|
|
// read entire file into memory buffer (which is a stringbuf)
|
|
f >> rdbuf();
|
|
}
|
|
|
|
virtual void output(ostream& f)
|
|
{
|
|
// write entire memory buffer to file
|
|
seekg(0);
|
|
f << rdbuf();
|
|
}
|
|
|
|
void printf(const char* format, ...)
|
|
{
|
|
va_list ap;
|
|
char buf[20000];
|
|
|
|
va_start(ap, format);
|
|
vsprintf(buf, format, ap);
|
|
va_end(ap);
|
|
*this << buf;
|
|
}
|
|
};
|
|
|
|
// AtomicFileSet defines a set of files which are written/read atomically. If
|
|
// the system crashes within a write(), the next read() will read what was
|
|
// written by the previous write(). Assumes that writing a single integer is
|
|
// atomic - a much safer assumption than just writing an arbitrary number of
|
|
// files of arbitrary length.
|
|
class AtomicFileSet
|
|
{
|
|
struct NamedCheckpointFile {
|
|
const char* filename;
|
|
BoincCheckpointFile* file;
|
|
bool keep;
|
|
};
|
|
|
|
int current_checkpoint_number;
|
|
typedef vector<NamedCheckpointFile> FilesList;
|
|
FilesList files;
|
|
|
|
// returns true on success
|
|
bool read_checkpoint_number()
|
|
{
|
|
ifstream f("boinc_checkpoint.dat");
|
|
return (bool) (void*) (f >> current_checkpoint_number);
|
|
}
|
|
|
|
void write_checkpoint_number() const
|
|
{
|
|
ofstream f("boinc_checkpoint.dat");
|
|
f << current_checkpoint_number;
|
|
}
|
|
|
|
string format_checkpointed_filename(const char* filename, int checkpoint_number)
|
|
{
|
|
char buf[1024];
|
|
sprintf(buf, "%s.%d", filename, checkpoint_number);
|
|
return buf;
|
|
}
|
|
|
|
void delete_files(int checkpoint_number)
|
|
{
|
|
for (FilesList::const_iterator i = files.begin(); i != files.end(); ++i) {
|
|
string cp_filename =
|
|
format_checkpointed_filename(i->filename, checkpoint_number);
|
|
if (unlink(cp_filename.c_str())) {
|
|
cerr << "Couldn't unlink \"" << cp_filename << "\": " << strerror(errno) << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
AtomicFileSet() : current_checkpoint_number(0) {}
|
|
|
|
// add a file to the set of files to atomically write. if keep, then the
|
|
// file exists as 'filename' after Finish(); else it is deleted after
|
|
// Finish().
|
|
void add(const char* filename,
|
|
BoincCheckpointFile* file,
|
|
bool keep = true)
|
|
{
|
|
NamedCheckpointFile namedfile;
|
|
namedfile.filename = filename;
|
|
namedfile.file = file;
|
|
namedfile.keep = keep;
|
|
files.push_back(namedfile);
|
|
}
|
|
|
|
// call when resuming. returns true on success of all files, false on failure.
|
|
bool read()
|
|
{
|
|
if (!read_checkpoint_number()) return false;
|
|
|
|
for (FilesList::const_iterator i = files.begin(); i != files.end(); ++i) {
|
|
string cp_filename =
|
|
format_checkpointed_filename(i->filename, current_checkpoint_number);
|
|
ifstream f(cp_filename.c_str(), ios::binary);
|
|
if (!f) {
|
|
cerr << "Error opening for input \"" << cp_filename << "\"\n";
|
|
return false;
|
|
}
|
|
i->file->input(f);
|
|
if (!f) {
|
|
cerr << "Error reading \"" << cp_filename << "\"\n";
|
|
return false;
|
|
}
|
|
}
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
void write()
|
|
{
|
|
// strategy:
|
|
// 1. write *.N
|
|
// 2. write N to checkpoint file
|
|
// 3. delete *.(N-1)
|
|
|
|
for (FilesList::const_iterator i = files.begin(); i != files.end(); ++i) {
|
|
string cp_filename =
|
|
format_checkpointed_filename(i->filename, current_checkpoint_number);
|
|
ofstream f(cp_filename.c_str(), ios::binary);
|
|
if (!f) {
|
|
cerr << "Couldn't open output \"" << cp_filename << "\"\n";
|
|
exit(101);
|
|
}
|
|
i->file->output(f);
|
|
if (!f) {
|
|
cerr << "Error writing \"" << cp_filename << "\"\n";
|
|
exit(102);
|
|
}
|
|
}
|
|
|
|
write_checkpoint_number();
|
|
|
|
delete_files(current_checkpoint_number-1);
|
|
++current_checkpoint_number;
|
|
}
|
|
|
|
// NOTE: you must call write() yourself, if there is any data to be written.
|
|
void finish()
|
|
{
|
|
// delete files that are no longer needed, and rename the ones we
|
|
// want to keep.
|
|
int checkpoint_number = current_checkpoint_number-1;
|
|
for (FilesList::const_iterator i = files.begin(); i != files.end(); ++i) {
|
|
string cp_filename =
|
|
format_checkpointed_filename(i->filename, checkpoint_number);
|
|
if (i->keep) {
|
|
if (unlink(cp_filename.c_str())) {
|
|
cerr << "Warning: Couldn't unlink \"" << cp_filename
|
|
<< "\": " << strerror(errno) << endl;
|
|
}
|
|
} else {
|
|
if (rename(cp_filename.c_str(), i->filename)) {
|
|
cerr << "Fatal error: Couldn't rename \"" << cp_filename
|
|
<< "\" to \"" << i->filename << "\": "
|
|
<< strerror(errno) << endl;
|
|
exit(100);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/* usage in astropulse:
|
|
|
|
AtomicFileSet files;
|
|
ostream* output;
|
|
|
|
void init()
|
|
{
|
|
files.add("ap_state.dat",
|
|
new BoincRawDataCheckpointFile(client.state, sizeof(client.state)));
|
|
files.add("pulse.out", (output=new BoincStreamCheckpointFile));
|
|
if (files.read()) {
|
|
// resuming
|
|
} else {
|
|
*output << "<astropulse> ...";
|
|
}
|
|
}
|
|
|
|
void output_pulse()
|
|
{
|
|
*output << "<pulse> ... </pulse>";
|
|
}
|
|
|
|
void checkpoint()
|
|
{
|
|
files.write();
|
|
}
|
|
|
|
*/
|