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"
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
{
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
) {
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.
38 *error_info
= work
.Run();
40 // Notify the UIModelWorker that scheduled us that we have run
42 scheduler
->OnTaskCompleted();
43 work_done
->Signal(); // Unblock the syncer thread that scheduled us.
48 UIModelWorker::UIModelWorker()
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();
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?";
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();
113 syncapi_event_
.Signal(); // Notify that the syncapi produced work for us.
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