chromium 中 quic 模块警报器 QuicAlarm 介绍

1. 位置

该类的定义在 quic/src/net/quic/core/quic_alarm.h 中。

2. 构成

  • 该类是一个虚基类。
  • 构造函数只有一个:explicit QuicAlarm(QuicArenaScopedPtr delegate);
    上面 explicit 关键字只用于修饰构造函数,用来防止隐式转换。
    关于 explicit 的作用可参考:https://www.cnblogs.com/cutepig/archive/2009/01/14/1375917.html
  • 需要派生类实现的纯虚函数有两个,均是 protected:void SetImpl() 和 void CancelImpl();
  • 该类嵌入了一个 Delegate 类,该类也是一个虚基类。只有一个纯虚函数,是 public:void OnAlarm();
    从 2)可看出 QuicAlarm 的构造需要一个 Delegate 对象。
  • 其他方法及其用法可参见下表:
成员方法访问属性作用备注
void Set(QuicTime new_deadline)public设置警报器在 deadline 时发出警报如果警报器已设置,需要先取消它才能再设置
void Cancel()public取消警报器可以重复调用,只保证 OnAlarm() 不被调用
void Update(QuicTime new_deadline QuicTime:Delta granularity)public更新警报器的 deadline
bool IsSet() constpublic判断警报器是否已设置
QuicTime deadline() constpublic返回警报器的 deadline
virtual void UpdateImpl()protected警报器更新的实现先取消原有警报器,再设置警报器为 new_deadline
void Fire()protected调用委托对象的 OnAlarm()

注: QuicTime 是时间粒度为微秒的时间类。

3. 用法

经过上述梳理可以发现,需要警报服务的类 C 可以继承 Delegate,
从而委托具体的警报器(继承自 QuicAlarm)在发生警情的时候告知 C 有警情。
警报器类完成特定警报器的管理,包括更新警报器时间点、取消警报器、设置警报器等。
委托类,则完成警报发生时需要完成的响应,类似发生火警了那么就需要委托类去灭火。

3.1 测试用例

下面定义一个需要被告知销毁警报器的委托类:

class DestructiveDelegate : public QuicAlarm::Delegate {
public:
    DestructiveDelegate() : alarm_(nullptr) {}
    void set_alarm(QuicAlarm* alarm) { alarm_ = alarm; }
        void OnAlarm() override {
        DCHECK(alarm_);
        delete alarm_;
    }
private:
    QuicAlarm* alarm_;
};

下面定义一个警情为销毁警报器的警报器类:

class DestructiveAlarm : public QuicAlarm {
public:
    explicit DestructiveAlarm(DestructiveDelegate* delegate)
    : QuicAlarm(QuicArenaScopedPtr<DestructiveDelegate>(delegate)) {}
    void FireAlarm() { Fire(); }
protected:
    void SetImpl() override {}
    void CancelImpl() override {}
};

测试代码:

DestructiveDelegate* delegate(new DestructiveDelegate);                   // 构建委托对象
DestructiveAlarm* alarm = new DestructiveAlarm(delegate);                 // 构建一个警报器
delegate->set_alarm(alarm);                                               // 委托对象设置自己的警报器
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);   // 警报时间
alarm->Set(deadline);                                                     // 警报器设置自己的警报时间
// This should not crash, even though it will destroy alarm.
alarm→FireAlarm();                                                        // 警报器通知委托对象该销毁警报器啦
3.2 实际应用——以 QuicConnection 类为例

QuicConnection 类定义在 quic/src/net/quic/core/quic_connection.h
该类有多个警报器指针成员:

// An alarm that fires when an ACK should be sent to the peer.
  QuicArenaScopedPtr<QuicAlarm> ack_alarm_;
  // An alarm that fires when a packet needs to be retransmitted.
  QuicArenaScopedPtr<QuicAlarm> retransmission_alarm_;
  // An alarm that is scheduled when the SentPacketManager requires a delay
  // before sending packets and fires when the packet may be sent.
  QuicArenaScopedPtr<QuicAlarm> send_alarm_;

这些指针均是 QuicArenaScopedPtr 模板类对象,指向的位于 Arena 上的对象。
下面分析 ack_alarm_ 在 QuicConnection 中是如何使用的。

3.2.1 ack_alarm_ 构建

查看 QuicConnection 的构造函数可知, ack_alarm_ 的构建位于 QuicConnection 的构造函数的初始化列表。即:

ack_alarm_(alarm_factory_->CreateAlarm(arena_.New<AckAlarmDelegate>(this), &arena_))

由 3.1 可知,完整的警报需要两个类 Delegate 和 Alarm,从 ack_alarm_ 的构建来看,
我们可知其 Delegate 为 AckAlarmDelegate,该类包含一个 QuicConnection 类型的指针成员, 该类重写了 OnAlarm 方法。

警报器 Alarm 有 alarm_factory_->CreateAlarm() 创建,下面是 alarm_factory_ 的声明:

 QuicAlarmFactory* alarm_factory_;   // Not owned.

其中 QuicAlarmFactory 为一个虚基类,使用了工厂模式,用于创建 Alarm 对象。
因此若要知道 ack_alarm_ 的具体类别,必须找到 alarm_factory_ 的具体类别,下面我们分析在哪儿构造了 QuicConnection 对象。

3.2.2 QuicConnection 的使用

使用 QuicConnection 的地方很多,我们挑选了 quartc_session_test.cc 作为示例,该文件位于 quic/src/net/quic/quartc/ 下。
QuicConnection 对象创建代码如下:

std::unique_ptr<QuicConnection> CreateConnection(Perspective perspective) {
    QuartcPacketWriter* writer = perspective == Perspective::IS_CLIENT
                                     ? client_writer_.get()
                                     : server_writer_.get();
    QuicIpAddress ip;
    ip.FromString("0.0.0.0");
    bool owns_writer = false;
    if (!alarm_factory_) {
      // QuartcFactory is only used as an alarm factory.
      QuartcFactoryConfig config;
      config.clock = &quartc_clock_;
      config.task_runner = &task_runner_;
      alarm_factory_.reset(new QuartcFactory(config));
    }
    return std::unique_ptr<QuicConnection>(new QuicConnection(
        0, QuicSocketAddress(ip, 0), this /*QuicConnectionHelperInterface*/,
        alarm_factory_.get(), writer, owns_writer, perspective,
        AllSupportedVersions()));
  }

其中 alarm_factory_ 对象,其定义为:

std::unique_ptr<QuicAlarmFactory> alarm_factory_;

可以看到 alarm_factory_ 被赋值为 new QuartcFactory(config);
而 QuartcFactory 属于 QuicAlarmFactory 的派生类,因此必定实现了 CreateAlarm 方法。
找到 quic/src/net/quic/quartc/quartc_factory.cc 中 CreateAlarm 的实现之一:

QuicArenaScopedPtr<QuicAlarm> QuartcFactory::CreateAlarm(
    QuicArenaScopedPtr<QuicAlarm::Delegate> delegate,
    QuicConnectionArena* arena) {
  if (arena != nullptr) {
    return arena->New<QuartcAlarm>(GetClock(), task_runner_,
                                   std::move(delegate));
  }
  return QuicArenaScopedPtr<QuicAlarm>(
      new QuartcAlarm(GetClock(), task_runner_, std::move(delegate)));
}

因此,最终是创建了一个 QuartcAlarm 对象。

4. 总结

在 QuartcSessionTest 使用 QuicConnection 类的场景中,只有一种类型的警报器即 QuartcAlarm,
在 QuicConnection 类中有多种委托类使用了该类型的警报器:

  • AckAlarmDelegate:在超时发生时调用 OnAlarm() 发送一个 ack;
  • RetransmissionAlarmDelegate:警报发生时,connection 会检查最旧的包是否被确认,
    如果没有,那么调用 OnAlarm() 重传该 packet;
  • SendAlarmDelegate:
  • TimeoutAlarmDelegate:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值