Chrome源代码分析之socket(五)

本文深入探讨了Chrome浏览器中ObjectWatcher的工作原理及其在网络I/O监控中的应用。详细介绍了StartWatching函数如何设置信号量监听,并在I/O完成后触发后续操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接着前面分析,通过对Chrome线程模型的分析以及对ObjectWatcher的初步了解,可以知道,ObjectWatcher通用在Chrome的几种线程中,包括处理界面操作的线程,网络I/O的线程以及其他线程,对于网络I/OObjectWatcher在初始化后与某个I/O信号量绑定,然后将其自身加入到某个线程的队列里面等待线程的处理,一旦信号量变为已执行状态,线程将调用WatchRun函数执行对该I/O的后续操作。可以看出,我们实际上是将一个完整的对象放入到指定的线程中去并使其代码在需要的时候得到执行(非本线程), 要理解这是如何实现的需要详细了解Chrome的线程模型,我将在另一个系列中再对其进行分析,现在回到正题,看看StartWatching是怎么做的:

bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
  if (watch_) {
    NOTREACHED() << "Already watching an object";
    return false;
  }
  Watch* watch = new Watch;
  watch->watcher = this;
  watch->object = object;
  watch->origin_loop = MessageLoop::current();
  watch->delegate = delegate;
  watch->did_signal = false;
  DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;

  if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
                                   watch, INFINITE, wait_flags)) {
    NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
    delete watch;
    return false;
  }
  watch_ = watch;
  MessageLoop::current()->AddDestructionObserver(this);
  return true;
}

函数首先创建一个Watch,然后初始化它,初始化的内容包括指向所属ObjectWatcher的指针,信号量句柄,线程的循环对象的指针,delegate对象以及标识。然后调用RegisterWaitForSingleObject将该信号量向线程池注册,线程池选择一个系统创建的线程来监听信号量,当信号量对应的I/O执行完之后,DoneWaiting将该线程调用。最后,MessageLoop还会调用AddDestructionObserver注册销毁通知。接着我们再看看DoneWaiting如何实现,DoneWaiting只有一个参数,一个指向在StartWatching初始化的Watch结构体的指针,DoneWaiting也只有一个动作,就是通过这个Watch指针找到对应的MessageLoop,然后通过调用PostTaskWatch放入该线程的队列中,这样WatchRun函数就能在I/O线程中得到执行。

Run函数就2个动作,watcher->StopWatching();停止本信号量在线程池中的注册, delegate->OnObjectSignaled(object);执行网络I/O的后续操作。delegate来自TCPClientSocketWin的成员变量WriteDelegate对象,之前我们已经介绍过,它的唯一目的就是指向对I/O的后续操作,还是以write为例,在OnObjectSignaled中调用的是core_->socket_->DidCompleteWrite();看看DidCompleteWrite的代码:

void TCPClientSocketWin::DidCompleteWrite() {
  DCHECK(waiting_write_);
  DWORD num_bytes, flags;
  BOOL ok = WSAGetOverlappedResult(socket_, &core_->write_overlapped_,
                                   &num_bytes, FALSE, &flags);
  WSAResetEvent(core_->write_overlapped_.hEvent);
  waiting_write_ = false;
  int rv;
  if (!ok) {
    rv = MapWinsockError(WSAGetLastError());
  } else {
    rv = static_cast<int>(num_bytes);
    if (rv > core_->write_buffer_length_ || rv < 0) {
      LOG(ERROR) << "Detected broken LSP: Asked to write "
                 << core_->write_buffer_length_ << " bytes, but " << rv
                 << " bytes reported.";
      rv = ERR_WINSOCK_UNEXPECTED_WRITTEN_BYTES;
    } else {
      static base::StatsCounter write_bytes("tcp.write_bytes");
      write_bytes.Add(num_bytes);
      if (num_bytes > 0)
        use_history_.set_was_used_to_convey_data();
      LogByteTransfer(net_log_, NetLog::TYPE_SOCKET_BYTES_SENT, num_bytes,
                      core_->write_buffer_.buf);
    }
  }
  core_->write_iobuffer_ = NULL;
  DoWriteCallback(rv);
}

代码很简单,首先调用WSAGetOverlappedResult取得该信号量对应I/O操作的结果,信号量在之前已经赋给了core_->write_overlapped_,而实际的数据一定是保存在core_->write_buffer_当中。最后调用DoWriteCallback执行上层传递下来的回调函数。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值