summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/glue/ui_model_worker.h
blob: 66a2a995e711a1362a0a948b15deb0c3bd7b2c4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) 2010 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_
#pragma once

#include "base/callback.h"
#include "base/condition_variable.h"
#include "base/lock.h"
#include "base/task.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"

namespace base {
class WaitableEvent;
}

class MessageLoop;

namespace browser_sync {

// A 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 browser_sync::ModelSafeWorker {
 public:
  explicit UIModelWorker(MessageLoop* ui_loop);
  virtual ~UIModelWorker();

  // A simple task to signal a waitable event after Run()ning a Closure.
  class CallDoWorkAndSignalTask : public Task {
   public:
    CallDoWorkAndSignalTask(Callback0::Type* work,
                            base::WaitableEvent* work_done,
                            UIModelWorker* scheduler)
        : work_(work), work_done_(work_done), scheduler_(scheduler) {
    }
    virtual ~CallDoWorkAndSignalTask() { }

    // Task implementation.
    virtual void Run();

   private:
    // Task data - a closure and a waitable event to signal after the work has
    // been done.
    Callback0::Type* work_;
    base::WaitableEvent* work_done_;

    // The UIModelWorker responsible for scheduling us.
    UIModelWorker* const scheduler_;

    DISALLOW_COPY_AND_ASSIGN(CallDoWorkAndSignalTask);
  };

  // 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();

  // ModelSafeWorker implementation. Called on syncapi SyncerThread.
  virtual void DoWorkAndWaitUntilDone(Callback0::Type* work);
  virtual ModelSafeGroup GetModelSafeGroup() { return GROUP_UI; }
  virtual bool CurrentThreadIsWorkThread();

  // Upon receiving this idempotent call, the 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_ = NULL; }

 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,
  };

  // 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.
  Task* 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_;

  // The UI model home-sweet-home MessageLoop.
  MessageLoop* const ui_loop_;

  // 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.
  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.
  ConditionVariable syncapi_event_;

  DISALLOW_COPY_AND_ASSIGN(UIModelWorker);
};

}  // namespace browser_sync

#endif  // CHROME_BROWSER_SYNC_GLUE_UI_MODEL_WORKER_H_