Skip to content
Snippets Groups Projects
Commit a619ba3e authored by Florian Fischer's avatar Florian Fischer
Browse files

[IO] make the lock implementation protecting a IoContext's cq configurable 

This change introduces a new synchronization primitive "PseudoCountingTryLock"
which takes an actual lock as template and provides a CountingTryLock interface.
By using a PseudoCountingTryLock we don't have to change any synchronization
code in IoContext::reapCompletion.

Since all PseudoCountingTryLock code is defined in a header the compiler
should see our constant return values and hopefully optimize away any check
depending on those constant return values.

Options:
* spin_lock - naive CAS spin lock
* mutex - std::mutex
* counting_try_lock (default) - our own lightweight special
                                purpose synchronization primitive
parent f825d238
Branches
No related tags found
1 merge request!126[IO] make the lock implementation protecting a IoContext's cq configurable 
......@@ -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
......
// 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
// 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
......@@ -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')
......
......@@ -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',
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment