summaryrefslogtreecommitdiffstats
path: root/src/common/RWLock.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/RWLock.h274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/common/RWLock.h b/src/common/RWLock.h
new file mode 100644
index 000000000..08c8edc7b
--- /dev/null
+++ b/src/common/RWLock.h
@@ -0,0 +1,274 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+
+
+#ifndef CEPH_RWLock_Posix__H
+#define CEPH_RWLock_Posix__H
+
+#include <pthread.h>
+#include <string>
+#include "include/ceph_assert.h"
+#include "acconfig.h"
+#include "lockdep.h"
+#include "common/valgrind.h"
+
+#include <atomic>
+
+class RWLock final
+{
+ mutable pthread_rwlock_t L;
+ std::string name;
+ mutable int id;
+ mutable std::atomic<unsigned> nrlock = { 0 }, nwlock = { 0 };
+ bool track, lockdep;
+
+ std::string unique_name(const char* name) const;
+
+public:
+ RWLock(const RWLock& other) = delete;
+ const RWLock& operator=(const RWLock& other) = delete;
+
+ RWLock(const std::string &n, bool track_lock=true, bool ld=true, bool prioritize_write=false)
+ : name(n), id(-1), track(track_lock),
+ lockdep(ld) {
+#if defined(HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP)
+ if (prioritize_write) {
+ pthread_rwlockattr_t attr;
+ pthread_rwlockattr_init(&attr);
+ // PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
+ // Setting the lock kind to this avoids writer starvation as long as
+ // long as any read locking is not done in a recursive fashion.
+ pthread_rwlockattr_setkind_np(&attr,
+ PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
+ pthread_rwlock_init(&L, &attr);
+ pthread_rwlockattr_destroy(&attr);
+ } else
+#endif
+ // Next block is in {} to possibly connect to the above if when code is used.
+ {
+ pthread_rwlock_init(&L, NULL);
+ }
+ ANNOTATE_BENIGN_RACE_SIZED(&id, sizeof(id), "RWLock lockdep id");
+ ANNOTATE_BENIGN_RACE_SIZED(&nrlock, sizeof(nrlock), "RWlock nrlock");
+ ANNOTATE_BENIGN_RACE_SIZED(&nwlock, sizeof(nwlock), "RWlock nwlock");
+ if (lockdep && g_lockdep) id = lockdep_register(name.c_str());
+ }
+
+ bool is_locked() const {
+ ceph_assert(track);
+ return (nrlock > 0) || (nwlock > 0);
+ }
+
+ bool is_wlocked() const {
+ ceph_assert(track);
+ return (nwlock > 0);
+ }
+ ~RWLock() {
+ // The following check is racy but we are about to destroy
+ // the object and we assume that there are no other users.
+ if (track)
+ ceph_assert(!is_locked());
+ pthread_rwlock_destroy(&L);
+ if (lockdep && g_lockdep) {
+ lockdep_unregister(id);
+ }
+ }
+
+ void unlock(bool lockdep=true) const {
+ if (track) {
+ if (nwlock > 0) {
+ nwlock--;
+ } else {
+ ceph_assert(nrlock > 0);
+ nrlock--;
+ }
+ }
+ if (lockdep && this->lockdep && g_lockdep)
+ id = lockdep_will_unlock(name.c_str(), id);
+ int r = pthread_rwlock_unlock(&L);
+ ceph_assert(r == 0);
+ }
+
+ // read
+ void get_read() const {
+ if (lockdep && g_lockdep) id = lockdep_will_lock(name.c_str(), id);
+ int r = pthread_rwlock_rdlock(&L);
+ ceph_assert(r == 0);
+ if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
+ if (track)
+ nrlock++;
+ }
+ bool try_get_read() const {
+ if (pthread_rwlock_tryrdlock(&L) == 0) {
+ if (track)
+ nrlock++;
+ if (lockdep && g_lockdep) id = lockdep_locked(name.c_str(), id);
+ return true;
+ }
+ return false;
+ }
+ void put_read() const {
+ unlock();
+ }
+ void lock_shared() {
+ get_read();
+ }
+ void unlock_shared() {
+ put_read();
+ }
+ // write
+ void get_write(bool lockdep=true) {
+ if (lockdep && this->lockdep && g_lockdep)
+ id = lockdep_will_lock(name.c_str(), id);
+ int r = pthread_rwlock_wrlock(&L);
+ ceph_assert(r == 0);
+ if (lockdep && this->lockdep && g_lockdep)
+ id = lockdep_locked(name.c_str(), id);
+ if (track)
+ nwlock++;
+
+ }
+ bool try_get_write(bool lockdep=true) {
+ if (pthread_rwlock_trywrlock(&L) == 0) {
+ if (lockdep && this->lockdep && g_lockdep)
+ id = lockdep_locked(name.c_str(), id);
+ if (track)
+ nwlock++;
+ return true;
+ }
+ return false;
+ }
+ void put_write() {
+ unlock();
+ }
+ void lock() {
+ get_write();
+ }
+ void get(bool for_write) {
+ if (for_write) {
+ get_write();
+ } else {
+ get_read();
+ }
+ }
+
+public:
+ class RLocker {
+ const RWLock &m_lock;
+
+ bool locked;
+
+ public:
+ explicit RLocker(const RWLock& lock) : m_lock(lock) {
+ m_lock.get_read();
+ locked = true;
+ }
+ void unlock() {
+ ceph_assert(locked);
+ m_lock.unlock();
+ locked = false;
+ }
+ ~RLocker() {
+ if (locked) {
+ m_lock.unlock();
+ }
+ }
+ };
+
+ class WLocker {
+ RWLock &m_lock;
+
+ bool locked;
+
+ public:
+ explicit WLocker(RWLock& lock) : m_lock(lock) {
+ m_lock.get_write();
+ locked = true;
+ }
+ void unlock() {
+ ceph_assert(locked);
+ m_lock.unlock();
+ locked = false;
+ }
+ ~WLocker() {
+ if (locked) {
+ m_lock.unlock();
+ }
+ }
+ };
+
+ class Context {
+ RWLock& lock;
+
+ public:
+ enum LockState {
+ Untaken = 0,
+ TakenForRead = 1,
+ TakenForWrite = 2,
+ };
+
+ private:
+ LockState state;
+
+ public:
+ explicit Context(RWLock& l) : lock(l), state(Untaken) {}
+ Context(RWLock& l, LockState s) : lock(l), state(s) {}
+
+ void get_write() {
+ ceph_assert(state == Untaken);
+
+ lock.get_write();
+ state = TakenForWrite;
+ }
+
+ void get_read() {
+ ceph_assert(state == Untaken);
+
+ lock.get_read();
+ state = TakenForRead;
+ }
+
+ void unlock() {
+ ceph_assert(state != Untaken);
+ lock.unlock();
+ state = Untaken;
+ }
+
+ void promote() {
+ ceph_assert(state == TakenForRead);
+ unlock();
+ get_write();
+ }
+
+ LockState get_state() { return state; }
+ void set_state(LockState s) {
+ state = s;
+ }
+
+ bool is_locked() {
+ return (state != Untaken);
+ }
+
+ bool is_rlocked() {
+ return (state == TakenForRead);
+ }
+
+ bool is_wlocked() {
+ return (state == TakenForWrite);
+ }
+ };
+};
+
+#endif // !CEPH_RWLock_Posix__H