ceph RBD中用到的回调函数和回调类
在看rbd-mirror的代码中,出现了以下的代码逻辑:
template <typename I>
void ImageReplayer<I>::wait_for_deletion() {
dout(20) << dendl;
Context *ctx = create_context_callback<
ImageReplayer, &ImageReplayer<I>::handle_wait_for_deletion>(this);
m_image_deleter->wait_for_scheduled_deletion(
m_local_pool_id, m_global_image_id, ctx, false);
}
template <typename I>
void ImageReplayer<I>::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 <typename I>
void ImageReplayer<I>::prepare_local_image() {
dout(20) << dendl;
m_local_image_id = "";
Context *ctx = create_context_callback<
ImageReplayer, &ImageReplayer<I>::handle_prepare_local_image>(this);
auto req = PrepareLocalImageRequest<I>::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 <typename I>
void ImageReplayer<I>::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中的回调函数和回调类有所了解。
- 回调类和回调函数
这里需要着重关注的是create_context_callback
这个函数。
原型位于src/librbd/Utils.h
:
template <typename T, void(T::*MF)(int) = &T::complete>
Context *create_context_callback(T *obj) {
return new detail::C_CallbackAdapter<T, MF>(obj);
}
还会牵涉到一个回调函数适配器C_CallbackAdapter
:
namespace detail
{
........
template <typename T, void (T::*MF)(int)>
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<I>::handle_prepare_local_image>(this);
通过create_context_callback<template T>(*obj)
这个模板函数,将ImageReplayer
这个类转换成了一个回调类,同时设置了回调函数就是handle_prepare_local_image
。其他的也是类似的。
下面来写个demo,剥离掉一些无用的,看看回调类和回调函数的基本,核心实现。
#include <iostream>
#include <string>
#include <errno.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <assert.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>
#include <vector>
#include <list>
using namespace std;
//Base class and function
class Context {
Context(const Context& other);
const Context& operator=(const Context& other);
protected:
virtual void finish(int r) = 0;
public:
Context() {}
virtual ~Context() {}
virtual void complete(int r) {
finish(r);
delete this;
}
};
template <typename T, void (T::*MF)(int)>
class C_CallbackAdapter: public Context {
T *obj;
public:
C_CallbackAdapter(T *obj) : obj(obj) {
}
protected:
void finish(int r) override {
(obj->*MF)(r);
}
};
template <typename T, void(T::*MF)(int) = &T::complete>
Context *create_context_callback(T *obj) {
return new C_CallbackAdapter<T, MF>(obj);
}
class TestCallback {
public:
protected:
public:
void start_test();
Context *pre_set_fn();
void handle_test_callback(int r);
private:
};
Context *TestCallback::pre_set_fn() {
Context *ctx = create_context_callback<
TestCallback, &TestCallback::handle_test_callback>(this);
return ctx;
}
void TestCallback::handle_test_callback(int r)
{
std::cout<<"This printed by <handle_test_callback>"<<std::endl;
}
int main()
{
TestCallback *test_callback = new TestCallback;
Context *ctx = test_callback->pre_set_fn();
int r = 0;
ctx->complete(r);
return 0;
}
编译和运行:
g++ -std=c++11 callback_class_function.cc -o test_callback
./test_callback
通过以上的模拟代码可以看出,当在main()
中调用了complete()
函数后,就会间接调用到类中实现的handle_xxxx
函数以模拟回调的功能。
另外,还可以在ceph 的class Finsisher
类中看到对complete()
函数的调用。
Finisher类是在src/common
中定义的一个专门查看操作是否结束的一个类。在这个类里面拥有一个线程finisher_thread和一个类型为Context指针的队列finisher_queue。当一个操作线程完成自己的操作后,会将Context类型对象送入队列。此时finisher_thread线程循环监视着自己的finisher_queue队列,当发现了有新进入的Context时,会调用这个Context::complete
函数,这个函数则会调用到Context
子类自己实现的finish()
函数。来处理操作完成后的后续工作。
class Finisher {
CephContext *cct;
……
vector<Context*> finisher_queue;
……
void *finisher_thread_entry();
struct FinisherThread : public Thread {
Finisher *fin;
FinisherThread(Finisher *f) : fin(f) {}
void* entry() { return (void*)fin->finisher_thread_entry(); }
} finisher_thread;
……
}
void *Finisher::finisher_thread_entry()
{
……
while(!finisher_stop){
while(!finisher_queue.empty()){
……
vector<Context*> ls
ls.swap(finisher_queue);
for (vector<Context*>::iterator p = ls.begin();
p != ls.end();
++p) {
if (*p) {
//这里面调用Context子类实现的finish函数
(*p)->complete(0);
}
}
}
}
}
既然这里提到了,再来写一个demo,来尝试使用下class Finisher
类处理回调函数。