| Index: components/sync/engine/model_safe_worker.cc
|
| diff --git a/components/sync/engine/model_safe_worker.cc b/components/sync/engine/model_safe_worker.cc
|
| index 530f206599673cf1b91239e5d40aeff463b0406c..773571675ba7806755af26c9d2ab6b1b7bec18f6 100644
|
| --- a/components/sync/engine/model_safe_worker.cc
|
| +++ b/components/sync/engine/model_safe_worker.cc
|
| @@ -4,6 +4,9 @@
|
|
|
| #include "components/sync/engine/model_safe_worker.h"
|
|
|
| +#include <utility>
|
| +
|
| +#include "base/bind.h"
|
| #include "base/json/json_writer.h"
|
| #include "base/values.h"
|
|
|
| @@ -69,24 +72,86 @@ std::string ModelSafeGroupToString(ModelSafeGroup group) {
|
| }
|
| }
|
|
|
| -ModelSafeWorker::ModelSafeWorker() {}
|
| +ModelSafeWorker::ModelSafeWorker()
|
| + : work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
|
| + base::WaitableEvent::InitialState::NOT_SIGNALED) {
|
| +}
|
| ModelSafeWorker::~ModelSafeWorker() {}
|
|
|
| void ModelSafeWorker::RequestStop() {
|
| - // Set stop flag. This prevents any *further* tasks from being posted to
|
| - // worker threads (see DoWorkAndWaitUntilDone below), but note that one may
|
| - // already be posted.
|
| - stopped_.Set();
|
| + base::AutoLock auto_lock(lock_);
|
| +
|
| + // Set stop flag to prevent any *further* WorkCallback from starting to run
|
| + // (note that one may alreay be running).
|
| + stopped_ = true;
|
| +
|
| + // If no work is running, unblock DoWorkAndWaitUntilDone(). If work is
|
| + // running, it is unsafe to return from DoWorkAndWaitUntilDone().
|
| + // ScopedSignalWorkDoneOrAbandoned will take care of signaling the event when
|
| + // the work is done.
|
| + if (!is_work_running_)
|
| + work_done_or_abandoned_.Signal();
|
| }
|
|
|
| -SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) {
|
| - if (stopped_.IsSet())
|
| - return CANNOT_DO_WORK;
|
| - return DoWorkAndWaitUntilDoneImpl(work);
|
| +SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(WorkCallback work) {
|
| + {
|
| + // It is important to check |stopped_| and reset |work_done_or_abandoned_|
|
| + // atomically to prevent this race:
|
| + //
|
| + // Thread Action
|
| + // Sync Sees that |stopped_| is false.
|
| + // UI Calls RequestStop(). Signals |work_done_or_abandoned_|.
|
| + // Sync Resets |work_done_or_abandoned_|.
|
| + // Waits on |work_done_or_abandoned_| forever since the task may not
|
| + // run after RequestStop() is called.
|
| + base::AutoLock auto_lock(lock_);
|
| + if (stopped_)
|
| + return CANNOT_DO_WORK;
|
| + DCHECK(!is_work_running_);
|
| + work_done_or_abandoned_.Reset();
|
| + }
|
| +
|
| + SyncerError error = UNSET;
|
| + bool did_run = false;
|
| + ScheduleWork(base::BindOnce(
|
| + &ModelSafeWorker::DoWork, this, base::Passed(std::move(work)),
|
| + base::Passed(base::ScopedClosureRunner(base::Bind(
|
| + [](scoped_refptr<ModelSafeWorker> worker) {
|
| + worker->work_done_or_abandoned_.Signal();
|
| + },
|
| + make_scoped_refptr(this)))),
|
| + base::Unretained(&error), base::Unretained(&did_run)));
|
| +
|
| + // Unblocked when the task runs or is deleted or when RequestStop() is called
|
| + // before the task starts running.
|
| + work_done_or_abandoned_.Wait();
|
| +
|
| + return did_run ? error : CANNOT_DO_WORK;
|
| }
|
|
|
| -bool ModelSafeWorker::IsStopped() {
|
| - return stopped_.IsSet();
|
| +void ModelSafeWorker::DoWork(WorkCallback work,
|
| + base::ScopedClosureRunner scoped_closure_runner,
|
| + SyncerError* error,
|
| + bool* did_run) {
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + if (stopped_)
|
| + return;
|
| +
|
| + // Set |is_work_running_| to make sure that DoWorkAndWaitUntilDone() doesn't
|
| + // return while |work| is running.
|
| + DCHECK(!is_work_running_);
|
| + is_work_running_ = true;
|
| + }
|
| +
|
| + *error = std::move(work).Run();
|
| + *did_run = true;
|
| +
|
| + {
|
| + base::AutoLock auto_lock(lock_);
|
| + DCHECK(is_work_running_);
|
| + is_work_running_ = false;
|
| + }
|
| }
|
|
|
| } // namespace syncer
|
|
|