]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
unit tests for ObjectContext read/write locks 407/head
authorLoic Dachary <loic@dachary.org>
Mon, 8 Jul 2013 14:18:08 +0000 (16:18 +0200)
committerLoic Dachary <loic@dachary.org>
Mon, 8 Jul 2013 14:45:12 +0000 (16:45 +0200)
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 <loic@dachary.org>
src/test/test_osd_types.cc

index c0a9a95b11a7e5bc8e0b642ad2719bbed698f5c8..80f3ebeb21a7465353d6a6335dd3ab11a73d0d71 100644 (file)
@@ -18,6 +18,7 @@
 #include "include/types.h"
 #include "osd/osd_types.h"
 #include "gtest/gtest.h"
+#include "common/Thread.h"
 
 #include <sstream>
 
@@ -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: