diff --git a/emper/io/IoContext.hpp b/emper/io/IoContext.hpp index 100a11a30f04c3b1332b1723365cf38576bb4e83..f72395cc78779ff05c6277eed89f4dbdaf92d254 100644 --- a/emper/io/IoContext.hpp +++ b/emper/io/IoContext.hpp @@ -18,10 +18,28 @@ #include "emper-config.h" // for EMPER_IO_WORKER_URING_ENTRIES #include "io/Stats.hpp" // for Stats #include "lib/adt/LockedSet.hpp" // for LockedSet -#include "lib/sync/CountingTryLock.hpp" class Fiber; +#ifdef EMPER_IO_CQ_LOCK_COUNTING_TRY_LOCK +#include "lib/sync/CountingTryLock.hpp" +using CqLock = emper::lib::sync::CountingTryLock; + +#elif defined EMPER_IO_CQ_LOCK_MUTEX +#include <mutex> + +#include "lib/sync/PseudoCountingTryLock.hpp" +using CqLock = emper::lib::sync::PseudoCountingTryLock<std::mutex>; + +#elif defined EMPER_IO_CQ_LOCK_SPIN_LOCK +#include "lib/sync/PseudoCountingTryLock.hpp" +#include "lib/sync/SpinLock.hpp" +using CqLock = emper::lib::sync::PseudoCountingTryLock<emper::lib::sync::SpinLock>; + +#else +#error Uknown cq lock implementation +#endif + namespace emper::io { class Future; @@ -40,7 +58,7 @@ class IoContext : public Logger<LogSubsystem::IO> { static thread_local IoContext *workerIo; // TryLock protecting the completion queue of ring. - ALIGN_TO_CACHE_LINE lib::sync::CountingTryLock cq_lock; + ALIGN_TO_CACHE_LINE CqLock cq_lock; struct io_uring ring; // In a worker's IoContext This eventfd is registered with the io_uring to get completion diff --git a/emper/lib/sync/PseudoCountingTryLock.hpp b/emper/lib/sync/PseudoCountingTryLock.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2b278bad9d531774d40332666a73affac8d70636 --- /dev/null +++ b/emper/lib/sync/PseudoCountingTryLock.hpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2021 Florian Fischer +#pragma once + +namespace emper::lib::sync { + +template <class Lockable> +class PseudoCountingTryLock { + private: + Lockable lock; + + public: + [[nodiscard]] auto try_lock() -> bool { + lock.lock(); + return true; + } + + [[nodiscard]] auto try_lock_or_increment() -> bool { return try_lock(); } + + auto unlock() -> uint32_t { + lock.unlock(); + return 0; + } +}; +} // namespace emper::lib::sync diff --git a/emper/lib/sync/SpinLock.hpp b/emper/lib/sync/SpinLock.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a0eef94bffc339a0dab70dfd998dcad83dcd84dc --- /dev/null +++ b/emper/lib/sync/SpinLock.hpp @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2021 Florian Fischer +#pragma once + +#include <atomic> + +#include "Common.hpp" + +namespace emper::lib::sync { + +class SpinLock { + private: + std::atomic<bool> locked; + + public: + SpinLock(bool locked = false) : locked(locked) {} + + inline void lock() { + bool isLocked; + do { + isLocked = false; + } while (!locked.compare_exchange_weak(isLocked, true, std::memory_order_acquire, + std::memory_order_relaxed)); + } + + inline void unlock() { locked.store(false, std::memory_order_release); } +}; +} // namespace emper::lib::sync diff --git a/meson.build b/meson.build index a73194416adeac577574d39bdcce9e66e4e77316..69f961dad178bd8fbc77320afe1058e4d3b64a52 100644 --- a/meson.build +++ b/meson.build @@ -93,6 +93,9 @@ foreach option : io_raw_options conf_data.set('EMPER_IO_' + option.to_upper(), get_option('io_' + option)) endforeach +io_cq_lock_impl = get_option('io_cq_lock_implementation') +conf_data.set('EMPER_IO_CQ_LOCK_' + io_cq_lock_impl.to_upper(), true) + subdir('emper') subdir('tests') subdir('apps') diff --git a/meson_options.txt b/meson_options.txt index d9fcab82b3091aca6bd853f2826b22cb69a772e8..ac769500be89ab35a157e2a6a7bf662b1f2cb100 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -115,3 +115,10 @@ option( value: false, description: 'Share a common async backend between all io_urings' ) +option( + 'io_cq_lock_implementation', + type: 'combo', + description: 'The lock implementation used to protect a worker IoContext CQ', + choices: ['spin_lock', 'counting_try_lock', 'mutex'], + value: 'counting_try_lock', +)