code_environment.cc
common_init.cc
compat.cc
+ condition_variable_debug.cc
config.cc
config_values.cc
dns_resolve.cc
reverse.c
run_cmd.cc
scrub_types.cc
+ shared_mutex_debug.cc
signal.cc
snap_types.cc
str_list.cc
--- /dev/null
+#include "condition_variable_debug.h"
+#include "common/mutex_debug.h"
+
+namespace ceph {
+
+condition_variable_debug::condition_variable_debug()
+ : waiter_mutex{nullptr}
+{
+ int r = pthread_cond_init(&cond, nullptr);
+ if (r) {
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+condition_variable_debug::~condition_variable_debug()
+{
+ pthread_cond_destroy(&cond);
+}
+
+void condition_variable_debug::wait(std::unique_lock<mutex_debug>& lock)
+{
+ // make sure this cond is used with one mutex only
+ assert(waiter_mutex == nullptr ||
+ waiter_mutex == lock.mutex());
+ waiter_mutex = lock.mutex();
+ assert(waiter_mutex->is_locked());
+ waiter_mutex->_pre_unlock();
+ if (int r = pthread_cond_wait(&cond, waiter_mutex->native_handle());
+ r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+ waiter_mutex->_post_lock();
+}
+
+void condition_variable_debug::notify_one()
+{
+ // make sure signaler is holding the waiter's lock.
+ assert(waiter_mutex == nullptr ||
+ waiter_mutex->is_locked());
+ if (int r = pthread_cond_signal(&cond); r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+void condition_variable_debug::notify_all(bool sloppy)
+{
+ // make sure signaler is holding the waiter's lock.
+ assert(waiter_mutex == NULL ||
+ waiter_mutex->is_locked());
+ if (int r = pthread_cond_broadcast(&cond); r != 0 && !sloppy) {
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+std::cv_status condition_variable_debug::_wait_until(mutex_debug* mutex,
+ timespec* ts)
+{
+ // make sure this cond is used with one mutex only
+ assert(waiter_mutex == nullptr ||
+ waiter_mutex == mutex);
+ waiter_mutex = mutex;
+ assert(waiter_mutex->is_locked());
+
+ waiter_mutex->_pre_unlock();
+ int r = pthread_cond_timedwait(&cond, waiter_mutex->native_handle(), ts);
+ switch (r) {
+ case 0:
+ waiter_mutex->_post_lock();
+ return std::cv_status::no_timeout;
+ case ETIMEDOUT:
+ return std::cv_status::timeout;
+ default:
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+} // namespace ceph
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <condition_variable>
+#include <ctime>
+#include <pthread.h>
+#include "common/ceph_time.h"
+
+namespace ceph {
+
+namespace mutex_debug_detail {
+ template<bool> class mutex_debug_impl;
+}
+
+class condition_variable_debug {
+ using mutex_debug = mutex_debug_detail::mutex_debug_impl<false>;
+
+ pthread_cond_t cond;
+ mutex_debug* waiter_mutex;
+
+ condition_variable_debug&
+ operator=(const condition_variable_debug&) = delete;
+ condition_variable_debug(const condition_variable_debug&) = delete;
+
+public:
+ condition_variable_debug();
+ ~condition_variable_debug();
+ void wait(std::unique_lock<mutex_debug>& lock);
+ template<class Predicate>
+ void wait(std::unique_lock<mutex_debug>& lock, Predicate pred) {
+ while (!pred()) {
+ wait(lock);
+ }
+ }
+ template<class Clock, class Duration>
+ std::cv_status wait_until(
+ std::unique_lock<mutex_debug>& lock,
+ const std::chrono::time_point<Clock, Duration>& when) {
+ timespec ts = when.to_timespec(when);
+ return _wait_until(lock.mutex(), &ts);
+ }
+ template<class Rep, class Period>
+ std::cv_status wait_for(
+ std::unique_lock<mutex_debug>& lock,
+ const std::chrono::duration<Rep, Period>& awhile) {
+ ceph::real_time when{ceph::real_clock::now()};
+ when += awhile;
+ timespec ts = ceph::real_clock::to_timespec(when);
+ return _wait_until(lock.mutex(), &ts);
+ }
+ void notify_one();
+ void notify_all(bool sloppy = false);
+private:
+ std::cv_status _wait_until(mutex_debug* mutex, timespec* ts);
+};
+
+} // namespace ceph
throw std::system_error(r, std::generic_category());
}
}
+ pthread_mutex_t* native_handle() {
+ return &m;
+ }
};
} // namespace mutex_debug_detail
typedef mutex_debug_detail::mutex_debug_impl<false> mutex_debug;
--- /dev/null
+#include "shared_mutex_debug.h"
+
+#include <system_error>
+
+#include "acconfig.h"
+#include "common/valgrind.h"
+
+namespace ceph {
+
+shared_mutex_debug::shared_mutex_debug(const std::string& n,
+ bool track_lock,
+ bool enable_lock_dep,
+ bool prioritize_write)
+ : mutex_debugging_base{n, false /* backtrace */,
+ nullptr /* cct for perf counter*/},
+ track(track_lock),
+ lockdep(enable_lock_dep)
+{
+#ifdef 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(&rwlock, &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(&rwlock, NULL);
+ }
+ ANNOTATE_BENIGN_RACE_SIZED(&id, sizeof(id), "shared_mutex_debug lockdep id");
+ ANNOTATE_BENIGN_RACE_SIZED(&nlock, sizeof(nlock), "shared_mutex_debug nwlock");
+ ANNOTATE_BENIGN_RACE_SIZED(&nrlock, sizeof(nrlock), "shared_mutex_debug nrlock");
+}
+
+// exclusive
+void shared_mutex_debug::lock()
+{
+ if (g_lockdep && lockdep) {
+ _will_lock();
+ }
+ if (int r = pthread_rwlock_wrlock(&rwlock); r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+ if (lockdep && g_lockdep) {
+ _locked();
+ }
+ _post_lock();
+}
+
+bool shared_mutex_debug::try_lock()
+{
+ int r = pthread_rwlock_trywrlock(&rwlock);
+ switch (r) {
+ case 0:
+ if (lockdep && g_lockdep) {
+ _locked();
+ }
+ _post_lock();
+ return true;
+ case EBUSY:
+ return false;
+ default:
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+void shared_mutex_debug::unlock()
+{
+ _pre_unlock();
+ if (lockdep && g_lockdep) {
+ _will_unlock();
+ }
+ if (int r = pthread_rwlock_unlock(&rwlock); r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+// shared locking
+void shared_mutex_debug::lock_shared()
+{
+ if (lockdep && g_lockdep) {
+ _will_lock();
+ }
+ if (int r = pthread_rwlock_rdlock(&rwlock); r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+ if (lockdep && g_lockdep) {
+ _locked();
+ }
+ _post_lock_shared();
+}
+
+bool shared_mutex_debug::try_lock_shared()
+{
+ if (lockdep && g_lockdep) {
+ _will_unlock();
+ }
+ switch (int r = pthread_rwlock_rdlock(&rwlock); r) {
+ case 0:
+ if (lockdep && g_lockdep) {
+ _locked();
+ }
+ _post_lock_shared();
+ return true;
+ case EBUSY:
+ return false;
+ default:
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+void shared_mutex_debug::unlock_shared()
+{
+ _pre_unlock_shared();
+ if (lockdep && g_lockdep) {
+ _will_unlock();
+ }
+ if (int r = pthread_rwlock_unlock(&rwlock); r != 0) {
+ throw std::system_error(r, std::generic_category());
+ }
+}
+
+// exclusive locking
+void shared_mutex_debug::_pre_unlock()
+{
+ if (track) {
+ assert(nlock > 0);
+ --nlock;
+ assert(locked_by == std::this_thread::get_id());
+ assert(nlock == 0);
+ locked_by = std::thread::id();
+ }
+}
+
+void shared_mutex_debug::_post_lock()
+{
+ if (track) {
+ assert(nlock == 0);
+ locked_by = std::this_thread::get_id();
+ ++nlock;
+ }
+}
+
+// shared locking
+void shared_mutex_debug::_pre_unlock_shared()
+{
+ if (track) {
+ assert(nrlock > 0);
+ nrlock--;
+ }
+}
+
+void shared_mutex_debug::_post_lock_shared()
+{
+ if (track) {
+ ++nrlock;
+ }
+}
+
+} // namespace ceph
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <pthread.h>
+#include <atomic>
+
+#include "common/mutex_debug.h"
+
+namespace ceph {
+
+class shared_mutex_debug :
+ public ceph::mutex_debug_detail::mutex_debugging_base
+{
+ pthread_rwlock_t rwlock;
+ const bool track;
+ const bool lockdep;
+ std::atomic<unsigned> nrlock{0};
+
+public:
+ // Mutex concept is DefaultConstructible
+ shared_mutex_debug()
+ : shared_mutex_debug{std::string{}}
+ {}
+ shared_mutex_debug(const std::string& n,
+ bool track_lock=true,
+ bool enable_lock_dep=true,
+ bool prioritize_write=false);
+ // exclusive locking
+ void lock();
+ bool try_lock();
+ void unlock();
+ // shared locking
+ void lock_shared();
+ bool try_lock_shared();
+ void unlock_shared();
+
+private:
+ // exclusive locking
+ void _pre_unlock();
+ void _post_lock();
+ // shared locking
+ void _pre_unlock_shared();
+ void _post_lock_shared();
+};
+
+} // namespace ceph