CyberRT(apollo) 定时器模块简述及bug分析

timer 模块

timer的定义,cyberrt中timer模块用于设置定时器任务,字面意思,设置设置定时周期及出发频次(周期 or oneshot),到达指定时间时间触发callback

time wheel

时钟节拍轮,常见的定时器设计,例如ucos中的定时器,Linux Crontab等,cyberrt也是采用了时钟轮

时间轮(TimingWheel)简单来说,是一个 存储定时任务的循环队列,队列中的每个元素都可以放置一个定时任务列表(TimeBucket) 。TimeBucket 是一个list,链表中的每一项表示的都是定时任务(TimerTask)。

时钟轮示意图

alt text

类图

在这里插入图片描述

@startuml
class Timer{
+explicit Timer(TimerOption opt)
+void Start()
+void Stop()
-TimerOption timer_opt_;
-TimingWheel* timing_wheel_ = nullptr;
-std::shared_ptr<TimerTask> task_;
}
class TimingWheel {
    - TimerBucket work_wheel_[WORK_WHEEL_SIZE]
    - TimerBucket assistant_wheel_[ASSISTANT_WHEEL_SIZE]
    - std::thread tick_thread_
    + ~TimingWheel()
    + void Start()
    + void Shutdown()
    + void Tick()
    + void AddTask(const std::shared_ptr<TimerTask>& task)  
    + void TickFunc()

}
class TimerBucket {
    - std::mutex mutex_
    - std::list<std::weak_ptr<TimerTask>> task_list_

    + void AddTask(const std::shared_ptr<TimerTask>& task)
    + std::mutex& mutex()
    + std::list<std::weak_ptr<TimerTask>>& task_list()
}

struct TimerTask {
    + TimerTask(uint64_t timer_id)
    + uint64_t timer_id_
    + std::function<void()> callback
    + uint64_t interval_ms
    + uint64_t remainder_interval_ms
    + uint64_t next_fire_duration_ms
    + int64_t accumulated_error_ns
    + uint64_t last_execute_time_ns
    + std::mutex mutex
}
class TimerOption {
    + uint32_t period
    + std::function<void()> callback
    + bool oneshot
    + TimerOption(uint32_t period, std::function<void()> callback, bool oneshot)
    + TimerOption()
}

note left of Timer
  外部主要调用类,定时器对象实体
end note

note left of TimerOption
  配置参数,主要作为入参构造timer,用于配置定时器
end note

note left of TimingWheel
  环形队列,cyberrt内部实现为二级时钟节拍轮,单例
end note

note left of TimerBucket
  环形队列中的元素,为链表,存储std::weak_ptr<TimerTask>,
  等价线程安全的list<std::weak_ptr<TimerTask>>
end note

Timer --> TimerOption : 入参依赖 
Timer --> TimingWheel : 成员依赖 
TimingWheel --> TimerBucket : 成员依赖 
TimerBucket --> TimerTask : 成员依赖 

@enduml

实现原理

timingwheel 中的tick线程用于统计时间时间,并获取指向的时钟节拍论里面的元素进行任务触发,并将任务丢到cyberrt的携程池里运行,timewheel的实现基本都大同小异,这里不细说。

bug

bug主要是call back的生命周期管理问题,在代码中其实已经考虑了一部分(shared_ptr+weak_ptr)已经保证了一部分的悬空指针的问题,但是不完全。

 bool Timer::InitTimerTask() {
 
  task_.reset(new TimerTask(timer_id_));
  task_->interval_ms = timer_opt_.period;
  task_->next_fire_duration_ms = task_->interval_ms;
  if (timer_opt_.oneshot) {
    std::weak_ptr<TimerTask> task_weak_ptr = task_;
    task_->callback = [callback = this->timer_opt_.callback, task_weak_ptr]() {
      auto task = task_weak_ptr.lock();
      if (task) {
        std::lock_guard<std::mutex> lg(task->mutex);
        callback();
      }
    };
  } else {
    std::weak_ptr<TimerTask> task_weak_ptr = task_;
    task_->callback = [callback = this->timer_opt_.callback, task_weak_ptr]() {
      auto task = task_weak_ptr.lock();
      if (!task) {
        return;
      }
      XXXX //省略
      TimingWheel::Instance()->AddTask(task);
    };
  }
  return true;
}

