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

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: Move only TimestampMap creation logic to blocking pool 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 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 ++iter_a; 63 ++iter_a;
64 } 64 }
65 while (iter_b != timestamp_map_b.end()) { 65 while (iter_b != timestamp_map_b.end()) {
66 changed_paths.emplace_back(iter_b->first); 66 changed_paths.emplace_back(iter_b->first);
67 ++iter_b; 67 ++iter_b;
68 } 68 }
69 69
70 return changed_paths; 70 return changed_paths;
71 } 71 }
72 72
73 // Scans files under |downloads_dir| recursively and builds a map from file
74 // paths (in Android filesystem) to last modified timestamps.
75 TimestampMap BuildTimestampMap(base::FilePath downloads_dir) {
76 DCHECK(!downloads_dir.EndsWithSeparator());
77 TimestampMap timestamp_map;
78
79 // Enumerate normal files only; directories and symlinks are skipped.
80 base::FileEnumerator enumerator(downloads_dir, true,
81 base::FileEnumerator::FILES);
82 for (base::FilePath cros_path = enumerator.Next(); !cros_path.empty();
83 cros_path = enumerator.Next()) {
84 // Android file path can be obtained by replacing |downloads_dir| prefix
85 // with |kAndroidDownloadDir|.
86 const base::FilePath& android_path =
87 base::FilePath(kAndroidDownloadDir)
88 .Append(
89 cros_path.value().substr(downloads_dir.value().length() + 1));
90 const base::FileEnumerator::FileInfo& info = enumerator.GetInfo();
91 timestamp_map[android_path] = info.GetLastModifiedTime();
92 }
93 return timestamp_map;
94 }
95
96 std::pair<base::Time, TimestampMap> BuildTimestampMapCallback(
97 base::FilePath downloads_dir) {
98 base::Time update_time = base::Time::Now();
99 TimestampMap current_timestamp_map = BuildTimestampMap(downloads_dir);
100 return std::pair<base::Time, TimestampMap>(update_time,
Shuhei Takahashi 2016/09/08 03:43:48 FYI, std::make_pair is useful to avoid explicit te
dspaid 2016/09/08 03:59:56 Done.
101 current_timestamp_map);
Shuhei Takahashi 2016/09/08 03:43:48 IIUC you need std::move() here to avoid copy.
dspaid 2016/09/08 03:59:56 Done.
102 }
103
73 } // namespace 104 } // namespace
74 105
75 // The core part of ArcDownloadsWatcherService to watch for file changes in 106 // The core part of ArcDownloadsWatcherService to watch for file changes in
76 // Downloads directory. 107 // Downloads directory.
77 class ArcDownloadsWatcherService::DownloadsWatcher { 108 class ArcDownloadsWatcherService::DownloadsWatcher {
78 public: 109 public:
79 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>; 110 using Callback = base::Callback<void(mojo::Array<mojo::String> paths)>;
80 111
81 explicit DownloadsWatcher(const Callback& callback); 112 explicit DownloadsWatcher(const Callback& callback);
82 ~DownloadsWatcher(); 113 ~DownloadsWatcher();
83 114
84 // Starts watching Downloads directory. 115 // Starts watching Downloads directory.
85 void Start(); 116 void Start();
86 117
87 private: 118 private:
88 // Called by base::FilePathWatcher to notify file changes. 119 // Called by base::FilePathWatcher to notify file changes.
120 // Wrapper around HandleFilePathChanged to pass the notification timestamp.
Shuhei Takahashi 2016/09/08 03:43:48 This line is outdated.
dspaid 2016/09/08 03:59:56 Done.
89 void OnFilePathChanged(const base::FilePath& path, bool error); 121 void OnFilePathChanged(const base::FilePath& path, bool error);
90 122
91 // Scans files under |downloads_dir_| recursively and builds a map from file 123 // If there has not been a full scan of updated files since notify_time,
92 // paths (in Android filesystem) to last modified timestamps. 124 // update the last_timestamp_map_ and send any new/changed files to the
93 TimestampMap BuildTimestampMap() const; 125 // android media scanner.
126 void HandleFilePathChanged(base::Time notify_time);
Shuhei Takahashi 2016/09/08 03:43:48 This function is no longer defined.
dspaid 2016/09/08 03:59:56 Done.
127
128 void OnBuildTimestampMap(
129 std::pair<base::Time, TimestampMap> timestamp_and_map);
94 130
95 Callback callback_; 131 Callback callback_;
96 base::FilePath downloads_dir_; 132 base::FilePath downloads_dir_;
97 std::unique_ptr<base::FilePathWatcher> watcher_; 133 std::unique_ptr<base::FilePathWatcher> watcher_;
98 TimestampMap last_timestamp_map_; 134 TimestampMap last_timestamp_map_;
135 // The timestamp of the last OnFilePathChanged callback received.
136 base::Time last_update_time_;
Shuhei Takahashi 2016/09/08 03:43:48 How about using "bool pending_task_" instead? Thou
dspaid 2016/09/08 03:59:56 The small timing difference actually has a reasona
Shuhei Takahashi 2016/09/08 04:25:48 I see. But then I think it's a bit optimistic to a
dspaid 2016/09/09 02:10:45 Experimentally I'm seeing a reasonable success rat
Shuhei Takahashi 2016/09/09 03:18:51 It is best if we have PostDelayedTaskAndReplyWithR
137 // Whether or not there is an outstanding task to update last_timestamp_map_.
138 bool outstanding_task_;
99 139
100 // Note: This should remain the last member so it'll be destroyed and 140 // Note: This should remain the last member so it'll be destroyed and
101 // invalidate the weak pointers before any other members are destroyed. 141 // invalidate the weak pointers before any other members are destroyed.
102 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_; 142 base::WeakPtrFactory<DownloadsWatcher> weak_ptr_factory_;
103 143
104 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher); 144 DISALLOW_COPY_AND_ASSIGN(DownloadsWatcher);
105 }; 145 };
106 146
107 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher( 147 ArcDownloadsWatcherService::DownloadsWatcher::DownloadsWatcher(
108 const Callback& callback) 148 const Callback& callback)
109 : callback_(callback), weak_ptr_factory_(this) { 149 : callback_(callback),
150 last_update_time_(base::Time()),
151 outstanding_task_(false),
152 weak_ptr_factory_(this) {
110 DCHECK_CURRENTLY_ON(BrowserThread::UI); 153 DCHECK_CURRENTLY_ON(BrowserThread::UI);
111 154
112 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile()) 155 downloads_dir_ = DownloadPrefs(ProfileManager::GetActiveUserProfile())
113 .GetDefaultDownloadDirectoryForProfile() 156 .GetDefaultDownloadDirectoryForProfile()
114 .StripTrailingSeparators(); 157 .StripTrailingSeparators();
115 } 158 }
116 159
117 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() { 160 ArcDownloadsWatcherService::DownloadsWatcher::~DownloadsWatcher() {
118 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 161 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
119 } 162 }
120 163
121 void ArcDownloadsWatcherService::DownloadsWatcher::Start() { 164 void ArcDownloadsWatcherService::DownloadsWatcher::Start() {
122 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 165 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
123 166
124 // Initialize with the current timestamp map and avoid initial notification. 167 // Initialize with the current timestamp map and avoid initial notification.
125 // It is not needed since MediaProvider scans whole storage area on boot. 168 // It is not needed since MediaProvider scans whole storage area on boot.
126 last_timestamp_map_ = BuildTimestampMap(); 169 last_update_time_ = base::Time::Now();
170 last_timestamp_map_ = BuildTimestampMap(downloads_dir_);
171
172 outstanding_task_ = false;
127 173
128 watcher_ = base::MakeUnique<base::FilePathWatcher>(); 174 watcher_ = base::MakeUnique<base::FilePathWatcher>();
129 // On Linux, base::FilePathWatcher::Watch() always returns true. 175 // On Linux, base::FilePathWatcher::Watch() always returns true.
130 watcher_->Watch(downloads_dir_, true, 176 watcher_->Watch(downloads_dir_, true,
131 base::Bind(&DownloadsWatcher::OnFilePathChanged, 177 base::Bind(&DownloadsWatcher::OnFilePathChanged,
132 weak_ptr_factory_.GetWeakPtr())); 178 weak_ptr_factory_.GetWeakPtr()));
133 } 179 }
134 180
135 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged( 181 void ArcDownloadsWatcherService::DownloadsWatcher::OnFilePathChanged(
136 const base::FilePath& path, 182 const base::FilePath& path,
137 bool error) { 183 bool error) {
138 // On Linux, |error| is always false. Also, |path| is always the same path 184 // On Linux, |error| is always false. Also, |path| is always the same path
139 // as one given to FilePathWatcher::Watch(). 185 // as one given to FilePathWatcher::Watch().
140 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 186 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
187 if (!outstanding_task_) {
188 base::PostTaskAndReplyWithResult(
189 BrowserThread::GetBlockingPool(), FROM_HERE,
190 base::Bind(&BuildTimestampMapCallback, downloads_dir_),
191 base::Bind(&DownloadsWatcher::OnBuildTimestampMap,
192 weak_ptr_factory_.GetWeakPtr()));
193 outstanding_task_ = true;
194 } else {
195 last_update_time_ = base::Time::Now();
196 }
197 }
141 198
142 TimestampMap current_timestamp_map = BuildTimestampMap(); 199 void ArcDownloadsWatcherService::DownloadsWatcher::OnBuildTimestampMap(
143 200 std::pair<base::Time, TimestampMap> timestamp_and_map) {
201 TimestampMap current_timestamp_map = timestamp_and_map.second;
Shuhei Takahashi 2016/09/08 03:43:48 Could you insert DCHECK_CURRENTLY_ON?
Shuhei Takahashi 2016/09/08 03:43:48 Also, could you insert DCHECK(outstanding_task_)?
dspaid 2016/09/08 03:59:56 Done.
dspaid 2016/09/08 03:59:57 Done.
144 std::vector<base::FilePath> changed_paths = 202 std::vector<base::FilePath> changed_paths =
145 CollectChangedPaths(last_timestamp_map_, current_timestamp_map); 203 CollectChangedPaths(last_timestamp_map_, current_timestamp_map);
146 204
147 last_timestamp_map_ = std::move(current_timestamp_map); 205 last_timestamp_map_ = std::move(current_timestamp_map);
148 206
149 mojo::Array<mojo::String> mojo_paths(changed_paths.size()); 207 mojo::Array<mojo::String> mojo_paths(changed_paths.size());
150 for (size_t i = 0; i < changed_paths.size(); ++i) { 208 for (size_t i = 0; i < changed_paths.size(); ++i) {
151 mojo_paths[i] = changed_paths[i].value(); 209 mojo_paths[i] = changed_paths[i].value();
152 } 210 }
153 BrowserThread::PostTask( 211 BrowserThread::PostTask(
154 BrowserThread::UI, FROM_HERE, 212 BrowserThread::UI, FROM_HERE,
155 base::Bind(callback_, base::Passed(std::move(mojo_paths)))); 213 base::Bind(callback_, base::Passed(std::move(mojo_paths))));
156 } 214 if (last_update_time_ > timestamp_and_map.first) {
157 215 base::PostTaskAndReplyWithResult(
158 TimestampMap ArcDownloadsWatcherService::DownloadsWatcher::BuildTimestampMap() 216 BrowserThread::GetBlockingPool(), FROM_HERE,
159 const { 217 base::Bind(&BuildTimestampMapCallback, downloads_dir_),
160 DCHECK(!downloads_dir_.EndsWithSeparator()); 218 base::Bind(&DownloadsWatcher::OnBuildTimestampMap,
161 TimestampMap timestamp_map; 219 weak_ptr_factory_.GetWeakPtr()));
162
163 // Enumerate normal files only; directories and symlinks are skipped.
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 } 220 }
177 return timestamp_map; 221 outstanding_task_ = false;
Shuhei Takahashi 2016/09/08 03:43:48 We must keep |outstanding_task_| true when there w
dspaid 2016/09/08 03:59:56 Done.
178 } 222 }
179 223
180 ArcDownloadsWatcherService::ArcDownloadsWatcherService( 224 ArcDownloadsWatcherService::ArcDownloadsWatcherService(
181 ArcBridgeService* bridge_service) 225 ArcBridgeService* bridge_service)
182 : ArcService(bridge_service), weak_ptr_factory_(this) { 226 : ArcService(bridge_service), weak_ptr_factory_(this) {
183 DCHECK_CURRENTLY_ON(BrowserThread::UI); 227 DCHECK_CURRENTLY_ON(BrowserThread::UI);
184 arc_bridge_service()->file_system()->AddObserver(this); 228 arc_bridge_service()->file_system()->AddObserver(this);
185 } 229 }
186 230
187 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() { 231 ArcDownloadsWatcherService::~ArcDownloadsWatcherService() {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
225 mojo::Array<mojo::String> paths) { 269 mojo::Array<mojo::String> paths) {
226 DCHECK_CURRENTLY_ON(BrowserThread::UI); 270 DCHECK_CURRENTLY_ON(BrowserThread::UI);
227 271
228 auto* instance = arc_bridge_service()->file_system()->instance(); 272 auto* instance = arc_bridge_service()->file_system()->instance();
229 if (!instance) 273 if (!instance)
230 return; 274 return;
231 instance->RequestMediaScan(std::move(paths)); 275 instance->RequestMediaScan(std::move(paths));
232 } 276 }
233 277
234 } // namespace arc 278 } // 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