mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
add atomic I/O operations
This commit is contained in:
parent
73b5c5ce6f
commit
74107f7df7
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user