1 // Copyright 2013 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/chromeos/drive/file_write_watcher.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path_watcher.h"
13 #include "base/stl_util.h"
14 #include "base/timer/timer.h"
15 #include "google_apis/drive/task_util.h"
21 const int64 kWriteEventDelayInSeconds
= 5;
24 // base::FileWatcher needs to live in a thread that is allowed to do File IO
25 // and has a TYPE_IO message loop: that is, FILE thread. This class bridges the
26 // UI thread and FILE thread, and does all the main tasks in the FILE thread.
27 class FileWriteWatcher::FileWriteWatcherImpl
{
29 explicit FileWriteWatcherImpl(base::SingleThreadTaskRunner
* file_task_runner
);
31 // Forwards the call to DestoryOnFileThread(). This method must be used to
32 // destruct the instance.
35 // Forwards the call to StartWatchOnFileThread(). |on_start_callback| is
36 // called back on the caller (UI) thread when the watch has started.
37 // |on_write_callback| is called when a write has happened to the path.
38 void StartWatch(const base::FilePath
& path
,
39 const StartWatchCallback
& on_start_callback
,
40 const base::Closure
& on_write_callback
);
42 void set_delay(base::TimeDelta delay
) { delay_
= delay
; }
45 ~FileWriteWatcherImpl();
47 void DestroyOnFileThread();
49 void StartWatchOnFileThread(const base::FilePath
& path
,
50 const StartWatchCallback
& on_start_callback
,
51 const base::Closure
& on_write_callback
);
53 void OnWriteEvent(const base::FilePath
& path
, bool error
);
55 void InvokeCallback(const base::FilePath
& path
);
57 struct PathWatchInfo
{
58 std::vector
<base::Closure
> on_write_callbacks
;
59 base::FilePathWatcher watcher
;
62 explicit PathWatchInfo(const base::Closure
& on_write_callback
)
63 : on_write_callbacks(1, on_write_callback
),
64 timer(false /* retain_closure_on_reset */, false /* is_repeating */) {
68 base::TimeDelta delay_
;
69 std::map
<base::FilePath
, PathWatchInfo
*> watchers_
;
70 scoped_refptr
<base::SingleThreadTaskRunner
> file_task_runner_
;
72 base::ThreadChecker thread_checker_
;
74 // Note: This should remain the last member so it'll be destroyed and
75 // invalidate its weak pointers before any other members are destroyed.
76 base::WeakPtrFactory
<FileWriteWatcherImpl
> weak_ptr_factory_
;
77 DISALLOW_COPY_AND_ASSIGN(FileWriteWatcherImpl
);
80 FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl(
81 base::SingleThreadTaskRunner
* file_task_runner
)
82 : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds
)),
83 file_task_runner_(file_task_runner
),
84 weak_ptr_factory_(this) {
87 void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
88 DCHECK(thread_checker_
.CalledOnValidThread());
90 // Just forwarding the call to FILE thread.
91 file_task_runner_
->PostTask(
92 FROM_HERE
, base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread
,
93 base::Unretained(this)));
96 void FileWriteWatcher::FileWriteWatcherImpl::StartWatch(
97 const base::FilePath
& path
,
98 const StartWatchCallback
& on_start_callback
,
99 const base::Closure
& on_write_callback
) {
100 DCHECK(thread_checker_
.CalledOnValidThread());
102 // Forwarding the call to FILE thread and relaying the |callback|.
103 file_task_runner_
->PostTask(
105 base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread
,
106 base::Unretained(this), path
,
107 google_apis::CreateRelayCallback(on_start_callback
),
108 google_apis::CreateRelayCallback(on_write_callback
)));
111 FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
112 DCHECK(file_task_runner_
->BelongsToCurrentThread());
114 STLDeleteContainerPairSecondPointers(watchers_
.begin(), watchers_
.end());
117 void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
118 DCHECK(file_task_runner_
->BelongsToCurrentThread());
123 void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread(
124 const base::FilePath
& path
,
125 const StartWatchCallback
& on_start_callback
,
126 const base::Closure
& on_write_callback
) {
127 DCHECK(file_task_runner_
->BelongsToCurrentThread());
129 std::map
<base::FilePath
, PathWatchInfo
*>::iterator it
= watchers_
.find(path
);
130 if (it
!= watchers_
.end()) {
131 // We are already watching the path.
132 on_start_callback
.Run(true);
133 it
->second
->on_write_callbacks
.push_back(on_write_callback
);
137 // Start watching |path|.
138 scoped_ptr
<PathWatchInfo
> info(new PathWatchInfo(on_write_callback
));
139 bool ok
= info
->watcher
.Watch(
142 base::Bind(&FileWriteWatcherImpl::OnWriteEvent
,
143 weak_ptr_factory_
.GetWeakPtr()));
144 watchers_
[path
] = info
.release();
145 on_start_callback
.Run(ok
);
148 void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
149 const base::FilePath
& path
,
151 DCHECK(file_task_runner_
->BelongsToCurrentThread());
156 std::map
<base::FilePath
, PathWatchInfo
*>::iterator it
= watchers_
.find(path
);
157 DCHECK(it
!= watchers_
.end());
159 // Heuristics for detecting the end of successive write operations.
160 // Delay running on_write_event_callback by |delay_| time, and if OnWriteEvent
161 // is called again in the period, the timer is reset. In other words, we
162 // invoke callback when |delay_| has passed after the last OnWriteEvent().
163 it
->second
->timer
.Start(FROM_HERE
,
165 base::Bind(&FileWriteWatcherImpl::InvokeCallback
,
166 weak_ptr_factory_
.GetWeakPtr(),
170 void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
171 const base::FilePath
& path
) {
172 DCHECK(file_task_runner_
->BelongsToCurrentThread());
174 std::map
<base::FilePath
, PathWatchInfo
*>::iterator it
= watchers_
.find(path
);
175 DCHECK(it
!= watchers_
.end());
177 std::vector
<base::Closure
> callbacks
;
178 callbacks
.swap(it
->second
->on_write_callbacks
);
182 for (size_t i
= 0; i
< callbacks
.size(); ++i
)
186 FileWriteWatcher::FileWriteWatcher(
187 base::SingleThreadTaskRunner
* file_task_runner
)
188 : watcher_impl_(new FileWriteWatcherImpl(file_task_runner
)) {
191 FileWriteWatcher::~FileWriteWatcher() {
192 DCHECK(thread_checker_
.CalledOnValidThread());
195 void FileWriteWatcher::StartWatch(const base::FilePath
& file_path
,
196 const StartWatchCallback
& on_start_callback
,
197 const base::Closure
& on_write_callback
) {
198 DCHECK(thread_checker_
.CalledOnValidThread());
199 watcher_impl_
->StartWatch(file_path
, on_start_callback
, on_write_callback
);
202 void FileWriteWatcher::DisableDelayForTesting() {
203 watcher_impl_
->set_delay(base::TimeDelta());
206 } // namespace internal