summaryrefslogtreecommitdiffstats
path: root/src/test/cls_lock/test_cls_lock.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/test/cls_lock/test_cls_lock.cc
parentInitial commit. (diff)
downloadceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz
ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/cls_lock/test_cls_lock.cc')
-rw-r--r--src/test/cls_lock/test_cls_lock.cc589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/test/cls_lock/test_cls_lock.cc b/src/test/cls_lock/test_cls_lock.cc
new file mode 100644
index 000000000..862720826
--- /dev/null
+++ b/src/test/cls_lock/test_cls_lock.cc
@@ -0,0 +1,589 @@
+// -*- 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.
+ *
+ */
+
+#include <iostream>
+#include <errno.h>
+
+#include "include/types.h"
+#include "common/Clock.h"
+#include "msg/msg_types.h"
+#include "include/rados/librados.hpp"
+
+#include "test/librados/test_cxx.h"
+#include "gtest/gtest.h"
+
+#include "cls/lock/cls_lock_client.h"
+#include "cls/lock/cls_lock_ops.h"
+
+using namespace std;
+using namespace librados;
+using namespace rados::cls::lock;
+
+void lock_info(IoCtx *ioctx, string& oid, string& name, map<locker_id_t, locker_info_t>& lockers,
+ ClsLockType *assert_type, string *assert_tag)
+{
+ ClsLockType lock_type = ClsLockType::NONE;
+ string tag;
+ lockers.clear();
+ ASSERT_EQ(0, get_lock_info(ioctx, oid, name, &lockers, &lock_type, &tag));
+ cout << "lock: " << name << std::endl;
+ cout << " lock_type: " << cls_lock_type_str(lock_type) << std::endl;
+ cout << " tag: " << tag << std::endl;
+ cout << " lockers:" << std::endl;
+
+ if (assert_type) {
+ ASSERT_EQ(*assert_type, lock_type);
+ }
+
+ if (assert_tag) {
+ ASSERT_EQ(*assert_tag, tag);
+ }
+
+ map<locker_id_t, locker_info_t>::iterator liter;
+ for (liter = lockers.begin(); liter != lockers.end(); ++liter) {
+ const locker_id_t& locker = liter->first;
+ cout << " " << locker.locker << " expiration=" << liter->second.expiration
+ << " addr=" << liter->second.addr << " cookie=" << locker.cookie << std::endl;
+ }
+}
+
+void lock_info(IoCtx *ioctx, string& oid, string& name, map<locker_id_t, locker_info_t>& lockers)
+{
+ lock_info(ioctx, oid, name, lockers, NULL, NULL);
+}
+
+bool lock_expired(IoCtx *ioctx, string& oid, string& name)
+{
+ ClsLockType lock_type = ClsLockType::NONE;
+ string tag;
+ map<locker_id_t, locker_info_t> lockers;
+ if (0 == get_lock_info(ioctx, oid, name, &lockers, &lock_type, &tag))
+ return false;
+ utime_t now = ceph_clock_now();
+ map<locker_id_t, locker_info_t>::iterator liter;
+ for (liter = lockers.begin(); liter != lockers.end(); ++liter) {
+ if (liter->second.expiration > now)
+ return false;
+ }
+
+ return true;
+}
+
+TEST(ClsLock, TestMultiLocking) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+ ClsLockType lock_type_shared = ClsLockType::SHARED;
+ ClsLockType lock_type_exclusive = ClsLockType::EXCLUSIVE;
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+ // we set the duration, so the log output contains a locker with a
+ // non-zero expiration time
+ l.set_duration(utime_t(120, 0));
+
+ /* test lock object */
+
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ /* test exclusive lock */
+ ASSERT_EQ(-EEXIST, l.lock_exclusive(&ioctx, oid));
+
+ /* test idempotency */
+ l.set_may_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ l.set_may_renew(false);
+
+ /* test second client */
+ Lock l2(lock_name);
+ ASSERT_EQ(-EBUSY, l2.lock_exclusive(&ioctx2, oid));
+ ASSERT_EQ(-EBUSY, l2.lock_shared(&ioctx2, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ ASSERT_EQ(1, (int)locks.size());
+ list<string>::iterator iter = locks.begin();
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, *iter, lockers, &lock_type_exclusive, NULL);
+
+ ASSERT_EQ(1, (int)lockers.size());
+
+ /* test unlock */
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+ locks.clear();
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ /* test shared lock */
+ ASSERT_EQ(0, l2.lock_shared(&ioctx2, oid));
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ locks.clear();
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+ ASSERT_EQ(1, (int)locks.size());
+ iter = locks.begin();
+ lock_info(&ioctx, oid, *iter, lockers, &lock_type_shared, NULL);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ /* test break locks */
+ entity_name_t name = entity_name_t::CLIENT(cluster.get_instance_id());
+ entity_name_t name2 = entity_name_t::CLIENT(cluster2.get_instance_id());
+
+ l2.break_lock(&ioctx2, oid, name);
+ lock_info(&ioctx, oid, *iter, lockers);
+ ASSERT_EQ(1, (int)lockers.size());
+ map<locker_id_t, locker_info_t>::iterator liter = lockers.begin();
+ const locker_id_t& id = liter->first;
+ ASSERT_EQ(name2, id.locker);
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(&ioctx, oid));
+
+
+ /* test modify description */
+ string description = "new description";
+ l.set_description(description);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMeta) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(&ioctx2, oid));
+
+
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ /* test description */
+ Lock l2(lock_name);
+ string description = "new description";
+ l2.set_description(description);
+ ASSERT_EQ(0, l2.lock_shared(&ioctx2, oid));
+
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, lock_name, lockers, NULL, NULL);
+ ASSERT_EQ(1, (int)lockers.size());
+
+ map<locker_id_t, locker_info_t>::iterator iter = lockers.begin();
+ locker_info_t locker = iter->second;
+ ASSERT_EQ("new description", locker.description);
+
+ ASSERT_EQ(0, l2.unlock(&ioctx2, oid));
+
+ /* check new tag */
+ string new_tag = "new_tag";
+ l.set_tag(new_tag);
+ l.set_may_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+ lock_info(&ioctx, oid, lock_name, lockers, NULL, &new_tag);
+ ASSERT_EQ(1, (int)lockers.size());
+ l.set_tag("");
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(&ioctx, oid));
+ l.set_tag(new_tag);
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string lock_name = "mylock";
+ Lock l(lock_name);
+
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ /* new cookie */
+ string cookie = "new cookie";
+ l.set_cookie(cookie);
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(&ioctx, oid));
+ ASSERT_EQ(-ENOENT, l.unlock(&ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ map<locker_id_t, locker_info_t> lockers;
+ lock_info(&ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(0, (int)lockers.size());
+
+ l.set_cookie(cookie);
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.lock_shared(&ioctx, oid));
+
+ lock_info(&ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMultipleLocks) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ Lock l2("lock2");
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(&ioctx, oid, &locks));
+
+ ASSERT_EQ(2, (int)locks.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestLockDuration) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock");
+ utime_t dur(5, 0);
+ l.set_duration(dur);
+ utime_t start = ceph_clock_now();
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+ int r = l.lock_exclusive(&ioctx, oid);
+ if (r == 0) {
+ // it's possible to get success if we were just really slow...
+ ASSERT_TRUE(ceph_clock_now() > start + dur);
+ } else {
+ ASSERT_EQ(-EEXIST, r);
+ }
+
+ sleep(dur.sec());
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestAssertLocked) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(&ioctx, oid));
+
+ librados::ObjectWriteOperation op1;
+ l.assert_locked_exclusive(&op1);
+ ASSERT_EQ(0, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ l.assert_locked_shared(&op2);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op2));
+
+ l.set_tag("tag");
+ librados::ObjectWriteOperation op3;
+ l.assert_locked_exclusive(&op3);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op3));
+ l.set_tag("");
+
+ l.set_cookie("cookie");
+ librados::ObjectWriteOperation op4;
+ l.assert_locked_exclusive(&op4);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+ l.set_cookie("");
+
+ ASSERT_EQ(0, l.unlock(&ioctx, oid));
+
+ librados::ObjectWriteOperation op5;
+ l.assert_locked_exclusive(&op5);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestSetCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string name = "name";
+ string tag = "tag";
+ string cookie = "cookie";
+ string new_cookie = "new cookie";
+ librados::ObjectWriteOperation op1;
+ set_cookie(&op1, name, ClsLockType::SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(-ENOENT, ioctx.operate(oid, &op1));
+
+ librados::ObjectWriteOperation op2;
+ lock(&op2, name, ClsLockType::SHARED, cookie, tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op2));
+
+ librados::ObjectWriteOperation op3;
+ lock(&op3, name, ClsLockType::SHARED, "cookie 2", tag, "", utime_t{}, 0);
+ ASSERT_EQ(0, ioctx.operate(oid, &op3));
+
+ librados::ObjectWriteOperation op4;
+ set_cookie(&op4, name, ClsLockType::SHARED, cookie, tag, cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op4));
+
+ librados::ObjectWriteOperation op5;
+ set_cookie(&op5, name, ClsLockType::SHARED, cookie, "wrong tag", new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op5));
+
+ librados::ObjectWriteOperation op6;
+ set_cookie(&op6, name, ClsLockType::SHARED, "wrong cookie", tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op6));
+
+ librados::ObjectWriteOperation op7;
+ set_cookie(&op7, name, ClsLockType::EXCLUSIVE, cookie, tag, new_cookie);
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op7));
+
+ librados::ObjectWriteOperation op8;
+ set_cookie(&op8, name, ClsLockType::SHARED, cookie, tag, "cookie 2");
+ ASSERT_EQ(-EBUSY, ioctx.operate(oid, &op8));
+
+ librados::ObjectWriteOperation op9;
+ set_cookie(&op9, name, ClsLockType::SHARED, cookie, tag, new_cookie);
+ ASSERT_EQ(0, ioctx.operate(oid, &op9));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestRenew) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ ASSERT_EQ(0, ioctx.write(oid1, bl, bl.length(), 0));
+
+ Lock l1(lock_name1);
+ utime_t lock_duration1(5, 0);
+ l1.set_duration(lock_duration1);
+
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1));
+ l1.set_may_renew(true);
+ sleep(2);
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1));
+ sleep(7);
+ ASSERT_EQ(0, l1.lock_exclusive(&ioctx, oid1)) <<
+ "when a cls_lock is set to may_renew, a relock after expiration "
+ "should still work";
+ ASSERT_EQ(0, l1.unlock(&ioctx, oid1));
+
+ // ***********************************************
+
+ string oid2 = "foo2";
+ string lock_name2 = "mylock2";
+
+ ASSERT_EQ(0, ioctx.write(oid2, bl, bl.length(), 0));
+
+ Lock l2(lock_name2);
+ utime_t lock_duration2(5, 0);
+ l2.set_duration(lock_duration2);
+
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ l2.set_must_renew(true);
+ sleep(2);
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ sleep(7);
+ ASSERT_EQ(-ENOENT, l2.lock_exclusive(&ioctx, oid2)) <<
+ "when a cls_lock is set to must_renew, a relock after expiration "
+ "should fail";
+ ASSERT_EQ(-ENOENT, l2.unlock(&ioctx, oid2));
+
+ // ***********************************************
+
+ string oid3 = "foo3";
+ string lock_name3 = "mylock3";
+
+ ASSERT_EQ(0, ioctx.write(oid3, bl, bl.length(), 0));
+
+ Lock l3(lock_name3);
+ l3.set_duration(utime_t(5, 0));
+ l3.set_must_renew(true);
+
+ ASSERT_EQ(-ENOENT, l3.lock_exclusive(&ioctx, oid3)) <<
+ "unable to create a lock with must_renew";
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestExclusiveEphemeralBasic) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string oid2 = "foo2";
+ string lock_name1 = "mylock1";
+ string lock_name2 = "mylock2";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(5, 0));
+
+ uint64_t size;
+ time_t mod_time;
+
+ l1.set_may_renew(true);
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ ASSERT_EQ(0, ioctx.stat(oid1, &size, &mod_time));
+ sleep(2);
+ int r1 = l1.unlock(&ioctx, oid1);
+ EXPECT_TRUE(r1 == 0 || ((r1 == -ENOENT) && (lock_expired(&ioctx, oid1, lock_name1))))
+ << "unlock should return 0 or -ENOENT return: " << r1;
+ ASSERT_EQ(-ENOENT, ioctx.stat(oid1, &size, &mod_time));
+
+ // ***********************************************
+
+ Lock l2(lock_name2);
+ utime_t lock_duration2(5, 0);
+ l2.set_duration(utime_t(5, 0));
+
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid2));
+ ASSERT_EQ(0, ioctx.stat(oid2, &size, &mod_time));
+ sleep(2);
+ int r2 = l2.unlock(&ioctx, oid2);
+ EXPECT_TRUE(r2 == 0 || ((r2 == -ENOENT) && (lock_expired(&ioctx, oid2, lock_name2))))
+ << "unlock should return 0 or -ENOENT return: " << r2;
+ ASSERT_EQ(0, ioctx.stat(oid2, &size, &mod_time));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+
+TEST(ClsLock, TestExclusiveEphemeralStealEphemeral) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(3, 0));
+
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(4);
+
+ // l1 is expired, l2 can take; l2 is also exclusive_ephemeral
+ Lock l2(lock_name1);
+ l2.set_duration(utime_t(3, 0));
+ ASSERT_EQ(0, l2.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(1);
+ ASSERT_EQ(0, l2.unlock(&ioctx, oid1));
+
+ // l2 cannot unlock its expired lock
+ ASSERT_EQ(-ENOENT, l1.unlock(&ioctx, oid1));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+
+TEST(ClsLock, TestExclusiveEphemeralStealExclusive) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ bufferlist bl;
+
+ string oid1 = "foo1";
+ string lock_name1 = "mylock1";
+
+ Lock l1(lock_name1);
+ l1.set_duration(utime_t(3, 0));
+
+ ASSERT_EQ(0, l1.lock_exclusive_ephemeral(&ioctx, oid1));
+ sleep(4);
+
+ // l1 is expired, l2 can take; l2 is exclusive (but not ephemeral)
+ Lock l2(lock_name1);
+ l2.set_duration(utime_t(3, 0));
+ ASSERT_EQ(0, l2.lock_exclusive(&ioctx, oid1));
+ sleep(1);
+ ASSERT_EQ(0, l2.unlock(&ioctx, oid1));
+
+ // l2 cannot unlock its expired lock
+ ASSERT_EQ(-ENOENT, l1.unlock(&ioctx, oid1));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}