Commit 1d114582 authored by Sorin Jianu's avatar Sorin Jianu Committed by Commit Bot

Fix sequence checker asserts in CrxDownloader.

The code crashes because there is a race condition between
the main sequence and another task runner when the instances
of the CrxDownloader are destroyed.

On the main sequence, the CrxDownloader is being used to
download a file, then the reference to this instance
is released. The CrxDownloader is using a task runner
to run a blocking task, which validate the file it has just
downloaded. This task runner receives a closure which holds a
reference to the CrxDownloader.
The reference is released after the task is run, and therefore,
it results in a race when the dtor of CrxDownloader is being
invoked.

Besides removing the offending sequence checkers of the
destructors of the CrxDownloader classes, this CL changes
where the timer of the BackgroundDownloader is destroyed.
The timer has sequence affinity and it must always
be destroyed on the main sequence.

Bug: 1105289,1105460
Change-Id: I78e002502ce6f5501c78d1e849f51b47bf3dc092
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2297696Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#788330}
parent 4e73616a
...@@ -37,10 +37,10 @@ using base::win::ScopedCoMem; ...@@ -37,10 +37,10 @@ using base::win::ScopedCoMem;
// The class BackgroundDownloader in this module is an adapter between // The class BackgroundDownloader in this module is an adapter between
// the CrxDownloader interface and the BITS service interfaces. // the CrxDownloader interface and the BITS service interfaces.
// The interface exposed on the CrxDownloader code runs on the main thread, // The interface exposed on the CrxDownloader code runs on the main sequence,
// while the BITS specific code runs on a separate thread passed in by the // while the BITS specific code runs in a separate sequence bound to a
// client. For every url to download, a BITS job is created, unless there is // COM apartment. For every url to download, a BITS job is created, unless
// already an existing job for that url, in which case, the downloader // there is already an existing job for that url, in which case, the downloader
// connects to it. Once a job is associated with the url, the code looks for // connects to it. Once a job is associated with the url, the code looks for
// changes in the BITS job state. The checks are triggered by a timer. // changes in the BITS job state. The checks are triggered by a timer.
// The BITS job contains just one file to download. There could only be one // The BITS job contains just one file to download. There could only be one
...@@ -402,25 +402,24 @@ BackgroundDownloader::BackgroundDownloader( ...@@ -402,25 +402,24 @@ BackgroundDownloader::BackgroundDownloader(
git_cookie_bits_manager_(0), git_cookie_bits_manager_(0),
git_cookie_job_(0) {} git_cookie_job_(0) {}
BackgroundDownloader::~BackgroundDownloader() { BackgroundDownloader::~BackgroundDownloader() = default;
DCHECK(thread_checker_.CalledOnValidThread());
timer_.reset();
}
void BackgroundDownloader::StartTimer() { void BackgroundDownloader::StartTimer() {
timer_.reset(new base::OneShotTimer); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_ = std::make_unique<base::OneShotTimer>();
timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(kJobPollingIntervalSec), timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(kJobPollingIntervalSec),
this, &BackgroundDownloader::OnTimer); this, &BackgroundDownloader::OnTimer);
} }
void BackgroundDownloader::OnTimer() { void BackgroundDownloader::OnTimer() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_ = nullptr;
com_task_runner_->PostTask( com_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloading, this)); FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloading, this));
} }
void BackgroundDownloader::DoStartDownload(const GURL& url) { void BackgroundDownloader::DoStartDownload(const GURL& url) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
com_task_runner_->PostTask( com_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&BackgroundDownloader::BeginDownload, this, url)); base::BindOnce(&BackgroundDownloader::BeginDownload, this, url));
...@@ -580,10 +579,6 @@ void BackgroundDownloader::EndDownload(HRESULT error) { ...@@ -580,10 +579,6 @@ void BackgroundDownloader::EndDownload(HRESULT error) {
main_task_runner()->PostTask( main_task_runner()->PostTask(
FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadComplete, this, FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadComplete, this,
is_handled, result, download_metrics)); is_handled, result, download_metrics));
// Once the task is posted to the the main thread, this object may be deleted
// by its owner. It is not safe to access members of this object on this task
// runner from now on.
} }
// Called when the BITS job has been transferred successfully. Completes the // Called when the BITS job has been transferred successfully. Completes the
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
#include <memory> #include <memory>
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "components/update_client/crx_downloader.h" #include "components/update_client/crx_downloader.h"
...@@ -27,13 +27,12 @@ namespace update_client { ...@@ -27,13 +27,12 @@ namespace update_client {
// Implements a downloader in terms of the BITS service. The public interface // Implements a downloader in terms of the BITS service. The public interface
// of this class and the CrxDownloader overrides are expected to be called // of this class and the CrxDownloader overrides are expected to be called
// from the main thread. The rest of the class code runs on a sequenced // from the main sequence. The rest of the class code runs on a sequenced
// task runner, usually associated with a blocking thread pool. The task runner // task runner. The task runner must initialize COM.
// must initialize COM.
// //
// This class manages a COM client for Windows BITS. The client uses polling, // This class manages a COM client for Windows BITS. The client uses polling,
// triggered by an one-shot timer, to get state updates from BITS. Since the // triggered by an one-shot timer, to get state updates from BITS. Since the
// timer has thread afinity, the callbacks from the timer are delegated to // timer has sequence afinity, the callbacks from the timer are delegated to
// a sequenced task runner, which handles all client COM interaction with // a sequenced task runner, which handles all client COM interaction with
// the BITS service. // the BITS service.
class BackgroundDownloader : public CrxDownloader { class BackgroundDownloader : public CrxDownloader {
...@@ -48,8 +47,7 @@ class BackgroundDownloader : public CrxDownloader { ...@@ -48,8 +47,7 @@ class BackgroundDownloader : public CrxDownloader {
// Called asynchronously on the |com_task_runner_| at different stages during // Called asynchronously on the |com_task_runner_| at different stages during
// the download. |OnDownloading| can be called multiple times. // the download. |OnDownloading| can be called multiple times.
// |EndDownload| switches the execution flow from the |com_task_runner_| to // |EndDownload| switches the execution flow from the |com_task_runner_| to
// the main thread. Accessing any data members of this object from the // the main sequence.
// |com_task_runner_| after calling |EndDownload| is unsafe.
void BeginDownload(const GURL& url); void BeginDownload(const GURL& url);
void OnDownloading(); void OnDownloading();
void EndDownload(HRESULT hr); void EndDownload(HRESULT hr);
...@@ -104,13 +102,11 @@ class BackgroundDownloader : public CrxDownloader { ...@@ -104,13 +102,11 @@ class BackgroundDownloader : public CrxDownloader {
// Revokes the interface pointers from GIT. // Revokes the interface pointers from GIT.
HRESULT ClearGit(); HRESULT ClearGit();
// Updates the BITS interface pointers so that they can be used by the // Updates the BITS interface pointers so that the COM functions of these
// thread calling the function. Call this function to get valid COM interface // interfaces can be called in this COM STA apartment.
// pointers when a thread from the thread pool enters the object.
HRESULT UpdateInterfacePointers(); HRESULT UpdateInterfacePointers();
// Resets the BITS interface pointers. Call this function when a thread // Resets the BITS interface pointers.
// from the thread pool leaves the object to release the interface pointers.
void ResetInterfacePointers(); void ResetInterfacePointers();
// Returns the number of jobs in the BITS queue which were created by this // Returns the number of jobs in the BITS queue which were created by this
...@@ -120,13 +116,13 @@ class BackgroundDownloader : public CrxDownloader { ...@@ -120,13 +116,13 @@ class BackgroundDownloader : public CrxDownloader {
// Cleans up incompleted jobs that are too old. // Cleans up incompleted jobs that are too old.
void CleanupStaleJobs(); void CleanupStaleJobs();
// Ensures that we are running on the same thread we created the object on. // This sequence checker is bound to the main sequence.
base::ThreadChecker thread_checker_; SEQUENCE_CHECKER(sequence_checker_);
// Executes blocking COM calls to BITS. // Executes blocking COM calls to BITS.
scoped_refptr<base::SequencedTaskRunner> com_task_runner_; scoped_refptr<base::SequencedTaskRunner> com_task_runner_;
// The timer has thread affinity. This member is initialized and destroyed // The timer has sequence affinity. This member is created and destroyed
// on the main task runner. // on the main task runner.
std::unique_ptr<base::OneShotTimer> timer_; std::unique_ptr<base::OneShotTimer> timer_;
......
...@@ -26,9 +26,7 @@ UrlFetcherDownloader::UrlFetcherDownloader( ...@@ -26,9 +26,7 @@ UrlFetcherDownloader::UrlFetcherDownloader(
: CrxDownloader(std::move(successor)), : CrxDownloader(std::move(successor)),
network_fetcher_factory_(network_fetcher_factory) {} network_fetcher_factory_(network_fetcher_factory) {}
UrlFetcherDownloader::~UrlFetcherDownloader() { UrlFetcherDownloader::~UrlFetcherDownloader() = default;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void UrlFetcherDownloader::DoStartDownload(const GURL& url) { void UrlFetcherDownloader::DoStartDownload(const GURL& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment