zoukankan      html  css  js  c++  java
  • 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中的回调函数和回调类有所了解。

    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函数调用自己需要回调的函数,就可以完成回调。

    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类处理回调函数。

  • 相关阅读:
    整理一些将窗口显示在前台办法
    工具
    [Windows Api 学习] Error Handling Functions
    Windows实用快捷键
    程序化交易资料汇总
    compile libpng
    zlib 1.2.8 编译笔记
    Cryptopp Usage Note
    linux环境中Java服务通过shell脚本重启(升级)自己
    搭建自己的maven库---nexus
  • 原文地址:https://www.cnblogs.com/powerrailgun/p/12342343.html
Copyright © 2011-2022 走看看