// -*- 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) 2011 New Dream Network * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License version 2, as published by the Free Software * Foundation. See file COPYING. * */ #include #include #include #include #include "common/ceph_time.h" #include "common/shunique_lock.h" #include "gtest/gtest.h" template static bool test_try_lock(SharedMutex* sm) { if (!sm->try_lock()) return false; sm->unlock(); return true; } template static bool test_try_lock_shared(SharedMutex* sm) { if (!sm->try_lock_shared()) return false; sm->unlock_shared(); return true; } template static void check_conflicts(SharedMutex sm, AcquireType) { } template static void ensure_conflicts(SharedMutex& sm, ceph::acquire_unique_t) { auto ttl = &test_try_lock; auto ttls = &test_try_lock_shared; ASSERT_FALSE(std::async(std::launch::async, ttl, &sm).get()); ASSERT_FALSE(std::async(std::launch::async, ttls, &sm).get()); } template static void ensure_conflicts(SharedMutex& sm, ceph::acquire_shared_t) { auto ttl = &test_try_lock; auto ttls = &test_try_lock_shared; ASSERT_FALSE(std::async(std::launch::async, ttl, &sm).get()); ASSERT_TRUE(std::async(std::launch::async, ttls, &sm).get()); } template static void ensure_free(SharedMutex& sm) { auto ttl = &test_try_lock; auto ttls = &test_try_lock_shared; ASSERT_TRUE(std::async(std::launch::async, ttl, &sm).get()); ASSERT_TRUE(std::async(std::launch::async, ttls, &sm).get()); } template static void check_owns_lock(const SharedMutex& sm, const ceph::shunique_lock& sul, AcquireType) { } template static void check_owns_lock(const SharedMutex& sm, const ceph::shunique_lock& sul, ceph::acquire_unique_t) { ASSERT_TRUE(sul.mutex() == &sm); ASSERT_TRUE(sul.owns_lock()); ASSERT_TRUE(!!sul); } template static void check_owns_lock(const SharedMutex& sm, const ceph::shunique_lock& sul, ceph::acquire_shared_t) { ASSERT_TRUE(sul.owns_lock_shared()); ASSERT_TRUE(!!sul); } template static void check_abjures_lock(const SharedMutex& sm, const ceph::shunique_lock& sul) { ASSERT_EQ(sul.mutex(), &sm); ASSERT_FALSE(sul.owns_lock()); ASSERT_FALSE(sul.owns_lock_shared()); ASSERT_FALSE(!!sul); } template static void check_abjures_lock(const ceph::shunique_lock& sul) { ASSERT_EQ(sul.mutex(), nullptr); ASSERT_FALSE(sul.owns_lock()); ASSERT_FALSE(sul.owns_lock_shared()); ASSERT_FALSE(!!sul); } TEST(ShuniqueLock, DefaultConstructor) { typedef ceph::shunique_lock shunique_lock; shunique_lock l; ASSERT_EQ(l.mutex(), nullptr); ASSERT_FALSE(l.owns_lock()); ASSERT_FALSE(!!l); ASSERT_THROW(l.lock(), std::system_error); ASSERT_THROW(l.try_lock(), std::system_error); ASSERT_THROW(l.lock_shared(), std::system_error); ASSERT_THROW(l.try_lock_shared(), std::system_error); ASSERT_THROW(l.unlock(), std::system_error); ASSERT_EQ(l.mutex(), nullptr); ASSERT_FALSE(l.owns_lock()); ASSERT_FALSE(!!l); ASSERT_EQ(l.release(), nullptr); ASSERT_EQ(l.mutex(), nullptr); ASSERT_FALSE(l.owns_lock()); ASSERT_FALSE(l.owns_lock_shared()); ASSERT_FALSE(!!l); } template void lock_unlock(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; shunique_lock l(sm, at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); l.unlock(); check_abjures_lock(sm, l); ensure_free(sm); l.lock(at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } TEST(ShuniqueLock, LockUnlock) { lock_unlock(ceph::acquire_unique); lock_unlock(ceph::acquire_shared); } template void lock_destruct(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } ensure_free(sm); } TEST(ShuniqueLock, LockDestruct) { lock_destruct(ceph::acquire_unique); lock_destruct(ceph::acquire_shared); } template void move_construct(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); shunique_lock o(std::move(l)); check_abjures_lock(l); check_owns_lock(sm, o, at); ensure_conflicts(sm, at); o.unlock(); shunique_lock c(std::move(o)); ASSERT_EQ(o.mutex(), nullptr); ASSERT_FALSE(!!o); check_abjures_lock(sm, c); ensure_free(sm); } } TEST(ShuniqueLock, MoveConstruct) { move_construct(ceph::acquire_unique); move_construct(ceph::acquire_shared); boost::shared_mutex sm; { std::unique_lock ul(sm); ensure_conflicts(sm, ceph::acquire_unique); ceph::shunique_lock l(std::move(ul)); check_owns_lock(sm, l, ceph::acquire_unique); ensure_conflicts(sm, ceph::acquire_unique); } { std::unique_lock ul(sm, std::defer_lock); ensure_free(sm); ceph::shunique_lock l(std::move(ul)); check_abjures_lock(sm, l); ensure_free(sm); } { std::unique_lock ul; ceph::shunique_lock l(std::move(ul)); check_abjures_lock(l); } { boost::shared_lock sl(sm); ensure_conflicts(sm, ceph::acquire_shared); ceph::shunique_lock l(std::move(sl)); check_owns_lock(sm, l, ceph::acquire_shared); ensure_conflicts(sm, ceph::acquire_shared); } { boost::shared_lock sl; ceph::shunique_lock l(std::move(sl)); check_abjures_lock(l); } } template void move_assign(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); shunique_lock o; o = std::move(l); check_abjures_lock(l); check_owns_lock(sm, o, at); ensure_conflicts(sm, at); o.unlock(); shunique_lock c(std::move(o)); check_abjures_lock(o); check_abjures_lock(sm, c); ensure_free(sm); shunique_lock k; c = std::move(k); check_abjures_lock(k); check_abjures_lock(c); ensure_free(sm); } } TEST(ShuniqueLock, MoveAssign) { move_assign(ceph::acquire_unique); move_assign(ceph::acquire_shared); boost::shared_mutex sm; { std::unique_lock ul(sm); ensure_conflicts(sm, ceph::acquire_unique); ceph::shunique_lock l; l = std::move(ul); check_owns_lock(sm, l, ceph::acquire_unique); ensure_conflicts(sm, ceph::acquire_unique); } { std::unique_lock ul(sm, std::defer_lock); ensure_free(sm); ceph::shunique_lock l; l = std::move(ul); check_abjures_lock(sm, l); ensure_free(sm); } { std::unique_lock ul; ceph::shunique_lock l; l = std::move(ul); check_abjures_lock(l); } { boost::shared_lock sl(sm); ensure_conflicts(sm, ceph::acquire_shared); ceph::shunique_lock l; l = std::move(sl); check_owns_lock(sm, l, ceph::acquire_shared); ensure_conflicts(sm, ceph::acquire_shared); } { boost::shared_lock sl; ceph::shunique_lock l; l = std::move(sl); check_abjures_lock(l); } } template void construct_deferred(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, std::defer_lock); check_abjures_lock(sm, l); ensure_free(sm); ASSERT_THROW(l.unlock(), std::system_error); check_abjures_lock(sm, l); ensure_free(sm); l.lock(at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } { shunique_lock l(sm, std::defer_lock); check_abjures_lock(sm, l); ensure_free(sm); ASSERT_THROW(l.unlock(), std::system_error); check_abjures_lock(sm, l); ensure_free(sm); } ensure_free(sm); } TEST(ShuniqueLock, ConstructDeferred) { construct_deferred(ceph::acquire_unique); construct_deferred(ceph::acquire_shared); } template void construct_try(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, at, std::try_to_lock); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } { std::unique_lock l(sm); ensure_conflicts(sm, ceph::acquire_unique); std::async(std::launch::async, [&sm, at]() { shunique_lock l(sm, at, std::try_to_lock); check_abjures_lock(sm, l); ensure_conflicts(sm, ceph::acquire_unique); }).get(); l.unlock(); std::async(std::launch::async, [&sm, at]() { shunique_lock l(sm, at, std::try_to_lock); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); }).get(); } } TEST(ShuniqueLock, ConstructTry) { construct_try(ceph::acquire_unique); construct_try(ceph::acquire_shared); } template void construct_adopt(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock d(sm, at); d.release(); } ensure_conflicts(sm, at); { shunique_lock l(sm, at, std::adopt_lock); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } ensure_free(sm); } TEST(ShuniqueLock, ConstructAdopt) { construct_adopt(ceph::acquire_unique); construct_adopt(ceph::acquire_shared); } template void try_lock(AcquireType at) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, std::defer_lock); l.try_lock(at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); } { std::unique_lock l(sm); std::async(std::launch::async, [&sm, at]() { shunique_lock l(sm, std::defer_lock); l.try_lock(at); check_abjures_lock(sm, l); ensure_conflicts(sm, ceph::acquire_unique); }).get(); l.unlock(); std::async(std::launch::async, [&sm, at]() { shunique_lock l(sm, std::defer_lock); l.try_lock(at); check_owns_lock(sm, l, at); ensure_conflicts(sm, at); }).get(); } } TEST(ShuniqueLock, TryLock) { try_lock(ceph::acquire_unique); try_lock(ceph::acquire_shared); } TEST(ShuniqueLock, Release) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, ceph::acquire_unique); check_owns_lock(sm, l, ceph::acquire_unique); ensure_conflicts(sm, ceph::acquire_unique); l.release(); check_abjures_lock(l); ensure_conflicts(sm, ceph::acquire_unique); } ensure_conflicts(sm, ceph::acquire_unique); sm.unlock(); ensure_free(sm); { shunique_lock l(sm, ceph::acquire_shared); check_owns_lock(sm, l, ceph::acquire_shared); ensure_conflicts(sm, ceph::acquire_shared); l.release(); check_abjures_lock(l); ensure_conflicts(sm, ceph::acquire_shared); } ensure_conflicts(sm, ceph::acquire_shared); sm.unlock_shared(); ensure_free(sm); sm.lock(); { shunique_lock l(sm, std::defer_lock); check_abjures_lock(sm, l); ensure_conflicts(sm, ceph::acquire_unique); l.release(); check_abjures_lock(l); ensure_conflicts(sm, ceph::acquire_unique); } ensure_conflicts(sm, ceph::acquire_unique); sm.unlock(); ensure_free(sm); { std::unique_lock ul; shunique_lock l(sm, std::defer_lock); check_abjures_lock(sm, l); ensure_free(sm); ASSERT_NO_THROW(ul = l.release_to_unique()); check_abjures_lock(l); ASSERT_EQ(ul.mutex(), &sm); ASSERT_FALSE(ul.owns_lock()); ensure_free(sm); } ensure_free(sm); { std::unique_lock ul; shunique_lock l; check_abjures_lock(l); ASSERT_NO_THROW(ul = l.release_to_unique()); check_abjures_lock(l); ASSERT_EQ(ul.mutex(), nullptr); ASSERT_FALSE(ul.owns_lock()); } } TEST(ShuniqueLock, NoRecursion) { boost::shared_mutex sm; typedef ceph::shunique_lock shunique_lock; { shunique_lock l(sm, ceph::acquire_unique); ASSERT_THROW(l.lock(), std::system_error); ASSERT_THROW(l.try_lock(), std::system_error); ASSERT_THROW(l.lock_shared(), std::system_error); ASSERT_THROW(l.try_lock_shared(), std::system_error); } { shunique_lock l(sm, ceph::acquire_shared); ASSERT_THROW(l.lock(), std::system_error); ASSERT_THROW(l.try_lock(), std::system_error); ASSERT_THROW(l.lock_shared(), std::system_error); ASSERT_THROW(l.try_lock_shared(), std::system_error); } }