Bug 1757100 - Move minimal platform-dependent part of RWLock to mozglue/misc/PlatformRWLock.h&cpp - r=glandium,xpcom-reviewers

This is consistent with how xpcom's Mutex has its platform-dependent code in mozglue, and will allow readers-writer locks in mozglue without duplication.

Differential Revision: https://phabricator.services.mozilla.com/D139669
This commit is contained in:
Gerald Squelart 2022-02-27 23:32:55 +00:00
parent c05a6a04a8
commit 97bc60386d
7 changed files with 179 additions and 125 deletions

View File

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_PlatformRWLock_h
#define mozilla_PlatformRWLock_h
#include "mozilla/Types.h"
#ifndef XP_WIN
# include <pthread.h>
#endif
namespace mozilla::detail {
class RWLockImpl {
public:
explicit MFBT_API RWLockImpl();
MFBT_API ~RWLockImpl();
protected:
[[nodiscard]] MFBT_API bool tryReadLock();
MFBT_API void readLock();
MFBT_API void readUnlock();
[[nodiscard]] MFBT_API bool tryWriteLock();
MFBT_API void writeLock();
MFBT_API void writeUnlock();
private:
RWLockImpl(const RWLockImpl&) = delete;
void operator=(const RWLockImpl&) = delete;
RWLockImpl(RWLockImpl&&) = delete;
void operator=(RWLockImpl&&) = delete;
bool operator==(const RWLockImpl& rhs) = delete;
#ifndef XP_WIN
pthread_rwlock_t mRWLock;
#else
// SRWLock is pointer-sized. We declare it in such a fashion here to avoid
// pulling in windows.h wherever this header is used.
void* mRWLock;
#endif
};
} // namespace mozilla::detail
#endif // mozilla_PlatformRWLock_h

View File

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef XP_WIN
# error This file should only be compiled on non-Windows platforms.
#endif
#include "mozilla/PlatformRWLock.h"
#include "mozilla/Assertions.h"
#include <errno.h>
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
mozilla::detail::RWLockImpl::RWLockImpl() {
MOZ_RELEASE_ASSERT(pthread_rwlock_init(&mRWLock, nullptr) == 0,
"pthread_rwlock_init failed");
}
mozilla::detail::RWLockImpl::~RWLockImpl() {
MOZ_RELEASE_ASSERT(pthread_rwlock_destroy(&mRWLock) == 0,
"pthread_rwlock_destroy failed");
}
bool mozilla::detail::RWLockImpl::tryReadLock() {
int rv = pthread_rwlock_tryrdlock(&mRWLock);
// We allow EDEADLK here because it has been observed returned on macos when
// the write lock is held by the current thread.
MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY || rv == EDEADLK,
"pthread_rwlock_tryrdlock failed");
return rv == 0;
}
void mozilla::detail::RWLockImpl::readLock() {
MOZ_RELEASE_ASSERT(pthread_rwlock_rdlock(&mRWLock) == 0,
"pthread_rwlock_rdlock failed");
}
void mozilla::detail::RWLockImpl::readUnlock() {
MOZ_RELEASE_ASSERT(pthread_rwlock_unlock(&mRWLock) == 0,
"pthread_rwlock_unlock failed");
}
bool mozilla::detail::RWLockImpl::tryWriteLock() {
int rv = pthread_rwlock_trywrlock(&mRWLock);
// We allow EDEADLK here because it has been observed returned on macos when
// the write lock is held by the current thread.
MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY || rv == EDEADLK,
"pthread_rwlock_trywrlock failed");
return rv == 0;
}
void mozilla::detail::RWLockImpl::writeLock() {
MOZ_RELEASE_ASSERT(pthread_rwlock_wrlock(&mRWLock) == 0,
"pthread_rwlock_wrlock failed");
}
void mozilla::detail::RWLockImpl::writeUnlock() {
MOZ_RELEASE_ASSERT(pthread_rwlock_unlock(&mRWLock) == 0,
"pthread_rwlock_unlock failed");
}

