add atomic I/O operations

This commit is contained in:
David Rose 2006-06-20 00:23:05 +00:00
parent 73b5c5ce6f
commit 74107f7df7
2 changed files with 191 additions and 5 deletions

View File

@ -392,7 +392,7 @@ expand_from(const string &os_specific, Filename::Type type) {
////////////////////////////////////////////////////////////////////
// Function: Filename::temporary
// Access: Published
// Access: Published, Static
// Description: Generates a temporary filename within the indicated
// directory, using the indicated prefix. If the
// directory is empty, a system-defined directory is
@ -404,7 +404,8 @@ expand_from(const string &os_specific, Filename::Type type) {
// could simultaneously create a file by the same name.
////////////////////////////////////////////////////////////////////
Filename Filename::
temporary(const string &dirname, const string &prefix, Type type) {
temporary(const string &dirname, const string &prefix, const string &suffix,
Type type) {
if (dirname.empty()) {
// If we are not given a dirname, use the system tempnam()
// function to create a system-defined temporary filename.
@ -419,8 +420,7 @@ temporary(const string &dirname, const string &prefix, Type type) {
// up a filename within that dirname. We do that because the system
// tempnam() (for instance, under Windows) may ignore the dirname.
Filename result(dirname, "");
result.set_type(type);
Filename result;
do {
// We take the time of day and multiply it by the process time.
// This will give us a very large number, of which we take the
@ -428,7 +428,8 @@ temporary(const string &dirname, const string &prefix, Type type) {
int hash = (clock() * time(NULL)) & 0xffffff;
char hex_code[10];
sprintf(hex_code, "%06x", hash);
result.set_basename(prefix + hex_code);
result = Filename(dirname, Filename(prefix + hex_code + suffix));
result.set_type(type);
} while (result.exists());
return result;
@ -1201,6 +1202,35 @@ get_timestamp() const {
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: Filename::get_access_timestamp
// Access: Published
// Description: Returns a time_t value that represents the time the
// file was last accessed, if this information is
// available. See also get_timestamp(), which returns
// the last modification time.
////////////////////////////////////////////////////////////////////
time_t Filename::
get_access_timestamp() const {
string os_specific = get_filename_index(0).to_os_specific();
#ifdef WIN32_VC
struct _stat this_buf;
if (_stat(os_specific.c_str(), &this_buf) == 0) {
return this_buf.st_atime;
}
#else // WIN32_VC
struct stat this_buf;
if (stat(os_specific.c_str(), &this_buf) == 0) {
return this_buf.st_atime;
}
#endif
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: Filename::get_file_size
// Access: Published
@ -1879,6 +1909,156 @@ make_dir() const {
}
////////////////////////////////////////////////////////////////////
// Function: Filename::atomic_compare_and_exchange_contents
// Access: Public
// Description: Uses native file-locking mechanisms to atomically
// replace the contents of a (small) file with the
// specified contents, assuming it hasn't changed since
// the last time the file was read.
//
// This is designed to be similar to
// AtomicAdjust::compare_and_exchange(). The method
// writes new_contents to the file, completely replacing
// the original contents; but only if the original
// contents exactly matched old_contents. If the file
// was modified, returns true. If, however, the
// original contents of the file did not exactly match
// old_contents, then the file is not modified, and
// false is returned. In either case, orig_contents is
// filled with the original contents of the file.
//
// If the file does not exist, it is implicitly created,
// and its original contents are empty.
//
// If an I/O error occurs on write, some of the file may
// or may not have been written, and false is returned.
//
// Expressed in pseudo-code, the logic is:
//
// orig_contents = file.read();
// if (orig_contents == old_contents) {
// file.write(new_contents);
// return true;
// }
// return false;
//
// The operation is guaranteed to be atomic only if the
// only operations that read and write to this file are
// atomic_compare_and_exchange_contents() and
// atomic_read_contents().
////////////////////////////////////////////////////////////////////
bool Filename::
atomic_compare_and_exchange_contents(string &orig_contents,
const string &old_contents,
const string &new_contents) const {
#ifdef WIN32_VC
// Todo.
return false;
#else // WIN32_VC
string os_specific = to_os_specific();
int fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666);
if (fd < 0) {
perror(os_specific.c_str());
return false;
}
static const size_t buf_size = 512;
char buf[buf_size];
orig_contents = string();
lockf(fd, F_LOCK, 0);
size_t bytes_read = read(fd, buf, buf_size);
while (bytes_read > 0) {
orig_contents += string(buf, bytes_read);
bytes_read = read(fd, buf, buf_size);
}
if (bytes_read < 0) {
perror(os_specific.c_str());
close(fd);
return false;
}
bool match = false;
if (orig_contents == old_contents) {
match = true;
lseek(fd, 0, SEEK_SET);
ssize_t bytes_written = write(fd, new_contents.data(), new_contents.size());
if (bytes_written < 0) {
perror(os_specific.c_str());
close(fd);
return false;
}
}
if (close(fd) < 0) {
perror(os_specific.c_str());
return false;
}
return match;
#endif // WIN32_VC
}
////////////////////////////////////////////////////////////////////
// Function: Filename::atomic_compare_and_exchange_contents
// Access: Public
// Description: Uses native file-locking mechanisms to atomically
// read the contents of a (small) file. This is the
// only way to read a file protected by
// atomic_compare_and_exchange_contents(), and be
// confident that the read operation is actually atomic
// with respect to that method.
//
// If the file does not exist, it is implicitly created,
// and its contents are empty.
//
// If the file is read successfully, fills its contents
// in the indicated string, and returns true. If the
// file cannot be read, returns false.
////////////////////////////////////////////////////////////////////
bool Filename::
atomic_read_contents(string &contents) const {
#ifdef WIN32_VC
// Todo.
return false;
#else // WIN32_VC
string os_specific = to_os_specific();
int fd = open(os_specific.c_str(), O_RDONLY | O_CREAT, 0666);
if (fd < 0) {
perror(os_specific.c_str());
return false;
}
static const size_t buf_size = 512;
char buf[buf_size];
contents = string();
lockf(fd, F_LOCK, 0);
size_t bytes_read = read(fd, buf, buf_size);
while (bytes_read > 0) {
contents += string(buf, bytes_read);
bytes_read = read(fd, buf, buf_size);
}
if (bytes_read < 0) {
perror(os_specific.c_str());
close(fd);
return false;
}
close(fd);
return true;
#endif // WIN32_VC
}
////////////////////////////////////////////////////////////////////
// Function: Filename::locate_basename
// Access: Protected

View File

@ -83,6 +83,7 @@ PUBLISHED:
static Filename expand_from(const string &user_string,
Type type = T_general);
static Filename temporary(const string &dirname, const string &prefix,
const string &suffix = string(),
Type type = T_general);
// Assignment is via the = operator.
@ -160,6 +161,7 @@ PUBLISHED:
bool this_missing_is_old = true,
bool other_missing_is_old = true) const;
time_t get_timestamp() const;
time_t get_access_timestamp() const;
off_t get_file_size() const;
bool resolve_filename(const DSearchPath &searchpath,
@ -188,6 +190,10 @@ PUBLISHED:
INLINE void output(ostream &out) const;
public:
bool atomic_compare_and_exchange_contents(string &orig_contents, const string &old_contents, const string &new_contents) const;
bool atomic_read_contents(string &contents) const;
protected:
void locate_basename();
void locate_extension();