blob: 9c813298fe12f0b09392f6c9abfbf23969c5d2d5 [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
avibc5337b2015-12-25 23:16:337#include <stddef.h>
8#include <stdint.h>
9
[email protected]799d53bf2013-08-07 06:59:5610#include <map>
[email protected]8df572cc2013-08-22 12:51:0911#include <vector>
[email protected]799d53bf2013-08-07 06:59:5612
13#include "base/bind.h"
14#include "base/callback.h"
15#include "base/files/file_path_watcher.h"
avibc5337b2015-12-25 23:16:3316#include "base/macros.h"
avi92a3e962016-09-21 02:50:2117#include "base/memory/ptr_util.h"
gab10ae4362016-11-02 23:34:5318#include "base/memory/ref_counted.h"
19#include "base/single_thread_task_runner.h"
[email protected]799d53bf2013-08-07 06:59:5620#include "base/timer/timer.h"
[email protected]e196bef32013-12-03 05:33:1321#include "google_apis/drive/task_util.h"
[email protected]799d53bf2013-08-07 06:59:5622
[email protected]799d53bf2013-08-07 06:59:5623namespace drive {
24namespace internal {
25
26namespace {
avibc5337b2015-12-25 23:16:3327const int64_t kWriteEventDelayInSeconds = 5;
[email protected]799d53bf2013-08-07 06:59:5628} // namespace
29
30// base::FileWatcher needs to live in a thread that is allowed to do File IO
31// and has a TYPE_IO message loop: that is, FILE thread. This class bridges the
32// UI thread and FILE thread, and does all the main tasks in the FILE thread.
33class FileWriteWatcher::FileWriteWatcherImpl {
34 public:
lukasza2ff73e642015-06-11 03:52:4335 explicit FileWriteWatcherImpl(base::SingleThreadTaskRunner* file_task_runner);
[email protected]799d53bf2013-08-07 06:59:5636
37 // Forwards the call to DestoryOnFileThread(). This method must be used to
38 // destruct the instance.
39 void Destroy();
40
[email protected]8df572cc2013-08-22 12:51:0941 // Forwards the call to StartWatchOnFileThread(). |on_start_callback| is
42 // called back on the caller (UI) thread when the watch has started.
43 // |on_write_callback| is called when a write has happened to the path.
[email protected]799d53bf2013-08-07 06:59:5644 void StartWatch(const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:0945 const StartWatchCallback& on_start_callback,
46 const base::Closure& on_write_callback);
[email protected]799d53bf2013-08-07 06:59:5647
48 void set_delay(base::TimeDelta delay) { delay_ = delay; }
49
50 private:
51 ~FileWriteWatcherImpl();
52
53 void DestroyOnFileThread();
54
55 void StartWatchOnFileThread(const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:0956 const StartWatchCallback& on_start_callback,
57 const base::Closure& on_write_callback);
[email protected]799d53bf2013-08-07 06:59:5658
59 void OnWriteEvent(const base::FilePath& path, bool error);
60
61 void InvokeCallback(const base::FilePath& path);
62
63 struct PathWatchInfo {
[email protected]8df572cc2013-08-22 12:51:0964 std::vector<base::Closure> on_write_callbacks;
[email protected]799d53bf2013-08-07 06:59:5665 base::FilePathWatcher watcher;
66 base::Timer timer;
67
[email protected]8df572cc2013-08-22 12:51:0968 explicit PathWatchInfo(const base::Closure& on_write_callback)
69 : on_write_callbacks(1, on_write_callback),
[email protected]799d53bf2013-08-07 06:59:5670 timer(false /* retain_closure_on_reset */, false /* is_repeating */) {
71 }
72 };
73
74 base::TimeDelta delay_;
avi92a3e962016-09-21 02:50:2175 std::map<base::FilePath, std::unique_ptr<PathWatchInfo>> watchers_;
lukasza2ff73e642015-06-11 03:52:4376 scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
[email protected]799d53bf2013-08-07 06:59:5677
lukasza037c10b12015-06-12 04:21:2578 base::ThreadChecker thread_checker_;
79
[email protected]799d53bf2013-08-07 06:59:5680 // Note: This should remain the last member so it'll be destroyed and
81 // invalidate its weak pointers before any other members are destroyed.
82 base::WeakPtrFactory<FileWriteWatcherImpl> weak_ptr_factory_;
83 DISALLOW_COPY_AND_ASSIGN(FileWriteWatcherImpl);
84};
85
lukasza2ff73e642015-06-11 03:52:4386FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl(
87 base::SingleThreadTaskRunner* file_task_runner)
[email protected]799d53bf2013-08-07 06:59:5688 : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)),
lukasza2ff73e642015-06-11 03:52:4389 file_task_runner_(file_task_runner),
[email protected]799d53bf2013-08-07 06:59:5690 weak_ptr_factory_(this) {
[email protected]799d53bf2013-08-07 06:59:5691}
92
93void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
lukasza037c10b12015-06-12 04:21:2594 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:5695
96 // Just forwarding the call to FILE thread.
lukasza2ff73e642015-06-11 03:52:4397 file_task_runner_->PostTask(
98 FROM_HERE, base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread,
99 base::Unretained(this)));
[email protected]799d53bf2013-08-07 06:59:56100}
101
102void FileWriteWatcher::FileWriteWatcherImpl::StartWatch(
103 const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:09104 const StartWatchCallback& on_start_callback,
105 const base::Closure& on_write_callback) {
lukasza037c10b12015-06-12 04:21:25106 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:56107
108 // Forwarding the call to FILE thread and relaying the |callback|.
lukasza2ff73e642015-06-11 03:52:43109 file_task_runner_->PostTask(
[email protected]799d53bf2013-08-07 06:59:56110 FROM_HERE,
111 base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread,
lukasza2ff73e642015-06-11 03:52:43112 base::Unretained(this), path,
[email protected]8df572cc2013-08-22 12:51:09113 google_apis::CreateRelayCallback(on_start_callback),
114 google_apis::CreateRelayCallback(on_write_callback)));
[email protected]799d53bf2013-08-07 06:59:56115}
116
117FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
lukasza2ff73e642015-06-11 03:52:43118 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56119}
120
121void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
lukasza2ff73e642015-06-11 03:52:43122 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56123
124 delete this;
125}
126
127void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread(
128 const base::FilePath& path,
[email protected]8df572cc2013-08-22 12:51:09129 const StartWatchCallback& on_start_callback,
130 const base::Closure& on_write_callback) {
lukasza2ff73e642015-06-11 03:52:43131 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56132
avi92a3e962016-09-21 02:50:21133 auto it = watchers_.find(path);
[email protected]799d53bf2013-08-07 06:59:56134 if (it != watchers_.end()) {
[email protected]8df572cc2013-08-22 12:51:09135 // We are already watching the path.
136 on_start_callback.Run(true);
137 it->second->on_write_callbacks.push_back(on_write_callback);
[email protected]799d53bf2013-08-07 06:59:56138 return;
139 }
140
141 // Start watching |path|.
avi92a3e962016-09-21 02:50:21142 std::unique_ptr<PathWatchInfo> info =
143 base::MakeUnique<PathWatchInfo>(on_write_callback);
[email protected]799d53bf2013-08-07 06:59:56144 bool ok = info->watcher.Watch(
145 path,
146 false, // recursive
147 base::Bind(&FileWriteWatcherImpl::OnWriteEvent,
148 weak_ptr_factory_.GetWeakPtr()));
avi92a3e962016-09-21 02:50:21149 watchers_[path] = std::move(info);
[email protected]8df572cc2013-08-22 12:51:09150 on_start_callback.Run(ok);
[email protected]799d53bf2013-08-07 06:59:56151}
152
153void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
154 const base::FilePath& path,
155 bool error) {
lukasza2ff73e642015-06-11 03:52:43156 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56157
158 if (error)
159 return;
160
avi92a3e962016-09-21 02:50:21161 auto it = watchers_.find(path);
[email protected]799d53bf2013-08-07 06:59:56162 DCHECK(it != watchers_.end());
163
164 // Heuristics for detecting the end of successive write operations.
165 // Delay running on_write_event_callback by |delay_| time, and if OnWriteEvent
166 // is called again in the period, the timer is reset. In other words, we
167 // invoke callback when |delay_| has passed after the last OnWriteEvent().
168 it->second->timer.Start(FROM_HERE,
169 delay_,
170 base::Bind(&FileWriteWatcherImpl::InvokeCallback,
171 weak_ptr_factory_.GetWeakPtr(),
172 path));
173}
174
175void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
176 const base::FilePath& path) {
lukasza2ff73e642015-06-11 03:52:43177 DCHECK(file_task_runner_->BelongsToCurrentThread());
[email protected]799d53bf2013-08-07 06:59:56178
avi92a3e962016-09-21 02:50:21179 auto it = watchers_.find(path);
[email protected]799d53bf2013-08-07 06:59:56180 DCHECK(it != watchers_.end());
181
[email protected]8df572cc2013-08-22 12:51:09182 std::vector<base::Closure> callbacks;
183 callbacks.swap(it->second->on_write_callbacks);
[email protected]799d53bf2013-08-07 06:59:56184 watchers_.erase(it);
185
avi92a3e962016-09-21 02:50:21186 for (const auto& callback : callbacks)
187 callback.Run();
[email protected]799d53bf2013-08-07 06:59:56188}
189
lukasza2ff73e642015-06-11 03:52:43190FileWriteWatcher::FileWriteWatcher(
191 base::SingleThreadTaskRunner* file_task_runner)
192 : watcher_impl_(new FileWriteWatcherImpl(file_task_runner)) {
[email protected]799d53bf2013-08-07 06:59:56193}
194
195FileWriteWatcher::~FileWriteWatcher() {
lukasza037c10b12015-06-12 04:21:25196 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]799d53bf2013-08-07 06:59:56197}
198
199void FileWriteWatcher::StartWatch(const base::FilePath& file_path,
[email protected]8df572cc2013-08-22 12:51:09200 const StartWatchCallback& on_start_callback,
201 const base::Closure& on_write_callback) {
lukasza037c10b12015-06-12 04:21:25202 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]8df572cc2013-08-22 12:51:09203 watcher_impl_->StartWatch(file_path, on_start_callback, on_write_callback);
[email protected]799d53bf2013-08-07 06:59:56204}
205
206void FileWriteWatcher::DisableDelayForTesting() {
207 watcher_impl_->set_delay(base::TimeDelta());
208}
209
[email protected]799d53bf2013-08-07 06:59:56210} // namespace internal
211} // namespace drive