Home | History | Annotate | Download | only in drive
      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 
      9 #include "base/bind.h"
     10 #include "base/callback.h"
     11 #include "base/files/file_path_watcher.h"
     12 #include "base/stl_util.h"
     13 #include "base/timer/timer.h"
     14 #include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
     15 #include "chrome/browser/chromeos/drive/logging.h"
     16 #include "chrome/browser/google_apis/task_util.h"
     17 #include "content/public/browser/browser_thread.h"
     18 
     19 using content::BrowserThread;
     20 
     21 namespace drive {
     22 namespace internal {
     23 
     24 namespace {
     25 const int64 kWriteEventDelayInSeconds = 5;
     26 }  // namespace
     27 
     28 // base::FileWatcher needs to live in a thread that is allowed to do File IO
     29 // and has a TYPE_IO message loop: that is, FILE thread. This class bridges the
     30 // UI thread and FILE thread, and does all the main tasks in the FILE thread.
     31 class FileWriteWatcher::FileWriteWatcherImpl {
     32  public:
     33   FileWriteWatcherImpl();
     34 
     35   // Forwards the call to DestoryOnFileThread(). This method must be used to
     36   // destruct the instance.
     37   void Destroy();
     38 
     39   // Forwards the call to StartWatchOnFileThread(). |callback| is called back
     40   // on the caller (UI) thread when the watch has started.
     41   // |on_write_event_callback| is called when a write has happened to the path.
     42   void StartWatch(const base::FilePath& path,
     43                   const StartWatchCallback& callback,
     44                   const base::Closure& on_write_event_callback);
     45 
     46   void set_delay(base::TimeDelta delay) { delay_ = delay; }
     47 
     48  private:
     49   ~FileWriteWatcherImpl();
     50 
     51   void DestroyOnFileThread();
     52 
     53   void StartWatchOnFileThread(const base::FilePath& path,
     54                               const StartWatchCallback& callback,
     55                               const base::Closure& on_write_event_callback);
     56 
     57   void OnWriteEvent(const base::FilePath& path, bool error);
     58 
     59   void InvokeCallback(const base::FilePath& path);
     60 
     61   struct PathWatchInfo {
     62     base::Closure on_write_event_callback;
     63     base::FilePathWatcher watcher;
     64     base::Timer timer;
     65 
     66     explicit PathWatchInfo(const base::Closure& callback)
     67         : on_write_event_callback(callback),
     68           timer(false /* retain_closure_on_reset */, false /* is_repeating */) {
     69     }
     70   };
     71 
     72   base::TimeDelta delay_;
     73   std::map<base::FilePath, PathWatchInfo*> watchers_;
     74 
     75   // Note: This should remain the last member so it'll be destroyed and
     76   // invalidate its weak pointers before any other members are destroyed.
     77   base::WeakPtrFactory<FileWriteWatcherImpl> weak_ptr_factory_;
     78   DISALLOW_COPY_AND_ASSIGN(FileWriteWatcherImpl);
     79 };
     80 
     81 FileWriteWatcher::FileWriteWatcherImpl::FileWriteWatcherImpl()
     82     : delay_(base::TimeDelta::FromSeconds(kWriteEventDelayInSeconds)),
     83       weak_ptr_factory_(this) {
     84   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     85 }
     86 
     87 void FileWriteWatcher::FileWriteWatcherImpl::Destroy() {
     88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     89 
     90   // Just forwarding the call to FILE thread.
     91   BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
     92       FROM_HERE,
     93       base::Bind(&FileWriteWatcherImpl::DestroyOnFileThread,
     94                  base::Unretained(this)));
     95 }
     96 
     97 void FileWriteWatcher::FileWriteWatcherImpl::StartWatch(
     98     const base::FilePath& path,
     99     const StartWatchCallback& callback,
    100     const base::Closure& on_write_event_callback) {
    101   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    102 
    103   // Forwarding the call to FILE thread and relaying the |callback|.
    104   BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)->PostTask(
    105       FROM_HERE,
    106       base::Bind(&FileWriteWatcherImpl::StartWatchOnFileThread,
    107                  base::Unretained(this),
    108                  path,
    109                  google_apis::CreateRelayCallback(callback),
    110                  google_apis::CreateRelayCallback(on_write_event_callback)));
    111 }
    112 
    113 FileWriteWatcher::FileWriteWatcherImpl::~FileWriteWatcherImpl() {
    114   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    115 
    116   STLDeleteContainerPairSecondPointers(watchers_.begin(), watchers_.end());
    117 }
    118 
    119 void FileWriteWatcher::FileWriteWatcherImpl::DestroyOnFileThread() {
    120   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    121 
    122   delete this;
    123 }
    124 
    125 void FileWriteWatcher::FileWriteWatcherImpl::StartWatchOnFileThread(
    126     const base::FilePath& path,
    127     const StartWatchCallback& callback,
    128     const base::Closure& on_write_event_callback) {
    129   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    130   util::Log(logging::LOG_INFO, "Started watching modification to %s.",
    131             path.AsUTF8Unsafe().c_str());
    132 
    133   std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
    134   if (it != watchers_.end()) {
    135     // Do nothing if we are already watching the path.
    136     callback.Run(true);
    137     return;
    138   }
    139 
    140   // Start watching |path|.
    141   scoped_ptr<PathWatchInfo> info(new PathWatchInfo(on_write_event_callback));
    142   bool ok = info->watcher.Watch(
    143       path,
    144       false,  // recursive
    145       base::Bind(&FileWriteWatcherImpl::OnWriteEvent,
    146                  weak_ptr_factory_.GetWeakPtr()));
    147   watchers_[path] = info.release();
    148   callback.Run(ok);
    149 }
    150 
    151 void FileWriteWatcher::FileWriteWatcherImpl::OnWriteEvent(
    152     const base::FilePath& path,
    153     bool error) {
    154   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    155   util::Log(logging::LOG_INFO, "Detected modification to %s.",
    156             path.AsUTF8Unsafe().c_str());
    157 
    158   if (error)
    159     return;
    160 
    161   std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
    162   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 
    175 void FileWriteWatcher::FileWriteWatcherImpl::InvokeCallback(
    176     const base::FilePath& path) {
    177   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    178   util::Log(logging::LOG_INFO, "Finished watching modification to %s.",
    179             path.AsUTF8Unsafe().c_str());
    180 
    181   std::map<base::FilePath, PathWatchInfo*>::iterator it = watchers_.find(path);
    182   DCHECK(it != watchers_.end());
    183 
    184   base::Closure callback = it->second->on_write_event_callback;
    185   delete it->second;
    186   watchers_.erase(it);
    187 
    188   callback.Run();
    189 }
    190 
    191 FileWriteWatcher::FileWriteWatcher(file_system::OperationObserver* observer)
    192     : watcher_impl_(new FileWriteWatcherImpl),
    193       operation_observer_(observer),
    194       weak_ptr_factory_(this) {
    195   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    196 }
    197 
    198 FileWriteWatcher::~FileWriteWatcher() {
    199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    200 }
    201 
    202 void FileWriteWatcher::StartWatch(const base::FilePath& file_path,
    203                                   const std::string& resource_id,
    204                                   const StartWatchCallback& callback) {
    205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    206 
    207   watcher_impl_->StartWatch(file_path,
    208                             callback,
    209                             base::Bind(&FileWriteWatcher::OnWriteEvent,
    210                                        weak_ptr_factory_.GetWeakPtr(),
    211                                        resource_id));
    212 }
    213 
    214 void FileWriteWatcher::DisableDelayForTesting() {
    215   watcher_impl_->set_delay(base::TimeDelta());
    216 }
    217 
    218 void FileWriteWatcher::OnWriteEvent(const std::string& resource_id) {
    219   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    220 
    221   operation_observer_->OnCacheFileUploadNeededByOperation(resource_id);
    222 }
    223 
    224 }  // namespace internal
    225 }  // namespace drive
    226