void Watcher::register_watch(Context *on_finish) {
ldout(m_cct, 10) << dendl;
- RWLock::RLocker watch_locker(m_watch_lock);
+ RWLock::WLocker watch_locker(m_watch_lock);
ceph_assert(is_unregistered(m_watch_lock));
m_watch_state = WATCH_STATE_REGISTERING;
+ m_watch_blacklisted = false;
librados::AioCompletion *aio_comp = create_rados_callback(
new C_RegisterWatch(this, on_finish));
lderr(m_cct) << "re-registering watch after error" << dendl;
m_watch_state = WATCH_STATE_REWATCHING;
watch_error = true;
+ } else {
+ m_watch_blacklisted = (r == -EBLACKLISTED);
}
}
return;
} else if (is_registered(m_watch_lock)) {
librados::AioCompletion *aio_comp = create_rados_callback(
- new C_UnwatchAndFlush(m_ioctx, on_finish));
+ new C_UnwatchAndFlush(m_ioctx, on_finish));
int r = m_ioctx.aio_unwatch(m_watch_handle, aio_comp);
ceph_assert(r == 0);
aio_comp->release();
+
m_watch_handle = 0;
+ m_watch_blacklisted = false;
return;
}
}
if (is_registered(m_watch_lock)) {
m_watch_state = WATCH_STATE_REWATCHING;
+ if (err == -EBLACKLISTED) {
+ m_watch_blacklisted = true;
+ }
FunctionContext *ctx = new FunctionContext(
boost::bind(&Watcher::rewatch, this));
RWLock::WLocker watch_locker(m_watch_lock);
ceph_assert(m_watch_state == WATCH_STATE_REWATCHING);
+ m_watch_blacklisted = false;
if (m_unregister_watch_ctx != nullptr) {
ldout(m_cct, 10) << "image is closing, skip rewatch" << dendl;
m_watch_state = WATCH_STATE_IDLE;
std::swap(unregister_watch_ctx, m_unregister_watch_ctx);
} else if (r == -EBLACKLISTED) {
lderr(m_cct) << "client blacklisted" << dendl;
+ m_watch_blacklisted = true;
} else if (r == -ENOENT) {
ldout(m_cct, 5) << "object does not exist" << dendl;
} else if (r < 0) {
RWLock::RLocker locker(m_watch_lock);
return is_unregistered(m_watch_lock);
}
+ bool is_blacklisted() const {
+ RWLock::RLocker locker(m_watch_lock);
+ return m_watch_blacklisted;
+ }
protected:
enum WatchState {
mutable RWLock m_watch_lock;
uint64_t m_watch_handle;
watcher::Notifier m_notifier;
+
WatchState m_watch_state;
+ bool m_watch_blacklisted = false;
+
AsyncOpTracker m_async_op_tracker;
bool is_registered(const RWLock&) const {
struct MockImageWatcher {
MOCK_METHOD0(is_registered, bool());
MOCK_METHOD0(is_unregistered, bool());
+ MOCK_METHOD0(is_blacklisted, bool());
MOCK_METHOD0(unregister_watch, void());
MOCK_METHOD1(flush, void(Context *));
return false;
}
}
+ m_watch_count -= count;
return true;
}
expect_aio_watch(mock_image_ctx, 0);
expect_aio_unwatch(mock_image_ctx, 0);
expect_aio_watch(mock_image_ctx, -EBLACKLISTED);
- expect_aio_watch(mock_image_ctx, 0);
+
+ C_SaferCond blacklist_ctx;
+ expect_aio_watch(mock_image_ctx, 0, [&blacklist_ctx]() {
+ blacklist_ctx.wait();
+ });
expect_aio_unwatch(mock_image_ctx, 0);
C_SaferCond register_ctx;
mock_image_watcher.register_watch(®ister_ctx);
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 1));
ASSERT_EQ(0, register_ctx.wait());
ceph_assert(m_watch_ctx != nullptr);
m_watch_ctx->handle_error(0, -EBLACKLISTED);
// wait for recovery unwatch/watch
- ASSERT_TRUE(wait_for_watch(mock_image_ctx, 4));
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 2));
+
+ ASSERT_TRUE(mock_image_watcher.is_blacklisted());
+ blacklist_ctx.complete(0);
+
+ // wait for post-blacklist recovery watch
+ ASSERT_TRUE(wait_for_watch(mock_image_ctx, 1));
C_SaferCond unregister_ctx;
mock_image_watcher.unregister_watch(&unregister_ctx);
ASSERT_EQ(0, unregister_ctx.wait());
+ ASSERT_FALSE(mock_image_watcher.is_blacklisted());
}
TEST_F(TestMockWatcher, ReregisterUnwatchPendingUnregister) {