Fix build break
[chromium-blink-merge.git] / chrome / browser / sync / glue / ui_model_worker.cc
blobc0336ba2ed8994b746bd6dfa766b010d1f10d2b9
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sync/glue/ui_model_worker.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/message_loop.h"
10 #include "base/synchronization/waitable_event.h"
11 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "content/public/browser/browser_thread.h"
15 using content::BrowserThread;
17 namespace browser_sync {
19 namespace {
21 // A simple callback to signal a waitable event after running a closure.
22 void CallDoWorkAndSignalCallback(const syncer::WorkCallback& work,
23 base::WaitableEvent* work_done,
24 UIModelWorker* const scheduler,
25 syncer::SyncerError* error_info) {
26 if (work.is_null()) {
27 // This can happen during tests or cases where there are more than just the
28 // default UIModelWorker in existence and it gets destroyed before
29 // the main UI loop has terminated. There is no easy way to assert the
30 // loop is running / not running at the moment, so we just provide cancel
31 // semantics here and short-circuit.
32 // TODO(timsteele): Maybe we should have the message loop destruction
33 // observer fire when the loop has ended, just a bit before it
34 // actually gets destroyed.
35 return;
38 *error_info = work.Run();
40 // Notify the UIModelWorker that scheduled us that we have run
41 // successfully.
42 scheduler->OnTaskCompleted();
43 work_done->Signal(); // Unblock the syncer thread that scheduled us.
46 } // namespace
48 UIModelWorker::UIModelWorker()
49 : state_(WORKING),
50 syncapi_has_shutdown_(false),
51 syncapi_event_(&lock_) {
54 void UIModelWorker::Stop() {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57 base::AutoLock lock(lock_);
58 DCHECK_EQ(state_, WORKING);
60 // We're on our own now, the beloved UI MessageLoop is no longer running.
61 // Any tasks scheduled or to be scheduled on the UI MessageLoop will not run.
62 state_ = RUNNING_MANUAL_SHUTDOWN_PUMP;
64 // Drain any final tasks manually until the SyncerThread tells us it has
65 // totally finished. There should only ever be 0 or 1 tasks Run() here.
66 while (!syncapi_has_shutdown_) {
67 if (!pending_work_.is_null())
68 pending_work_.Run(); // OnTaskCompleted will set reset |pending_work_|.
70 // http://crbug.com/19757
71 base::ThreadRestrictions::ScopedAllowWait allow_wait;
72 // Wait for either a new task or SyncerThread termination.
73 syncapi_event_.Wait();
76 state_ = STOPPED;
79 syncer::SyncerError UIModelWorker::DoWorkAndWaitUntilDone(
80 const syncer::WorkCallback& work) {
81 // In most cases, this method is called in WORKING state. It is possible this
82 // gets called when we are in the RUNNING_MANUAL_SHUTDOWN_PUMP state, because
83 // the UI loop has initiated shutdown but the syncer hasn't got the memo yet.
84 // This is fine, the work will get scheduled and run normally or run by our
85 // code handling this case in Stop(). Note there _no_ way we can be in here
86 // with state_ = STOPPED, so it is safe to read / compare in this case.
87 CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED);
88 syncer::SyncerError error_info;
89 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
90 DLOG(WARNING) << "DoWorkAndWaitUntilDone called from "
91 << "ui_loop_. Probably a nested invocation?";
92 return work.Run();
95 // Create an unsignaled event to wait on.
96 base::WaitableEvent work_done(false, false);
98 // We lock only to avoid PostTask'ing a NULL pending_work_ (because it
99 // could get Run() in Stop() and call OnTaskCompleted before we post).
100 // The task is owned by the message loop as per usual.
101 base::AutoLock lock(lock_);
102 DCHECK(pending_work_.is_null());
103 pending_work_ = base::Bind(&CallDoWorkAndSignalCallback, work, &work_done,
104 base::Unretained(this), &error_info);
105 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) {
106 DLOG(WARNING) << "Could not post work to UI loop.";
107 error_info = syncer::CANNOT_DO_WORK;
108 pending_work_.Reset();
109 syncapi_event_.Signal();
110 return error_info;
113 syncapi_event_.Signal(); // Notify that the syncapi produced work for us.
114 work_done.Wait();
115 return error_info;
118 syncer::ModelSafeGroup UIModelWorker::GetModelSafeGroup() {
119 return syncer::GROUP_UI;
122 void UIModelWorker::OnSyncerShutdownComplete() {
123 base::AutoLock lock(lock_);
124 // The SyncerThread has terminated and we are no longer needed by syncapi.
125 // The UI loop initiated shutdown and is (or will be) waiting in Stop().
126 // We could either be WORKING or RUNNING_MANUAL_SHUTDOWN_PUMP, depending
127 // on where we timeslice the UI thread in Stop; but we can't be STOPPED,
128 // because that would imply OnSyncerShutdownComplete already signaled.
129 DCHECK_NE(state_, STOPPED);
131 syncapi_has_shutdown_ = true;
132 syncapi_event_.Signal();
135 UIModelWorker::~UIModelWorker() {
136 DCHECK_EQ(state_, STOPPED);
139 } // namespace browser_sync