void TimingWheel::Tick() {
  auto& bucket = work_wheel_[current_work_wheel_index_];
  {
    std::lock_guard<std::mutex> lock(bucket.mutex());
    auto ite = bucket.task_list().begin();
    while (ite != bucket.task_list().end()) {
      auto task = ite->lock();
      if (task) {
        ADEBUG << "index: " << current_work_wheel_index_
               << " timer id: " << task->timer_id_;
        auto* callback =
            reinterpret_cast<std::function<void()>*>(&(task->callback));
        cyber::Async([this, callback] {
          if (this->running_) {
            (*callback)();
          }
        });
      }
      ite = bucket.task_list().erase(ite);
    }
  }
}


以上代码,构建TimerTask添加到timing wheel中,task为share_ptr,所有权归属timer对象很合理,传递weak_ptr对象給timingWhee,timer对象释放后,节拍轮里面的weak_ptr不再有效进行不必要的触发,这也很正确,合理的考虑了timer对象持有的task和timingwheel中task的异步生命周期管理的问题。

但是在tick函数中,将触发的task放到异步协程池运行的时候则出现问题了,未考虑异步生命周期的问题。

现在假设我们有这样一个场景

创建了一个10ms的周期timer,经过第一个10ms时触发了定时器并将回掉丢到协程池中并推出tick,假设当时协程池比较繁忙,有恰巧我们迅速释放掉timer对象或者stop掉timer对象,you lose,程序boom了。

因为传递task这个share_ptr对象里的callback 成员函数进行了异步调用,tick函数内虽然正确获取了task,退出该函数,task引用计数-1,于此同时我们stop了timer 对象s或者 析构了对象,time持有的task,引用再次-1归0,tick函数内cyber::Async虽然正确获取了task的callback,但此时,已经是悬空指针了。

修改

知道了原因,改起来也很方便,无非就是异步调用指针的管理而已。

