From 74107f7df7d67a02bd64e96a522389af385dfdc0 Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 20 Jun 2006 00:23:05 +0000 Subject: [PATCH] add atomic I/O operations --- dtool/src/dtoolutil/filename.cxx | 190 ++++++++++++++++++++++++++++++- dtool/src/dtoolutil/filename.h | 6 + 2 files changed, 191 insertions(+), 5 deletions(-) diff --git a/dtool/src/dtoolutil/filename.cxx b/dtool/src/dtoolutil/filename.cxx index 19383ba8f0..80eb7f8be0 100644 --- a/dtool/src/dtoolutil/filename.cxx +++ b/dtool/src/dtoolutil/filename.cxx @@ -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 diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index f95dfe7333..f5fc35c97c 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -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();