Cobalt 23网络抓取模块崩溃问题及解决方案合理性咨询
Cobalt 23.lts.3.310436 Net Fetcher模块崩溃问题排查与解决方案验证
问题背景
我们在Cobalt 23.lts.3.310436版本的net fetcher模块中多次遭遇崩溃。通过添加日志排查,定位到url_fetcher_core.cc文件,确认是线程安全问题:对象在仍被使用时被删除。
日志显示,地址2574072352的对象在SetLoadTimingInfo调用前后被删除,触发崩溃;而2574091304的对象使用后删除无异常。
经分析,Stop等方法在同一线程执行,GetLoadTimingInfo在另一线程执行,导致并发冲突。
我提出解决方案:通过PostTask将GetLoadTimingInfo调度到delegate线程执行,避免并发,确保delegate_指针检查与使用一致,同时微调代码。具体修改见下文代码。
请问该解决方案是否合理?另外,此问题可能与部分请求文件(如图片)在服务器上不可用有关,但尚未验证该关联。
日志详情
╭[NetworkModule/21:0306/170243.371985:INFO:url_request.cc(1171)] Call Run │[NetworkModule/21:0306/170243.372074:INFO:url_fetcher_core.cc(1134)] URLFetcherCore::GetLoadTimingInfo() delegate_=2574091304, this=2573807496, &timing_info=2574194072 │[NetworkModule/21:0306/170243.372140:INFO:url_fetcher_core.cc(1138)] Call ReportLoadTimingInfo, d=2574091304, delegate_=2574091304 │[NetworkModule/21:0306/170243.372233:INFO:net_fetcher.cc(305)] Call SetLoadTimingInfo, h=2574143520, this=2574091296, &timing_info=2574194072 │[NetworkModule/21:0306/170243.372308:INFO:fetcher.h(68)] SetLoadTimingInfo() this=2574143520, load_timing_info_callback_=2574143524 │[NetworkModule/21:0306/170243.372399:INFO:fetcher.h(70)] load_timing_info_callback_.is_null()=0 [MainWebModule/68:0306/170243.372732:INFO:url_fetcher_core.cc(181)] URLFetcherCore::Stop(), delegate_=2574121288, will now set it to NULL. ╰[NetworkModule/21:0306/170243.705613:INFO:fetcher.h(72)] Call load_timing_info_callback_.Run, &timing_info=2574194072 [MainWebModule/68:0306/170243.706208:INFO:url_fetcher_core.cc(181)] URLFetcherCore::Stop(), delegate_=2573905472, will now set it to NULL. ╭[NetworkModule/21:0306/170243.706464:INFO:url_request.cc(1171)] Call Run [MainWebModule/68:0306/170243.706509:INFO:url_fetcher_core.cc(181)] URLFetcherCore::Stop(), delegate_=2574091304, will now set it to NULL. │[NetworkModule/21:0306/170243.706545:INFO:url_fetcher_core.cc(1134)] URLFetcherCore::GetLoadTimingInfo() delegate_=2574072352, this=2573828040, &timing_info=2574229600 │[NetworkModule/21:0306/170243.706622:INFO:url_fetcher_core.cc(1138)] Call ReportLoadTimingInfo, d=2574072352, delegate_=2574072352 ![MainWebModule/68:0306/170243.706648:INFO:url_fetcher_core.cc(181)] URLFetcherCore::Stop(), delegate_=2574072352, will now set it to NULL. │[NetworkModule/21:0306/170243.706679:INFO:net_fetcher.cc(305)] Call SetLoadTimingInfo, h=2573730272, this=2574072344, &timing_info=2574229600 │signal 11
问题调用链
This call: delegate_->ReportLoadTimingInfo(timing_info); goes to this: void NetFetcher::ReportLoadTimingInfo(const net::LoadTimingInfo& timing_info) { // About here another thread comes in and set delegate_ to null. handler()->SetLoadTimingInfo(timing_info); } // About here the other thread deletes the URLFetcherDelegate object, that delegate_ points to. and down into this: virtual void SetLoadTimingInfo(const net::LoadTimingInfo& timing_info) { if (!load_timing_info_callback_.is_null()) { load_timing_info_callback_.Run(timing_info); } }
代码修改
--- a/net/url_request/url_fetcher_core.cc +++ b/net/url_request/url_fetcher_core.cc @@ -175,8 +175,9 @@ void URLFetcherCore::Start() { } void URLFetcherCore::Stop() { - if (delegate_task_runner_) // May be NULL in tests. + if (delegate_task_runner_) { // May be NULL in tests. DCHECK(delegate_task_runner_->RunsTasksInCurrentSequence()); + } delegate_ = NULL; fetcher_ = NULL; @@ -779,8 +780,11 @@ void URLFetcherCore::StartURLRequest() { if (!extra_request_headers_.IsEmpty()) request_->SetExtraRequestHeaders(extra_request_headers_); +#if defined(STARBOARD) request_->SetLoadTimingInfoCallback(base::Bind(&URLFetcherCore::GetLoadTimingInfo, base::Unretained(this))); +#endif + request_->Start(); } @@ -1129,6 +1133,14 @@ void URLFetcherCore::AssertHasNoUploadData() const { #if defined(STARBOARD) void URLFetcherCore::GetLoadTimingInfo( const net::LoadTimingInfo& timing_info) { + delegate_task_runner_->PostTask( + FROM_HERE, + base::Bind(&URLFetcherCore::GetLoadTimingInfoInDelegateThread, + this, timing_info)); +} + +void URLFetcherCore::GetLoadTimingInfoInDelegateThread( + const net::LoadTimingInfo& timing_info) { // Check if the URLFetcherCore has been stopped before. if (delegate_) { delegate_->ReportLoadTimingInfo(timing_info); diff --git a/net/url_request/url_fetcher_core.h b/net/url_request/url_fetcher_core.h index 4359ac5495..a06f82f721 100644 --- a/net/url_request/url_fetcher_core.h +++ b/net/url_request/url_fetcher_core.h @@ -164,6 +164,7 @@ class URLFetcherCore : public base::RefCountedThreadSafe<URLFetcherCore>, static void SetIgnoreCertificateRequests(bool ignored); #if defined (STARBOARD) void GetLoadTimingInfo(const net::LoadTimingInfo& timing_info); + void GetLoadTimingInfoInDelegateThread(const net::LoadTimingInfo& timing_info); #endif // defined(STARBOARD) private: friend class base::RefCountedThreadSafe<URLFetcherCore>;
解决方案合理性分析
这个解决方案是合理的,核心原因如下:
- 彻底消除竞态条件:原问题中
GetLoadTimingInfo在网络线程执行,而Stop在delegate线程执行,两者并发访问delegate_指针导致对象使用中被删除。通过PostTask将GetLoadTimingInfo的逻辑转发到delegate线程执行,让delegate_的检查和使用都在同一线程完成,从根源避免了多线程冲突。 - 符合线程模型规范:Cobalt基于Chromium线程模型,
delegate_task_runner是专门用于执行delegate相关操作的线程,将回调调度到该线程符合框架设计原则,不会引入新的线程安全问题。 - 代码修改严谨:新增的
GetLoadTimingInfoInDelegateThread方法保持了原有的业务逻辑,仅调整了执行线程;对Stop方法的大括号补全和SetLoadTimingInfoCallback的宏包裹都是规范的代码调整,没有破坏原有功能。
关于请求文件不可用的关联推测:当请求资源不存在时,网络请求会快速终止,触发Stop逻辑的时机更早,此时GetLoadTimingInfo的异步回调可能还未完成,更容易出现delegate_被删除后回调仍在执行的场景。这个关联是成立的,建议构造资源不可用的测试用例,验证是否能稳定复现崩溃,从而确认该触发条件。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