View File

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef XP_WIN
# error This file should only be compiled on Windows.
#endif
#include "mozilla/PlatformRWLock.h"
#include <windows.h>
#define NativeHandle(m) (reinterpret_cast<SRWLOCK*>(&m))
mozilla::detail::RWLockImpl::RWLockImpl() {
static_assert(sizeof(SRWLOCK) <= sizeof(mRWLock), "SRWLOCK is too big!");
InitializeSRWLock(NativeHandle(mRWLock));
}
mozilla::detail::RWLockImpl::~RWLockImpl() {}
bool mozilla::detail::RWLockImpl::tryReadLock() {
return TryAcquireSRWLockShared(NativeHandle(mRWLock));
}
void mozilla::detail::RWLockImpl::readLock() {
AcquireSRWLockShared(NativeHandle(mRWLock));
}
void mozilla::detail::RWLockImpl::readUnlock() {
ReleaseSRWLockShared(NativeHandle(mRWLock));
}
bool mozilla::detail::RWLockImpl::tryWriteLock() {
return TryAcquireSRWLockExclusive(NativeHandle(mRWLock));
}
void mozilla::detail::RWLockImpl::writeLock() {
AcquireSRWLockExclusive(NativeHandle(mRWLock));
}
void mozilla::detail::RWLockImpl::writeUnlock() {
ReleaseSRWLockExclusive(NativeHandle(mRWLock));
}
#undef NativeHandle

View File

