2023-07-06 15:11:19 +02:00
|
|
|
#include "orbis/utils/SharedCV.hpp"
|
2024-10-31 20:54:16 +01:00
|
|
|
#include <chrono>
|
|
|
|
|
|
|
|
|
|
#ifdef ORBIS_HAS_FUTEX
|
2023-07-06 15:11:19 +02:00
|
|
|
#include <linux/futex.h>
|
|
|
|
|
#include <syscall.h>
|
|
|
|
|
#include <unistd.h>
|
2024-10-31 20:54:16 +01:00
|
|
|
#endif
|
2023-07-06 15:11:19 +02:00
|
|
|
|
|
|
|
|
namespace orbis::utils {
|
2024-10-31 20:54:16 +01:00
|
|
|
std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val,
|
|
|
|
|
std::uint64_t usec_timeout) noexcept {
|
2023-07-06 15:11:19 +02:00
|
|
|
// Not supposed to fail
|
2023-11-13 19:38:21 +01:00
|
|
|
if (!_val) {
|
2023-07-06 15:11:19 +02:00
|
|
|
std::abort();
|
2023-11-13 19:38:21 +01:00
|
|
|
}
|
2023-07-06 15:11:19 +02:00
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
std::errc result = {};
|
2024-01-13 18:57:02 +01:00
|
|
|
|
2023-11-13 19:38:21 +01:00
|
|
|
while (true) {
|
2024-11-01 07:14:51 +01:00
|
|
|
result = m_value.wait(_val, usec_timeout == static_cast<std::uint64_t>(-1)
|
|
|
|
|
? std::chrono::microseconds::max()
|
|
|
|
|
: std::chrono::microseconds(usec_timeout));
|
2023-07-06 15:11:19 +02:00
|
|
|
|
2023-11-13 19:38:21 +01:00
|
|
|
// Cleanup
|
2024-10-31 20:54:16 +01:00
|
|
|
const auto old = m_value.fetch_op([&](unsigned &value) {
|
2023-11-13 19:38:21 +01:00
|
|
|
// Remove waiter if no signals
|
2024-10-31 20:54:16 +01:00
|
|
|
if (!(value & ~c_waiter_mask) &&
|
|
|
|
|
result != std::errc::resource_unavailable_try_again) {
|
2023-11-13 19:38:21 +01:00
|
|
|
value -= 1;
|
|
|
|
|
}
|
2023-07-06 15:11:19 +02:00
|
|
|
|
2023-11-13 19:38:21 +01:00
|
|
|
// Try to remove signal
|
|
|
|
|
if (value & c_signal_mask) {
|
|
|
|
|
value -= c_signal_one;
|
|
|
|
|
}
|
2023-07-07 12:49:59 +02:00
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
#ifdef ORBIS_HAS_FUTEX
|
2023-11-13 19:38:21 +01:00
|
|
|
if (value & c_locked_mask) {
|
|
|
|
|
value -= c_locked_mask;
|
|
|
|
|
}
|
2024-10-31 20:54:16 +01:00
|
|
|
#endif
|
2023-11-13 19:38:21 +01:00
|
|
|
});
|
|
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
#ifdef ORBIS_HAS_FUTEX
|
2023-11-13 19:38:21 +01:00
|
|
|
// Lock is already acquired
|
|
|
|
|
if (old & c_locked_mask) {
|
2024-10-31 20:54:16 +01:00
|
|
|
return {};
|
2023-11-13 19:38:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait directly (waiter has been added)
|
|
|
|
|
if (old & c_signal_mask) {
|
2024-01-13 18:57:02 +01:00
|
|
|
return mutex.impl_wait();
|
2023-11-13 19:38:21 +01:00
|
|
|
}
|
2024-10-31 20:54:16 +01:00
|
|
|
#else
|
|
|
|
|
if (old & c_signal_mask) {
|
|
|
|
|
result = {};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2023-11-13 19:38:21 +01:00
|
|
|
|
|
|
|
|
// Possibly spurious wakeup
|
2024-10-31 20:54:16 +01:00
|
|
|
if (result != std::errc::resource_unavailable_try_again) {
|
2023-11-13 19:38:21 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2024-01-13 18:57:02 +01:00
|
|
|
|
|
|
|
|
_val = old;
|
2023-07-06 15:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mutex.lock();
|
2024-01-13 18:57:02 +01:00
|
|
|
return result;
|
2023-07-06 15:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void shared_cv::impl_wake(shared_mutex &mutex, int _count) noexcept {
|
2024-10-31 20:54:16 +01:00
|
|
|
#ifdef ORBIS_HAS_FUTEX
|
|
|
|
|
while (true) {
|
|
|
|
|
unsigned _old = m_value.load();
|
|
|
|
|
const bool is_one = _count == 1;
|
|
|
|
|
|
|
|
|
|
// Enqueue _count waiters
|
|
|
|
|
_count = std::min<int>(_count, _old & c_waiter_mask);
|
|
|
|
|
if (_count <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Try to lock the mutex
|
|
|
|
|
const bool locked = mutex.lock_forced(_count);
|
|
|
|
|
|
|
|
|
|
const int max_sig = m_value.op([&](unsigned &value) {
|
|
|
|
|
// Verify the number of waiters
|
|
|
|
|
int max_sig = std::min<int>(_count, value & c_waiter_mask);
|
|
|
|
|
|
|
|
|
|
// Add lock signal (mutex was immediately locked)
|
|
|
|
|
if (locked && max_sig)
|
|
|
|
|
value |= c_locked_mask;
|
|
|
|
|
else if (locked)
|
|
|
|
|
std::abort();
|
|
|
|
|
|
|
|
|
|
// Add normal signals
|
|
|
|
|
value += c_signal_one * max_sig;
|
|
|
|
|
|
|
|
|
|
// Remove waiters
|
|
|
|
|
value -= max_sig;
|
|
|
|
|
_old = value;
|
|
|
|
|
return max_sig;
|
|
|
|
|
});
|
2023-07-06 15:11:19 +02:00
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
if (max_sig < _count) {
|
|
|
|
|
// Fixup mutex
|
|
|
|
|
mutex.lock_forced(max_sig - _count);
|
|
|
|
|
_count = max_sig;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_count) {
|
|
|
|
|
// Wake up one thread + requeue remaining waiters
|
|
|
|
|
unsigned awake_count = locked ? 1 : 0;
|
|
|
|
|
if (auto r = syscall(SYS_futex, &m_value, FUTEX_REQUEUE, awake_count,
|
|
|
|
|
_count - awake_count, &mutex, 0);
|
|
|
|
|
r < _count) {
|
|
|
|
|
// Keep awaking waiters
|
|
|
|
|
_count = is_one ? 1 : INT_MAX;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
unsigned _old = m_value.load();
|
2023-07-06 15:11:19 +02:00
|
|
|
_count = std::min<int>(_count, _old & c_waiter_mask);
|
|
|
|
|
if (_count <= 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
mutex.lock_forced(1);
|
2023-07-06 15:11:19 +02:00
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
const int wakeupWaiters = m_value.op([&](unsigned &value) {
|
2023-07-06 15:11:19 +02:00
|
|
|
int max_sig = std::min<int>(_count, value & c_waiter_mask);
|
|
|
|
|
|
2023-07-07 12:49:59 +02:00
|
|
|
// Add normal signals
|
|
|
|
|
value += c_signal_one * max_sig;
|
|
|
|
|
|
|
|
|
|
// Remove waiters
|
|
|
|
|
value -= max_sig;
|
2023-07-06 15:11:19 +02:00
|
|
|
_old = value;
|
|
|
|
|
return max_sig;
|
|
|
|
|
});
|
|
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
if (wakeupWaiters > 0) {
|
|
|
|
|
m_value.notify_n(wakeupWaiters);
|
2023-07-06 15:11:19 +02:00
|
|
|
}
|
|
|
|
|
|
2024-10-31 20:54:16 +01:00
|
|
|
mutex.unlock();
|
|
|
|
|
#endif
|
2023-07-06 15:11:19 +02:00
|
|
|
}
|
|
|
|
|
} // namespace orbis::utils
|