1 // Copyright (c) 2010 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. 4 5 #include "chrome/browser/sync/glue/ui_model_worker.h" 6 7 #include "base/message_loop.h" 8 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 9 #include "base/synchronization/waitable_event.h" 10 #include "content/browser/browser_thread.h" 11 12 namespace browser_sync { 13 14 void UIModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) { 15 // In most cases, this method is called in WORKING state. It is possible this 16 // gets called when we are in the RUNNING_MANUAL_SHUTDOWN_PUMP state, because 17 // the UI loop has initiated shutdown but the syncer hasn't got the memo yet. 18 // This is fine, the work will get scheduled and run normally or run by our 19 // code handling this case in Stop(). Note there _no_ way we can be in here 20 // with state_ = STOPPED, so it is safe to read / compare in this case. 21 CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED); 22 23 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { 24 DLOG(WARNING) << "DoWorkAndWaitUntilDone called from " 25 << "ui_loop_. Probably a nested invocation?"; 26 work->Run(); 27 return; 28 } 29 30 // Create an unsignaled event to wait on. 31 base::WaitableEvent work_done(false, false); 32 { 33 // We lock only to avoid PostTask'ing a NULL pending_work_ (because it 34 // could get Run() in Stop() and call OnTaskCompleted before we post). 35 // The task is owned by the message loop as per usual. 36 base::AutoLock lock(lock_); 37 DCHECK(!pending_work_); 38 pending_work_ = new CallDoWorkAndSignalTask(work, &work_done, this); 39 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) { 40 LOG(WARNING) << "Could not post work to UI loop."; 41 pending_work_ = NULL; 42 syncapi_event_.Signal(); 43 return; 44 } 45 } 46 syncapi_event_.Signal(); // Notify that the syncapi produced work for us. 47 work_done.Wait(); 48 } 49 50 UIModelWorker::UIModelWorker() 51 : state_(WORKING), 52 pending_work_(NULL), 53 syncapi_has_shutdown_(false), 54 syncapi_event_(&lock_) { 55 } 56 57 UIModelWorker::~UIModelWorker() { 58 DCHECK_EQ(state_, STOPPED); 59 } 60 61 void UIModelWorker::OnSyncerShutdownComplete() { 62 base::AutoLock lock(lock_); 63 // The SyncerThread has terminated and we are no longer needed by syncapi. 64 // The UI loop initiated shutdown and is (or will be) waiting in Stop(). 65 // We could either be WORKING or RUNNING_MANUAL_SHUTDOWN_PUMP, depending 66 // on where we timeslice the UI thread in Stop; but we can't be STOPPED, 67 // because that would imply OnSyncerShutdownComplete already signaled. 68 DCHECK_NE(state_, STOPPED); 69 70 syncapi_has_shutdown_ = true; 71 syncapi_event_.Signal(); 72 } 73 74 void UIModelWorker::Stop() { 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 76 77 base::AutoLock lock(lock_); 78 DCHECK_EQ(state_, WORKING); 79 80 // We're on our own now, the beloved UI MessageLoop is no longer running. 81 // Any tasks scheduled or to be scheduled on the UI MessageLoop will not run. 82 state_ = RUNNING_MANUAL_SHUTDOWN_PUMP; 83 84 // Drain any final tasks manually until the SyncerThread tells us it has 85 // totally finished. There should only ever be 0 or 1 tasks Run() here. 86 while (!syncapi_has_shutdown_) { 87 if (pending_work_) 88 pending_work_->Run(); // OnTaskCompleted will set pending_work_ to NULL. 89 90 // Wait for either a new task or SyncerThread termination. 91 syncapi_event_.Wait(); 92 } 93 94 state_ = STOPPED; 95 } 96 97 ModelSafeGroup UIModelWorker::GetModelSafeGroup() { 98 return GROUP_UI; 99 } 100 101 bool UIModelWorker::CurrentThreadIsWorkThread() { 102 return BrowserThread::CurrentlyOn(BrowserThread::UI); 103 } 104 105 void UIModelWorker::CallDoWorkAndSignalTask::Run() { 106 if (!work_) { 107 // This can happen during tests or cases where there are more than just the 108 // default UIModelWorker in existence and it gets destroyed before 109 // the main UI loop has terminated. There is no easy way to assert the 110 // loop is running / not running at the moment, so we just provide cancel 111 // semantics here and short-circuit. 112 // TODO(timsteele): Maybe we should have the message loop destruction 113 // observer fire when the loop has ended, just a bit before it 114 // actually gets destroyed. 115 return; 116 } 117 work_->Run(); 118 119 // Sever ties with work_ to allow the sanity-checking above that we don't 120 // get run twice. 121 work_ = NULL; 122 123 // Notify the UIModelWorker that scheduled us that we have run 124 // successfully. 125 scheduler_->OnTaskCompleted(); 126 work_done_->Signal(); // Unblock the syncer thread that scheduled us. 127 } 128 129 } // namespace browser_sync 130