Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: chrome/browser/sync/glue/ui_model_worker.cc

Issue 14046031: Worker changes to prepare for lock-free shutdown. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/sync/glue/ui_model_worker.h" 5 #include "chrome/browser/sync/glue/ui_model_worker.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "base/synchronization/waitable_event.h" 10 #include "base/synchronization/waitable_event.h"
11 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 11 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
12 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
13 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/browser_thread.h"
14 14
15 using content::BrowserThread; 15 using content::BrowserThread;
16 16
17 namespace browser_sync { 17 namespace browser_sync {
18 18
19 namespace { 19 UIModelWorker::UIModelWorker(syncer::WorkerLoopDestructionObserver* observer)
20 20 : syncer::ModelSafeWorker(observer),
21 // A simple callback to signal a waitable event after running a closure. 21 state_(WORKING),
22 void CallDoWorkAndSignalCallback(const syncer::WorkCallback& work, 22 syncapi_has_shutdown_(false),
23 base::WaitableEvent* work_done, 23 syncapi_event_(&lock_) {
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;
36 }
37
38 *error_info = work.Run();
39
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.
44 } 24 }
45 25
46 } // namespace 26 void UIModelWorker::RegisterForLoopDestruction() {
47 27 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
48 UIModelWorker::UIModelWorker() 28 MessageLoop::current()->AddDestructionObserver(this);
49 : state_(WORKING),
50 syncapi_has_shutdown_(false),
51 syncapi_event_(&lock_) {
52 } 29 }
53 30
54 void UIModelWorker::Stop() { 31 void UIModelWorker::Stop() {
55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
56 33
57 base::AutoLock lock(lock_); 34 base::AutoLock lock(lock_);
58 DCHECK_EQ(state_, WORKING); 35 DCHECK_EQ(state_, WORKING);
59 36
60 // We're on our own now, the beloved UI MessageLoop is no longer running. 37 // 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. 38 // Any tasks scheduled or to be scheduled on the UI MessageLoop will not run.
62 state_ = RUNNING_MANUAL_SHUTDOWN_PUMP; 39 state_ = RUNNING_MANUAL_SHUTDOWN_PUMP;
63 40
64 // Drain any final tasks manually until the SyncerThread tells us it has 41 // 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. 42 // totally finished. There should only ever be 0 or 1 tasks Run() here.
66 while (!syncapi_has_shutdown_) { 43 while (!syncapi_has_shutdown_) {
67 if (!pending_work_.is_null()) 44 if (!pending_work_.is_null())
68 pending_work_.Run(); // OnTaskCompleted will set reset |pending_work_|. 45 pending_work_.Run(); // OnTaskCompleted will set reset |pending_work_|.
69 46
70 // http://crbug.com/19757 47 // http://crbug.com/19757
71 base::ThreadRestrictions::ScopedAllowWait allow_wait; 48 base::ThreadRestrictions::ScopedAllowWait allow_wait;
72 // Wait for either a new task or SyncerThread termination. 49 // Wait for either a new task or SyncerThread termination.
73 syncapi_event_.Wait(); 50 syncapi_event_.Wait();
74 } 51 }
75 52
76 state_ = STOPPED; 53 state_ = STOPPED;
77 } 54 }
78 55
79 syncer::SyncerError UIModelWorker::DoWorkAndWaitUntilDone( 56 syncer::SyncerError UIModelWorker::DoWorkAndWaitUntilDoneImpl(
80 const syncer::WorkCallback& work) { 57 const syncer::WorkCallback& work, base::WaitableEvent* done) {
81 // In most cases, this method is called in WORKING state. It is possible this 58 // 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 59 // 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. 60 // 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 61 // 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 62 // 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. 63 // with state_ = STOPPED, so it is safe to read / compare in this case.
87 CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED); 64 CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED);
88 syncer::SyncerError error_info; 65 syncer::SyncerError error_info;
89 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 66 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
90 DLOG(WARNING) << "DoWorkAndWaitUntilDone called from " 67 DLOG(WARNING) << "DoWorkAndWaitUntilDone called from "
91 << "ui_loop_. Probably a nested invocation?"; 68 << "ui_loop_. Probably a nested invocation?";
92 return work.Run(); 69 return work.Run();
93 } 70 }
94 71
95 // Create an unsignaled event to wait on.
96 base::WaitableEvent work_done(false, false);
97 { 72 {
98 // We lock only to avoid PostTask'ing a NULL pending_work_ (because it 73 // 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). 74 // could get Run() in Stop() and call OnTaskCompleted before we post).
100 // The task is owned by the message loop as per usual. 75 // The task is owned by the message loop as per usual.
101 base::AutoLock lock(lock_); 76 base::AutoLock lock(lock_);
102 DCHECK(pending_work_.is_null()); 77 DCHECK(pending_work_.is_null());
103 pending_work_ = base::Bind(&CallDoWorkAndSignalCallback, work, &work_done, 78 pending_work_ = base::Bind(&UIModelWorker::CallDoWorkAndSignalCallback,
104 base::Unretained(this), &error_info); 79 this, work, done, &error_info);
105 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) { 80 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) {
106 DLOG(WARNING) << "Could not post work to UI loop."; 81 DLOG(WARNING) << "Could not post work to UI loop.";
107 error_info = syncer::CANNOT_DO_WORK; 82 error_info = syncer::CANNOT_DO_WORK;
108 pending_work_.Reset(); 83 pending_work_.Reset();
109 syncapi_event_.Signal(); 84 syncapi_event_.Signal();
110 return error_info; 85 return error_info;
111 } 86 }
112 } 87 }
113 syncapi_event_.Signal(); // Notify that the syncapi produced work for us. 88 syncapi_event_.Signal(); // Notify that the syncapi produced work for us.
114 work_done.Wait(); 89 done->Wait();
tim (not reviewing) 2013/05/17 20:51:33 Do we need to pass the event around? It's a membe
haitaol1 2013/05/17 22:06:57 Added protected accessor. On 2013/05/17 20:51:33,
115 return error_info; 90 return error_info;
116 } 91 }
117 92
93 void UIModelWorker::CallDoWorkAndSignalCallback(
94 const syncer::WorkCallback& work,
95 base::WaitableEvent* work_done,
96 syncer::SyncerError* error_info) {
97 if (work.is_null()) {
98 // This can happen during tests or cases where there are more than just the
99 // default UIModelWorker in existence and it gets destroyed before
tim (not reviewing) 2013/05/17 20:51:33 You're not passing UIModelWorker* to a static func
haitaol1 2013/05/17 22:06:57 The UI worker (or any worker) will not be destroye
100 // the main UI loop has terminated. There is no easy way to assert the
101 // loop is running / not running at the moment, so we just provide cancel
102 // semantics here and short-circuit.
103 // TODO(timsteele): Maybe we should have the message loop destruction
104 // observer fire when the loop has ended, just a bit before it
105 // actually gets destroyed.
106 return;
107 }
108
109 if (!IsStopped())
tim (not reviewing) 2013/05/17 20:51:33 This is hard to follow because RequestStop sets st
haitaol1 2013/05/17 22:06:57 Reverted. On 2013/05/17 20:51:33, timsteele wrote
110 *error_info = work.Run();
111 else
112 *error_info = syncer::CANNOT_DO_WORK;
113
114 // Notify the UIModelWorker that scheduled us that we have run
115 // successfully.
116 OnTaskCompleted();
tim (not reviewing) 2013/05/17 20:51:33 OnTaskCompleted is now notifying |this| instead of
haitaol1 2013/05/17 22:06:57 Yes
117 work_done->Signal(); // Unblock the syncer thread that scheduled us.
118 }
119
118 syncer::ModelSafeGroup UIModelWorker::GetModelSafeGroup() { 120 syncer::ModelSafeGroup UIModelWorker::GetModelSafeGroup() {
119 return syncer::GROUP_UI; 121 return syncer::GROUP_UI;
120 } 122 }
121 123
122 void UIModelWorker::OnSyncerShutdownComplete() { 124 void UIModelWorker::OnSyncerShutdownComplete() {
123 base::AutoLock lock(lock_); 125 base::AutoLock lock(lock_);
124 // The SyncerThread has terminated and we are no longer needed by syncapi. 126 // 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(). 127 // 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 128 // 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, 129 // on where we timeslice the UI thread in Stop; but we can't be STOPPED,
128 // because that would imply OnSyncerShutdownComplete already signaled. 130 // because that would imply OnSyncerShutdownComplete already signaled.
129 DCHECK_NE(state_, STOPPED); 131 DCHECK_NE(state_, STOPPED);
130 132
131 syncapi_has_shutdown_ = true; 133 syncapi_has_shutdown_ = true;
132 syncapi_event_.Signal(); 134 syncapi_event_.Signal();
133 } 135 }
134 136
135 UIModelWorker::~UIModelWorker() { 137 UIModelWorker::~UIModelWorker() {
136 DCHECK_EQ(state_, STOPPED); 138 DCHECK_EQ(state_, STOPPED);
137 } 139 }
138 140
139 } // namespace browser_sync 141 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698