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() const | public | 判断警报器是否已设置 | — |
QuicTime deadline() const | public | 返回警报器的 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: