Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Side by Side Diff: chrome/browser/chromeos/arc/arc_downloads_watcher_service.cc

Issue 2310833002: Don't block FILE thread for media scanning (Closed)
Patch Set: addressed comments Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/arc/arc_downloads_watcher_service.h" 5 #include "chrome/browser/chromeos/arc/arc_downloads_watcher_service.h"
6 6
7 #include <map> 7 #include <map>
8 #include <memory> 8 #include <memory>
9 #include <utility> 9 #include <utility>
10 10
(...skipping 15 matching lines...) Expand all
26 // Mapping from Android file paths to last modified timestamps. 26 // Mapping from Android file paths to last modified timestamps.
27 using TimestampMap = std::map<base::FilePath, base::Time>; 27 using TimestampMap = std::map<base::FilePath, base::Time>;
28 28
29 namespace arc { 29 namespace arc {
30 30
31 namespace { 31 namespace {
32 32
33 const base::FilePath::CharType kAndroidDownloadDir[] = 33 const base::FilePath::CharType kAndroidDownloadDir[] =
34 FILE_PATH_LITERAL("/storage/emulated/0/Download"); 34 FILE_PATH_LITERAL("/storage/emulated/0/Download");
35 35
36 // How long to wait for new inotify events before building the updated timestamp
37 // map.
38 const base::TimeDelta kBuildTimestampMapDelay =
39 base::TimeDelta::FromMilliseconds(1000);
hidehiko 2016/09/12 05:44:10 Optional: 1s sounds a bit longer delay...? Anyway,
dspaid 2016/09/12 05:56:51 Completely arbitrary. Testing shows that we can a
40
36 // Compares two TimestampMaps and returns the list of file paths added/removed 41 // Compares two TimestampMaps and returns the list of file paths added/removed
37 // or whose timestamp have changed. 42 // or whose timestamp have changed.
38 std::vector<base::FilePath> CollectChangedPaths( 43 std::vector<base::FilePath> CollectChangedPaths(
39 const TimestampMap& timestamp_map_a, 44 const TimestampMap& timestamp_map_a,
40 const TimestampMap& timestamp_map_b) { 45 const TimestampMap& timestamp_map_b) {
41 std::vector<base::FilePath> changed_paths; 46 std::vector<base::FilePath> changed_paths;
42 47
43 TimestampMap::const_iterator iter_a = timestamp_map_a.begin(); 48 TimestampMap::const_iterator iter_a = timestamp_map_a.begin();
44 TimestampMap::const_iterator iter_b = timestamp_map_b.begin(); 49 TimestampMap::const_iterator iter_b = timestamp_map_b.begin();
45 while (iter_a != timestamp_map_a.end() && iter_b != timestamp_map_b.end()) { 50 while (iter_a != timestamp_map_a.end() && iter_b != timestamp_map_b.end()) {
(...skipping 17 matching lines...) Expand all
63 ++iter_a; 68 ++iter_a;
64 } 69 }
65 while (iter_b != timestamp_map_b.end()) { 70 while (iter_b != timestamp_map_b.end()) {
66 changed_paths.emplace_back(iter_b->first); 71 changed_paths.emplace_back(iter_b->first);
67 ++iter_b; 72 ++iter_b;
68 } 73 }
69 74
70 return changed_paths; 75 return changed_paths;
71 } 76 }
72 77
78 // Scans files under |downloads_dir| recursively and builds a map from file
79 // paths (in Android filesystem) to last modified timestamps.
80 TimestampMap BuildTimestampMap(base::FilePath downloads_dir) {
81 DCHECK(!downloads_dir.EndsWithSeparator());
82 TimestampMap timestamp_map;
83
84 // Enumerate normal files only; directories and symlinks are skipped.
85 base::FileEnumerator enumerator(downloads_dir, true,
86 base::FileEnumerator::FILES);
87 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty();
88 cros_path = enumerator.Next()) {
89 // Android file path can be obtained by replacing |downloads_dir| prefix
90 // with |kAndroidDownloadDir|.
91 const base::FilePath& android_path =
92 base::FilePath(kAndroidDownloadDir)
93 .Append(
hidehiko 2016/09/12 05:44:10 Maybe, FilePath::AppendRelativePath() is what you
dspaid 2016/09/12 05:56:51 Done.
94 cros_path.value().substr(downloads_dir.value().length() + 1));
95 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo();
96 timestamp_map[android_path] = info.GetLastModifiedTime();
97 }
98 return timestamp_map;
99 }
100
101 std::pair<base::TimeTicks, TimestampMap> BuildTimestampMapCallback(
102 base::FilePath downloads_dir) {
103 base::TimeTicks snapshot_time = base::TimeTicks::Now();
hidehiko 2016/09/12 05:44:10 Could you comment that, - BuildTimestampMap take a
dspaid 2016/09/12 05:56:52 Done.
104 TimestampMap current_timestamp_map = BuildTimestampMap(downloads_dir);
105 return std::make_pair(snapshot_time, std::move(current_timestamp_map));
106 }
107
73 } // namespace 108 } // namespace
74 109
75 // The core part of ArcDownloadsWatcherService to watch for file changes in 110 // The core part of ArcDownloadsWatcherService to watch for file changes in
76 // Downloads directory. 111 // Downloads directory.
77 class ArcDownloadsWatcherService::DownloadsWatcher { 112 class ArcDownloadsWatcherService::DownloadsWatcher {
78 public: 113 public:
79 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>; 114 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>;
80 115
81 explicit DownloadsWatcher(const Callback& callback); 116 explicit DownloadsWatcher(const Callback& callback);
82 ~DownloadsWatcher(); 117 ~DownloadsWatcher();
83 118
84 // Starts watching Downloads directory. 119 // Starts watching Downloads directory.
85 void Start(); 120 void Start();
86 121
87 private: 122 private:
88 // Called by base::FilePathWatcher to notify file changes. 123 // Called by base::FilePathWatcher to notify file changes.
124 // Kicks off the update of last_timestamp_map_ if one is not already in
125 // progress.
89 void OnFilePathChanged(const base::FilePath& path, bool error); 126 void OnFilePathChanged(const base::FilePath& path, bool error);
90 127
91 // Scans files under |downloads_dir_| recursively and builds a map from file 128 // Called with a delay to allow additional inotify events for the same user
92 // paths (in Android filesystem) to last modified timestamps. 129 // action to queue up so that they can be dealt with in batch.
93 TimestampMap BuildTimestampMap() const; 130 void DelayBuildTimestampMap();
131
132 // Called after a new timestamp map has been created and causes any recently
133 // modified files to be sent to the media scanner.
134 void OnBuildTimestampMap(
135 std::pair<base::TimeTicks, TimestampMap> timestamp_and_map);
94 136
95 Callback callback_; 137 Callback callback_;
96 base::FilePath downloads_dir_; 138 base::FilePath downloads_dir_;
97 std::unique_ptr<base::FilePathWatcher> watcher_; 139 std::unique_ptr<base::FilePathWatcher> watcher_;
98 TimestampMap last_timestamp_map_; 140 TimestampMap last_timestamp_map_;
141 // The timestamp of the last OnFilePathChanged callback received.
142 base::TimeTicks last_notify_time_;
143 // Whether or not there is an outstanding task to update last_timestamp_map_.
144 bool outstanding_task_;
99 145
100 // Note: This should remain the last member so it'll be destroyed and 146 // Note: This should remain the last member so it'll be destroyed and
101 // invalidate the weak pointers before any other members are destroyed. 147 // invalidate the weak pointers before any other members are destroyed.
102 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_; 148 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_;
103 149
104 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher); 150 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher);
105 }; 151 };
106 152
107 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher( 153 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher(
108 const Callback& callback) 154 const Callback& callback)
109 : callback_(callback), weak_ptr_factory_(this) { 155 : callback_(callback),
156 last_notify_time_(base::TimeTicks()),
157 outstanding_task_(false),
158 weak_ptr_factory_(this) {
110 DCHECK_CURRENTLY_ON(BrowserThread::UI); 159 DCHECK_CURRENTLY_ON(BrowserThread::UI);
111 160
112 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile()) 161 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile())
113 .GetDefaultDownloadDirectoryForProfile() 162 .GetDefaultDownloadDirectoryForProfile()
114 .StripTrailingSeparators(); 163 .StripTrailingSeparators();
115 } 164 }
116 165
117 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() { 166 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() {
118 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 167 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
119 } 168 }
120 169
121 void ArcDownloadsWatcherService::DownloadsWatcher::Start() { 170 void ArcDownloadsWatcherService::DownloadsWatcher::Start() {
122 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 171 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
123 172
124 // Initialize with the current timestamp map and avoid initial notification. 173 // Initialize with the current timestamp map and avoid initial notification.
125 // It is not needed since MediaProvider scans whole storage area on boot. 174 // It is not needed since MediaProvider scans whole storage area on boot.
126 last_timestamp_map_ = BuildTimestampMap(); 175 last_notify_time_ = base::TimeTicks::Now();
176 last_timestamp_map_ = BuildTimestampMap(downloads_dir_);
177
178 outstanding_task_ = false;
hidehiko 2016/09/12 05:44:10 Clarification: what is this for? Here, outstandig_
dspaid 2016/09/12 05:56:51 Done.
127 179
128 watcher_ = base::MakeUnique<base::FilePathWatcher>(); 180 watcher_ = base::MakeUnique<base::FilePathWatcher>();
129 // On Linux, base::FilePathWatcher::Watch() always returns true. 181 // On Linux, base::FilePathWatcher::Watch() always returns true.
130 watcher_->Watch(downloads_dir_, true, 182 watcher_->Watch(downloads_dir_, true,
131 base::Bind(&DownloadsWatcher::OnFilePathChanged, 183 base::Bind(&DownloadsWatcher::OnFilePathChanged,
132 weak_ptr_factory_.GetWeakPtr())); 184 weak_ptr_factory_.GetWeakPtr()));
133 } 185 }
134 186
135 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged( 187 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged(
136 const base::FilePath& path, 188 const base::FilePath& path,
137 bool error) { 189 bool error) {
138 // On Linux, |error| is always false. Also, |path| is always the same path 190 // On Linux, |error| is always false. Also, |path| is always the same path
139 // as one given to FilePathWatcher::Watch(). 191 // as one given to FilePathWatcher::Watch().
140 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 192 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
193 if (!outstanding_task_) {
194 outstanding_task_ = true;
195 BrowserThread::PostDelayedTask(
196 BrowserThread::FILE, FROM_HERE,
197 base::Bind(&DownloadsWatcher::DelayBuildTimestampMap,
198 weak_ptr_factory_.GetWeakPtr()),
199 kBuildTimestampMapDelay);
200 } else {
201 last_notify_time_ = base::TimeTicks::Now();
202 }
203 }
141 204
142 TimestampMap current_timestamp_map = BuildTimestampMap(); 205 void ArcDownloadsWatcherService::DownloadsWatcher::DelayBuildTimestampMap() {
206 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
207 DCHECK(outstanding_task_);
208 base::PostTaskAndReplyWithResult(
209 BrowserThread::GetBlockingPool(), FROM_HERE,
210 base::Bind(&BuildTimestampMapCallback, downloads_dir_),
211 base::Bind(&DownloadsWatcher::OnBuildTimestampMap,
212 weak_ptr_factory_.GetWeakPtr()));
213 }
143 214
215 void ArcDownloadsWatcherService::DownloadsWatcher::OnBuildTimestampMap(
216 std::pair<base::TimeTicks, TimestampMap> timestamp_and_map) {
217 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
Shuhei Takahashi 2016/09/12 04:29:13 This should be not true, it's on blocking pool.
hidehiko 2016/09/12 05:44:10 This should be on FILE thread, IIUC.
Shuhei Takahashi 2016/09/12 05:46:00 Oops, I misread the code. Please don't mind.
218 DCHECK(outstanding_task_);
219 base::TimeTicks snapshot_time = timestamp_and_map.first;
220 TimestampMap current_timestamp_map = timestamp_and_map.second;
Shuhei Takahashi 2016/09/12 04:29:13 Could you use std::move() here?
dspaid 2016/09/12 05:56:52 Done.
144 std::vector<base::FilePath> changed_paths = 221 std::vector<base::FilePath> changed_paths =
145 CollectChangedPaths(last_timestamp_map_, current_timestamp_map); 222 CollectChangedPaths(last_timestamp_map_, current_timestamp_map);
146 223
147 last_timestamp_map_ = std::move(current_timestamp_map); 224 last_timestamp_map_ = std::move(current_timestamp_map);
148 225
149 mojo::Array<mojo::String> mojo_paths(changed_paths.size()); 226 mojo::Array<mojo::String> mojo_paths(changed_paths.size());
150 for (size_t i = 0; i < changed_paths.size(); ++i) { 227 for (size_t i = 0; i < changed_paths.size(); ++i) {
151 mojo_paths[i] = changed_paths[i].value(); 228 mojo_paths[i] = changed_paths[i].value();
152 } 229 }
153 BrowserThread::PostTask( 230 BrowserThread::PostTask(
154 BrowserThread::UI, FROM_HERE, 231 BrowserThread::UI, FROM_HERE,
155 base::Bind(callback_, base::Passed(std::move(mojo_paths)))); 232 base::Bind(callback_, base::Passed(std::move(mojo_paths))));
156 } 233 if (last_notify_time_ > snapshot_time) {
157 234 base::PostTaskAndReplyWithResult(
158 TimestampMap ArcDownloadsWatcherService::DownloadsWatcher::BuildTimestampMap() 235 BrowserThread::GetBlockingPool(), FROM_HERE,
159 const { 236 base::Bind(&BuildTimestampMapCallback, downloads_dir_),
160 DCHECK(!downloads_dir_.EndsWithSeparator()); 237 base::Bind(&DownloadsWatcher::OnBuildTimestampMap,
161 TimestampMap timestamp_map; 238 weak_ptr_factory_.GetWeakPtr()));
162 239 } else {
163 // Enumerate normal files only; directories and symlinks are skipped. 240 outstanding_task_ = false;
164 base::FileEnumerator enumerator(downloads_dir_, true,
165 base::FileEnumerator::FILES);
166 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty();
167 cros_path = enumerator.Next()) {
168 // Android file path can be obtained by replacing |downloads_dir_| prefix
169 // with |kAndroidDownloadDir|.
170 const base::FilePath& android_path =
171 base::FilePath(kAndroidDownloadDir)
172 .Append(
173 cros_path.value().substr(downloads_dir_.value().length() + 1));
174 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo();
175 timestamp_map[android_path] = info.GetLastModifiedTime();
176 } 241 }
177 return timestamp_map;
178 } 242 }
179 243
180 ArcDownloadsWatcherService::ArcDownloadsWatcherService( 244 ArcDownloadsWatcherService::ArcDownloadsWatcherService(
181 ArcBridgeService* bridge_service) 245 ArcBridgeService* bridge_service)
182 : ArcService(bridge_service), weak_ptr_factory_(this) { 246 : ArcService(bridge_service), weak_ptr_factory_(this) {
183 DCHECK_CURRENTLY_ON(BrowserThread::UI); 247 DCHECK_CURRENTLY_ON(BrowserThread::UI);
184 arc_bridge_service()->file_system()->AddObserver(this); 248 arc_bridge_service()->file_system()->AddObserver(this);
185 } 249 }
186 250
187 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() { 251 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 mojo::Array<mojo::String> paths) { 289 mojo::Array<mojo::String> paths) {
226 DCHECK_CURRENTLY_ON(BrowserThread::UI); 290 DCHECK_CURRENTLY_ON(BrowserThread::UI);
227 291
228 auto* instance = arc_bridge_service()->file_system()->instance(); 292 auto* instance = arc_bridge_service()->file_system()->instance();
229 if (!instance) 293 if (!instance)
230 return; 294 return;
231 instance->RequestMediaScan(std::move(paths)); 295 instance->RequestMediaScan(std::move(paths));
232 } 296 }
233 297
234 } // namespace arc 298 } // namespace arc
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698