EventBus的C++实现、代码分析

目录

1 CNStream中消息机制整体介绍

2 CNStream中EventBus主要代码

2.1 framework/include/cnstream_eventbus.hpp

2.2 framework/src/cnstream_eventbus.cpp

3 CNStream中EventBus代码分析、机制介绍

参考文献:


看一下https://github.dev/Cambricon/CNStream中EventBus原理以及代码分析。

1 CNStream中消息机制整体介绍

在CNStream流处理多路并发Pipeline框架中,不同模块之间或者说不同线程之间需要传递一些信息或者说事件,比如⽆效、错误、警告、EOS(End of Stream)、停⽌,数据流错误等消息,在CNStream流处理多路并发Pipeline框架中是通过EventBus模式来处理事件的,主要包括三个部分

  • 事件源(Event Source):将消息发布到事件总线上。
  • 事件总线(EventBus):事件发布到总线上时被监听器接收。
  • 事件监听器(Observer/Listener):监听器订阅事件。

cnstream::Event 类是模块和 pipepline 之间通信的基本单元,即事件。事件由四个部分组成:事件类型、消息、发布事件的模块、发布事件的线程号。消息类型包括:⽆效、错误、警告、EOS(End ofStream)、停⽌,数据流错误,以及⼀个预留类型。cnstream::Event 类 在 cnstream_eventbus.hpp ⽂ 件 定 义,cnstream_eventbus.hpp ⽂ 件 存 放 在framework/core/include ⽂件夹下。

cnstream::EventBus 类是各个模块与 pipeline 通信的事件总线。各模块发布事件到总线上,由总线监听器接收。⼀条事件总线可以拥有多个监听器。每条 pipeline 有⼀条事件总线及对应的⼀个默认事件监听器。pipeline 会对事件总线进⾏轮询,收到事件后分发给监听器。cnstream::EventBus 类 在 cnstream_eventbus.hpp ⽂ 件 中 定 义, 主 要 接 ⼝ 如 下。cnstream_eventbus.hpp ⽂件存放在 framework/core/include ⽂件夹下。

2 CNStream中EventBus主要代码

2.1 framework/include/cnstream_eventbus.hpp

/*************************************************************************
 * Copyright (C) [2019] by Cambricon, Inc. All rights reserved
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *************************************************************************/

#ifndef CNSTREAM_EVENT_BUS_HPP_
#define CNSTREAM_EVENT_BUS_HPP_

/**
 *  @file cnstream_eventbus.hpp
 *
 *  This file contains a declaration of the EventBus class.
 */

#include <atomic>
#include <functional>
#include <list>
#include <mutex>
#include <string>
#include <thread>
#include <utility>

#include "cnstream_common.hpp"
#include "util/cnstream_queue.hpp"

namespace cnstream {

class Pipeline;

/*!
 * @enum EventType
 *
 * @brief Enumeration variables describing the type of event.
 */
enum class EventType {
  EVENT_INVALID,      /*!< An invalid event type. */
  EVENT_ERROR,        /*!< An error event. */
  EVENT_WARNING,      /*!< A warning event. */
  EVENT_EOS,          /*!< An EOS event. */
  EVENT_STOP,         /*!< A stop event. */
  EVENT_STREAM_ERROR, /*!< A stream error event. */
  EVENT_TYPE_END      /*!< Reserved for users custom events. */
};

/**
 * @enum EventHandleFlag
 *
 * @brief Enumeration variables describing the way how bus watchers handle an event.
 */
enum class EventHandleFlag {
  EVENT_HANDLE_NULL,         /*!< The event is not handled. */
  EVENT_HANDLE_INTERCEPTION, /*!< The event has been handled and other bus watchers needn't to handle it. */
  EVENT_HANDLE_SYNCED,       /*!< The event has been handled and other bus watchers are going to handle it. */
  EVENT_HANDLE_STOP          /*!< The event has been handled and bus watchers stop all other events' processing. */
};

/**
 * @struct Event
 *
 * @brief The Event is a structure describing the event information.
 */
struct Event {
  EventType type;             ///< The event type.
  std::string stream_id;      ///< The stream that posts this event.
  std::string message_id;    ///< The message type id.
  std::string message;        ///< More detailed messages describing the event.
  std::string module_name;    ///< The module that posts this event.
  std::thread::id thread_id;  ///< The thread ID from which the event is posted.
};

/**
 * @brief Defines an alias of bus watcher function.
 *
 * @param[in] event The event is polled from the event bus.
 *
 * @return Returns the flag that specifies how the event is handled.
 */
using BusWatcher = std::function<EventHandleFlag(const Event &)>;

/**
 * @class EventBus
 *
 * @brief EventBus is a class that transmits events from modules to a pipeline.
 */
class EventBus : private NonCopyable {
 public:
  friend class Pipeline;
  /**
   * @brief Destructor. A destructor to destruct event bus.
   *
   * @return No return value.
   */
  ~EventBus();
  /**
   * @brief Starts an event bus thread.
   *
   * @return Returns true if start successfully, otherwise false.
   */
  bool Start();
  /**
   * @brief Stops an event bus thread.
   *
   * @return No return values.
   */
  void Stop();
  /**
   * @brief Adds a watcher to the event bus.
   *
   * @param[in] func The bus watcher to be added.
   *
   * @return The number of bus watchers that has been added to this event bus.
   */
  uint32_t AddBusWatch(BusWatcher func);

