不落辰

知不可乎骤得,托遗响于悲风

0%

关于weak_ptr的使用

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
    58
    class 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的时候被销毁。

  • 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的生命周期的管理。可见即便是单线程也有困难。