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 #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