  /**
   * @brief Posts an event to a bus.
   *
   * @param[in] event The event to be posted.
   *
   * @return Returns true if this function run successfully. Otherwise, returns false.
   */
  bool PostEvent(Event event);

#ifndef UNIT_TEST
 private:  // NOLINT
#else
  Event PollEventToTest();
#endif
  EventBus() = default;

  /**
   * @brief Polls an event from a bus.
   *
   * @return Returns the event.
   *
   * @note This function is blocked until an event availabe or the bus stopped.
   */
  Event PollEvent();

  /**
   * @brief Gets all bus watchers from the event bus.
   *
   * @return A list with pairs of bus watcher and module.
   */
  const std::list<BusWatcher> &GetBusWatchers() const;

  /**
   * @brief Removes all bus watchers.
   *
   * @return No return value.
   */
  void ClearAllWatchers();

  /**
   * @brief Checks if the event bus is running.
   *
   * @return Returns true if the event bus is running. Otherwise, returns false.
   */
  bool IsRunning();

  void EventLoop();

 private:
  mutable std::mutex watcher_mtx_;
  ThreadSafeQueue<Event> queue_;
#ifdef UNIT_TEST
  ThreadSafeQueue<Event> test_eventq_;
  bool unit_test = true;
#endif
  std::list<BusWatcher> bus_watchers_;
  std::thread event_thread_;
  std::atomic<bool> running_{false};
};  // class EventBus

}  // namespace cnstream

#endif  // CNSTREAM_EVENT_BUS_HPP_

2.2 framework/src/cnstream_eventbus.cpp

/*************************************************************************
 * Copyright (C) [2019] by Cambricon, Inc. All rights reserved
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *************************************************************************/

#include "cnstream_eventbus.hpp"

#include <list>
#include <memory>
#include <thread>
#include <utility>

#include "cnstream_pipeline.hpp"

