Add a simplified lock file class to use to prevent two applications from using the same resource

This commit is contained in:
Eric Frias 2015-01-21 19:29:03 -05:00
parent 0642eb63d3
commit a33bb60cf6
2 changed files with 135 additions and 0 deletions

View file

@ -1,5 +1,7 @@
#pragma once
#include <utility>
#include <memory>
#include <fc/string.hpp>
#include <fc/reflect/typename.hpp>
#include <fc/optional.hpp>
@ -226,5 +228,37 @@ namespace fc {
temp_directory(const fc::path& tempFolder = fc::temp_directory_path());
};
#if !defined(__APPLE__)
// this code is known to work on linux and windows. It may work correctly on mac,
// or it may need slight tweaks or extra includes. It's disabled now to avoid giving
// a false sense of security.
# define FC_HAS_SIMPLE_FILE_LOCK
#endif
#ifdef FC_HAS_SIMPLE_FILE_LOCK
/** simple class which only allows one process to open any given file.
* approximate usage:
* int main() {
* fc::simple_file_lock instance_lock("~/.my_app/.lock");
* if (!instance_lock.try_lock()) {
* elog("my_app is already running");
* return 1;
* }
* // do stuff here, file will be unlocked when instance_lock goes out of scope
* }
*/
class simple_lock_file
{
public:
simple_lock_file(const path& lock_file_path);
~simple_lock_file();
bool try_lock();
void unlock();
private:
class impl;
std::unique_ptr<impl> my;
};
#endif // FC_HAS_SIMPLE_FILE_LOCK
}

View file

@ -19,6 +19,10 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
# ifdef FC_HAS_SIMPLE_FILE_LOCK
#include <sys/file.h>
#include <fcntl.h>
# endif
#endif
namespace fc {
@ -495,4 +499,101 @@ namespace fc {
return appCurrentPath;
}
#ifdef FC_HAS_SIMPLE_FILE_LOCK
class simple_lock_file::impl
{
public:
#ifdef _WIN32
HANDLE file_handle;
#else
int file_handle;
#endif
bool is_locked;
path lock_file_path;
impl(const path& lock_file_path);
~impl();
bool try_lock();
void unlock();
};
simple_lock_file::impl::impl(const path& lock_file_path) :
is_locked(false),
lock_file_path(lock_file_path),
#ifdef _WIN32
file_handle(INVALID_HANDLE_VALUE)
#else
file_handle(-1)
#endif
{}
simple_lock_file::impl::~impl()
{
unlock();
}
bool simple_lock_file::impl::try_lock()
{
#ifdef _WIN32
HANDLE fh = CreateFileA(lock_file_path.to_native_ansi_path().c_str(),
GENERIC_READ | GENERIC_WRITE,
0, 0,
OPEN_ALWAYS, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
return false;
is_locked = true;
file_handle = fh;
return true;
#else
int fd = open(lock_file_path.string().c_str(), O_RDWR|O_CREAT, 0644);
if (fd < 0)
return false;
if (flock(fd, LOCK_EX|LOCK_NB) == -1)
{
close(fd);
return false;
}
is_locked = true;
file_handle = fd;
return true;
#endif
}
void simple_lock_file::impl::unlock()
{
#ifdef WIN32
CloseHandle(file_handle);
file_handle = INVALID_HANDLE_VALUE;
is_locked = false;
#else
flock(file_handle, LOCK_UN);
close(file_handle);
file_handle = -1;
is_locked = false;
#endif
}
simple_lock_file::simple_lock_file(const path& lock_file_path) :
my(new impl(lock_file_path))
{
}
simple_lock_file::~simple_lock_file()
{
}
bool simple_lock_file::try_lock()
{
return my->try_lock();
}
void simple_lock_file::unlock()
{
my->unlock();
}
#endif // FC_HAS_SIMPLE_FILE_LOCK
}