// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_SYNC_GLUE_UI_MODEL_WORKER_H_ #define CHROME_BROWSER_SYNC_GLUE_UI_MODEL_WORKER_H_ #include "base/callback.h" #include "base/compiler_specific.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "sync/internal_api/public/engine/model_safe_worker.h" #include "sync/internal_api/public/util/unrecoverable_error_info.h" class MessageLoop; namespace browser_sync { // A syncer::ModelSafeWorker for UI models (e.g. bookmarks) that // accepts work requests from the syncapi that need to be fulfilled // from the MessageLoop home to the native model. // // Lifetime note: Instances of this class will generally be owned by the // SyncerThread. When the SyncerThread _object_ is destroyed, the // UIModelWorker will be destroyed. The SyncerThread object is destroyed // after the actual syncer pthread has exited. class UIModelWorker : public syncer::ModelSafeWorker { public: UIModelWorker(); // Called by the UI thread on shutdown of the sync service. Blocks until // the UIModelWorker has safely met termination conditions, namely that // no task scheduled by CallDoWorkFromModelSafeThreadAndWait remains un- // processed and that syncapi will not schedule any further work for us to do. void Stop(); // syncer::ModelSafeWorker implementation. Called on syncapi SyncerThread. virtual syncer::SyncerError DoWorkAndWaitUntilDone( const syncer::WorkCallback& work) OVERRIDE; virtual syncer::ModelSafeGroup GetModelSafeGroup() OVERRIDE; // Upon receiving this idempotent call, the syncer::ModelSafeWorker can // assume no work will ever be scheduled again from now on. If it has any work // that it has not yet completed, it must make sure to run it as soon as // possible as the Syncer is trying to shut down. Called from the CoreThread. void OnSyncerShutdownComplete(); // Callback from |pending_work_| to notify us that it has been run. // Called on ui loop. void OnTaskCompleted() { pending_work_.Reset(); } private: // The life-cycle of a UIModelWorker in three states. enum State { // We hit the ground running in this state and remain until // the UI loop calls Stop(). WORKING, // Stop() sequence has been initiated, but we have not received word that // the SyncerThread has terminated and doesn't need us anymore. Since the // UI MessageLoop is not running at this point, we manually process any // last pending_task_ that the Syncer throws at us, effectively dedicating // the UI thread to terminating the Syncer. RUNNING_MANUAL_SHUTDOWN_PUMP, // We have come to a complete stop, no scheduled work remains, and no work // will be scheduled from now until our destruction. STOPPED, }; virtual ~UIModelWorker(); // This is set by the UI thread, but is not explicitly thread safe, so only // read this value from other threads when you know it is absolutely safe. State state_; // We keep a reference to any task we have scheduled so we can gracefully // force them to run if the syncer is trying to shutdown. base::Closure pending_work_; // Set by the SyncCoreThread when Syncapi shutdown has completed and the // SyncerThread has terminated, so no more work will be scheduled. Read by // the UI thread in Stop(). bool syncapi_has_shutdown_; // We use a Lock for all data members and a ConditionVariable to synchronize. // We do this instead of using a WaitableEvent and a bool condition in order // to guard against races that could arise due to the fact that the lack of a // barrier permits instructions to be reordered by compiler optimizations. // Possible or not, that route makes for very fragile code due to existence // of theoretical races. base::Lock lock_; // Used as a barrier at shutdown to ensure the SyncerThread terminates before // we allow the UI thread to return from Stop(). This gets signalled whenever // one of two events occur: a new pending_work_ task was scheduled, or the // SyncerThread has terminated. We only care about (1) when we are in Stop(), // because we have to manually Run() the task. base::ConditionVariable syncapi_event_; DISALLOW_COPY_AND_ASSIGN(UIModelWorker); }; } // namespace browser_sync #endif // CHROME_BROWSER_SYNC_GLUE_UI_MODEL_WORKER_H_