namespace cnstream {

EventBus::~EventBus() { Stop(); }

bool EventBus::IsRunning() { return running_.load(); }

bool EventBus::Start() {
  running_.store(true);
  event_thread_ = std::thread(&EventBus::EventLoop, this);
  return true;
}

void EventBus::Stop() {
  if (IsRunning()) {
    running_.store(false);
    if (event_thread_.joinable()) {
      event_thread_.join();
    }
  }
}

// @return The number of bus watchers that has been added to this event bus.
uint32_t EventBus::AddBusWatch(BusWatcher func) {
  std::lock_guard<std::mutex> lk(watcher_mtx_);
  bus_watchers_.push_front(func);
  return bus_watchers_.size();
}

void EventBus::ClearAllWatchers() {
  std::lock_guard<std::mutex> lk(watcher_mtx_);
  bus_watchers_.clear();
}

const std::list<BusWatcher> &EventBus::GetBusWatchers() const {
  std::lock_guard<std::mutex> lk(watcher_mtx_);
  return bus_watchers_;
}

bool EventBus::PostEvent(Event event) {
  if (!running_.load()) {
    LOGW(CORE) << "Post event failed, pipeline not running";
    return false;
  }
  // LOGI(CORE) << "Receieve event from [" << event.module->GetName() << "] :" << event.message;
  queue_.Push(event);
#ifdef UNIT_TEST
  if (unit_test) {
    test_eventq_.Push(event);
    unit_test = false;
  }
#endif
  return true;
}

Event EventBus::PollEvent() {
  Event event;
  event.type = EventType::EVENT_INVALID;
  while (running_.load()) {
    if (queue_.WaitAndTryPop(event, std::chrono::milliseconds(100))) {
      break;
    }
  }
  if (!running_.load()) event.type = EventType::EVENT_STOP;
  return event;
}

void EventBus::EventLoop() {
  const std::list<BusWatcher> &kWatchers = GetBusWatchers();
  EventHandleFlag flag = EventHandleFlag::EVENT_HANDLE_NULL;

  // start loop
  while (IsRunning()) {
    Event event = PollEvent();
    if (event.type == EventType::EVENT_INVALID) {
      LOGI(CORE) << "[EventLoop] event type is invalid";
      break;
    } else if (event.type == EventType::EVENT_STOP) {
      LOGI(CORE) << "[EventLoop] Get stop event";
      break;
    }
    std::unique_lock<std::mutex> lk(watcher_mtx_);
    for (auto &watcher : kWatchers) {
      flag = watcher(event);
      if (flag == EventHandleFlag::EVENT_HANDLE_INTERCEPTION || flag == EventHandleFlag::EVENT_HANDLE_STOP) {
        break;
      }
    }
    if (flag == EventHandleFlag::EVENT_HANDLE_STOP) {
      break;
    }
  }
  LOGI(CORE) << "Event bus exit.";
}

#ifdef UNIT_TEST
Event EventBus::PollEventToTest() {
  Event event;
  event.type = EventType::EVENT_INVALID;
  while (running_.load()) {
    if (test_eventq_.WaitAndTryPop(event, std::chrono::milliseconds(100))) {
      break;
    }
  }
  if (!running_.load()) event.type = EventType::EVENT_STOP;
  return event;
}
#endif

}  // namespace cnstream

3 CNStream中EventBus代码分析、机制介绍

首先在Pipeline类里面就会有这样一个成员

  std::unique_ptr<EventBus> event_bus_ = nullptr;

然后反过来,在EventBus类里面又把Pipeline声明成了友元类,这样pipeline可以直接调用EventBus的函数进行相应的操作。

/**
 * @class EventBus
 *
 * @brief EventBus is a class that transmits events from modules to a pipeline.
 */
