blob: 75fc1e708066086472d507ee9702cf032386228f [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/rw_mutex.h"
#include <cerrno>
#include <cstring>
#include <mutex>
#include <ostream>
#include <glog/logging.h>
#include "kudu/gutil/map-util.h"
#include "kudu/util/env.h"
using std::lock_guard;
namespace {
void unlock_rwlock(pthread_rwlock_t* rwlock) {
int rv = pthread_rwlock_unlock(rwlock);
DCHECK_EQ(0, rv) << strerror(rv);
}
} // anonymous namespace
namespace kudu {
RWMutex::RWMutex()
#ifndef NDEBUG
: writer_tid_(0)
#endif
{
Init(Priority::PREFER_READING);
}
RWMutex::RWMutex(Priority prio)
#ifndef NDEBUG
: writer_tid_(0)
#endif
{
Init(prio);
}
void RWMutex::Init(Priority prio) {
#ifdef __linux__
// Adapt from priority to the pthread type.
int kind = PTHREAD_RWLOCK_PREFER_READER_NP;
switch (prio) {
case Priority::PREFER_READING:
kind = PTHREAD_RWLOCK_PREFER_READER_NP;
break;
case Priority::PREFER_WRITING:
kind = PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP;
break;
}
// Initialize the new rwlock with the user's preference.
pthread_rwlockattr_t attr;
int rv = pthread_rwlockattr_init(&attr);
DCHECK_EQ(0, rv) << strerror(rv);
rv = pthread_rwlockattr_setkind_np(&attr, kind);
DCHECK_EQ(0, rv) << strerror(rv);
rv = pthread_rwlock_init(&native_handle_, &attr);
DCHECK_EQ(0, rv) << strerror(rv);
rv = pthread_rwlockattr_destroy(&attr);
DCHECK_EQ(0, rv) << strerror(rv);
#else
int rv = pthread_rwlock_init(&native_handle_, NULL);
DCHECK_EQ(0, rv) << strerror(rv);
#endif
}
RWMutex::~RWMutex() {
int rv = pthread_rwlock_destroy(&native_handle_);
DCHECK_EQ(0, rv) << strerror(rv);
}
void RWMutex::ReadLock() {
CheckLockState(LockState::NEITHER);
int rv = pthread_rwlock_rdlock(&native_handle_);
DCHECK_EQ(0, rv) << strerror(rv);
MarkForReading();
}
void RWMutex::ReadUnlock() {
CheckLockState(LockState::READER);
UnmarkForReading();
unlock_rwlock(&native_handle_);
}
bool RWMutex::TryReadLock() {
CheckLockState(LockState::NEITHER);
int rv = pthread_rwlock_tryrdlock(&native_handle_);
if (rv == EBUSY) {
return false;
}
DCHECK_EQ(0, rv) << strerror(rv);
MarkForReading();
return true;
}
void RWMutex::WriteLock() {
CheckLockState(LockState::NEITHER);
int rv = pthread_rwlock_wrlock(&native_handle_);
DCHECK_EQ(0, rv) << strerror(rv);
MarkForWriting();
}
void RWMutex::WriteUnlock() {
CheckLockState(LockState::WRITER);
UnmarkForWriting();
unlock_rwlock(&native_handle_);
}
bool RWMutex::TryWriteLock() {
CheckLockState(LockState::NEITHER);
int rv = pthread_rwlock_trywrlock(&native_handle_);
if (rv == EBUSY) {
return false;
}
DCHECK_EQ(0, rv) << strerror(rv);
MarkForWriting();
return true;
}
#ifndef NDEBUG
void RWMutex::AssertAcquired() const {
lock_guard<simple_spinlock> l(tid_lock_);
CHECK(ContainsKey(reader_tids_, Env::Default()->gettid()) ||
Env::Default()->gettid() == writer_tid_);
}
void RWMutex::AssertAcquiredForReading() const {
lock_guard<simple_spinlock> l(tid_lock_);
CHECK(ContainsKey(reader_tids_, Env::Default()->gettid()));
}
void RWMutex::AssertAcquiredForWriting() const {
lock_guard<simple_spinlock> l(tid_lock_);
CHECK_EQ(Env::Default()->gettid(), writer_tid_);
}
void RWMutex::CheckLockState(LockState state) const {
pid_t my_tid = Env::Default()->gettid();
bool is_reader;
bool is_writer;
{
lock_guard<simple_spinlock> l(tid_lock_);
is_reader = ContainsKey(reader_tids_, my_tid);
is_writer = writer_tid_ == my_tid;
}
switch (state) {
case LockState::NEITHER:
CHECK(!is_reader) << "Invalid state, already holding lock for reading";
CHECK(!is_writer) << "Invalid state, already holding lock for writing";
break;
case LockState::READER:
CHECK(!is_writer) << "Invalid state, already holding lock for writing";
CHECK(is_reader) << "Invalid state, wasn't holding lock for reading";
break;
case LockState::WRITER:
CHECK(!is_reader) << "Invalid state, already holding lock for reading";
CHECK(is_writer) << "Invalid state, wasn't holding lock for writing";
break;
}
}
void RWMutex::MarkForReading() {
lock_guard<simple_spinlock> l(tid_lock_);
reader_tids_.insert(Env::Default()->gettid());
}
void RWMutex::MarkForWriting() {
lock_guard<simple_spinlock> l(tid_lock_);
writer_tid_ = Env::Default()->gettid();
}
void RWMutex::UnmarkForReading() {
lock_guard<simple_spinlock> l(tid_lock_);
reader_tids_.erase(Env::Default()->gettid());
}
void RWMutex::UnmarkForWriting() {
lock_guard<simple_spinlock> l(tid_lock_);
writer_tid_ = 0;
}
#endif
} // namespace kudu