* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/core/SkSharedMutex.h"
#include "include/core/SkTypes.h"
#include "include/private/SkSemaphore.h"
#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
#if __has_feature(thread_sanitizer)
#define ANNOTATE_RWLOCK_CREATE(lock) \
AnnotateRWLockCreate(__FILE__, __LINE__, lock)
#define ANNOTATE_RWLOCK_DESTROY(lock) \
AnnotateRWLockDestroy(__FILE__, __LINE__, lock)
is_w=1 for writer lock, is_w=0 for reader lock. */
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
#if defined(DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK)
#if defined(__GNUC__)
#define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak))
#else
to prepend __declspec(selectany) to the annotations' declarations. */
#error weak annotations are not supported for your compiler
#endif
#else
#define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK
#endif
extern "C" {
void AnnotateRWLockCreate(
const char *file, int line,
const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockDestroy(
const char *file, int line,
const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockAcquired(
const char *file, int line,
const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
void AnnotateRWLockReleased(
const char *file, int line,
const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
}
#else
#define ANNOTATE_RWLOCK_CREATE(lock)
#define ANNOTATE_RWLOCK_DESTROY(lock)
#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)
#define ANNOTATE_RWLOCK_RELEASED(lock, is_w)
#endif
#ifdef SK_DEBUG
#include "include/private/SkTDArray.h"
#include "include/private/SkThreadID.h"
class SkSharedMutex::ThreadIDSet {
public:
bool find(SkThreadID threadID) const {
for (auto& t : fThreadIDs) {
if (t == threadID) return true;
}
return false;
}
bool tryAdd(SkThreadID threadID) {
for (auto& t : fThreadIDs) {
if (t == threadID) return false;
}
fThreadIDs.append(1, &threadID);
return true;
}
bool tryRemove(SkThreadID threadID) {
for (int i = 0; i < fThreadIDs.count(); ++i) {
if (fThreadIDs[i] == threadID) {
fThreadIDs.remove(i);
return true;
}
}
return false;
}
void swap(ThreadIDSet& other) {
fThreadIDs.swap(other.fThreadIDs);
}
int count() const {
return fThreadIDs.count();
}
private:
SkTDArray<SkThreadID> fThreadIDs;
};
SkSharedMutex::SkSharedMutex()
: fCurrentShared(new ThreadIDSet)
, fWaitingExclusive(new ThreadIDSet)
, fWaitingShared(new ThreadIDSet){
ANNOTATE_RWLOCK_CREATE(this);
}
SkSharedMutex::~SkSharedMutex() { ANNOTATE_RWLOCK_DESTROY(this); }
void SkSharedMutex::acquire() {
SkThreadID threadID(SkGetThreadID());
int currentSharedCount;
int waitingExclusiveCount;
{
SkAutoMutexExclusive l(fMu);
SkASSERTF(!fCurrentShared->find(threadID),
"Thread %lx already has an shared lock\n", threadID);
if (!fWaitingExclusive->tryAdd(threadID)) {
SkDEBUGFAILF("Thread %lx already has an exclusive lock\n", threadID);
}
currentSharedCount = fCurrentShared->count();
waitingExclusiveCount = fWaitingExclusive->count();
}
if (currentSharedCount > 0 || waitingExclusiveCount > 1) {
fExclusiveQueue.wait();
}
ANNOTATE_RWLOCK_ACQUIRED(this, 1);
}
void SkSharedMutex::release() {
ANNOTATE_RWLOCK_RELEASED(this, 1);
SkThreadID threadID(SkGetThreadID());
int sharedWaitingCount;
int exclusiveWaitingCount;
int sharedQueueSelect;
{
SkAutoMutexExclusive l(fMu);
SkASSERT(0 == fCurrentShared->count());
if (!fWaitingExclusive->tryRemove(threadID)) {
SkDEBUGFAILF("Thread %lx did not have the lock held.\n", threadID);
}
exclusiveWaitingCount = fWaitingExclusive->count();
sharedWaitingCount = fWaitingShared->count();
fWaitingShared.swap(fCurrentShared);
sharedQueueSelect = fSharedQueueSelect;
if (sharedWaitingCount > 0) {
fSharedQueueSelect = 1 - fSharedQueueSelect;
}
}
if (sharedWaitingCount > 0) {
fSharedQueue[sharedQueueSelect].signal(sharedWaitingCount);
} else if (exclusiveWaitingCount > 0) {
fExclusiveQueue.signal();
}
}
void SkSharedMutex::assertHeld() const {
SkThreadID threadID(SkGetThreadID());
SkAutoMutexExclusive l(fMu);
SkASSERT(0 == fCurrentShared->count());
SkASSERT(fWaitingExclusive->find(threadID));
}
void SkSharedMutex::acquireShared() {
SkThreadID threadID(SkGetThreadID());
int exclusiveWaitingCount;
int sharedQueueSelect;
{
SkAutoMutexExclusive l(fMu);
exclusiveWaitingCount = fWaitingExclusive->count();
if (exclusiveWaitingCount > 0) {
if (!fWaitingShared->tryAdd(threadID)) {
SkDEBUGFAILF("Thread %lx was already waiting!\n", threadID);
}
} else {
if (!fCurrentShared->tryAdd(threadID)) {
SkDEBUGFAILF("Thread %lx already holds a shared lock!\n", threadID);
}
}
sharedQueueSelect = fSharedQueueSelect;
}
if (exclusiveWaitingCount > 0) {
fSharedQueue[sharedQueueSelect].wait();
}
ANNOTATE_RWLOCK_ACQUIRED(this, 0);
}
void SkSharedMutex::releaseShared() {
ANNOTATE_RWLOCK_RELEASED(this, 0);
SkThreadID threadID(SkGetThreadID());
int currentSharedCount;
int waitingExclusiveCount;
{
SkAutoMutexExclusive l(fMu);
if (!fCurrentShared->tryRemove(threadID)) {
SkDEBUGFAILF("Thread %lx does not hold a shared lock.\n", threadID);
}
currentSharedCount = fCurrentShared->count();
waitingExclusiveCount = fWaitingExclusive->count();
}
if (0 == currentSharedCount && waitingExclusiveCount > 0) {
fExclusiveQueue.signal();
}
}
void SkSharedMutex::assertHeldShared() const {
SkThreadID threadID(SkGetThreadID());
SkAutoMutexExclusive l(fMu);
SkASSERT(fCurrentShared->find(threadID));
}
#else
static const int kLogThreadCount = 10;
enum {
kSharedOffset = (0 * kLogThreadCount),
kWaitingExlusiveOffset = (1 * kLogThreadCount),
kWaitingSharedOffset = (2 * kLogThreadCount),
kSharedMask = ((1 << kLogThreadCount) - 1) << kSharedOffset,
kWaitingExclusiveMask = ((1 << kLogThreadCount) - 1) << kWaitingExlusiveOffset,
kWaitingSharedMask = ((1 << kLogThreadCount) - 1) << kWaitingSharedOffset,
};
SkSharedMutex::SkSharedMutex() : fQueueCounts(0) { ANNOTATE_RWLOCK_CREATE(this); }
SkSharedMutex::~SkSharedMutex() { ANNOTATE_RWLOCK_DESTROY(this); }
void SkSharedMutex::acquire() {
int32_t oldQueueCounts = fQueueCounts.fetch_add(1 << kWaitingExlusiveOffset,
std::memory_order_acquire);
if ((oldQueueCounts & kWaitingExclusiveMask) > 0 || (oldQueueCounts & kSharedMask) > 0) {
fExclusiveQueue.wait();
}
ANNOTATE_RWLOCK_ACQUIRED(this, 1);
}
void SkSharedMutex::release() {
ANNOTATE_RWLOCK_RELEASED(this, 1);
int32_t oldQueueCounts = fQueueCounts.load(std::memory_order_relaxed);
int32_t waitingShared;
int32_t newQueueCounts;
do {
newQueueCounts = oldQueueCounts;
newQueueCounts -= 1 << kWaitingExlusiveOffset;
waitingShared = (oldQueueCounts & kWaitingSharedMask) >> kWaitingSharedOffset;
if (waitingShared > 0) {
newQueueCounts &= ~kWaitingSharedMask;
newQueueCounts |= waitingShared << kSharedOffset;
}
} while (!fQueueCounts.compare_exchange_strong(oldQueueCounts, newQueueCounts,
std::memory_order_release,
std::memory_order_relaxed));
if (waitingShared > 0) {
fSharedQueue.signal(waitingShared);
} else if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
fExclusiveQueue.signal();
}
}
void SkSharedMutex::acquireShared() {
int32_t oldQueueCounts = fQueueCounts.load(std::memory_order_relaxed);
int32_t newQueueCounts;
do {
newQueueCounts = oldQueueCounts;
if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
newQueueCounts += 1 << kWaitingSharedOffset;
} else {
newQueueCounts += 1 << kSharedOffset;
}
} while (!fQueueCounts.compare_exchange_strong(oldQueueCounts, newQueueCounts,
std::memory_order_acquire,
std::memory_order_relaxed));
if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
fSharedQueue.wait();
}
ANNOTATE_RWLOCK_ACQUIRED(this, 0);
}
void SkSharedMutex::releaseShared() {
ANNOTATE_RWLOCK_RELEASED(this, 0);
int32_t oldQueueCounts = fQueueCounts.fetch_sub(1 << kSharedOffset,
std::memory_order_release);
if (((oldQueueCounts & kSharedMask) >> kSharedOffset) == 1
&& (oldQueueCounts & kWaitingExclusiveMask) > 0) {
fExclusiveQueue.signal();
}
}
#endif