weak_ptr的使用 : 延长TCPConnection生命周期,防止channel->handleEvent时被析构
TcpConnection channel 的 tie before handleEvent
muduo中在class channel里使用了weak_ptr
class Channel and class TcpConnection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58class Channel
{
public:
// tie this channel to the owner object managed by shared_ptr
// to prevent the owner object being destroyed during channel's handleEvent;
void tie(const std::shared_ptr<void> &)
{
tie_ = ptr;
tied_ = true;
}
void Channel::handleEvent(const Timestamp& receiveTime)
{
// channel的所有者是TcpConnection对象
if(tied_)
{
// channel当前要处理tied_指向的TcpConnection的事件
// 提升成shared_ptr 防止在handleEvent的时候TcpConnection析构
std::shared_ptr<void> guard = tie_.lock();
if(guard) // 我认为一般不会发生guard == null 除非外部拿到并移走了TcpConnection内部的channel资源
{
handlerEventWithGuard(receiveTime);
}
}
// channel的所有者是Acceptor对象 没必要tie,因为Acceptor监听连接会贯穿server始终。
else
{
handlerEventWithGuard(receiveTime);
}
}
private:
// pointed to TcpConnection
std::weak_ptr<void> tie_;
// whether pointed to TcpConnection(与Acceptor区分开)
bool tied_;
some callback;
// reigistered by TcpConnectionPtr or Acceptor
// weak_ptr在上层注册者是TcpConnectionPtr时起作用
}
class TcpConnection
{
public:
// 连接建立
void TcpConnection::connectionEstablished()
{
// 防止channel执行回调时TcpConnection被析构
channel_->tie(shared_from_this());
// 给新建立的connfd 注册读事件到Poller
channel_->enableReading();
// user set the connectionCallback
connectionCallback_(shared_from_this());
}
private:
EventLoop *loop_;
std::unique_ptr<Channel> channel_; // channel for connfd to poller
}问题:为什么channel要使用一个weak_ptr 并在 handleEvent之前lock住TcpConnection ?
- TcpConnection是拥有一个Channel的,它负责管理该Channel的生命周期,但是却必须把Channel的裸指针暴露给EventLoop(确切的讲是暴露给Poller),因为Poller需要对Channel的事件进行管理(添加、修改、删除)。
- 也即channel、以及在其中注册的callback的生命周期都依赖于TcpConnection。
- 而TcpConnection又有可能在channel进行handleEvent的时候被销毁。
- TcpConnection是拥有一个Channel的,它负责管理该Channel的生命周期,但是却必须把Channel的裸指针暴露给EventLoop(确切的讲是暴露给Poller),因为Poller需要对Channel的事件进行管理(添加、修改、删除)。
weak_ptr
tie_; - 弱指针,指向channel自己的所有者:TcpConnection对象。
- channel在handleEvent之前,**lock提升为强智能指针(TcpConnection的引用计数+1)**(在lock之后 TcpConnection的引用计数才+1),防止在handleEvent的时候TcpConnection被析构,进而把当前正在调用的channel对象给析构,发生未定义事件。如果那样程序直接core dump都算个好结果。
- channel_->tie(shared_from_this());结束之后,TCPConnection的引用计数并没有+1,因为只是传递给了一个弱智能指针,只有要handleEvent,即lock之后引用计数才++
注意以上都是在一个loop的thread内对TcpConnection和channel的生命周期的管理。可见即便是单线程也有困难。