blob: c1b48150079a795ed97971c0a9d7df2fda4f5434 [file] [log] [blame]
[email protected]799d53bf2013-08-07 06:59:561// 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.
4
lukasza3fb22622015-08-27 21:04:345#include "components/drive/file_write_watcher.h"
[email protected]799d53bf2013-08-07 06:59:566
7#include <map>
[email protected]8df572cc2013-08-22 12:51:098#include <vector>
[email protected]799d53bf2013-08-07 06:59:569
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"
[email protected]e196bef32013-12-03 05:33:1315#include "google_apis/drive/task_util.h"
[email protected]799d53bf2013-08-07 06:59:5616
[email protected]799d53bf2013-08-07 06:59:5617namespace drive {
18namespace internal {
19
20namespace {
21const int64 kWriteEventDelayInSeconds = 5;
22} // namespace
23
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.
27class FileWriteWatcher::FileWriteWatcherImpl {
28 public:
lukasza2ff73e642015-06-11 03:52:4329 explicit FileWriteWatcherImpl(base::SingleThreadTaskRunner* file_task_runner);
[email protected]799d53bf2013-08-07 06:59:5630
31 // Forwards the call to DestoryOnFileThread(). This method must be used to
32 // destruct the instance.
33 void Destroy();
34
[email protected]8df572cc2013-08-22 12:51:0935 // 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.
[email protected]799d53bf2013-08-07 06:59:5638 void StartWatch(const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:0939 const StartWatchCallback& on_start_callback,
40 const base::Closure& on_write_callback);
[email protected]799d53bf2013-08-07 06:59:5641
42 void set_delay(base::TimeDelta delay) { delay_ = delay; }
43
44 private:
45 ~FileWriteWatcherImpl();
46
47 void DestroyOnFileThread();
48
49 void StartWatchOnFileThread(const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:0950 const StartWatchCallback& on_start_callback,
51 const base::Closure& on_write_callback);
[email protected]799d53bf2013-08-07 06:59:5652
53 void OnWriteEvent(const base::FilePath& path, bool error);
54
55 void InvokeCallback(const base::FilePath& path);
56
57 struct PathWatchInfo {
[email protected]8df572cc2013-08-22 12:51:0958 std::vector<base::Closure> on_write_callbacks;
[email protected]799d53bf2013-08-07 06:59:5659 base::FilePathWatcher watcher;
60 base::Timer timer;
61
[email protected]8df572cc2013-08-22 12:51:0962 explicit PathWatchInfo(const base::Closure& on_write_callback)
63 : on_write_callbacks(1, on_write_callback),
[email protected]799d53bf2013-08-07 06:59:5664 timer(false /* retain_closure_on_reset */, false /* is_repeating */) {
65 }
66 };
67
68 base::TimeDelta delay_;
69 std::map<base::FilePath, PathWatchInfo*> watchers_;
lukasza2ff73e642015-06-11 03:52:4370 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
[email protected]799d53bf2013-08-07 06:59:5671
lukasza037c10b12015-06-12 04:21:2572 base::ThreadChecker thread_checker_;
73
[email protected]799d53bf2013-08-07 06:59:5674 // 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);
78};
79
lukasza2ff73e642015-06-11 03:52:4380FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl(
81 base::SingleThreadTaskRunner* file_task_runner)
[email protected]799d53bf2013-08-07 06:59:5682 : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)),
lukasza2ff73e642015-06-11 03:52:4383 file_task_runner_(file_task_runner),
[email protected]799d53bf2013-08-07 06:59:5684 weak_ptr_factory_(this) {
[email protected]799d53bf2013-08-07 06:59:5685}
86
87void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
lukasza037c10b12015-06-12 04:21:2588 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:5689
90 // Just forwarding the call to FILE thread.
lukasza2ff73e642015-06-11 03:52:4391 file_task_runner_->PostTask(
92 FROM_HERE, base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread,
93 base::Unretained(this)));
[email protected]799d53bf2013-08-07 06:59:5694}
95
96void FileWriteWatcher::FileWriteWatcherImpl::StartWatch(
97 const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:0998 const StartWatchCallback& on_start_callback,
99 const base::Closure& on_write_callback) {
lukasza037c10b12015-06-12 04:21:25100 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:56101
102 // Forwarding the call to FILE thread and relaying the |callback|.
lukasza2ff73e642015-06-11 03:52:43103 file_task_runner_->PostTask(
[email protected]799d53bf2013-08-07 06:59:56104 FROM_HERE,
105 base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread,
lukasza2ff73e642015-06-11 03:52:43106 base::Unretained(this), path,
[email protected]8df572cc2013-08-22 12:51:09107 google_apis::CreateRelayCallback(on_start_callback),
108 google_apis::CreateRelayCallback(on_write_callback)));
[email protected]799d53bf2013-08-07 06:59:56109}
110
111FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
lukasza2ff73e642015-06-11 03:52:43112 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56113
114 STLDeleteContainerPairSecondPointers(watchers_.begin(), watchers_.end());
115}
116
117void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
lukasza2ff73e642015-06-11 03:52:43118 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56119
120 delete this;
121}
122
123void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread(
124 const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:09125 const StartWatchCallback& on_start_callback,
126 const base::Closure& on_write_callback) {
lukasza2ff73e642015-06-11 03:52:43127 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56128
129 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
130 if (it != watchers_.end()) {
[email protected]8df572cc2013-08-22 12:51:09131 // We are already watching the path.
132 on_start_callback.Run(true);
133 it->second->on_write_callbacks.push_back(on_write_callback);
[email protected]799d53bf2013-08-07 06:59:56134 return;
135 }
136
137 // Start watching |path|.
[email protected]8df572cc2013-08-22 12:51:09138 scoped_ptr<PathWatchInfo> info(new PathWatchInfo(on_write_callback));
[email protected]799d53bf2013-08-07 06:59:56139 bool ok = info->watcher.Watch(
140 path,
141 false, // recursive
142 base::Bind(&FileWriteWatcherImpl::OnWriteEvent,
143 weak_ptr_factory_.GetWeakPtr()));
144 watchers_[path] = info.release();
[email protected]8df572cc2013-08-22 12:51:09145 on_start_callback.Run(ok);
[email protected]799d53bf2013-08-07 06:59:56146}
147
148void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
149 const base::FilePath& path,
150 bool error) {
lukasza2ff73e642015-06-11 03:52:43151 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56152
153 if (error)
154 return;
155
156 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
157 DCHECK(it != watchers_.end());
158
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,
164 delay_,
165 base::Bind(&FileWriteWatcherImpl::InvokeCallback,
166 weak_ptr_factory_.GetWeakPtr(),
167 path));
168}
169
170void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
171 const base::FilePath& path) {
lukasza2ff73e642015-06-11 03:52:43172 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56173
174 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
175 DCHECK(it != watchers_.end());
176
[email protected]8df572cc2013-08-22 12:51:09177 std::vector<base::Closure> callbacks;
178 callbacks.swap(it->second->on_write_callbacks);
[email protected]799d53bf2013-08-07 06:59:56179 delete it->second;
180 watchers_.erase(it);
181
[email protected]8df572cc2013-08-22 12:51:09182 for (size_t i = 0; i < callbacks.size(); ++i)
183 callbacks[i].Run();
[email protected]799d53bf2013-08-07 06:59:56184}
185
lukasza2ff73e642015-06-11 03:52:43186FileWriteWatcher::FileWriteWatcher(
187 base::SingleThreadTaskRunner* file_task_runner)
188 : watcher_impl_(new FileWriteWatcherImpl(file_task_runner)) {
[email protected]799d53bf2013-08-07 06:59:56189}
190
191FileWriteWatcher::~FileWriteWatcher() {
lukasza037c10b12015-06-12 04:21:25192 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:56193}
194
195void FileWriteWatcher::StartWatch(const base::FilePath& file_path,
[email protected]8df572cc2013-08-22 12:51:09196 const StartWatchCallback& on_start_callback,
197 const base::Closure& on_write_callback) {
lukasza037c10b12015-06-12 04:21:25198 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]8df572cc2013-08-22 12:51:09199 watcher_impl_->StartWatch(file_path, on_start_callback, on_write_callback);
[email protected]799d53bf2013-08-07 06:59:56200}
201
202void FileWriteWatcher::DisableDelayForTesting() {
203 watcher_impl_->set_delay(base::TimeDelta());
204}
205
[email protected]799d53bf2013-08-07 06:59:56206} // namespace internal
207} // namespace drive