class EventBus : private NonCopyable {
 public:
  friend class Pipeline;

然后在Pipeline的构造函数里面会创建EventBus,并且调用AddBusWatch函数。

Pipeline::Pipeline(const std::string& name) : name_(name) {
  // stream message handle thread
  exit_msg_loop_ = false;
  smsg_thread_ = std::thread(&Pipeline::StreamMsgHandleFunc, this);

  event_bus_.reset(new (std::nothrow) EventBus());
  LOGF_IF(CORE, nullptr == event_bus_) << "Pipeline::Pipeline() failed to alloc EventBus";
  GetEventBus()->AddBusWatch(std::bind(&Pipeline::DefaultBusWatch, this, std::placeholders::_1));

  idxManager_.reset(new (std::nothrow) IdxManager());
  LOGF_IF(CORE, nullptr == idxManager_) << "Pipeline::Pipeline() failed to alloc IdxManager";

  graph_.reset(new (std::nothrow) CNGraph<NodeContext>());
  LOGF_IF(CORE, nullptr == graph_) << "Pipeline::Pipeline() failed to alloc CNGraph";
}

这里是给他添加了一个默认的watch函数

GetEventBus()->AddBusWatch(std::bind(&Pipeline::DefaultBusWatch, this, std::placeholders::_1));

这个默认的watch函数其实就是用来处理event的


EventHandleFlag Pipeline::DefaultBusWatch(const Event& event) {
  StreamMsg smsg;
  EventHandleFlag ret;
  switch (event.type) {
    case EventType::EVENT_ERROR:
      smsg.type = StreamMsgType::ERROR_MSG;
      smsg.module_name = event.module_name;
      smsg.stream_id = event.stream_id;
      UpdateByStreamMsg(smsg);
      LOGE(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_STOP;
      break;
    case EventType::EVENT_WARNING:
      LOGW(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    case EventType::EVENT_STOP:
      LOGI(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_STOP;
      break;
    case EventType::EVENT_EOS: {
      VLOG2(CORE) << "Pipeline received eos from module " + event.module_name << " of stream " << event.stream_id;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    }
    case EventType::EVENT_STREAM_ERROR: {
      smsg.type = StreamMsgType::STREAM_ERR_MSG;
      smsg.message_id = event.message_id;
      smsg.message = event.message;
      smsg.module_name = event.module_name;
      smsg.stream_id = event.stream_id;
      UpdateByStreamMsg(smsg);
      VLOG2(CORE) << "Pipeline received stream error from module " + event.module_name << " of stream "
                  << event.stream_id;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    }
    case EventType::EVENT_INVALID:
      LOGE(CORE) << "[" << event.module_name << "]: " << event.message;
    default:
      ret = EventHandleFlag::EVENT_HANDLE_NULL;
      break;
  }
  return ret;
}

除了上面的默认的watch函数,我们自己还会在外层再添加一个自己的watch函数


int build_pipeline(string const& config) {
    s_pipeline = std::shared_ptr<Pipeline>(new Pipeline("TACLPipeline"), [](Pipeline* pipeline) { pipeline->Stop(); });
    if (s_pipeline == nullptr) {
        return ERROR_ALLOC_MEMORY;
    }

    //build pipeline
    if (!s_pipeline->BuildPipelineByJSONFile(config)) {
        LOGE(SESSION_MGR) << "Build pipeline failed.";
        return EXIT_FAILURE;
    }

    //message observer
    static MsgObserver msg_observer(s_pipeline.get(), g_source_name);
    s_pipeline->SetStreamMsgObserver(reinterpret_cast<cnstream::StreamMsgObserver*>(&msg_observer));

    s_pipeline->GetEventBus()->AddBusWatch([&](const Event & event)->EventHandleFlag{
        if(event.module_name == "TuringPipeline/decode/source" && EventType::EVENT_EOS == event.type){
            LOGI(SESSION_MGR) << "recv bus event: " << static_cast<int8_t>(event.type) << "_" << event.module_name << "_" << event.stream_id << "_" << event.message;
            {
                std::string run_msg_str;
                std::unique_lock<std::mutex> locker(s_session_locker);
                auto it = s_task_sessions.find(event.stream_id);
                if (it != s_task_sessions.end()) {
                    LOGE(SESSION_MGR) << event.stream_id << "stream interruption!!!!!!!!!!";
                    //如果是文件就直接停止,如果不是文件就进入重连机制
                    if(it->second->get_video_type() == EFILE)
                    {
                        it->second->stop(g_source_name);
                        run_msg_str = it->second->post_end_mark();

                        LOGI(SESSION_MGR) << "run msg is: " << run_msg_str;

                        //puglish msg
                        cnstream::ActiveMQ* activemq_run_msg = dynamic_cast<cnstream::ActiveMQ*>(s_pipeline->GetModule(g_activemq_name));
                        if (activemq_run_msg != nullptr && run_msg_str != "") {
                            activemq_run_msg->PublishEvent(run_msg_str);
                        }

                        s_task_sessions.erase(it->first);
                    }
                    else if(it->second->get_s_state_() != S_USER_CLOSE)
                    {
                        it->second->set_s_state_(S_EXCEPTION_CLOSE);
                    }
                }
            }
        }
    });

    //start pipeline
    if (!s_pipeline->Start()) {
        LOGE(SESSION_MGR) << "Pipeline start failed.";
        return EXIT_FAILURE;
    }
    return ERROR_OK;
}

然后比如某个module会调用PostEvent推送event

    void FileHandlerImpl::Loop() {
        if (!SetCurrentDevice(param_.device_id)) return;

        if (!PrepareResources()) {
            ClearResources();
            if (nullptr != module_) {
                Event e;
                e.type = EventType::EVENT_STREAM_ERROR;
                e.module_name = module_->GetName();
                e.message = "Prepare codec resources failed.";
                e.stream_id = stream_id_;
                e.thread_id = std::this_thread::get_id();
                module_->PostEvent(e);
            }
            LOGE(SOURCE) << "[FileHandlerImpl] Loop(): [" << stream_id_ << "]: PrepareResources failed.";
            return;
        }

        set_thread_name("demux_decode");

        FrController controller(handle_param_.framerate);
        if (handle_param_.framerate > 0) controller.Start();

        VLOG1(SOURCE) << "[FileHandlerImpl] Loop(): [" << stream_id_ << "]: DecoderLoop";
        while (running_.load()) {
            if (!Process()) {
                break;
            }
            if (handle_param_.framerate > 0) controller.Control();
        }

        VLOG1(SOURCE) << "[FileHandlerImpl] Loop(): [" << stream_id_ << "]: DecoderLoop Exit.";
        ClearResources();
    }

然后module的PostEvent又调用总线的PostEvent

bool Module::PostEvent(Event e) {
  RwLockReadGuard guard(container_lock_);
  if (container_) {
    return container_->GetEventBus()->PostEvent(e);
  } else {
    LOGW(CORE) << "[" << GetName() << "] module's container is not set";
    return false;
  }
}

然后这个PostEvent其实就是把event放到队列ThreadSafeQueue<Event> queue_;里面

bool EventBus::PostEvent(Event event) {
  if (!running_.load()) {
    LOGW(CORE) << "Post event failed, pipeline not running";
    return false;
  }
  // LOGI(CORE) << "Receieve event from [" << event.module->GetName() << "] :" << event.message;
  queue_.Push(event);
#ifdef UNIT_TEST
  if (unit_test) {
    test_eventq_.Push(event);
    unit_test = false;
  }
#endif
  return true;
}

然后其实在程序刚开始启动的时候,有

bool EventBus::Start() {
  running_.store(true);
  event_thread_ = std::thread(&EventBus::EventLoop, this);
  return true;
}

然后


void EventBus::EventLoop() {
  const std::list<BusWatcher> &kWatchers = GetBusWatchers();
  EventHandleFlag flag = EventHandleFlag::EVENT_HANDLE_NULL;

  // start loop
  while (IsRunning()) {
    Event event = PollEvent();
    if (event.type == EventType::EVENT_INVALID) {
      LOGI(CORE) << "[EventLoop] event type is invalid";
      break;
    } else if (event.type == EventType::EVENT_STOP) {
      LOGI(CORE) << "[EventLoop] Get stop event";
      break;
    }
    std::unique_lock<std::mutex> lk(watcher_mtx_);
    for (auto &watcher : kWatchers) {
      flag = watcher(event);
      if (flag == EventHandleFlag::EVENT_HANDLE_INTERCEPTION || flag == EventHandleFlag::EVENT_HANDLE_STOP) {
        break;
      }
    }
    if (flag == EventHandleFlag::EVENT_HANDLE_STOP) {
      break;
    }
  }
  LOGI(CORE) << "Event bus exit.";
}

可以看到在这个循环里面,会从队列中PollEvent出来event然后调用watch函数,这里的const std::list<BusWatcher> &kWatchers = GetBusWatchers();就是前面增加的两个watch函数,一个是默认的watch函数,一个是我们用户自己定义的watch函数。

下面再回去,分别看一下这两个watch函数,首先看默认的watch函数


EventHandleFlag Pipeline::DefaultBusWatch(const Event& event) {
  StreamMsg smsg;
  EventHandleFlag ret;
  switch (event.type) {
    case EventType::EVENT_ERROR:
      smsg.type = StreamMsgType::ERROR_MSG;
      smsg.module_name = event.module_name;
      smsg.stream_id = event.stream_id;
      UpdateByStreamMsg(smsg);
      LOGE(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_STOP;
      break;
    case EventType::EVENT_WARNING:
      LOGW(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    case EventType::EVENT_STOP:
      LOGI(CORE) << "[" << event.module_name << "]: " << event.message;
      ret = EventHandleFlag::EVENT_HANDLE_STOP;
      break;
    case EventType::EVENT_EOS: {
      VLOG2(CORE) << "Pipeline received eos from module " + event.module_name << " of stream " << event.stream_id;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    }
    case EventType::EVENT_STREAM_ERROR: {
      smsg.type = StreamMsgType::STREAM_ERR_MSG;
      smsg.message_id = event.message_id;
      smsg.message = event.message;
      smsg.module_name = event.module_name;
      smsg.stream_id = event.stream_id;
      UpdateByStreamMsg(smsg);
      VLOG2(CORE) << "Pipeline received stream error from module " + event.module_name << " of stream "
                  << event.stream_id;
      ret = EventHandleFlag::EVENT_HANDLE_SYNCED;
      break;
    }
    case EventType::EVENT_INVALID:
      LOGE(CORE) << "[" << event.module_name << "]: " << event.message;
    default:
      ret = EventHandleFlag::EVENT_HANDLE_NULL;
      break;
  }
  return ret;
}

在这个watch函数里面有个UpdateByStreamMsg,

void Pipeline::UpdateByStreamMsg(const StreamMsg& msg) {
  VLOG2(CORE) << "[" << GetName() << "] "
              << "stream: " << msg.stream_id << " got message: " << static_cast<std::size_t>(msg.type);
  msgq_.Push(msg);
}

这里是把msgpush到了队列里面,  ThreadSafeQueue<StreamMsg> msgq_;

然后其实在Pipeline的构造函数里面起了一个线程StreamMsgHandleFunc

Pipeline::Pipeline(const std::string& name) : name_(name) {
  // stream message handle thread
  exit_msg_loop_ = false;
  smsg_thread_ = std::thread(&Pipeline::StreamMsgHandleFunc, this);

  event_bus_.reset(new (std::nothrow) EventBus());
  LOGF_IF(CORE, nullptr == event_bus_) << "Pipeline::Pipeline() failed to alloc EventBus";
  GetEventBus()->AddBusWatch(std::bind(&Pipeline::DefaultBusWatch, this, std::placeholders::_1));

  idxManager_.reset(new (std::nothrow) IdxManager());
  LOGF_IF(CORE, nullptr == idxManager_) << "Pipeline::Pipeline() failed to alloc IdxManager";

  graph_.reset(new (std::nothrow) CNGraph<NodeContext>());
  LOGF_IF(CORE, nullptr == graph_) << "Pipeline::Pipeline() failed to alloc CNGraph";
}

然后在这个线程里面就会从ThreadSafeQueue<StreamMsg> msgq_;里面不断的取出msg然后进行处理


void Pipeline::StreamMsgHandleFunc() {
  while (!exit_msg_loop_) {
    StreamMsg msg;
    while (!exit_msg_loop_ && !msgq_.WaitAndTryPop(msg, std::chrono::milliseconds(200))) {
    }

    if (exit_msg_loop_) {
      LOGI(CORE) << "[" << GetName() << "] stop updating stream message";
      return;
    }
    switch (msg.type) {
      case StreamMsgType::EOS_MSG:
      case StreamMsgType::ERROR_MSG:
      case StreamMsgType::STREAM_ERR_MSG:
      case StreamMsgType::FRAME_ERR_MSG:
      case StreamMsgType::USER_MSG0:
      case StreamMsgType::USER_MSG1:
      case StreamMsgType::USER_MSG2:
      case StreamMsgType::USER_MSG3:
      case StreamMsgType::USER_MSG4:
      case StreamMsgType::USER_MSG5:
      case StreamMsgType::USER_MSG6:
      case StreamMsgType::USER_MSG7:
      case StreamMsgType::USER_MSG8:
      case StreamMsgType::USER_MSG9:
        VLOG2(CORE) << "[" << GetName() << "] "
                    << "stream: " << msg.stream_id << " notify message: " << static_cast<std::size_t>(msg.type);
        if (smsg_observer_) {
          smsg_observer_->Update(msg);
        }
        break;
      default:
        break;
    }
  }
}

然后注意其中的smsg_observer_->Update(msg);,这个是我们自己定义的观察者函数,

class MsgObserver : cnstream::StreamMsgObserver {
public:
    MsgObserver(cnstream::Pipeline* pipeline, std::string source_name) :
        pipeline_(pipeline),
        source_name_(source_name) {}

    void Update(const cnstream::StreamMsg& smsg) override {
        switch (smsg.type) {
        case cnstream::StreamMsgType::EOS_MSG:
            LOGI(SESSION_MGR) << "[" << pipeline_->GetName() << "] received EOS message from stream: [" << smsg.stream_id << "]";
            //task_session_remove(smsg.stream_id);
            break;
        case cnstream::StreamMsgType::STREAM_ERR_MSG:
            LOGW(SESSION_MGR) << "[" << pipeline_->GetName() << "] received stream error from stream: " << smsg.stream_id
                << ", remove it from pipeline.";
            if (!smsg.message_id.empty()) {
                post_run_msg(smsg);
            }
            break;
        case cnstream::StreamMsgType::ERROR_MSG:
            LOGE(SESSION_MGR) << "[" << pipeline_->GetName() << "] received ERROR_MSG";
            break;
        case cnstream::StreamMsgType::FRAME_ERR_MSG:
            LOGW(SESSION_MGR) << "[" << pipeline_->GetName() << "] received frame error from stream: " << smsg.stream_id
                << ", pts: " << smsg.pts << ".";
            break;
        default:
            LOGE(SESSION_MGR) << "[" << pipeline_->GetName() << "] unkonw message type.";
            break;
        }
    }

private:
    void task_session_remove(string const& stream_id) {
        std::lock_guard<std::mutex> logcker(s_session_locker);
        auto iter = s_task_sessions.find(stream_id);
        if (iter != s_task_sessions.end()) {
            if (iter->second->get_video_type() == VideoType::EFILE)
            {
                s_task_sessions.erase(iter);
                LOGI(SESSION_MGR) << "task session mgr remove Task ID succeed: " << stream_id;
            }
        }
        else {
            LOGI(SESSION_MGR) << "task session mgr remvoe Task ID Failed, not find Task ID: " << stream_id;
        }
    }

private:
    cnstream::Pipeline* pipeline_ = nullptr;
    std::string source_name_;
};

所以这就相当于在update函数里面根据msg的不同类型做不同的处理。

然后再看另一个我们自己的第二个watch函数。

    s_pipeline->GetEventBus()->AddBusWatch([&](const Event & event)->EventHandleFlag{
        if(event.module_name == "TuringPipeline/decode/source" && EventType::EVENT_EOS == event.type){
            LOGI(SESSION_MGR) << "recv bus event: " << static_cast<int8_t>(event.type) << "_" << event.module_name << "_" << event.stream_id << "_" << event.message;
            {
                std::string run_msg_str;
                std::unique_lock<std::mutex> locker(s_session_locker);
                auto it = s_task_sessions.find(event.stream_id);
                if (it != s_task_sessions.end()) {
                    LOGE(SESSION_MGR) << event.stream_id << "stream interruption!!!!!!!!!!";
                    //如果是文件就直接停止,如果不是文件就进入重连机制
                    if(it->second->get_video_type() == EFILE)
                    {
                        it->second->stop(g_source_name);
                        run_msg_str = it->second->post_end_mark();

                        LOGI(SESSION_MGR) << "run msg is: " << run_msg_str;

                        //puglish msg
                        cnstream::ActiveMQ* activemq_run_msg = dynamic_cast<cnstream::ActiveMQ*>(s_pipeline->GetModule(g_activemq_name));
                        if (activemq_run_msg != nullptr && run_msg_str != "") {
                            activemq_run_msg->PublishEvent(run_msg_str);
                        }

                        s_task_sessions.erase(it->first);
                    }
                    else if(it->second->get_s_state_() != S_USER_CLOSE)
                    {
                        it->second->set_s_state_(S_EXCEPTION_CLOSE);
                    }
                }
            }
        }
    });

这个函数里面不需要将msg发给观察者了,直接在这里面做我们想做的处理。

参考文献:

手写EventBus(观察者模式、源码阅读、反射) - 简书

CNStream流处理多路并发Pipeline框架整体介绍-CSDN博客

寒武纪CNStream用户手册 — 寒武纪CNStream用户手册 6.2.0 文档

GitHub - Cambricon/CNStream: CNStream is a streaming framework for building Cambricon machine learning pipelines http://forum.cambricon.com https://gitee.com/SolutionSDK/CNStream

CNStream/docs/release_document/latest/Cambricon-CNStream-User-Guide-CN-vlatest.pdf at master · Cambricon/CNStream · GitHub

C++内存池Memory Pool的高级实现、代码详解、CMake构建工程、应用实例-CSDN博客

aclStream流处理多路并发Pipeline框架中VEncode Module代码调用流程整理、类的层次关系整理、回调函数赋值和调用流程整理-CSDN博客

 CNStream代码中C++反射机制的使用-CSDN博客

aclStream流处理多路并发Pipeline框架中 视频解码 代码调用流程整理、类的层次关系整理、回调函数赋值和调用流程整理-CSDN博客

GitHub - gelldur/EventBus: A lightweight and very fast event bus / event framework for C++17