@ -15,6 +15,7 @@ EXPORTS.mozilla += [
"MmapFaultHandler.h",
"PlatformConditionVariable.h",
"PlatformMutex.h",
"PlatformRWLock.h",
"Printf.h",
"Sprintf.h",
"StackWalk.h",
@ -105,6 +106,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
SOURCES += [
"ConditionVariable_windows.cpp",
"Mutex_windows.cpp",
"RWLock_windows.cpp",
]
# WASI hasn't supported cond vars and mutexes yet so noop implementation is used.
elif CONFIG["OS_ARCH"] == "WASI":
@ -116,6 +118,7 @@ else:
SOURCES += [
"ConditionVariable_posix.cpp",
"Mutex_posix.cpp",
"RWLock_posix.cpp",
]
if CONFIG["MOZ_LINKER"] and CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":

View File

@ -337,7 +337,7 @@ void OffTheBooksMutex::AssertCurrentThreadOwns() const {
//
bool RWLock::TryReadLock() {
bool locked = this->TryReadLockInternal();
bool locked = this->detail::RWLockImpl::tryReadLock();
MOZ_ASSERT_IF(locked, mOwningThread == nullptr);
return locked;
}
@ -346,17 +346,17 @@ void RWLock::ReadLock() {
// All we want to ensure here is that we're not attempting to acquire the
// read lock while this thread is holding the write lock.
CheckAcquire();
this->ReadLockInternal();
this->detail::RWLockImpl::readLock();
MOZ_ASSERT(mOwningThread == nullptr);
}
void RWLock::ReadUnlock() {
MOZ_ASSERT(mOwningThread == nullptr);
this->ReadUnlockInternal();
this->detail::RWLockImpl::readUnlock();
}
bool RWLock::TryWriteLock() {
bool locked = this->TryWriteLockInternal();
bool locked = this->detail::RWLockImpl::tryWriteLock();
if (locked) {
mOwningThread = PR_GetCurrentThread();
Acquire();
@ -366,7 +366,7 @@ bool RWLock::TryWriteLock() {
void RWLock::WriteLock() {
CheckAcquire();
this->WriteLockInternal();
this->detail::RWLockImpl::writeLock();
mOwningThread = PR_GetCurrentThread();
Acquire();
}
@ -374,7 +374,7 @@ void RWLock::WriteLock() {
void RWLock::WriteUnlock() {
Release();
mOwningThread = nullptr;
this->WriteUnlockInternal();
this->detail::RWLockImpl::writeUnlock();
}
//

View File

@ -6,16 +6,6 @@
#include "mozilla/RWLock.h"
#ifdef XP_WIN
# include <windows.h>
static_assert(sizeof(SRWLOCK) <= sizeof(void*), "SRWLOCK is too big!");
# define NativeHandle(m) (reinterpret_cast<SRWLOCK*>(&m))
#else
# define NativeHandle(m) (&m)
#endif
namespace mozilla {
RWLock::RWLock(const char* aName)
@ -25,12 +15,6 @@ RWLock::RWLock(const char* aName)
mOwningThread(nullptr)
#endif
{
#ifdef XP_WIN
InitializeSRWLock(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_init(NativeHandle(mRWLock), nullptr) == 0,
"pthread_rwlock_init failed");
#endif
}
#ifdef DEBUG
@ -39,75 +23,6 @@ bool RWLock::LockedForWritingByCurrentThread() {
}
#endif
#ifndef XP_WIN
RWLock::~RWLock() {
MOZ_RELEASE_ASSERT(pthread_rwlock_destroy(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_destroy failed");
}
#endif
bool RWLock::TryReadLockInternal() {
#ifdef XP_WIN
return TryAcquireSRWLockShared(NativeHandle(mRWLock));
#else
int rv = pthread_rwlock_tryrdlock(NativeHandle(mRWLock));
// We allow EDEADLK here because it has been observed returned on macos when
// the write lock is held by the current thread.
MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY || rv == EDEADLK,
"pthread_rwlock_tryrdlock failed");
return rv == 0;
#endif
}
void RWLock::ReadLockInternal() {
#ifdef XP_WIN
AcquireSRWLockShared(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_rdlock(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_rdlock failed");
#endif
}
void RWLock::ReadUnlockInternal() {
#ifdef XP_WIN
ReleaseSRWLockShared(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_unlock(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_unlock failed");
#endif
}
bool RWLock::TryWriteLockInternal() {
#ifdef XP_WIN
return TryAcquireSRWLockExclusive(NativeHandle(mRWLock));
#else
int rv = pthread_rwlock_trywrlock(NativeHandle(mRWLock));
// We allow EDEADLK here because it has been observed returned on macos when
// the write lock is held by the current thread.
MOZ_RELEASE_ASSERT(rv == 0 || rv == EBUSY || rv == EDEADLK,
"pthread_rwlock_trywrlock failed");
return rv == 0;
#endif
}
void RWLock::WriteLockInternal() {
#ifdef XP_WIN
AcquireSRWLockExclusive(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_wrlock(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_wrlock failed");
#endif
}
void RWLock::WriteUnlockInternal() {
#ifdef XP_WIN
ReleaseSRWLockExclusive(NativeHandle(mRWLock));
#else
MOZ_RELEASE_ASSERT(pthread_rwlock_unlock(NativeHandle(mRWLock)) == 0,
"pthread_rwlock_unlock failed");
#endif
}
} // namespace mozilla
#undef NativeHandle

View File

@ -12,10 +12,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/BlockingResourceBase.h"
#ifndef XP_WIN
# include <pthread.h>
#endif
#include "mozilla/PlatformRWLock.h"
namespace mozilla {
@ -41,18 +38,10 @@ namespace mozilla {
//
// It is unspecified whether RWLock gives priority to waiting readers or
// a waiting writer when unlocking.
class RWLock : public BlockingResourceBase {
class RWLock : public detail::RWLockImpl, public BlockingResourceBase {
public:
explicit RWLock(const char* aName);
// Windows rwlocks don't need any special handling to be destroyed, but
// POSIX ones do.
#ifdef XP_WIN
~RWLock() = default;
#else
~RWLock();
#endif
#ifdef DEBUG
bool LockedForWritingByCurrentThread();
bool TryReadLock();
@ -62,34 +51,19 @@ class RWLock : public BlockingResourceBase {
void WriteLock();
void WriteUnlock();
#else
bool TryReadLock() { return TryReadLockInternal(); }
void ReadLock() { ReadLockInternal(); }
void ReadUnlock() { ReadUnlockInternal(); }
bool TryWriteLock() { return TryWriteLockInternal(); }
void WriteLock() { WriteLockInternal(); }
void WriteUnlock() { WriteUnlockInternal(); }
bool TryReadLock() { return detail::RWLockImpl::tryReadLock(); }
void ReadLock() { detail::RWLockImpl::readLock(); }
void ReadUnlock() { detail::RWLockImpl::readUnlock(); }
bool TryWriteLock() { return detail::RWLockImpl::tryWriteLock(); }
void WriteLock() { detail::RWLockImpl::writeLock(); }
void WriteUnlock() { detail::RWLockImpl::writeUnlock(); }
#endif
private:
bool TryReadLockInternal();
void ReadLockInternal();
void ReadUnlockInternal();
bool TryWriteLockInternal();
void WriteLockInternal();
void WriteUnlockInternal();
RWLock() = delete;
RWLock(const RWLock&) = delete;
RWLock& operator=(const RWLock&) = delete;
#ifndef XP_WIN
pthread_rwlock_t mRWLock;
#else
// SRWLock is pointer-sized. We declare it in such a fashion here to
// avoid pulling in windows.h wherever this header is used.
void* mRWLock;
#endif
#ifdef DEBUG
// We record the owning thread for write locks only.
PRThread* mOwningThread;