From a33bb60cf673a8de121f5efe2e6de5ee39b9ba7c Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Wed, 21 Jan 2015 19:29:03 -0500 Subject: [PATCH] Add a simplified lock file class to use to prevent two applications from using the same resource --- include/fc/filesystem.hpp | 34 +++++++++++++ src/filesystem.cpp | 101 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/include/fc/filesystem.hpp b/include/fc/filesystem.hpp index 4a05cd4..1a16926 100644 --- a/include/fc/filesystem.hpp +++ b/include/fc/filesystem.hpp @@ -1,5 +1,7 @@ #pragma once #include +#include + #include #include #include @@ -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 my; + }; +#endif // FC_HAS_SIMPLE_FILE_LOCK + } diff --git a/src/filesystem.cpp b/src/filesystem.cpp index db33f1c..85dd1f5 100644 --- a/src/filesystem.cpp +++ b/src/filesystem.cpp @@ -19,6 +19,10 @@ #include #include #include +# ifdef FC_HAS_SIMPLE_FILE_LOCK + #include + #include +# 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 + }