]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test: separate testing of exclusive lock from managed lock
authorJason Dillaman <dillaman@redhat.com>
Wed, 18 Jan 2017 15:44:52 +0000 (10:44 -0500)
committerJason Dillaman <dillaman@redhat.com>
Wed, 18 Jan 2017 21:44:43 +0000 (16:44 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/ExclusiveLock.cc
src/librbd/ManagedLock.cc
src/librbd/ManagedLock.h
src/test/librbd/test_mock_ExclusiveLock.cc

index 7d8e19ba296224feadc618ea44a8d0ef1fb186b7..b500a980e1407f13f31d34d5baad9f1bb95a3772 100644 (file)
@@ -14,7 +14,8 @@
 
 #define dout_subsys ceph_subsys_rbd
 #undef dout_prefix
-#define dout_prefix *_dout << "librbd::ExclusiveLock: " << this << " " <<  __func__
+#define dout_prefix *_dout << "librbd::ExclusiveLock: " << this << " " \
+                           <<  __func__
 
 namespace librbd {
 
@@ -30,16 +31,17 @@ ExclusiveLock<I>::ExclusiveLock(I &image_ctx)
           image_ctx.blacklist_on_break_lock,
           image_ctx.blacklist_expire_seconds),
     m_image_ctx(image_ctx), m_pre_post_callback(nullptr),
-    m_shutting_down(false)  {
-  ML<I>::m_state = ML<I>::STATE_UNINITIALIZED;
+    m_shutting_down(false) {
+  Mutex::Locker locker(ML<I>::m_lock);
+  ML<I>::set_state_uninitialized();
 }
 
 template <typename I>
 bool ExclusiveLock<I>::accept_requests(int *ret_val) const {
   Mutex::Locker locker(ML<I>::m_lock);
 
-  bool accept_requests = (!ML<I>::is_shutdown_locked() &&
-                          ML<I>::m_state == ML<I>::STATE_LOCKED &&
+  bool accept_requests = (!ML<I>::is_state_shutdown() &&
+                          ML<I>::is_state_locked() &&
                           m_request_blocked_count == 0);
   *ret_val = m_request_blocked_ret_val;
 
@@ -79,8 +81,7 @@ void ExclusiveLock<I>::init(uint64_t features, Context *on_init) {
 
   {
     Mutex::Locker locker(ML<I>::m_lock);
-    assert(ML<I>::m_state == ML<I>::STATE_UNINITIALIZED);
-    ML<I>::m_state = ML<I>::STATE_INITIALIZING;
+    ML<I>::set_state_initializing();
   }
 
   m_image_ctx.aio_work_queue->block_writes(new C_InitComplete(this, on_init));
@@ -102,12 +103,12 @@ void ExclusiveLock<I>::shut_down(Context *on_shut_down) {
 template <typename I>
 void ExclusiveLock<I>::handle_peer_notification(int r) {
   Mutex::Locker locker(ML<I>::m_lock);
-  if (ML<I>::m_state != ML<I>::STATE_WAITING_FOR_LOCK) {
+  if (!ML<I>::is_state_waiting_for_lock()) {
     return;
   }
 
   ldout(m_image_ctx.cct, 10) << dendl;
-  assert(ML<I>::get_active_action() == ML<I>::ACTION_ACQUIRE_LOCK);
+  assert(ML<I>::is_action_acquire_lock());
 
   m_acquire_lock_peer_ret_val = r;
   ML<I>::execute_next_action();
@@ -118,7 +119,7 @@ void ExclusiveLock<I>::handle_init_complete() {
   ldout(m_image_ctx.cct, 10) << dendl;
 
   Mutex::Locker locker(ML<I>::m_lock);
-  ML<I>::m_state = ML<I>::STATE_UNLOCKED;
+  ML<I>::set_state_unlocked();
 }
 
 template <typename I>
@@ -137,6 +138,8 @@ void ExclusiveLock<I>::shutdown_handler(int r, Context *on_finish) {
 
 template <typename I>
 void ExclusiveLock<I>::pre_acquire_lock_handler(Context *on_finish) {
+  ldout(m_image_ctx.cct, 10) << dendl;
+
   int acquire_lock_peer_ret_val = 0;
   {
     Mutex::Locker locker(ML<I>::m_lock);
@@ -166,24 +169,28 @@ void ExclusiveLock<I>::post_acquire_lock_handler(int r, Context *on_finish) {
     return;
   } else if (r < 0) {
     ML<I>::m_lock.Lock();
-    assert(ML<I>::m_state == ML<I>::STATE_ACQUIRING);
+    assert(ML<I>::is_state_acquiring());
 
     // PostAcquire state machine will not run, so we need complete prepare
     m_image_ctx.state->handle_prepare_lock_complete();
 
-    typename ML<I>::Action action = ML<I>::get_active_action();
-    if (action == ML<I>::ACTION_ACQUIRE_LOCK && r < 0 && r != -EBLACKLISTED) {
-      ML<I>::m_state = ML<I>::STATE_WAITING_FOR_LOCK;
+    // if lock is in-use by another client, request the lock
+    if (ML<I>::is_action_acquire_lock() && (r == -EBUSY || r == -EAGAIN)) {
+      ML<I>::set_state_waiting_for_lock();
       ML<I>::m_lock.Unlock();
 
       // request the lock from a peer
       m_image_ctx.image_watcher->notify_request_lock();
-      return;
-    }
 
-    ML<I>::m_lock.Unlock();
-    if (r == -EAGAIN) {
-      r = 0;
+      // inform manage lock that we have interrupted the state machine
+      r = -ECANCELED;
+    } else {
+      ML<I>::m_lock.Unlock();
+
+      // clear error if peer owns lock
+      if (r == -EAGAIN) {
+        r = 0;
+      }
     }
 
     on_finish->complete(r);
@@ -209,10 +216,9 @@ void ExclusiveLock<I>::handle_post_acquiring_lock(int r) {
   Mutex::Locker locker(ML<I>::m_lock);
 
   assert(r == 0);
-  assert(ML<I>::m_state == ML<I>::STATE_ACQUIRING);
 
   // lock is owned at this point
-  ML<I>::m_state = ML<I>::STATE_POST_ACQUIRING;
+  ML<I>::set_state_post_acquiring();
 }
 
 template <typename I>
@@ -222,8 +228,7 @@ void ExclusiveLock<I>::handle_post_acquired_lock(int r) {
   Context *on_finish = nullptr;
   {
     Mutex::Locker locker(ML<I>::m_lock);
-    assert(ML<I>::m_state == ML<I>::STATE_ACQUIRING ||
-           ML<I>::m_state == ML<I>::STATE_POST_ACQUIRING);
+    assert(ML<I>::is_state_acquiring() || ML<I>::is_state_post_acquiring());
 
     assert (m_pre_post_callback != nullptr);
     std::swap(m_pre_post_callback, on_finish);
@@ -263,29 +268,24 @@ void ExclusiveLock<I>::handle_pre_releasing_lock(int r) {
   Mutex::Locker locker(ML<I>::m_lock);
 
   assert(r == 0);
-  if (!m_shutting_down) {
-    assert(ML<I>::m_state == ML<I>::STATE_PRE_RELEASING);
 
-    // all IO and ops should be blocked/canceled by this point
-    ML<I>::m_state = ML<I>::STATE_RELEASING;
+  // all IO and ops should be blocked/canceled by this point
+  if (!m_shutting_down) {
+    ML<I>::set_state_releasing();
   } else {
-    assert(ML<I>::m_state == ML<I>::STATE_PRE_SHUTTING_DOWN);
-
-    // all IO and ops should be blocked/canceled by this point
-    ML<I>::m_state = ML<I>::STATE_SHUTTING_DOWN;
+    ML<I>::set_state_shutting_down();
   }
 }
 
 template <typename I>
 void ExclusiveLock<I>::post_release_lock_handler(bool shutting_down, int r,
-                                                   Context *on_finish) {
+                                                 Context *on_finish) {
   ldout(m_image_ctx.cct, 10) << ": r=" << r << " shutting_down="
                              << shutting_down << dendl;
   if (!shutting_down) {
     {
       Mutex::Locker locker(ML<I>::m_lock);
-      assert(ML<I>::m_state == ML<I>::STATE_PRE_RELEASING ||
-             ML<I>::m_state == ML<I>::STATE_RELEASING);
+      assert(ML<I>::is_state_pre_releasing() || ML<I>::is_state_releasing());
     }
 
     if (r >= 0) {
index 67504a9b3eceb92796dfd03f2bc82403f1c3bb0a..5673694b01629d2650c6f957774db8a516700a67 100644 (file)
@@ -104,7 +104,7 @@ void ManagedLock<I>::shut_down(Context *on_shut_down) {
   ldout(m_cct, 10) << dendl;
 
   Mutex::Locker locker(m_lock);
-  assert(!is_shutdown_locked());
+  assert(!is_state_shutdown());
   execute_action(ACTION_SHUT_DOWN, on_shut_down);
 }
 
@@ -113,7 +113,7 @@ void ManagedLock<I>::acquire_lock(Context *on_acquired) {
   int r = 0;
   {
     Mutex::Locker locker(m_lock);
-    if (is_shutdown_locked()) {
+    if (is_state_shutdown()) {
       r = -ESHUTDOWN;
     } else if (m_state != STATE_LOCKED || !m_actions_contexts.empty()) {
       ldout(m_cct, 10) << dendl;
@@ -132,7 +132,7 @@ void ManagedLock<I>::try_acquire_lock(Context *on_acquired) {
   int r = 0;
   {
     Mutex::Locker locker(m_lock);
-    if (is_shutdown_locked()) {
+    if (is_state_shutdown()) {
       r = -ESHUTDOWN;
     } else if (m_state != STATE_LOCKED || !m_actions_contexts.empty()) {
       ldout(m_cct, 10) << dendl;
@@ -151,7 +151,7 @@ void ManagedLock<I>::release_lock(Context *on_released) {
   int r = 0;
   {
     Mutex::Locker locker(m_lock);
-    if (is_shutdown_locked()) {
+    if (is_state_shutdown()) {
       r = -ESHUTDOWN;
     } else if (m_state != STATE_UNLOCKED || !m_actions_contexts.empty()) {
       ldout(m_cct, 10) << dendl;
@@ -177,7 +177,7 @@ void ManagedLock<I>::reacquire_lock(Context *on_reacquired) {
       assert(active_action == ACTION_TRY_LOCK ||
              active_action == ACTION_ACQUIRE_LOCK);
       execute_next_action();
-    } else if (!is_shutdown_locked() &&
+    } else if (!is_state_shutdown() &&
                (m_state == STATE_LOCKED ||
                 m_state == STATE_ACQUIRING ||
                 m_state == STATE_POST_ACQUIRING ||
@@ -358,7 +358,7 @@ void ManagedLock<I>::complete_active_action(State next_state, int r) {
 }
 
 template <typename I>
-bool ManagedLock<I>::is_shutdown_locked() const {
+bool ManagedLock<I>::is_state_shutdown() const {
   assert(m_lock.is_locked());
 
   return ((m_state == STATE_SHUTDOWN) ||
@@ -439,7 +439,8 @@ void ManagedLock<I>::handle_post_acquire_lock(int r) {
   if (r < 0 && m_post_next_state == STATE_LOCKED) {
     // release_lock without calling pre and post handlers
     revert_to_unlock_state(r);
-  } else {
+  } else if (r != -ECANCELED) {
+    // fail the lock request
     complete_active_action(m_post_next_state, r);
   }
 }
@@ -511,7 +512,7 @@ void ManagedLock<I>::handle_reacquire_lock(int r) {
                    << dendl;
     }
 
-    if (!is_shutdown_locked()) {
+    if (!is_state_shutdown()) {
       // queue a release and re-acquire of the lock since cookie cannot
       // be updated on older OSDs
       execute_action(ACTION_RELEASE_LOCK, nullptr);
index baa28fdd4dccc3088eae9f8069acb0c3932c42b8..7cbc295a93665aa521e675ee3cf0ebf6ad933d5c 100644 (file)
@@ -58,15 +58,90 @@ public:
 
   bool is_shutdown() const {
     Mutex::Locker l(m_lock);
-    return is_shutdown_locked();
+    return is_state_shutdown();
   }
 
-  bool is_locked_state() const {
+protected:
+  mutable Mutex m_lock;
+
+  inline void set_state_uninitialized() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_UNLOCKED);
+    m_state = STATE_UNINITIALIZED;
+  }
+  inline void set_state_initializing() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_UNINITIALIZED);
+    m_state = STATE_INITIALIZING;
+  }
+  inline void set_state_unlocked() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_INITIALIZING || m_state == STATE_RELEASING);
+    m_state = STATE_UNLOCKED;
+  }
+  inline void set_state_waiting_for_lock() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_ACQUIRING);
+    m_state = STATE_WAITING_FOR_LOCK;
+  }
+  inline void set_state_post_acquiring() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_ACQUIRING);
+    m_state = STATE_POST_ACQUIRING;
+  }
+  inline void set_state_releasing() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_PRE_RELEASING);
+    m_state = STATE_RELEASING;
+  }
+  inline void set_state_shutting_down() {
+    assert(m_lock.is_locked());
+    assert(m_state == STATE_PRE_SHUTTING_DOWN);
+    m_state = STATE_SHUTTING_DOWN;
+  }
+
+  bool is_state_shutdown() const;
+  inline bool is_state_acquiring() const {
+    assert(m_lock.is_locked());
+    return m_state == STATE_ACQUIRING;
+  }
+  inline bool is_state_post_acquiring() const {
+    assert(m_lock.is_locked());
+    return m_state == STATE_POST_ACQUIRING;
+  }
+  inline bool is_state_releasing() const {
+    assert(m_lock.is_locked());
+    return m_state == STATE_RELEASING;
+  }
+  inline bool is_state_pre_releasing() const {
+    assert(m_lock.is_locked());
+    return m_state == STATE_PRE_RELEASING;
+  }
+  inline bool is_state_locked() const {
+    assert(m_lock.is_locked());
     return m_state == STATE_LOCKED;
   }
+  inline bool is_state_waiting_for_lock() {
+    assert(m_lock.is_locked());
+    return m_state == STATE_WAITING_FOR_LOCK;
+  }
 
-protected:
+  inline bool is_action_acquire_lock() {
+    assert(m_lock.is_locked());
+    return get_active_action() == ACTION_ACQUIRE_LOCK;
+  }
+
+  virtual void shutdown_handler(int r, Context *on_finish);
+  virtual void pre_acquire_lock_handler(Context *on_finish);
+  virtual void post_acquire_lock_handler(int r, Context *on_finish);
+  virtual void pre_release_lock_handler(bool shutting_down,
+                                        Context *on_finish);
+  virtual void post_release_lock_handler(bool shutting_down, int r,
+                                          Context *on_finish);
 
+  void execute_next_action();
+
+private:
   /**
    * @verbatim
    *
@@ -124,22 +199,6 @@ protected:
     ACTION_SHUT_DOWN
   };
 
-  mutable Mutex m_lock;
-  State m_state;
-
-  virtual void shutdown_handler(int r, Context *on_finish);
-  virtual void pre_acquire_lock_handler(Context *on_finish);
-  virtual void post_acquire_lock_handler(int r, Context *on_finish);
-  virtual void pre_release_lock_handler(bool shutting_down,
-                                        Context *on_finish);
-  virtual void post_release_lock_handler(bool shutting_down, int r,
-                                          Context *on_finish);
-
-  Action get_active_action() const;
-  bool is_shutdown_locked() const;
-  void execute_next_action();
-
-private:
   typedef std::list<Context *> Contexts;
   typedef std::pair<Action, Contexts> ActionContexts;
   typedef std::list<ActionContexts> ActionsContexts;
@@ -166,6 +225,7 @@ private:
   std::string m_cookie;
   std::string m_new_cookie;
 
+  State m_state;
   State m_post_next_state;
 
   ActionsContexts m_actions_contexts;
@@ -176,6 +236,7 @@ private:
   void append_context(Action action, Context *ctx);
   void execute_action(Action action, Context *ctx);
 
+  Action get_active_action() const;
   void complete_active_action(State next_state, int r);
 
 
index 85b5570f95f2aa7e5c7e4cb945c23de6439838ed..cf046f5442990d8e944ec44864f1412957101e98 100644 (file)
@@ -9,9 +9,6 @@
 #include "librbd/exclusive_lock/PreAcquireRequest.h"
 #include "librbd/exclusive_lock/PostAcquireRequest.h"
 #include "librbd/exclusive_lock/PreReleaseRequest.h"
-#include "librbd/managed_lock/AcquireRequest.h"
-#include "librbd/managed_lock/ReacquireRequest.h"
-#include "librbd/managed_lock/ReleaseRequest.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include <list>
@@ -37,6 +34,51 @@ struct Traits<MockExclusiveLockImageCtx> {
 };
 }
 
+template <>
+struct ManagedLock<MockExclusiveLockImageCtx> {
+  ManagedLock(librados::IoCtx& ioctx, ContextWQ *work_queue,
+              const std::string& oid, librbd::MockImageWatcher *watcher,
+              managed_lock::Mode  mode, bool blacklist_on_break_lock,
+              uint32_t blacklist_expire_seconds)
+    : m_lock("ManagedLock::m_lock") {
+  }
+
+  mutable Mutex m_lock;
+
+  virtual void shutdown_handler(int r, Context *) = 0;
+  virtual void pre_acquire_lock_handler(Context *) = 0;
+  virtual void post_acquire_lock_handler(int, Context *) = 0;
+  virtual void pre_release_lock_handler(bool, Context *) = 0;
+  virtual void post_release_lock_handler(bool, int, Context *) = 0;
+
+  MOCK_CONST_METHOD0(is_lock_owner, bool());
+
+  MOCK_METHOD1(shut_down, void(Context*));
+  MOCK_METHOD1(acquire_lock, void(Context*));
+
+  void set_state_uninitialized() {
+  }
+
+  MOCK_METHOD0(set_state_initializing, void());
+  MOCK_METHOD0(set_state_unlocked, void());
+  MOCK_METHOD0(set_state_waiting_for_lock, void());
+  MOCK_METHOD0(set_state_post_acquiring, void());
+  MOCK_METHOD0(set_state_releasing, void());
+  MOCK_METHOD0(set_state_shutting_down, void());
+
+  MOCK_CONST_METHOD0(is_state_shutdown, bool());
+  MOCK_CONST_METHOD0(is_state_acquiring, bool());
+  MOCK_CONST_METHOD0(is_state_post_acquiring, bool());
+  MOCK_CONST_METHOD0(is_state_releasing, bool());
+  MOCK_CONST_METHOD0(is_state_pre_releasing, bool());
+  MOCK_CONST_METHOD0(is_state_locked, bool());
+  MOCK_CONST_METHOD0(is_state_waiting_for_lock, bool());
+
+  MOCK_CONST_METHOD0(is_action_acquire_lock, bool());
+  MOCK_METHOD0(execute_next_action, void());
+
+};
+
 namespace exclusive_lock {
 
 using librbd::ImageWatcher;
@@ -86,82 +128,12 @@ struct PreReleaseRequest<MockExclusiveLockImageCtx> : public BaseRequest<PreRele
 };
 
 } // namespace exclusive_lock
-
-namespace managed_lock {
-
-template<typename T>
-struct BaseRequest {
-  static std::list<T *> s_requests;
-  Context *on_finish = nullptr;
-
-  static T* create(librados::IoCtx& ioctx, MockImageWatcher *watcher,
-                   ContextWQ *work_queue, const std::string& oid,
-                   const std::string& cookie, Context *on_finish) {
-    assert(!s_requests.empty());
-    T* req = s_requests.front();
-    req->on_finish = on_finish;
-    s_requests.pop_front();
-    return req;
-  }
-
-  BaseRequest() {
-    s_requests.push_back(reinterpret_cast<T*>(this));
-  }
-};
-
-template<typename T>
-std::list<T *> BaseRequest<T>::s_requests;
-
-template <>
-struct AcquireRequest<MockExclusiveLockImageCtx> : public BaseRequest<AcquireRequest<MockExclusiveLockImageCtx> > {
-  static AcquireRequest* create(librados::IoCtx& ioctx,
-                                MockImageWatcher *watcher,
-                                ContextWQ *work_queue, const std::string& oid,
-                                const std::string& cookie, bool exclusive,
-                                bool blacklist_on_break_lock,
-                                uint32_t blacklist_expire_seconds,
-                                Context *on_finish) {
-    return BaseRequest::create(ioctx, watcher, work_queue, oid, cookie,
-                               on_finish);
-  }
-  MOCK_METHOD0(send, void());
-};
-
-template <>
-struct ReacquireRequest<MockExclusiveLockImageCtx> : public BaseRequest<ReacquireRequest<MockExclusiveLockImageCtx> > {
-  static ReacquireRequest* create(librados::IoCtx &ioctx, const std::string& oid,
-                                const string& old_cookie, const std::string& new_cookie,
-                                bool exclusive, Context *on_finish) {
-    return BaseRequest::create(ioctx, nullptr, nullptr, oid, new_cookie,
-                               on_finish);
-  }
-
-  MOCK_METHOD0(send, void());
-};
-
-template <>
-struct ReleaseRequest<MockExclusiveLockImageCtx> : public BaseRequest<ReleaseRequest<MockExclusiveLockImageCtx> > {
-  static ReleaseRequest* create(librados::IoCtx& ioctx, MockImageWatcher *watcher,
-                                ContextWQ *work_queue, const std::string& oid,
-                                const std::string& cookie, Context *on_finish) {
-    return BaseRequest::create(ioctx, watcher, work_queue, oid, cookie,
-                               on_finish);
-  }
-
-  MOCK_METHOD0(send, void());
-};
-
-
-} // namespace managed_lock
 } // namespace librbd
 
 // template definitions
 #include "librbd/ExclusiveLock.cc"
 template class librbd::ExclusiveLock<librbd::MockExclusiveLockImageCtx>;
 
-#include "librbd/ManagedLock.cc"
-template class librbd::ManagedLock<librbd::MockExclusiveLockImageCtx>;
-
 ACTION_P(FinishLockUnlock, request) {
   if (request->on_lock_unlock != nullptr) {
     request->on_lock_unlock->complete(0);
@@ -182,18 +154,60 @@ using ::testing::Return;
 
 class TestMockExclusiveLock : public TestMockFixture {
 public:
+  typedef ManagedLock<MockExclusiveLockImageCtx> MockManagedLock;
   typedef ExclusiveLock<MockExclusiveLockImageCtx> MockExclusiveLock;
   typedef exclusive_lock::PreAcquireRequest<MockExclusiveLockImageCtx> MockPreAcquireRequest;
   typedef exclusive_lock::PostAcquireRequest<MockExclusiveLockImageCtx> MockPostAcquireRequest;
   typedef exclusive_lock::PreReleaseRequest<MockExclusiveLockImageCtx> MockPreReleaseRequest;
-  typedef managed_lock::AcquireRequest<MockExclusiveLockImageCtx> MockManagedAcquireRequest;
-  typedef managed_lock::ReacquireRequest<MockExclusiveLockImageCtx> MockManagedReacquireRequest;
-  typedef managed_lock::ReleaseRequest<MockExclusiveLockImageCtx> MockManagedReleaseRequest;
 
-  void expect_get_watch_handle(MockExclusiveLockImageCtx &mock_image_ctx,
-                               uint64_t watch_handle = 1234567890) {
-    EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
-                  .WillOnce(Return(watch_handle));
+  void expect_set_state_initializing(MockManagedLock &managed_lock) {
+    EXPECT_CALL(managed_lock, set_state_initializing());
+  }
+
+  void expect_set_state_unlocked(MockManagedLock &managed_lock) {
+    EXPECT_CALL(managed_lock, set_state_unlocked());
+  }
+
+  void expect_set_state_waiting_for_lock(MockManagedLock &managed_lock) {
+    EXPECT_CALL(managed_lock, set_state_waiting_for_lock());
+  }
+
+  void expect_is_state_acquiring(MockManagedLock &managed_lock, bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_acquiring())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_state_waiting_for_lock(MockManagedLock &managed_lock,
+                                        bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_waiting_for_lock())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_state_pre_releasing(MockManagedLock &managed_lock,
+                                     bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_pre_releasing())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_state_releasing(MockManagedLock &managed_lock, bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_releasing())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_state_locked(MockManagedLock &managed_lock, bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_locked())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_state_shutdown(MockManagedLock &managed_lock, bool ret_val) {
+    EXPECT_CALL(managed_lock, is_state_shutdown())
+      .WillOnce(Return(ret_val));
+  }
+
+  void expect_is_action_acquire_lock(MockManagedLock &managed_lock,
+                                     bool ret_val) {
+    EXPECT_CALL(managed_lock, is_action_acquire_lock())
+      .WillOnce(Return(ret_val));
   }
 
   void expect_set_require_lock_on_read(MockExclusiveLockImageCtx &mock_image_ctx) {
@@ -217,65 +231,31 @@ public:
     EXPECT_CALL(*mock_image_ctx.aio_work_queue, unblock_writes());
   }
 
-  void expect_acquire_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                           MockPreAcquireRequest &pre_acquire_request,
-                           MockManagedAcquireRequest &managed_acquire_request,
-                           MockPostAcquireRequest *post_acquire_request,
-                           int pre_r, int managed_r, int post_r) {
-
-    expect_get_watch_handle(mock_image_ctx);
+  void expect_prepare_lock_complete(MockExclusiveLockImageCtx &mock_image_ctx) {
+    EXPECT_CALL(*mock_image_ctx.state, handle_prepare_lock_complete());
+  }
 
+  void expect_pre_acquire_request(MockPreAcquireRequest &pre_acquire_request,
+                                  int r) {
     EXPECT_CALL(pre_acquire_request, send())
-                .WillOnce(CompleteRequest(&pre_acquire_request, pre_r));
-    EXPECT_CALL(managed_acquire_request, send())
-                .WillOnce(CompleteRequest(&managed_acquire_request, managed_r));
-    if (managed_r == 0) {
-      assert(post_acquire_request != nullptr);
-      EXPECT_CALL(*post_acquire_request, send())
-          .WillOnce(DoAll(FinishLockUnlock(post_acquire_request),
-                          CompleteRequest(post_acquire_request, post_r)));
-    }
-
-    if (pre_r == 0 && managed_r == 0 && post_r == 0) {
-      expect_notify_acquired_lock(mock_image_ctx);
-      expect_unblock_writes(mock_image_ctx);
-    }
+      .WillOnce(CompleteRequest(&pre_acquire_request, r));
   }
 
-  void expect_release_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                           MockPreReleaseRequest &pre_release_request,
-                           MockManagedReleaseRequest &managed_release_request,
-                           int pre_r, int managed_r, bool shutting_down = false) {
-    EXPECT_CALL(pre_release_request, send())
-        .WillOnce(DoAll(FinishLockUnlock(&pre_release_request),
-                        CompleteRequest(&pre_release_request, pre_r)));
-    EXPECT_CALL(managed_release_request, send())
-        .WillOnce(CompleteRequest(&managed_release_request, managed_r));
-
-    if (pre_r == 0 && managed_r == 0) {
-      if (shutting_down) {
-        expect_unblock_writes(mock_image_ctx);
-      }
-      expect_notify_released_lock(mock_image_ctx);
-      expect_is_lock_request_needed(mock_image_ctx, false);
-    }
+  void expect_post_acquire_request(MockPostAcquireRequest &post_acquire_request,
+                                   int r) {
+    EXPECT_CALL(post_acquire_request, send())
+      .WillOnce(CompleteRequest(&post_acquire_request, r));
   }
 
-  void expect_reacquire_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                             MockManagedReacquireRequest &mock_reacquire_request,
-                             int r) {
-    EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
-                  .WillOnce(Return(98765));
-    EXPECT_CALL(mock_reacquire_request, send())
-                  .WillOnce(CompleteRequest(&mock_reacquire_request, r));
+  void expect_pre_release_request(MockPreReleaseRequest &pre_release_request,
+                                  int r) {
+    EXPECT_CALL(pre_release_request, send())
+      .WillOnce(CompleteRequest(&pre_release_request, r));
   }
 
   void expect_notify_request_lock(MockExclusiveLockImageCtx &mock_image_ctx,
                                   MockExclusiveLock &mock_exclusive_lock) {
-    EXPECT_CALL(*mock_image_ctx.image_watcher, notify_request_lock())
-                  .WillRepeatedly(Invoke([&mock_exclusive_lock]() {
-                                           mock_exclusive_lock.handle_peer_notification(0);
-                                         }));
+    EXPECT_CALL(*mock_image_ctx.image_watcher, notify_request_lock());
   }
 
   void expect_notify_acquired_lock(MockExclusiveLockImageCtx &mock_image_ctx) {
@@ -298,6 +278,11 @@ public:
                   .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
   }
 
+  void expect_shut_down(MockManagedLock &managed_lock) {
+    EXPECT_CALL(managed_lock, shut_down(_))
+      .WillOnce(CompleteContext(0, static_cast<ContextWQ*>(nullptr)));
+  }
+
   int when_init(MockExclusiveLockImageCtx &mock_image_ctx,
                 MockExclusiveLock &exclusive_lock) {
     C_SaferCond ctx;
@@ -308,33 +293,32 @@ public:
     return ctx.wait();
   }
 
-  int when_try_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                    MockExclusiveLock &exclusive_lock) {
+  int when_pre_acquire_lock_handler(MockManagedLock &managed_lock) {
     C_SaferCond ctx;
-    {
-      RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
-      exclusive_lock.try_acquire_lock(&ctx);
-    }
+    managed_lock.pre_acquire_lock_handler(&ctx);
     return ctx.wait();
   }
-  int when_request_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                        MockExclusiveLock &exclusive_lock) {
+
+  int when_post_acquire_lock_handler(MockManagedLock &managed_lock, int r) {
     C_SaferCond ctx;
-    {
-      RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-      exclusive_lock.acquire_lock(&ctx);
-    }
+    managed_lock.post_acquire_lock_handler(r, &ctx);
     return ctx.wait();
   }
-  int when_release_lock(MockExclusiveLockImageCtx &mock_image_ctx,
-                        MockExclusiveLock &exclusive_lock) {
+
+  int when_pre_release_lock_handler(MockManagedLock &managed_lock,
+                                    bool shutting_down) {
     C_SaferCond ctx;
-    {
-      RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
-      exclusive_lock.release_lock(&ctx);
-    }
+    managed_lock.pre_release_lock_handler(shutting_down, &ctx);
+    return ctx.wait();
+  }
+
+  int when_post_release_lock_handler(MockManagedLock &managed_lock,
+                                     bool shutting_down, int r) {
+    C_SaferCond ctx;
+    managed_lock.post_release_lock_handler(shutting_down, r, &ctx);
     return ctx.wait();
   }
+
   int when_shut_down(MockExclusiveLockImageCtx &mock_image_ctx,
                      MockExclusiveLock &exclusive_lock) {
     C_SaferCond ctx;
@@ -363,97 +347,61 @@ TEST_F(TestMockExclusiveLock, StateTransitions) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
+  // (try) acquire lock
   MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
   MockPostAcquireRequest try_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_post_acquire_request(try_lock_post_acquire, 0);
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_notify_acquired_lock(mock_image_ctx);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, 0));
 
+  // release lock
   MockPreReleaseRequest pre_request_release;
-  MockManagedReleaseRequest managed_request_release;
-  expect_release_lock(mock_image_ctx, pre_request_release,
-                      managed_request_release, 0, 0);
-  ASSERT_EQ(0, when_release_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_pre_release_request(pre_request_release, 0);
+  ASSERT_EQ(0, when_pre_release_lock_handler(exclusive_lock, false));
 
+  expect_is_state_pre_releasing(exclusive_lock, false);
+  expect_is_state_releasing(exclusive_lock, true);
+  expect_notify_released_lock(mock_image_ctx);
+  expect_is_lock_request_needed(mock_image_ctx, false);
+  ASSERT_EQ(0, when_post_release_lock_handler(exclusive_lock, false, 0));
+
+  // (try) acquire lock
   MockPreAcquireRequest request_lock_pre_acquire;
-  MockManagedAcquireRequest request_lock_managed_acquire;
+  expect_pre_acquire_request(request_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
   MockPostAcquireRequest request_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire,
-                      request_lock_managed_acquire, &request_lock_post_acquire,
-                      0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_post_acquire_request(request_lock_post_acquire, 0);
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_notify_acquired_lock(mock_image_ctx);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, 0));
 
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
+  // shut down (and release)
+  expect_shut_down(exclusive_lock);
+  expect_is_state_waiting_for_lock(exclusive_lock, false);
   ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, TryLockLockedState) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  MockPostAcquireRequest try_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
 
   MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, TryLockAlreadyLocked) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, nullptr, 0, -EAGAIN, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_pre_release_request(shutdown_pre_release, 0);
+  ASSERT_EQ(0, when_pre_release_lock_handler(exclusive_lock, true));
 
   expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  expect_notify_released_lock(mock_image_ctx);
+  ASSERT_EQ(0, when_post_release_lock_handler(exclusive_lock, true, 0));
 }
 
-TEST_F(TestMockExclusiveLock, TryLockBusy) {
+TEST_F(TestMockExclusiveLock, TryLockAlreadyLocked) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -464,19 +412,20 @@ TEST_F(TestMockExclusiveLock, TryLockBusy) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
+  // try acquire lock
   MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, nullptr, 0, -EBUSY, 0);
-  ASSERT_EQ(-EBUSY, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
 
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_prepare_lock_complete(mock_image_ctx);
+  expect_is_action_acquire_lock(exclusive_lock, false);
+  ASSERT_EQ(0, when_post_acquire_lock_handler(exclusive_lock, -EAGAIN));
 }
 
 TEST_F(TestMockExclusiveLock, TryLockError) {
@@ -490,22 +439,23 @@ TEST_F(TestMockExclusiveLock, TryLockError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
+  // try acquire lock
   MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, nullptr, 0, -EINVAL, 0);
-  ASSERT_EQ(-EINVAL, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
 
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_prepare_lock_complete(mock_image_ctx);
+  expect_is_action_acquire_lock(exclusive_lock, false);
+  ASSERT_EQ(-EBUSY, when_post_acquire_lock_handler(exclusive_lock, -EBUSY));
 }
 
-TEST_F(TestMockExclusiveLock, RequestLockLockedState) {
+TEST_F(TestMockExclusiveLock, AcquireLockAlreadyLocked) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -516,26 +466,26 @@ TEST_F(TestMockExclusiveLock, RequestLockLockedState) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
+  // acquire lock
   MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  MockPostAcquireRequest try_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
 
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_prepare_lock_complete(mock_image_ctx);
+  expect_is_action_acquire_lock(exclusive_lock, true);
+  expect_set_state_waiting_for_lock(exclusive_lock);
+  expect_notify_request_lock(mock_image_ctx, exclusive_lock);
+  ASSERT_EQ(-ECANCELED, when_post_acquire_lock_handler(exclusive_lock,
+                                                       -EAGAIN));
 }
 
-TEST_F(TestMockExclusiveLock, RequestLockBlacklist) {
+TEST_F(TestMockExclusiveLock, AcquireLockBusy) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -546,60 +496,26 @@ TEST_F(TestMockExclusiveLock, RequestLockBlacklist) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
-  // will abort after seeing blacklist error (avoid infinite request loop)
-  MockPreAcquireRequest request_pre_acquire;
-  MockManagedAcquireRequest request_managed_acquire;
-  expect_acquire_lock(mock_image_ctx, request_pre_acquire,
-                      request_managed_acquire, nullptr, 0, -EBLACKLISTED, 0);
-  ASSERT_EQ(-EBLACKLISTED, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, RequestLockBusy) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+  // acquire lock
+  MockPreAcquireRequest try_lock_pre_acquire;
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
 
-  // will repeat until successfully acquires the lock
-  MockPreAcquireRequest request_lock_pre_acquire1;
-  MockManagedAcquireRequest request_lock_managed_acquire1;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire1,
-                      request_lock_managed_acquire1, nullptr, 0, -EBUSY, 0);
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_prepare_lock_complete(mock_image_ctx);
+  expect_is_action_acquire_lock(exclusive_lock, true);
+  expect_set_state_waiting_for_lock(exclusive_lock);
   expect_notify_request_lock(mock_image_ctx, exclusive_lock);
-
-  MockPreAcquireRequest request_lock_pre_acquire2;
-  MockManagedAcquireRequest request_lock_managed_acquire2;
-  MockPostAcquireRequest request_lock_post_acquire2;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire2,
-                      request_lock_managed_acquire2,
-                      &request_lock_post_acquire2, 0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  ASSERT_EQ(-ECANCELED, when_post_acquire_lock_handler(exclusive_lock,
+                                                       -EBUSY));
 }
 
-TEST_F(TestMockExclusiveLock, RequestLockError) {
+TEST_F(TestMockExclusiveLock, AcquireLockError) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -610,33 +526,24 @@ TEST_F(TestMockExclusiveLock, RequestLockError) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
-  // will repeat until successfully acquires the lock
-  MockPreAcquireRequest request_lock_pre_acquire1;
-  MockManagedAcquireRequest request_lock_managed_acquire1;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire1,
-                      request_lock_managed_acquire1, nullptr, 0, -EINVAL, 0);
-  expect_notify_request_lock(mock_image_ctx, exclusive_lock);
-
-  MockPreAcquireRequest request_lock_pre_acquire2;
-  MockManagedAcquireRequest request_lock_managed_acquire2;
-  MockPostAcquireRequest request_lock_post_acquire2;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire2,
-                      request_lock_managed_acquire2,
-                      &request_lock_post_acquire2, 0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  // acquire lock
+  MockPreAcquireRequest try_lock_pre_acquire;
+  expect_pre_acquire_request(try_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
+
+  expect_is_state_acquiring(exclusive_lock, true);
+  expect_prepare_lock_complete(mock_image_ctx);
+  expect_is_action_acquire_lock(exclusive_lock, true);
+  ASSERT_EQ(-EBLACKLISTED, when_post_acquire_lock_handler(exclusive_lock,
+                                                          -EBLACKLISTED));
 }
 
-TEST_F(TestMockExclusiveLock, RequestLockJournalEPERM) {
+TEST_F(TestMockExclusiveLock, PostAcquireLockError) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -647,85 +554,23 @@ TEST_F(TestMockExclusiveLock, RequestLockJournalEPERM) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
-  // will abort after seeing perm error (avoid infinite request loop)
+  // (try) acquire lock
   MockPreAcquireRequest request_lock_pre_acquire;
-  MockManagedAcquireRequest request_lock_managed_acquire;
-  MockPostAcquireRequest request_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire,
-                      request_lock_managed_acquire,
-                      &request_lock_post_acquire, 0, 0, -EPERM);
-  MockManagedReleaseRequest release_on_error;
-  EXPECT_CALL(release_on_error, send())
-    .WillOnce(CompleteRequest(&release_on_error, 0));
-  ASSERT_EQ(-EPERM, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, ReleaseLockUnlockedState) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
+  expect_pre_acquire_request(request_lock_pre_acquire, 0);
+  ASSERT_EQ(0, when_pre_acquire_lock_handler(exclusive_lock));
 
-  ASSERT_EQ(0, when_release_lock(mock_image_ctx, exclusive_lock));
-
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, ReleaseLockError) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  MockPostAcquireRequest try_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-
-  MockPreReleaseRequest request_pre_release;
-  MockManagedReleaseRequest request_managed_release;
-  expect_release_lock(mock_image_ctx, request_pre_release,
-                      request_managed_release, 0, -EINVAL, false);
-  ASSERT_EQ(-EINVAL, when_release_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
+  MockPostAcquireRequest request_lock_post_acquire;
+  expect_post_acquire_request(request_lock_post_acquire, -EPERM);
+  expect_is_state_acquiring(exclusive_lock, true);
+  ASSERT_EQ(-EPERM, when_post_acquire_lock_handler(exclusive_lock, 0));
 }
 
-TEST_F(TestMockExclusiveLock, ConcurrentRequests) {
+TEST_F(TestMockExclusiveLock, PreReleaseLockError) {
   REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
 
   librbd::ImageCtx *ictx;
@@ -736,86 +581,19 @@ TEST_F(TestMockExclusiveLock, ConcurrentRequests) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
-  MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  MockPostAcquireRequest try_lock_post_acquire;
-  C_SaferCond wait_for_send_ctx1;
-  expect_get_watch_handle(mock_image_ctx);
-  EXPECT_CALL(try_lock_pre_acquire, send())
-                .WillOnce(CompleteRequest(&try_lock_pre_acquire, 0));
-  EXPECT_CALL(try_lock_managed_acquire, send())
-                .WillOnce(CompleteRequest(&try_lock_managed_acquire, 0));
-  EXPECT_CALL(try_lock_post_acquire, send())
-                .WillOnce(Notify(&wait_for_send_ctx1));
-
-  MockManagedReleaseRequest managed_release_on_error;
-  EXPECT_CALL(managed_release_on_error, send())
-                .WillOnce(CompleteRequest(&managed_release_on_error, 0));
-
-  MockPreAcquireRequest request_pre_acquire;
-  MockManagedAcquireRequest request_managed_acquire;
-  MockPostAcquireRequest request_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_pre_acquire,
-                      request_managed_acquire, &request_post_acquire, 0, 0, 0);
-
-  MockPreReleaseRequest pre_release;
-  MockManagedReleaseRequest managed_release;
-  C_SaferCond wait_for_send_ctx2;
-  EXPECT_CALL(pre_release, send()).WillOnce(Notify(&wait_for_send_ctx2));
-  EXPECT_CALL(managed_release, send())
-        .WillOnce(CompleteRequest(&managed_release, 0));
-  expect_notify_released_lock(mock_image_ctx);
-  expect_is_lock_request_needed(mock_image_ctx, false);
-
-  C_SaferCond try_request_ctx1;
-  {
-    RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.try_acquire_lock(&try_request_ctx1);
-  }
-
-  C_SaferCond request_lock_ctx1;
-  C_SaferCond request_lock_ctx2;
-  {
-    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.acquire_lock(&request_lock_ctx1);
-    exclusive_lock.acquire_lock(&request_lock_ctx2);
-  }
-
-  C_SaferCond release_lock_ctx1;
-  {
-    RWLock::WLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.release_lock(&release_lock_ctx1);
-  }
-
-  C_SaferCond request_lock_ctx3;
-  {
-    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.acquire_lock(&request_lock_ctx3);
-  }
-
-  // fail the try_lock
-  ASSERT_EQ(0, wait_for_send_ctx1.wait());
-  try_lock_post_acquire.on_lock_unlock->complete(0);
-  try_lock_post_acquire.on_finish->complete(-EINVAL);
-  ASSERT_EQ(-EINVAL, try_request_ctx1.wait());
-
-  // all three pending request locks should complete
-  ASSERT_EQ(0, request_lock_ctx1.wait());
-  ASSERT_EQ(0, request_lock_ctx2.wait());
-  ASSERT_EQ(0, request_lock_ctx3.wait());
-
-  // proceed with the release
-  ASSERT_EQ(0, wait_for_send_ctx2.wait());
-  pre_release.on_lock_unlock->complete(0);
-  pre_release.on_finish->complete(0);
-  ASSERT_EQ(0, release_lock_ctx1.wait());
+  // release lock
+  MockPreReleaseRequest pre_request_release;
+  expect_pre_release_request(pre_request_release, -EINVAL);
+  ASSERT_EQ(-EINVAL, when_pre_release_lock_handler(exclusive_lock, false));
 
-  expect_unblock_writes(mock_image_ctx);
-  expect_flush_notifies(mock_image_ctx);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
+  expect_is_state_pre_releasing(exclusive_lock, true);
+  ASSERT_EQ(-EINVAL, when_post_release_lock_handler(exclusive_lock, false,
+                                                    -EINVAL));
 }
 
 TEST_F(TestMockExclusiveLock, BlockRequests) {
@@ -830,170 +608,28 @@ TEST_F(TestMockExclusiveLock, BlockRequests) {
   expect_op_work_queue(mock_image_ctx);
 
   InSequence seq;
+  expect_set_state_initializing(exclusive_lock);
   expect_block_writes(mock_image_ctx);
+  expect_set_state_unlocked(exclusive_lock);
   ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
 
-  MockPreAcquireRequest try_lock_pre_acquire;
-  MockManagedAcquireRequest try_lock_managed_acquire;
-  MockPostAcquireRequest try_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, try_lock_pre_acquire,
-                      try_lock_managed_acquire, &try_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_try_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
   int ret_val;
+  expect_is_state_shutdown(exclusive_lock, false);
+  expect_is_state_locked(exclusive_lock, true);
   ASSERT_TRUE(exclusive_lock.accept_requests(&ret_val));
   ASSERT_EQ(0, ret_val);
 
   exclusive_lock.block_requests(-EROFS);
+  expect_is_state_shutdown(exclusive_lock, false);
+  expect_is_state_locked(exclusive_lock, true);
   ASSERT_FALSE(exclusive_lock.accept_requests(&ret_val));
   ASSERT_EQ(-EROFS, ret_val);
 
   exclusive_lock.unblock_requests();
+  expect_is_state_shutdown(exclusive_lock, false);
+  expect_is_state_locked(exclusive_lock, true);
   ASSERT_TRUE(exclusive_lock.accept_requests(&ret_val));
   ASSERT_EQ(0, ret_val);
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, RequestLockWatchNotRegistered) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
-    .WillOnce(DoAll(Invoke([&mock_image_ctx, &exclusive_lock]() {
-                      mock_image_ctx.image_ctx->op_work_queue->queue(
-                        new FunctionContext([&mock_image_ctx, &exclusive_lock](int r) {
-                          RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-                          exclusive_lock.reacquire_lock();
-                        }));
-                    }),
-                    Return(0)));
-
-  MockPreAcquireRequest request_lock_pre_acquire;
-  MockManagedAcquireRequest request_lock_managed_acquire;
-  MockPostAcquireRequest request_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire,
-                      request_lock_managed_acquire,
-                      &request_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, ReacquireLock) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  MockPreAcquireRequest request_lock_pre_acquire;
-  MockManagedAcquireRequest request_lock_managed_acquire;
-  MockPostAcquireRequest request_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire,
-                      request_lock_managed_acquire,
-                      &request_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  EXPECT_CALL(*mock_image_ctx.image_watcher, get_watch_handle())
-                 .WillOnce(Return(1234567890));
-
-  C_SaferCond reacquire_ctx;
-  {
-    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.reacquire_lock(&reacquire_ctx);
-  }
-  ASSERT_EQ(0, reacquire_ctx.wait());
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
-}
-
-TEST_F(TestMockExclusiveLock, ReacquireLockError) {
-  REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
-
-  librbd::ImageCtx *ictx;
-  ASSERT_EQ(0, open_image(m_image_name, &ictx));
-
-  MockExclusiveLockImageCtx mock_image_ctx(*ictx);
-  MockExclusiveLock exclusive_lock(mock_image_ctx);
-  expect_op_work_queue(mock_image_ctx);
-
-  InSequence seq;
-  expect_block_writes(mock_image_ctx);
-  ASSERT_EQ(0, when_init(mock_image_ctx, exclusive_lock));
-
-  MockPreAcquireRequest request_lock_pre_acquire;
-  MockManagedAcquireRequest request_lock_managed_acquire;
-  MockPostAcquireRequest request_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, request_lock_pre_acquire,
-                      request_lock_managed_acquire,
-                      &request_lock_post_acquire, 0, 0, 0);
-  ASSERT_EQ(0, when_request_lock(mock_image_ctx, exclusive_lock));
-  ASSERT_TRUE(is_lock_owner(mock_image_ctx, exclusive_lock));
-
-  MockManagedReacquireRequest mock_reacquire_request;
-  C_SaferCond reacquire_ctx;
-  expect_reacquire_lock(mock_image_ctx, mock_reacquire_request, -EOPNOTSUPP);
-
-  MockPreReleaseRequest reacquire_lock_pre_release;
-  MockManagedReleaseRequest reacquire_lock_managed_release;
-  expect_release_lock(mock_image_ctx, reacquire_lock_pre_release,
-                      reacquire_lock_managed_release, 0, 0, false);
-
-  MockPreAcquireRequest reacquire_lock_pre_acquire;
-  MockManagedAcquireRequest reacquire_lock_managed_acquire;
-  MockPostAcquireRequest reacquire_lock_post_acquire;
-  expect_acquire_lock(mock_image_ctx, reacquire_lock_pre_acquire,
-                      reacquire_lock_managed_acquire,
-                      &reacquire_lock_post_acquire, 0, 0, 0);
-
-  {
-    RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
-    exclusive_lock.reacquire_lock(&reacquire_ctx);
-  }
-  ASSERT_EQ(-EOPNOTSUPP, reacquire_ctx.wait());
-
-  MockPreReleaseRequest shutdown_pre_release;
-  MockManagedReleaseRequest shutdown_managed_release;
-  expect_release_lock(mock_image_ctx, shutdown_pre_release,
-                      shutdown_managed_release, 0, 0, true);
-  ASSERT_EQ(0, when_shut_down(mock_image_ctx, exclusive_lock));
-  ASSERT_FALSE(is_lock_owner(mock_image_ctx, exclusive_lock));
 }
 
 } // namespace librbd