Created
November 24, 2025 21:58
-
-
Save ruurdadema/0c0efe42634244ee14e24e8e05724413 to your computer and use it in GitHub Desktop.
ExclusiveAccessGuard
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Owllab License Agreement | |
| * | |
| * This software is provided by Owllab and may not be used, copied, modified, | |
| * merged, published, distributed, sublicensed, or sold without a valid and | |
| * explicit agreement with Owllab. | |
| * | |
| * Copyright (c) 2024 Owllab. All rights reserved. | |
| */ | |
| #pragma once | |
| #include "ravennakit/core/assert.hpp" | |
| #include "ravennakit/core/util/defer.hpp" | |
| #define CONCAT(a, b) CONCAT_INNER(a, b) | |
| #define CONCAT_INNER(a, b) a##b | |
| /** | |
| * Helper macro which asserts exclusive access to scope. Whenever 2 different threads access the scope, an assertion | |
| * will be triggered. | |
| */ | |
| #define RAV_ASSERT_EXCLUSIVE_ACCESS(guard) \ | |
| rav::ExclusiveAccessGuard::Lock CONCAT(lock, __LINE__)(guard); \ | |
| RAV_ASSERT(!CONCAT(lock, __LINE__).violated(), "exclusive access violation"); | |
| #include <atomic> | |
| #include <stdexcept> | |
| namespace rav { | |
| /** | |
| * Guards exclusive access to a resource. Throws if the resource is already accessed. | |
| */ | |
| class ExclusiveAccessGuard { | |
| public: | |
| /** | |
| * Locks and exclusive access guard. | |
| */ | |
| class Lock { | |
| public: | |
| /** | |
| * Constructs a new lock. | |
| * @throws std::runtime_error if exclusive access is violated. | |
| */ | |
| explicit Lock(ExclusiveAccessGuard& access_guard) : exclusive_access_guard_(access_guard) { | |
| const auto prev = exclusive_access_guard_.counter_.fetch_add(1, std::memory_order_relaxed); | |
| if (prev != 0) { | |
| violated_ = true; | |
| } | |
| } | |
| ~Lock() { | |
| exclusive_access_guard_.counter_.fetch_sub(1, std::memory_order_relaxed); | |
| } | |
| /** | |
| * @return True if exclusive access is violated. | |
| */ | |
| [[nodiscard]] bool violated() const { | |
| return violated_; | |
| } | |
| private: | |
| ExclusiveAccessGuard& exclusive_access_guard_; | |
| bool violated_ = false; | |
| }; | |
| /** | |
| * Constructs a new exclusive access guard. | |
| */ | |
| explicit ExclusiveAccessGuard() = default; | |
| ExclusiveAccessGuard(const ExclusiveAccessGuard&) = delete; | |
| ExclusiveAccessGuard& operator=(const ExclusiveAccessGuard&) = delete; | |
| ExclusiveAccessGuard(ExclusiveAccessGuard&&) = delete; | |
| ExclusiveAccessGuard& operator=(ExclusiveAccessGuard&&) = delete; | |
| private: | |
| std::atomic<int8_t> counter_ {}; | |
| }; | |
| } // namespace rav |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Owllab License Agreement | |
| * | |
| * This software is provided by Owllab and may not be used, copied, modified, | |
| * merged, published, distributed, sublicensed, or sold without a valid and | |
| * explicit agreement with Owllab. | |
| * | |
| * Copyright (c) 2024 Owllab. All rights reserved. | |
| */ | |
| #include "ravennakit/core/util/exclusive_access_guard.hpp" | |
| #include <future> | |
| #include <thread> | |
| #include <catch2/catch_all.hpp> | |
| TEST_CASE("rav::ExclusiveAccessGuard") { | |
| SECTION("Exclusive access violation") { | |
| rav::ExclusiveAccessGuard guard; | |
| const rav::ExclusiveAccessGuard::Lock lock1(guard); | |
| const rav::ExclusiveAccessGuard::Lock lock2(guard); | |
| REQUIRE_FALSE(lock1.violated()); | |
| REQUIRE(lock2.violated()); | |
| } | |
| SECTION("Trigger exclusive access violation by running two threads") { | |
| std::atomic keep_going {true}; | |
| rav::ExclusiveAccessGuard guard; | |
| auto function = [&keep_going, &guard]() { | |
| while (keep_going) { | |
| const rav::ExclusiveAccessGuard::Lock lock(guard); | |
| if (lock.violated()) { | |
| keep_going = false; | |
| return true; | |
| } | |
| // Introduce a delay to increase the chance of violation | |
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); | |
| } | |
| return false; | |
| }; | |
| auto f1 = std::async(std::launch::async, function); | |
| auto f2 = std::async(std::launch::async, function); | |
| f1.wait_for(std::chrono::seconds(5)); | |
| f2.wait_for(std::chrono::seconds(5)); | |
| REQUIRE((f1.get() || f2.get())); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment