LibSync: Abstract away Mutex implementation
This commit adds in-place pimpl to abstract away the implementation of Mutex. It also adds policies to configure the type of mutex desired. Because of the tight integration between mutex and condition variables they also needed to be reworked and the changes have to be in one commit to retain atomicity. A win32 and pthread implemenation is provided to make sure the api works with both.
This commit is contained in:
parent
02bb892d7a
commit
50be9493d2
14 changed files with 543 additions and 101 deletions
|
|
@ -271,7 +271,7 @@ struct ThreadData {
|
|||
s_thread_data.remove(s_thread_id);
|
||||
}
|
||||
|
||||
Sync::Mutex mutex;
|
||||
Sync::RecursiveMutex mutex;
|
||||
|
||||
// Each thread has its own timers, notifiers and a wake pipe.
|
||||
TimeoutSet timeouts;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
namespace Audio {
|
||||
|
||||
static PulseAudioContext* s_pulse_audio_context;
|
||||
static Sync::Mutex s_pulse_audio_context_mutex;
|
||||
static Sync::RecursiveMutex s_pulse_audio_context_mutex;
|
||||
|
||||
ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::the()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ private:
|
|||
|
||||
void seek(AK::Duration timestamp, SeekCompletionHandler&&);
|
||||
|
||||
[[nodiscard]] Sync::MutexLocker take_lock() const { return Sync::MutexLocker(m_mutex); }
|
||||
[[nodiscard]] Sync::MutexLocker<Sync::Mutex> take_lock() const { return Sync::MutexLocker(m_mutex); }
|
||||
void wake() const { m_wait_condition.broadcast(); }
|
||||
|
||||
AudioDecoder const& decoder() const { return *m_decoder; }
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ private:
|
|||
|
||||
TimeRanges buffered_time_ranges() const;
|
||||
|
||||
[[nodiscard]] Sync::MutexLocker take_lock() const { return Sync::MutexLocker(m_mutex); }
|
||||
[[nodiscard]] Sync::MutexLocker<Sync::Mutex> take_lock() const { return Sync::MutexLocker(m_mutex); }
|
||||
void wake() const { m_wait_condition.broadcast(); }
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
set(SOURCES
|
||||
Mutex.cpp
|
||||
)
|
||||
ladybird_lib(LibSync sync EXPLICIT_SYMBOL_EXPORT)
|
||||
if (WIN32)
|
||||
set(SOURCES MutexWindows.cpp ConditionVariableWindows.cpp)
|
||||
else()
|
||||
set(SOURCES MutexPOSIX.cpp ConditionVariablePOSIX.cpp)
|
||||
endif()
|
||||
|
||||
ladybird_lib(LibSync sync EXPLICIT_SYMBOL_EXPORT)
|
||||
|
|
|
|||
|
|
@ -1,63 +1,69 @@
|
|||
/*
|
||||
* Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>.
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <LibSync/Export.h>
|
||||
#include <LibSync/Mutex.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <LibSync/Policy.h>
|
||||
|
||||
#if !defined(AK_OS_WINDOWS)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Sync {
|
||||
|
||||
// A signaling condition variable that wraps over the pthread_cond_* APIs.
|
||||
class ConditionVariable {
|
||||
friend class Mutex;
|
||||
// A signaling condition variable that wraps over the platform APIs.
|
||||
// On posix it is a wrapper of pthread_cond_*.
|
||||
// On Windows it wraps ConditionVariable
|
||||
// TODO: Implement timed_wait()
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
class SYNC_API ConditionVariableBase {
|
||||
AK_MAKE_NONCOPYABLE(ConditionVariableBase);
|
||||
AK_MAKE_NONMOVABLE(ConditionVariableBase);
|
||||
|
||||
public:
|
||||
ConditionVariable(Mutex& to_wait_on)
|
||||
: m_to_wait_on(to_wait_on)
|
||||
{
|
||||
auto result = pthread_cond_init(&m_condition, nullptr);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE ~ConditionVariable()
|
||||
{
|
||||
auto result = pthread_cond_destroy(&m_condition);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
ConditionVariableBase(MutexType& to_wait_on);
|
||||
~ConditionVariableBase();
|
||||
|
||||
// As with pthread APIs, the mutex must be locked or undefined behavior ensues.
|
||||
ALWAYS_INLINE void wait()
|
||||
{
|
||||
auto result = pthread_cond_wait(&m_condition, &m_to_wait_on.m_mutex);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
// Condition variables are allowed spurious wakeups. As such waiting on a condition in a loop is preferred.
|
||||
void wait();
|
||||
|
||||
ALWAYS_INLINE void wait_while(Function<bool()> condition)
|
||||
{
|
||||
while (condition())
|
||||
wait();
|
||||
}
|
||||
// Release at least one of the threads waiting on this variable.
|
||||
ALWAYS_INLINE void signal()
|
||||
{
|
||||
auto result = pthread_cond_signal(&m_condition);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
void signal();
|
||||
|
||||
// Release all of the threads waiting on this variable.
|
||||
ALWAYS_INLINE void broadcast()
|
||||
{
|
||||
auto result = pthread_cond_broadcast(&m_condition);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
void broadcast();
|
||||
|
||||
private:
|
||||
pthread_cond_t m_condition;
|
||||
Mutex& m_to_wait_on;
|
||||
#ifdef AK_OS_WINDOWS
|
||||
using StorageType = void*;
|
||||
#else
|
||||
using StorageType = pthread_cond_t;
|
||||
#endif
|
||||
|
||||
alignas(StorageType) unsigned char m_storage[sizeof(StorageType)];
|
||||
MutexType& m_to_wait_on;
|
||||
};
|
||||
|
||||
template<typename MutexType>
|
||||
ConditionVariableBase(MutexType&) -> ConditionVariableBase<MutexType>;
|
||||
|
||||
using ConditionVariable = ConditionVariableBase<Mutex>;
|
||||
|
||||
}
|
||||
|
|
|
|||
68
Libraries/LibSync/ConditionVariablePOSIX.cpp
Normal file
68
Libraries/LibSync/ConditionVariablePOSIX.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>.
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibSync/ConditionVariable.h>
|
||||
#include <LibSync/Export.h>
|
||||
#include <LibSync/Mutex.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE pthread_cond_t* to_impl(void* ptr)
|
||||
{
|
||||
return reinterpret_cast<pthread_cond_t*>(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
ConditionVariableBase<MutexType>::ConditionVariableBase(MutexType& to_wait_on)
|
||||
: m_to_wait_on(to_wait_on)
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(pthread_cond_t));
|
||||
int result = pthread_cond_init(to_impl(m_storage), nullptr);
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
ConditionVariableBase<MutexType>::~ConditionVariableBase()
|
||||
{
|
||||
int result = pthread_cond_destroy(to_impl(m_storage));
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
void ConditionVariableBase<MutexType>::wait()
|
||||
{
|
||||
int result = pthread_cond_wait(to_impl(m_storage), reinterpret_cast<pthread_mutex_t*>(m_to_wait_on.m_storage));
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
void ConditionVariableBase<MutexType>::signal()
|
||||
{
|
||||
int result = pthread_cond_signal(to_impl(m_storage));
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
void ConditionVariableBase<MutexType>::broadcast()
|
||||
{
|
||||
int result = pthread_cond_broadcast(to_impl(m_storage));
|
||||
VERIFY(result == 0);
|
||||
}
|
||||
|
||||
template class SYNC_API ConditionVariableBase<Mutex>;
|
||||
|
||||
}
|
||||
63
Libraries/LibSync/ConditionVariableWindows.cpp
Normal file
63
Libraries/LibSync/ConditionVariableWindows.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Windows.h>
|
||||
#include <LibSync/ConditionVariable.h>
|
||||
#include <LibSync/Mutex.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE PCONDITION_VARIABLE to_impl(void* ptr)
|
||||
{
|
||||
return reinterpret_cast<PCONDITION_VARIABLE>(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
ConditionVariableBase<MutexType>::ConditionVariableBase(MutexType& to_wait_on)
|
||||
: m_to_wait_on(to_wait_on)
|
||||
{
|
||||
InitializeConditionVariable(to_impl(m_storage));
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
ConditionVariableBase<MutexType>::~ConditionVariableBase() = default;
|
||||
|
||||
template<>
|
||||
void ConditionVariableBase<Mutex>::wait()
|
||||
{
|
||||
BOOL result = SleepConditionVariableSRW(to_impl(m_storage), reinterpret_cast<PSRWLOCK>(m_to_wait_on.m_storage), INFINITE, 0);
|
||||
if (!result) {
|
||||
warnln("SleepConditionVariableSRW failed with: {}", Error::from_windows_error());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
void ConditionVariableBase<MutexType>::signal()
|
||||
{
|
||||
WakeConditionVariable(to_impl(m_storage));
|
||||
}
|
||||
|
||||
template<typename MutexType>
|
||||
requires Detail::IsIntraprocess<MutexType> && Detail::IsNonRecursive<MutexType>
|
||||
void ConditionVariableBase<MutexType>::broadcast()
|
||||
{
|
||||
WakeAllConditionVariable(to_impl(m_storage));
|
||||
}
|
||||
|
||||
template class SYNC_API ConditionVariableBase<Mutex>;
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibSync/Mutex.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
Mutex::Mutex()
|
||||
: m_lock_count(0)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&m_mutex, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
VERIFY(m_lock_count == 0);
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
|
@ -8,36 +9,81 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibSync/Export.h>
|
||||
#include <pthread.h>
|
||||
#include <LibSync/Policy.h>
|
||||
|
||||
#if !defined(AK_OS_WINDOWS)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Sync {
|
||||
|
||||
class SYNC_API Mutex {
|
||||
AK_MAKE_NONCOPYABLE(Mutex);
|
||||
AK_MAKE_NONMOVABLE(Mutex);
|
||||
friend class ConditionVariable;
|
||||
template<typename RecursivePolicy, typename InterprocessPolicy>
|
||||
class MutexBase;
|
||||
|
||||
template<typename T>
|
||||
requires Detail::IsIntraprocess<T> && Detail::IsNonRecursive<T>
|
||||
class ConditionVariableBase;
|
||||
|
||||
template<typename RecursivePolicy, typename InterprocessPolicy>
|
||||
class SYNC_API MutexBase {
|
||||
AK_MAKE_NONCOPYABLE(MutexBase);
|
||||
AK_MAKE_NONMOVABLE(MutexBase);
|
||||
|
||||
template<typename T>
|
||||
requires Detail::IsIntraprocess<T> && Detail::IsNonRecursive<T>
|
||||
friend class ConditionVariableBase;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
using InterprocessPolicyType = InterprocessPolicy;
|
||||
using RecursivePolicyType = RecursivePolicy;
|
||||
|
||||
MutexBase();
|
||||
~MutexBase();
|
||||
|
||||
bool try_lock();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_mutex;
|
||||
unsigned m_lock_count { 0 };
|
||||
static consteval u64 storage_size()
|
||||
{
|
||||
#ifdef AK_OS_WINDOWS
|
||||
if constexpr (IsSame<InterprocessPolicy, PolicyInterprocess>) {
|
||||
// Size of a handle
|
||||
return sizeof(void*);
|
||||
}
|
||||
if constexpr (IsSame<RecursivePolicy, PolicyRecursive>) {
|
||||
// The size of a critical section. This is guaranteed
|
||||
return 40;
|
||||
}
|
||||
// SRWLock is just a void*
|
||||
return sizeof(void*);
|
||||
#else
|
||||
return sizeof(pthread_mutex_t);
|
||||
#endif
|
||||
}
|
||||
|
||||
alignas(void*) unsigned char m_storage[storage_size()];
|
||||
};
|
||||
|
||||
using Mutex = MutexBase<PolicyNonRecursive, PolicyIntraprocess>;
|
||||
using RecursiveMutex = MutexBase<PolicyRecursive, PolicyIntraprocess>;
|
||||
using IPCMutex = MutexBase<PolicyNonRecursive, PolicyInterprocess>;
|
||||
using IPCRecursiveMutex = MutexBase<PolicyRecursive, PolicyInterprocess>;
|
||||
|
||||
template<typename MutexType>
|
||||
class [[nodiscard]] SYNC_API MutexLocker {
|
||||
AK_MAKE_NONCOPYABLE(MutexLocker);
|
||||
AK_MAKE_NONMOVABLE(MutexLocker);
|
||||
|
||||
public:
|
||||
ALWAYS_INLINE explicit MutexLocker(Mutex& mutex)
|
||||
ALWAYS_INLINE explicit MutexLocker(MutexType& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
|
|
@ -50,23 +96,10 @@ public:
|
|||
ALWAYS_INLINE void lock() { m_mutex.lock(); }
|
||||
|
||||
private:
|
||||
Mutex& m_mutex;
|
||||
MutexType& m_mutex;
|
||||
};
|
||||
|
||||
ALWAYS_INLINE void Mutex::lock()
|
||||
{
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
m_lock_count++;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Mutex::unlock()
|
||||
{
|
||||
VERIFY(m_lock_count > 0);
|
||||
// FIXME: We need to protect the lock count with the mutex itself.
|
||||
// This may be bad because we're not *technically* unlocked yet,
|
||||
// but we're not handling any errors from pthread_mutex_unlock anyways.
|
||||
m_lock_count--;
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
template<typename MutexType>
|
||||
MutexLocker(MutexType&) -> MutexLocker<MutexType>;
|
||||
|
||||
}
|
||||
|
|
|
|||
100
Libraries/LibSync/MutexPOSIX.cpp
Normal file
100
Libraries/LibSync/MutexPOSIX.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021, kleines Filmröllchen <malu.bertsch@gmail.com>
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Diagnostics.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <LibSync/Export.h>
|
||||
#include <LibSync/Mutex.h>
|
||||
#include <LibSync/Policy.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE pthread_mutex_t* to_impl(void* ptr)
|
||||
{
|
||||
return reinterpret_cast<pthread_mutex_t*>(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename R, typename I>
|
||||
MutexBase<R, I>::~MutexBase()
|
||||
{
|
||||
int result = pthread_mutex_destroy(to_impl(m_storage));
|
||||
if (result != 0) {
|
||||
warnln("pthread_mutex_destroy failed with: {}", Error::from_errno(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename I>
|
||||
bool MutexBase<R, I>::try_lock()
|
||||
{
|
||||
int result = pthread_mutex_trylock(to_impl(m_storage));
|
||||
if (result == 0)
|
||||
return true;
|
||||
if (result == EBUSY)
|
||||
return false;
|
||||
warnln("pthread_mutex_lock failed with: {}", Error::from_errno(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
template<typename R, typename I>
|
||||
void MutexBase<R, I>::lock()
|
||||
{
|
||||
int result = pthread_mutex_lock(to_impl(m_storage));
|
||||
if (result != 0) {
|
||||
warnln("pthread_mutex_lock failed with: {}", Error::from_errno(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename R, typename I>
|
||||
void MutexBase<R, I>::unlock()
|
||||
{
|
||||
int result = pthread_mutex_unlock(to_impl(m_storage));
|
||||
if (result != 0) {
|
||||
warnln("pthread_mutex_unlock failed with: {}", Error::from_errno(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RecursivePolicy, typename InterprocessPolicy>
|
||||
MutexBase<RecursivePolicy, InterprocessPolicy>::MutexBase()
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(pthread_mutex_t));
|
||||
pthread_mutex_t* mutex_ptr = new (m_storage) pthread_mutex_t;
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
if constexpr (IsSame<RecursivePolicy, PolicyRecursive>) {
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
} else {
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
|
||||
}
|
||||
if constexpr (IsSame<InterprocessPolicy, PolicyInterprocess>)
|
||||
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||
int result = pthread_mutex_init(mutex_ptr, &attr);
|
||||
if (result != 0) {
|
||||
warnln("pthread_mutex_init failed with: {}", Error::from_errno(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
template class SYNC_API MutexBase<PolicyNonRecursive, PolicyIntraprocess>;
|
||||
template class SYNC_API MutexBase<PolicyRecursive, PolicyIntraprocess>;
|
||||
template class SYNC_API MutexBase<PolicyNonRecursive, PolicyInterprocess>;
|
||||
template class SYNC_API MutexBase<PolicyRecursive, PolicyInterprocess>;
|
||||
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
namespace Sync {
|
||||
|
||||
template<typename T>
|
||||
template<typename T, typename MutexType = Mutex>
|
||||
class MutexProtected {
|
||||
AK_MAKE_NONCOPYABLE(MutexProtected);
|
||||
AK_MAKE_NONMOVABLE(MutexProtected);
|
||||
|
|
@ -48,10 +48,10 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] ALWAYS_INLINE MutexLocker lock() { return MutexLocker(m_lock); }
|
||||
[[nodiscard]] ALWAYS_INLINE MutexLocker<MutexType> lock() { return MutexLocker(m_lock); }
|
||||
|
||||
T m_value;
|
||||
Mutex m_lock {};
|
||||
MutexType m_lock {};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
169
Libraries/LibSync/MutexWindows.cpp
Normal file
169
Libraries/LibSync/MutexWindows.cpp
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Concepts.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Windows.h>
|
||||
#include <LibSync/Export.h>
|
||||
#include <LibSync/Mutex.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
template<>
|
||||
Mutex::MutexBase()
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(SRWLOCK));
|
||||
PSRWLOCK pSRW = new (m_storage) SRWLOCK;
|
||||
InitializeSRWLock(pSRW);
|
||||
}
|
||||
template<>
|
||||
Mutex::~MutexBase() = default;
|
||||
|
||||
template<>
|
||||
void Mutex::lock()
|
||||
{
|
||||
AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
bool Mutex::try_lock()
|
||||
{
|
||||
return TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
void Mutex::unlock()
|
||||
{
|
||||
ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
RecursiveMutex::MutexBase()
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(CRITICAL_SECTION));
|
||||
LPCRITICAL_SECTION pCS = new (m_storage) CRITICAL_SECTION;
|
||||
// TODO: Optimize this for our use case
|
||||
InitializeCriticalSectionAndSpinCount(pCS, 4000);
|
||||
}
|
||||
|
||||
template<>
|
||||
RecursiveMutex::~MutexBase()
|
||||
{
|
||||
DeleteCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
void RecursiveMutex::lock()
|
||||
{
|
||||
EnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
bool RecursiveMutex::try_lock()
|
||||
{
|
||||
return TryEnterCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_storage));
|
||||
}
|
||||
|
||||
template<>
|
||||
void RecursiveMutex::unlock()
|
||||
{
|
||||
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(m_storage));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void init_ipc_mutex(void* storage)
|
||||
{
|
||||
PHANDLE pHandle = new (storage) HANDLE;
|
||||
SECURITY_ATTRIBUTES sa = { .nLength = sizeof(SECURITY_ATTRIBUTES), .lpSecurityDescriptor = nullptr, .bInheritHandle = TRUE };
|
||||
// TODO: If we want to send these over IPC then some other methods have to be exposed to construct a MutexBase from
|
||||
// the duplicated HANDLE. Otherwise it can use handle inheritance. We might want to only make it inheritable as an option.
|
||||
HANDLE handle = CreateMutexW(&sa, FALSE, nullptr);
|
||||
if (!handle) {
|
||||
warnln("Failed to create mutex object with: {}", Error::from_windows_error());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
*pHandle = handle;
|
||||
}
|
||||
|
||||
void destroy_ipc_mutex(void* storage)
|
||||
{
|
||||
CloseHandle(*reinterpret_cast<PHANDLE>(storage));
|
||||
}
|
||||
|
||||
void lock_ipc_mutex(void* storage)
|
||||
{
|
||||
DWORD result = WaitForSingleObject(*reinterpret_cast<PHANDLE>(storage), INFINITE);
|
||||
if (result != WAIT_OBJECT_0) {
|
||||
warnln("Failed to acquire mutex: {}", Error::from_windows_error(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_ipc_mutex(void* storage)
|
||||
{
|
||||
DWORD result = WaitForSingleObject(*reinterpret_cast<PHANDLE>(storage), 0);
|
||||
if (result == WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
if (result == WAIT_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
if (result == WAIT_ABANDONED)
|
||||
VERIFY_NOT_REACHED();
|
||||
warnln("Failed trying to acquire mutex: {}", Error::from_windows_error(result));
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
void unlock_ipc_mutex(void* storage)
|
||||
{
|
||||
BOOL result = ReleaseMutex(*reinterpret_cast<PHANDLE>(storage));
|
||||
if (!result) {
|
||||
warnln("Failed to release mutex: {}", Error::from_windows_error());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
IPCMutex::MutexBase()
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(HANDLE));
|
||||
init_ipc_mutex(m_storage);
|
||||
}
|
||||
template<>
|
||||
IPCMutex::~MutexBase() { destroy_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
void IPCMutex::lock() { lock_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
bool IPCMutex::try_lock() { return try_lock_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
void IPCMutex::unlock() { unlock_ipc_mutex(m_storage); }
|
||||
|
||||
template<>
|
||||
IPCRecursiveMutex::MutexBase()
|
||||
{
|
||||
static_assert(sizeof(m_storage) == sizeof(HANDLE));
|
||||
init_ipc_mutex(m_storage);
|
||||
}
|
||||
template<>
|
||||
IPCRecursiveMutex::~MutexBase() { destroy_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
void IPCRecursiveMutex::lock() { lock_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
bool IPCRecursiveMutex::try_lock() { return try_lock_ipc_mutex(m_storage); }
|
||||
template<>
|
||||
void IPCRecursiveMutex::unlock() { unlock_ipc_mutex(m_storage); }
|
||||
|
||||
template class SYNC_API MutexBase<PolicyNonRecursive, PolicyIntraprocess>;
|
||||
template class SYNC_API MutexBase<PolicyRecursive, PolicyIntraprocess>;
|
||||
template class SYNC_API MutexBase<PolicyNonRecursive, PolicyInterprocess>;
|
||||
template class SYNC_API MutexBase<PolicyRecursive, PolicyInterprocess>;
|
||||
|
||||
}
|
||||
31
Libraries/LibSync/Policy.h
Normal file
31
Libraries/LibSync/Policy.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ryszard Goc <ryszardgoc@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Concepts.h>
|
||||
|
||||
namespace Sync {
|
||||
|
||||
struct PolicyNonRecursive { };
|
||||
struct PolicyRecursive { };
|
||||
struct PolicyIntraprocess { };
|
||||
struct PolicyInterprocess { };
|
||||
|
||||
namespace Detail {
|
||||
|
||||
template<typename T>
|
||||
concept IsIntraprocess = requires {
|
||||
requires IsSame<typename T::InterprocessPolicyType, PolicyIntraprocess>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept IsNonRecursive = requires {
|
||||
requires IsSame<typename T::RecursivePolicyType, PolicyNonRecursive>;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue