Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / memory / low_memory_observer.cc
1 // Copyright (c) 2012 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/memory/low_memory_observer.h"
6
7 #include <fcntl.h>
8
9 #include "base/bind.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/sys_info.h"
13 #include "base/time/time.h"
14 #include "base/timer/timer.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/browser_process_platform_part_chromeos.h"
17 #include "chrome/browser/chromeos/memory/oom_priority_manager.h"
18 #include "content/public/browser/browser_thread.h"
19
20 using content::BrowserThread;
21
22 namespace chromeos {
23
24 namespace {
25 // This is the file that will exist if low memory notification is available
26 // on the device.  Whenever it becomes readable, it signals a low memory
27 // condition.
28 const char kLowMemFile[] = "/dev/chromeos-low-mem";
29
30 // This is the minimum amount of time in milliseconds between checks for
31 // low memory.
32 const int kLowMemoryCheckTimeoutMs = 750;
33 }  // namespace
34
35 ////////////////////////////////////////////////////////////////////////////////
36 // LowMemoryObserverImpl
37 //
38 // Does the actual work of observing.  The observation work happens on the FILE
39 // thread, and the discarding of tabs happens on the UI thread.
40 // If low memory is detected, then we discard a tab, wait
41 // kLowMemoryCheckTimeoutMs milliseconds and then start watching again to see
42 // if we're still in a low memory state.  This is to keep from discarding all
43 // tabs the first time we enter the state, because it takes time for the
44 // tabs to deallocate their memory.  A timer isn't the perfect solution, but
45 // without any reliable indicator that a tab has had all its parts deallocated,
46 // it's the next best thing.
47 class LowMemoryObserverImpl
48     : public base::RefCountedThreadSafe<LowMemoryObserverImpl> {
49  public:
50   LowMemoryObserverImpl() : watcher_delegate_(this), file_descriptor_(-1) {}
51
52   // Start watching the low memory file for readability.
53   // Calls to StartObserving should always be matched with calls to
54   // StopObserving.  This method should only be called from the FILE thread.
55   void StartObservingOnFileThread();
56
57   // Stop watching the low memory file for readability.
58   // May be safely called if StartObserving has not been called.
59   // This method should only be called from the FILE thread.
60   void StopObservingOnFileThread();
61
62  private:
63   friend class base::RefCountedThreadSafe<LowMemoryObserverImpl>;
64
65   ~LowMemoryObserverImpl() {
66     StopObservingOnFileThread();
67   }
68
69   // Start a timer to resume watching the low memory file descriptor.
70   void ScheduleNextObservation();
71
72   // Actually start watching the file descriptor.
73   void StartWatchingDescriptor();
74
75   // Delegate to receive events from WatchFileDescriptor.
76   class FileWatcherDelegate : public base::MessageLoopForIO::Watcher {
77    public:
78     explicit FileWatcherDelegate(LowMemoryObserverImpl* owner)
79         : owner_(owner) {}
80     virtual ~FileWatcherDelegate() {}
81
82     // Overrides for base::MessageLoopForIO::Watcher
83     virtual void OnFileCanWriteWithoutBlocking(int fd) override {}
84     virtual void OnFileCanReadWithoutBlocking(int fd) override {
85       LOG(WARNING) << "Low memory condition detected.  Discarding a tab.";
86       // We can only discard tabs on the UI thread.
87       base::Callback<void(void)> callback = base::Bind(&DiscardTab);
88       BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
89       owner_->ScheduleNextObservation();
90     }
91
92     // Sends off a discard request to the OomPriorityManager.  Must be run on
93     // the UI thread.
94     static void DiscardTab() {
95       CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96       if (g_browser_process &&
97           g_browser_process->platform_part()->oom_priority_manager()) {
98         g_browser_process->platform_part()->
99             oom_priority_manager()->LogMemoryAndDiscardTab();
100       }
101     }
102
103    private:
104     LowMemoryObserverImpl* owner_;
105     DISALLOW_COPY_AND_ASSIGN(FileWatcherDelegate);
106   };
107
108   scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> watcher_;
109   FileWatcherDelegate watcher_delegate_;
110   int file_descriptor_;
111   base::OneShotTimer<LowMemoryObserverImpl> timer_;
112
113   DISALLOW_COPY_AND_ASSIGN(LowMemoryObserverImpl);
114 };
115
116 void LowMemoryObserverImpl::StartObservingOnFileThread() {
117   DCHECK_LE(file_descriptor_, 0)
118       << "Attempted to start observation when it was already started.";
119   DCHECK(watcher_.get() == NULL);
120   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
121   DCHECK(base::MessageLoopForIO::current());
122
123   file_descriptor_ = ::open(kLowMemFile, O_RDONLY);
124   // Don't report this error unless we're really running on ChromeOS
125   // to avoid testing spam.
126   if (file_descriptor_ < 0 && base::SysInfo::IsRunningOnChromeOS()) {
127     PLOG(ERROR) << "Unable to open " << kLowMemFile;
128     return;
129   }
130   watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
131   StartWatchingDescriptor();
132 }
133
134 void LowMemoryObserverImpl::StopObservingOnFileThread() {
135   // If StartObserving failed, StopObserving will still get called.
136   timer_.Stop();
137   if (file_descriptor_ >= 0) {
138     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
139     watcher_.reset(NULL);
140     ::close(file_descriptor_);
141     file_descriptor_ = -1;
142   }
143 }
144
145 void LowMemoryObserverImpl::ScheduleNextObservation() {
146   timer_.Start(FROM_HERE,
147                base::TimeDelta::FromMilliseconds(kLowMemoryCheckTimeoutMs),
148                this,
149                &LowMemoryObserverImpl::StartWatchingDescriptor);
150 }
151
152 void LowMemoryObserverImpl::StartWatchingDescriptor() {
153   DCHECK(watcher_.get());
154   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
155   DCHECK(base::MessageLoopForIO::current());
156   if (file_descriptor_ < 0)
157     return;
158   if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
159           file_descriptor_,
160           false,  // persistent=false: We want it to fire once and reschedule.
161           base::MessageLoopForIO::WATCH_READ,
162           watcher_.get(),
163           &watcher_delegate_)) {
164     LOG(ERROR) << "Unable to watch " << kLowMemFile;
165   }
166 }
167
168 ////////////////////////////////////////////////////////////////////////////////
169 // LowMemoryObserver
170
171 LowMemoryObserver::LowMemoryObserver() : observer_(new LowMemoryObserverImpl) {}
172
173 LowMemoryObserver::~LowMemoryObserver() { Stop(); }
174
175 void LowMemoryObserver::Start() {
176   BrowserThread::PostTask(
177       BrowserThread::FILE,
178       FROM_HERE,
179       base::Bind(&LowMemoryObserverImpl::StartObservingOnFileThread,
180                  observer_.get()));
181 }
182
183 void LowMemoryObserver::Stop() {
184   BrowserThread::PostTask(
185       BrowserThread::FILE,
186       FROM_HERE,
187       base::Bind(&LowMemoryObserverImpl::StopObservingOnFileThread,
188                  observer_.get()));
189 }
190
191 }  // namespace chromeos