It sounds like you want to access the file after another program has released the lock. UNIX (and Cygwin) gives you this behavior by simply locking the file.
Using something like ScopeGuard
can make File_locker
unnecessary, but if you're not using ScopeGuard
, go like this:
UNIX:
#include <stdexcept>
#include <string>
#include "sys/file.h" //flock
#include "sys/fcntl.h" //open
class File_locker {
int file_descriptor;
public:
File_locker(std::string filename)
{
// you can use errno to determine why the open/flock failed,
// but this is a demo, not production code
file_descriptor = ::open(filename.c_str(), O_RDWR);
if (file_descriptor < 0)
throw std::runtime_error((std::string("unable to open file ")
+ filename).c_str());
if (::flock(file_descriptor, LOCK_EX)) {
::close(file_descriptor);
throw std::runtime_error((std::string("unable to flock file ")
+ filename).c_str());
}
}
~File_locker()
{
::flock(file_descriptor, LOCK_UN); // don't forget to unlock
::close(file_descriptor);
}
};
In Windows it appears you have to poll the file.
Windows:
#include <string>
#include "windows.h"
class File_locker {
HANDLE file_handle;
static const int MAX_TRIES = 10;
static const int SLEEP_INTERVAL = 500;
public:
File_locker(std::string filename)
{
// you can use GetLastError() to determine why the open failed,
// but this is a demo, not production code
for (int i = 0; i < MAX_TRIES; ++i) {
file_handle = ::CreateFile(filename.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (file_handle != INVALID_HANDLE_VALUE)
return;
::Sleep(SLEEP_INTERVAL);
}
throw std::runtime_error((std::string("unable to open file ")
+ filename).c_str());
}
~File_locker()
{
::CloseHandle(file_handle);
}
};
Use it like so:
#include <fstream>
#include <stdexcept>
// .. define File_locker, as above
int main()
{
try {
File_locker fl("filename.txt");
// once fl is constructed, nobody else has the file locked
std::fstream file("filename.txt");
// ...
return 0;
}
catch (std::runtime_error& ex)
{
// just bail
return 1;
}
}