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. 4 5 #include "chrome/browser/chromeos/drive/file_write_watcher.h" 6 7 #include <map> 8 #include <vector> 9 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 "content/public/browser/browser_thread.h" 16 #include "google_apis/drive/task_util.h" 17 18 using content::BrowserThread; 19 20 namespace drive { 21 namespace internal { 22 23 namespace { 24 const int64 kWriteEventDelayInSeconds = 5; 25 } // namespace 26 27 // base::FileWatcher needs to live in a thread that is allowed to do File IO 28 // and has a TYPE_IO message loop: that is, FILE thread. This class bridges the 29 // UI thread and FILE thread, and does all the main tasks in the FILE thread. 30 class FileWriteWatcher::FileWriteWatcherImpl { 31 public: 32 FileWriteWatcherImpl(); 33 34 // Forwards the call to DestoryOnFileThread(). This method must be used to 35 // destruct the instance. 36 void Destroy(); 37 38 // Forwards the call to StartWatchOnFileThread(). |on_start_callback| is 39 // called back on the caller (UI) thread when the watch has started. 40 // |on_write_callback| is called when a write has happened to the path. 41 void StartWatch(const base::FilePath& path, 42 const StartWatchCallback& on_start_callback, 43 const base::Closure& on_write_callback); 44 45 void set_delay(base::TimeDelta delay) { delay_ = delay; } 46 47 private: 48 ~FileWriteWatcherImpl(); 49 50 void DestroyOnFileThread(); 51 52 void StartWatchOnFileThread(const base::FilePath& path, 53 const StartWatchCallback& on_start_callback, 54 const base::Closure& on_write_callback); 55 56 void OnWriteEvent(const base::FilePath& path, bool error); 57 58 void InvokeCallback(const base::FilePath& path); 59 60 struct PathWatchInfo { 61 std::vector<base::Closure> on_write_callbacks; 62 base::FilePathWatcher watcher; 63 base::Timer timer; 64 65 explicit PathWatchInfo(const base::Closure& on_write_callback) 66 : on_write_callbacks(1, on_write_callback), 67 timer(false /* retain_closure_on_reset */, false /* is_repeating */) { 68 } 69 }; 70 71 base::TimeDelta delay_; 72 std::map<base::FilePath, PathWatchInfo*> watchers_; 73 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); 78 }; 79 80 FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl() 81 : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)), 82 weak_ptr_factory_(this) { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 } 85 86 void FileWriteWatcher::FileWriteWatcherImpl::Destroy() { 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 88 89 // Just forwarding the call to FILE thread. 90 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask( 91 FROM_HERE, 92 base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread, 93 base::Unretained(this))); 94 } 95 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(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 102 // Forwarding the call to FILE thread and relaying the |callback|. 103 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask( 104 FROM_HERE, 105 base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread, 106 base::Unretained(this), 107 path, 108 google_apis::CreateRelayCallback(on_start_callback), 109 google_apis::CreateRelayCallback(on_write_callback))); 110 } 111 112 FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() { 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 114 115 STLDeleteContainerPairSecondPointers(watchers_.begin(), watchers_.end()); 116 } 117 118 void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() { 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 120 121 delete this; 122 } 123 124 void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread( 125 const base::FilePath& path, 126 const StartWatchCallback& on_start_callback, 127 const base::Closure& on_write_callback) { 128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 129 130 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path); 131 if (it != watchers_.end()) { 132 // We are already watching the path. 133 on_start_callback.Run(true); 134 it->second->on_write_callbacks.push_back(on_write_callback); 135 return; 136 } 137 138 // Start watching |path|. 139 scoped_ptr<PathWatchInfo> info(new PathWatchInfo(on_write_callback)); 140 bool ok = info->watcher.Watch( 141 path, 142 false, // recursive 143 base::Bind(&FileWriteWatcherImpl::OnWriteEvent, 144 weak_ptr_factory_.GetWeakPtr())); 145 watchers_[path] = info.release(); 146 on_start_callback.Run(ok); 147 } 148 149 void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent( 150 const base::FilePath& path, 151 bool error) { 152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 153 154 if (error) 155 return; 156 157 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path); 158 DCHECK(it != watchers_.end()); 159 160 // Heuristics for detecting the end of successive write operations. 161 // Delay running on_write_event_callback by |delay_| time, and if OnWriteEvent 162 // is called again in the period, the timer is reset. In other words, we 163 // invoke callback when |delay_| has passed after the last OnWriteEvent(). 164 it->second->timer.Start(FROM_HERE, 165 delay_, 166 base::Bind(&FileWriteWatcherImpl::InvokeCallback, 167 weak_ptr_factory_.GetWeakPtr(), 168 path)); 169 } 170 171 void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback( 172 const base::FilePath& path) { 173 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 174 175 std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path); 176 DCHECK(it != watchers_.end()); 177 178 std::vector<base::Closure> callbacks; 179 callbacks.swap(it->second->on_write_callbacks); 180 delete it->second; 181 watchers_.erase(it); 182 183 for (size_t i = 0; i < callbacks.size(); ++i) 184 callbacks[i].Run(); 185 } 186 187 FileWriteWatcher::FileWriteWatcher() 188 : watcher_impl_(new FileWriteWatcherImpl) { 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 190 } 191 192 FileWriteWatcher::~FileWriteWatcher() { 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 194 } 195 196 void FileWriteWatcher::StartWatch(const base::FilePath& file_path, 197 const StartWatchCallback& on_start_callback, 198 const base::Closure& on_write_callback) { 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 200 watcher_impl_->StartWatch(file_path, on_start_callback, on_write_callback); 201 } 202 203 void FileWriteWatcher::DisableDelayForTesting() { 204 watcher_impl_->set_delay(base::TimeDelta()); 205 } 206 207 } // namespace internal 208 } // namespace drive 209