searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

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

2023-06-29 06:03:07
5
0
在看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中的回调函数和回调类有所了解。
1. 回调类和回调函数
这里需要着重关注的是```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函数调用自己需要回调的函数,就可以完成回调。
```c
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()```函数。来处理操作完成后的后续工作。
```c
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);
            }
            }
        }
    }
}
0条评论
0 / 1000
c39n
2文章数
0粉丝数
c39n
2 文章 | 0 粉丝
c39n
2文章数
0粉丝数
c39n
2 文章 | 0 粉丝
原创

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

2023-06-29 06:03:07
5
0
在看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中的回调函数和回调类有所了解。
1. 回调类和回调函数
这里需要着重关注的是```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函数调用自己需要回调的函数,就可以完成回调。
```c
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()```函数。来处理操作完成后的后续工作。
```c
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);
            }
            }
        }
    }
}
文章来自个人专栏
ZOS对象存储
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0