blob: ffe4cbb46dfa38c36cb03e6ba4982113cc9f8133 [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/rwc_lock.h"
#include <glog/logging.h>
#ifndef NDEBUG
#include "kudu/gutil/walltime.h"
#include "kudu/util/debug-util.h"
#include "kudu/util/thread.h"
#endif // NDEBUG
namespace kudu {
RWCLock::RWCLock()
: no_mutators_(&lock_),
no_readers_(&lock_),
reader_count_(0),
#ifdef NDEBUG
write_locked_(false) {
#else
write_locked_(false),
writer_tid_(0),
last_writelock_acquire_time_(0) {
last_writer_backtrace_[0] = '\0';
#endif // NDEBUG
}
RWCLock::~RWCLock() {
DCHECK(!HasReaders());
DCHECK(!HasWriteLock());
}
void RWCLock::ReadLock() {
MutexLock l(lock_);
reader_count_++;
}
void RWCLock::ReadUnlock() {
MutexLock l(lock_);
DCHECK(HasReadersUnlocked());
reader_count_--;
if (reader_count_ == 0) {
no_readers_.Signal();
}
}
bool RWCLock::HasReaders() const {
MutexLock l(lock_);
return HasReadersUnlocked();
}
bool RWCLock::HasReadersUnlocked() const {
lock_.AssertAcquired();
return reader_count_ > 0;
}
bool RWCLock::HasWriteLock() const {
MutexLock l(lock_);
return HasWriteLockUnlocked();
}
bool RWCLock::HasWriteLockUnlocked() const {
lock_.AssertAcquired();
#ifndef NDEBUG
return writer_tid_ == Thread::CurrentThreadId();
#else
return write_locked_;
#endif
}
void RWCLock::WriteLock() {
MutexLock l(lock_);
// Wait for any other mutations to finish.
while (write_locked_) {
no_mutators_.Wait();
}
#ifndef NDEBUG
last_writelock_acquire_time_ = GetCurrentTimeMicros();
writer_tid_ = Thread::CurrentThreadId();
HexStackTraceToString(last_writer_backtrace_, kBacktraceBufSize);
#endif // NDEBUG
write_locked_ = true;
}
void RWCLock::WriteUnlock() {
MutexLock l(lock_);
DCHECK(HasWriteLockUnlocked());
write_locked_ = false;
#ifndef NDEBUG
writer_tid_ = 0;
last_writer_backtrace_[0] = '\0';
#endif // NDEBUG
no_mutators_.Signal();
}
void RWCLock::UpgradeToCommitLock() {
lock_.lock();
DCHECK(HasWriteLockUnlocked());
while (reader_count_ > 0) {
no_readers_.Wait();
}
DCHECK(HasWriteLockUnlocked());
// Leaves the lock held, which prevents any new readers
// or writers.
}
void RWCLock::CommitUnlock() {
DCHECK(!HasReadersUnlocked());
DCHECK(HasWriteLockUnlocked());
write_locked_ = false;
#ifndef NDEBUG
writer_tid_ = 0;
last_writer_backtrace_[0] = '\0';
#endif // NDEBUG
no_mutators_.Broadcast();
lock_.unlock();
}
} // namespace kudu