From 7b7f752c69299d8d9623892a1480ddc6a71b3ccc Mon Sep 17 00:00:00 2001 From: Loic Dachary Date: Mon, 8 Jul 2013 16:18:08 +0200 Subject: [PATCH] unit tests for ObjectContext read/write locks unit tests for the ObjectContext methods ondisk_write_lock, ondisk_write_unlock, ondisk_read_lock and ondisk_read_unlock. A class derived from ::testing::Test is created with two sub-classes ( Thread_read_lock & Thread_write_lock ) to provide a separate thread that can block with cond.Wait(). usleep(3) is used in the main thread to wait for the expected side effect with increasing delays ( up to MAX_DELAY ). http://tracker.ceph.com/issues/5487 refs #5487 Signed-off-by: Loic Dachary --- src/test/test_osd_types.cc | 205 +++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) diff --git a/src/test/test_osd_types.cc b/src/test/test_osd_types.cc index c0a9a95b11a7e..80f3ebeb21a74 100644 --- a/src/test/test_osd_types.cc +++ b/src/test/test_osd_types.cc @@ -18,6 +18,7 @@ #include "include/types.h" #include "osd/osd_types.h" #include "gtest/gtest.h" +#include "common/Thread.h" #include @@ -551,6 +552,210 @@ TEST(pg_missing_t, split_into) EXPECT_TRUE(missing.is_missing(oid2)); } +class ObjectContextTest : public ::testing::Test { +protected: + + static const useconds_t DELAY_MAX = 20 * 1000 * 1000; + + class Thread_read_lock : public Thread { + public: + ObjectContext &obc; + + Thread_read_lock(ObjectContext& _obc) : + obc(_obc) + { + } + + virtual void *entry() { + obc.ondisk_read_lock(); + return NULL; + } + }; + + class Thread_write_lock : public Thread { + public: + ObjectContext &obc; + + Thread_write_lock(ObjectContext& _obc) : + obc(_obc) + { + } + + virtual void *entry() { + obc.ondisk_write_lock(); + return NULL; + } + }; + +}; + +TEST_F(ObjectContextTest, read_write_lock) +{ + { + object_info_t oi; + ObjectContext obc(oi, false, NULL); + + // + // write_lock + // write_lock + // write_unlock + // write_unlock + // + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(2, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + } + + useconds_t delay = 0; + + { + object_info_t oi; + ObjectContext obc(oi, false, NULL); + + // + // write_lock + // read_lock => wait + // write_unlock => signal + // read_unlock + // + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_write_lock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + Thread_read_lock t(obc); + t.create(); + + do { + cout << "Trying (1) with delay " << delay << "us\n"; + usleep(delay); + } while (obc.readers_waiting == 0 && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(1, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(1, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + do { + cout << "Trying (2) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.readers == 0 || obc.readers_waiting == 1) && + ( delay = delay * 2 + 1) < DELAY_MAX); + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_unlock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + t.join(); + } + + { + object_info_t oi; + ObjectContext obc(oi, false, NULL); + + // + // read_lock + // write_lock => wait + // read_unlock => signal + // write_unlock + // + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_lock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + Thread_write_lock t(obc); + t.create(); + + do { + cout << "Trying (3) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.writers_waiting == 0) && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(1, obc.readers); + EXPECT_EQ(1, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + obc.ondisk_read_unlock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(1, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + do { + cout << "Trying (4) with delay " << delay << "us\n"; + usleep(delay); + } while ((obc.unstable_writes == 0 || obc.writers_waiting == 1) && + ( delay = delay * 2 + 1) < DELAY_MAX); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(1, obc.unstable_writes); + + obc.ondisk_write_unlock(); + + EXPECT_EQ(0, obc.readers_waiting); + EXPECT_EQ(0, obc.readers); + EXPECT_EQ(0, obc.writers_waiting); + EXPECT_EQ(0, obc.unstable_writes); + + t.join(); + } + +} + // Local Variables: // compile-command: "cd .. ; make unittest_osd_types ; ./unittest_osd_types # --gtest_filter=pg_missing_t.constructor " // End: -- 2.39.5