| @@ -0,0 +1,112 @@ | |||
| #ifdef _WIN32 | |||
| #include "./flock.hpp" | |||
| #include <dds/util/signal.hpp> | |||
| #include <spdlog/fmt/fmt.h> | |||
| #include <wil/resource.h> | |||
| using namespace dds; | |||
| namespace { | |||
| enum fail_mode { | |||
| blocking, | |||
| nonblocking, | |||
| }; | |||
| enum lock_mode { | |||
| shared, | |||
| exclusive, | |||
| }; | |||
| struct lock_data { | |||
| wil::unique_handle file; | |||
| bool do_lock(fail_mode fm, lock_mode lm, path_ref p) { | |||
| auto lockflags = lm == exclusive ? LOCKFILE_EXCLUSIVE_LOCK : 0; | |||
| auto failflags = fm == nonblocking ? LOCKFILE_FAIL_IMMEDIATELY : 0; | |||
| auto flags = lockflags | failflags; | |||
| const auto okay = ::LockFileEx(file.get(), flags, 0, 0, 0, nullptr); | |||
| if (!okay) { | |||
| cancellation_point(); | |||
| if (::GetLastError() == ERROR_IO_PENDING) { | |||
| return false; | |||
| } | |||
| throw std::system_error(std::error_code(errno, std::system_category()), | |||
| fmt::format("Failed to modify file lock [{}]", p.string())); | |||
| } | |||
| return true; | |||
| } | |||
| void unlock(path_ref p) { | |||
| const auto okay = ::UnlockFileEx(file.get(), 0, 0, 0, nullptr); | |||
| if (!okay) { | |||
| cancellation_point(); | |||
| throw std::system_error(std::error_code(::GetLastError(), std::system_category()), | |||
| fmt::format("Failed to unlock file [{}]", p.string())); | |||
| } | |||
| } | |||
| }; | |||
| } // namespace | |||
| #define THIS_DATA static_cast<lock_data*>(_lock_data) | |||
| shared_file_mutex::shared_file_mutex(path_ref filepath) | |||
| : _path{filepath} { | |||
| auto h = ::CreateFileA(_path.string().c_str(), | |||
| GENERIC_READ | GENERIC_WRITE, | |||
| FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |||
| nullptr, | |||
| CREATE_ALWAYS, | |||
| FILE_ATTRIBUTE_NORMAL, | |||
| nullptr); | |||
| wil::unique_handle file{h}; | |||
| if (!file) { | |||
| throw std::system_error(std::error_code(::GetLastError(), std::system_category()), | |||
| fmt::format("Failed to open file for locking [{}]", | |||
| _path.string())); | |||
| } | |||
| _lock_data = new lock_data(lock_data{std::move(file)}); | |||
| } | |||
| #define MY_LOCK_DATA (*static_cast<lock_data*>(_lock_data)) | |||
| shared_file_mutex::~shared_file_mutex() { | |||
| assert(_lock_data); | |||
| delete &MY_LOCK_DATA; | |||
| _lock_data = nullptr; | |||
| } | |||
| bool shared_file_mutex::try_lock() noexcept { | |||
| // Attempt to take an exclusive lock | |||
| return MY_LOCK_DATA.do_lock(nonblocking, exclusive, _path); | |||
| } | |||
| bool shared_file_mutex::try_lock_shared() noexcept { | |||
| // Take a non-exclusive lock | |||
| return MY_LOCK_DATA.do_lock(nonblocking, shared, _path); | |||
| } | |||
| void shared_file_mutex::lock() { | |||
| // Blocking exclusive lock | |||
| MY_LOCK_DATA.do_lock(blocking, exclusive, _path); | |||
| } | |||
| void shared_file_mutex::lock_shared() { | |||
| // Blocking shared lock | |||
| MY_LOCK_DATA.do_lock(blocking, shared, _path); | |||
| } | |||
| void shared_file_mutex::unlock() { | |||
| // Unlock | |||
| MY_LOCK_DATA.unlock(_path); | |||
| } | |||
| void shared_file_mutex::unlock_shared() { unlock(); } | |||
| #endif | |||