void TimingWheel::Tick() {
  auto& bucket = work_wheel_[current_work_wheel_index_];
  {
    std::lock_guard<std::mutex> lock(bucket.mutex());
    auto ite = bucket.task_list().begin();
    while (ite != bucket.task_list().end()) {
      auto task = ite->lock();
      if (task) {
        ADEBUG << "index: " << current_work_wheel_index_
               << " timer id: " << task->timer_id_;
       // auto* callback =
       //   reinterpret_cast<std::function<void()>*>(&(task->callback));
        cyber::Async([this, weakTask = std::weak_ptr<TimerTask>(task)] {
  	  auto task = weakTask->lock();
          if (this->running_ && task ) {
            task->callback();
          }
        });
      }
      ite = bucket.task_list().erase(ite);
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981160.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全月度报告

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 3.1.1网络安全现状及安全挑战 网络的出现给人们的工作和生活带来了极大的便利&#xff0c;但同时也带来了极大的安全风险。在信息传输和交换时&#xff0c;需要对…

nio多线程版本

多线程多路复用 多线程NIO&#xff0c;&#xff0c;就是多个线程&#xff0c;每个线程上都有一个Selector&#xff0c;&#xff0c;&#xff0c;比如说一个系统中一个线程用来接收请求&#xff0c;&#xff0c;剩余的线程用来读写数据&#xff0c;&#xff0c;每个线程独立干自…

一周学会Flask3 Python Web开发-Flask3之表单处理WTForms安装与定义WTForms表单类

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们平时开发项目&#xff0c;都会用到表单&#xff0c;编写表单&#xff0c;提交表单&#xff0c;验证表单&#xff0c;如果…

基于NI USRP 硬件的下一代O-RAN研究测试台​

目录 基于NI SDR硬件的下一代O-RAN研究测试台​挑战&#xff1a;解决方案&#xff1a; 基于NI SDR硬件的下一代O-RAN研究测试台​ “OAIC提供了一个开放平台&#xff08;包括软件架构、库和工具集&#xff09;&#xff0c;用于对基于AI的无线接入网(RAN)控制器进行原型开发和测…

【开源-鸿蒙土拨鼠大理石系统】鸿蒙 HarmonyOS Next App+微信小程序+云平台

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍GitHub开源项目地址&#x1f449;&#xff1a;https://github.com/cheinlu/HarmonyOS-groundhog-mar…

笔记本电脑本地部署ollama大模型(显存不足调用CUDA Unified Memory方法)

软硬件&#xff1a;win11,NVIDIA GeForce RTX 3050 显存4g 一.ollama模型最低要求 1. Llama 3.1 (8B) 模型 GPU: 至少需要 1 张具有 16 GB 显存的 GPU&#xff08;例如 NVIDIA Tesla V100 或 A100&#xff09;。CPU: 高性能的多核处理器&#xff08;例如 Intel Xeon 或 AMD …

【Rancher】简化Kubernetes容器管理与部署的开源平台

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是Rancher 2、Rancher诞生里程 …

vscode通过ssh远程连接(linux系统)不能跳转问题

1.问题描述 unbantu中的vscode能够通过函数跳转到函数定义&#xff0c;而windows通过ssh连接unbantu的vscode却无法跳转 2.原因&#xff1a; 主要原因是这里缺少插件&#xff0c;这里是unbantu给主机的服务器&#xff0c;与ubantu本地vscode插件相互独立&#xff0c;能否跳转…

思维链 Chain-of-Thought Prompting

论文: Chain-of-Thought Prompting Elicits Reasoning in Large Language Models (Wei et al., 2022) 核心贡献: 首次提出通过显式的中间推理步骤&#xff08;即思维链&#xff09;提升大语言模型的复杂推理能力。该方法通过示例展示多步推理过程&#xff0c;引导模型生成逻辑…

计算机毕业设计SpringBoot+Vue.js体育馆管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

MySQL笔记---Ubuntu环境下从零开始的MySQL

1. 安装MySQL 1.1 自动安装&#xff08;固定版本&#xff09; 更新软件包列表&#xff1a;在终端中执行以下命令&#xff0c;以更新系统的软件包列表&#xff1a; sudo apt update安装MySQL服务器&#xff1a;运行以下命令安装MySQL服务器&#xff1a; sudo apt install mysql…

【六祎 - Note】SQL备忘录;DDL,DML,DQL,DCL

SQL备忘录 from to : 点击访问源地址

简易的微信聊天网页版【项目测试报告】

文章目录 一、项目背景二、项目简介登录功能好友列表页面好友会话页面 三、测试工具和环境四、测试计划测试用例部分人工手动测试截图web自动化测试测试用例代码框架配置内容代码文件&#xff08;Utils.py&#xff09;登录页面代码文件&#xff08;WeChatLogin.py&#xff09;好…

FinRobot:一个使用大型语言模型进行金融分析的开源AI代理平台

文章目录 前言一、生态系统1. 金融AI代理&#xff08;Financial AI Agents&#xff09;2. 金融大型语言模型&#xff08;Financial LLMs&#xff09;3. LLMOps4. 数据操作&#xff08;DataOps&#xff09;5. 多源LLM基础模型&#xff08;Multi-Source LLM Foundation Models&am…

【软考-架构】1.3、磁盘-输入输出技术-总线

GitHub地址&#xff1a;https://github.com/tyronczt/system_architect ✨资料&文章更新✨ 文章目录 存储系统&#x1f4af;考试真题输入输出技术&#x1f4af;考试真题第一题第二题 存储系统 寻道时间是指磁头移动到磁道所需的时间&#xff1b; 等待时间为等待读写的扇区…

USRP4120-通用软件无线电平台

1、产品描述 USRP4120平台是彬鸿科技公司推出的以XILINX XC7Z020 SOC处理器为核心&#xff0c;搭配ADI AD9361射频集成芯片&#xff0c;针对无线通信系统科研与教学实验场景的一款通用软件无线电平台。产品频率范围70MHz~6GHz&#xff0c;模拟带宽200KHz~56MHz&#xff0c;支持…

MAVEN的安装和配置指南【超详细】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装Maven1.下载适合自己的版本2.配置环境变量3.验证环境变量是否配置成功 二、MAVEN的配置1.配置本地仓库2.配置镜像仓库3.创建一个简单的Maven项目 总结 …

数据结构:二叉搜索树(排序树)

1.二叉搜索树的定义 二叉搜索树要么是空树&#xff0c;要么是满足以下特性的树 &#xff08;1&#xff09;左子树不为空&#xff0c;那么左子树左右节点的值都小于根节点的值 &#xff08;2&#xff09;右子树不为空&#xff0c;那么右子树左右节点的值都大于根节点的值 &#…

Observability:使用 Elastic Agent 跟踪你的 Steam Deck 游戏

作者&#xff1a;来自 Elastic AndersonQ 让我们以不同的方式看待可观察性&#xff0c;并使用我们最喜欢的工具来监控我们的游戏性能。今天&#xff0c;我们将探讨如何使用 Elastic Agent 来监控 Steam Deck&#xff0c;以便我们可以看到我们玩得最多的游戏、它们消耗了多少资源…

20250227解决飞凌OK3588-C的linux R4通过adb拷贝文件速度过慢的问题

20250227解决飞凌OK3588-C的linux R4通过adb拷贝文件速度过慢的问题 2025/2/27 16:51 缘起&#xff1a;最近测试OK3588-C的最新的R1版本的SDK&#xff0c;adb pull的速度为28.8 MB/s Z:\version\OK3588-C_Linux5.10.209Qt5.15.10_用户资料_R1 我司使用4线的USB2.0&#xff0c;…