I want to coordinate access to a shared text file between two processes, specifically:
- a writer that seldom updates the file;
- a reader that frequently read the file.
I want a solution implemented entirely with standard C++, avoiding platform-specific features like flock (Unix) or LockFile (Windows).
My current approach is as follows:
bool reader(const std::filesystem::path &f)
{
namespace fs = std::filesystem;
// Check for write lock.
auto write_lock_file = fs::path(f).replace_extension(".write.lock");
if (fs::exists(write_lock_file))
return false; // writer is active, aborting
// Create read lock.
auto read_lock_file = fs::path(f).replace_extension(".read.lock");
std::ofstream(read_lock_file).close();
// Recheck for write lock.
// When the writer creates the write lock, readers that check for the write
// lock will immediately abort, ensuring the writer can proceed as soon as
// it's ready.
if (fs::exists(write_lock_file))
{
fs::remove(read_lock_file); // cleanup
return false;
}
// READ WHAT NEEDED FROM f
fs::remove(read_lock_file);
return true;
}
Reader and writer use separate lock files (.read.lock and .write.lock) to signal their activity.
Before performing any operation, a process checks for the presence of the other's lock file and, after creating its own lock file, it rechecks for conflicts to prevent race conditions.
void writer(const std::filesystem::path &f)
{
namespace fs = std::filesystem;
// Create write lock.
auto write_lock_file = fs::path(f).replace_extension(".write.lock");
std::ofstream(write_lock_file).close();
// Wait for readers to finish.
// After creating the write lock, the writer waits for all readers to finish
// before proceeding. This ensures that existing readers are not interrupted,
// but no new readers can start.
auto read_lock_file = fs::path(f).replace_extension(".read.lock");
while (fs::exists(read_lock_file))
std::this_thread::sleep_for(100ms);
// WRITE NEW DATA INTO f
}
The writer waits for active readers to finish before proceeding, but new readers are blocked once the writer creates its lock.
Does this approach:
- prevent race conditions between reader and writer?
- avoid deadlocks or prolonged blocking?
PS: stale lock files need to be handled, but I am considering that as a separate issue.
-
From your description, it looks like you need a multile-read single-write lock. Please take a look at https://stackoverflow.com/questions/27860685/how-to-make-a-multiple-read-single-write-lock-from-more-basic-synchronization-pr.Sergey A Kryukov– Sergey A Kryukov2025年01月28日 15:25:11 +00:00Commented Jan 28, 2025 at 15:25
-
@SergeyAKryukov • locking the multiple reading and single writing across multiple processes.Eljay– Eljay2025年01月28日 17:57:08 +00:00Commented Jan 28, 2025 at 17:57
-
@Eljay — "locking the multiple reading and single writing across multiple processes". Yes, this is what I mean. Do you think it's wrong, or what?Sergey A Kryukov– Sergey A Kryukov2025年01月28日 18:05:03 +00:00Commented Jan 28, 2025 at 18:05
-
1@SergeyAKryukov • okay, excellent. An example of multiple processes coordinating multiple reading or only one writing access to the same file using standard C++ and not OS APIs would be very good -- just what the OP wants to know!Eljay– Eljay2025年01月28日 19:41:45 +00:00Commented Jan 28, 2025 at 19:41
-
1@Eljay — "...not OS APIs..." And yes, not using platform-specific API is the key here, and the inquirer already stressed avoiding platform-specific features. I always feel disappointed when C++ developers tend to offer only platform-specific solutions and not standard ones, as I practice and highly value cross-platform development.Sergey A Kryukov– Sergey A Kryukov2025年01月28日 19:48:08 +00:00Commented Jan 28, 2025 at 19:48