ceph RBD中用到的回调函数和回调类

在看rbd-mirror的代码中,出现了以下的代码逻辑:

template 
void ImageReplayer::wait_for_deletion() {
  dout(20) << dendl;

  Context *ctx = create_context_callback<
    ImageReplayer, &ImageReplayer::handle_wait_for_deletion>(this);
  m_image_deleter->wait_for_scheduled_deletion(
    m_local_pool_id, m_global_image_id, ctx, false);
}

template 
void ImageReplayer::handle_wait_for_deletion(int r) {
  dout(20) << "r=" << r << dendl;

  if (r == -ECANCELED) {
    on_start_fail(0, "");
    return;
  } else if (r < 0) {
    on_start_fail(r, "error waiting for image deletion");
    return;
  }

  prepare_local_image();
}
template 
void ImageReplayer::prepare_local_image() {
  dout(20) << dendl;

  m_local_image_id = "";
  Context *ctx = create_context_callback<
    ImageReplayer, &ImageReplayer::handle_prepare_local_image>(this);
  auto req = PrepareLocalImageRequest::create(
    m_local_ioctx, m_global_image_id, &m_local_image_id, &m_local_image_name,
    &m_local_image_tag_owner, m_threads->work_queue, ctx);
  req->send();
}

template 
void ImageReplayer::handle_prepare_local_image(int r) {
  dout(20) << "r=" << r << dendl;

  if (r == -ENOENT) {
    dout(20) << "local image does not exist" << dendl;
  } else if (r < 0) {
    on_start_fail(r, "error preparing local image for replay");
    return;
  } else {
    reregister_admin_socket_hook();
  }

  // local image doesn't exist or is non-primary
  prepare_remote_image();
}

首先说明下以上代码的逻辑,以prepare_local_image为例,在这个函数中,使用了create_context_callback注册了一个回调函数handle_prepare_local_image(后面可以看到很多以handle开头的函数,都可以视为回调函数),注册好了后就继续执行req->send();send()函数返回时,那么就会调用handle_prepare_local_image函数了,接着执行handle_prepare_local_image中的逻辑。以此类推,如果进入到req->send(),可以发现在send()函数中也是有同样的逻辑(即就是注册回调函数,然后执行一个任务,执行结束后调用回调函数)

下面再来详细分析。

要看懂这两部分的代码逻辑需要对ceph rbd中的回调函数和回调类有所了解。

  1. 回调类和回调函数
    这里需要着重关注的是create_context_callback这个函数。
    原型位于src/librbd/Utils.h:
template 
Context *create_context_callback(T *obj) {
  return new detail::C_CallbackAdapter(obj);
}

还会牵涉到一个回调函数适配器C_CallbackAdapter

namespace detail
{
    ........
    template 
    class C_CallbackAdapter : public Context {
    T *obj;
        public:
            C_CallbackAdapter(T *obj) : obj(obj) {
     }
 
        protected:
           void finish(int r) override {
           (obj->*MF)(r);
           }
     };
}

C_CallbackAdapter类型继承自Context类型。同时,重载了finish()函数。
Context类定义在src/include文件中,该类是一个回调函数类的抽象类,继承它的类只要在子类实现它的finish函数,在finish函数调用自己需要回调的函数,就可以完成回调。

class Context {
    Context(const Context& other);
    const Context& operator=(const Context& other);
protected:
    virtual void finish(int r) = 0;
public:
    Context() {}
    virtual ~Context() {}       
    // we want a virtual destructor!!!
    virtual void complete(int r) {
    finish(r);
    delete this;
    }
};

看到这里大概就会明白文章开头的rbd-mirror这段代码的意思了:

Context *ctx = create_context_callback<
    ImageReplayer, &ImageReplayer::handle_prepare_local_image>(this);

通过create_context_callback