15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/memory/low_memory_listener.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <fcntl.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/sys_info.h"
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/timer/timer.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chromeos/memory/low_memory_listener_delegate.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/zygote_host_linux.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace chromeos {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is the file that will exist if low memory notification is available
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// on the device.  Whenever it becomes readable, it signals a low memory
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// condition.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kLowMemFile[] = "/dev/chromeos-low-mem";
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This is the minimum amount of time in milliseconds between checks for
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// low memory.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kLowMemoryCheckTimeoutMs = 750;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// LowMemoryListenerImpl
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Does the actual work of observing.  The observation work happens on the FILE
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// thread, and notification happens on the UI thread.  If low memory is
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// detected, then we notify, wait kLowMemoryCheckTimeoutMs milliseconds and then
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// start watching again to see if we're still in a low memory state.  This is to
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// keep from sending out multiple notifications before the UI has a chance to
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// respond (it may take the UI a while to actually deallocate memory). A timer
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// isn't the perfect solution, but without any reliable indicator that a tab has
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// had all its parts deallocated, it's the next best thing.
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class LowMemoryListenerImpl
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : public base::RefCountedThreadSafe<LowMemoryListenerImpl> {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  LowMemoryListenerImpl() : watcher_delegate_(this), file_descriptor_(-1) {}
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start watching the low memory file for readability.
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calls to StartObserving should always be matched with calls to
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // StopObserving.  This method should only be called from the FILE thread.
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // |low_memory_callback| is run when memory is low.
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  void StartObservingOnFileThread(const base::Closure& low_memory_callback);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop watching the low memory file for readability.
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // May be safely called if StartObserving has not been called.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This method should only be called from the FILE thread.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StopObservingOnFileThread();
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  friend class base::RefCountedThreadSafe<LowMemoryListenerImpl>;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ~LowMemoryListenerImpl() {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    StopObservingOnFileThread();
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start a timer to resume watching the low memory file descriptor.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void ScheduleNextObservation();
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Actually start watching the file descriptor.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void StartWatchingDescriptor();
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delegate to receive events from WatchFileDescriptor.
75868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  class FileWatcherDelegate : public base::MessageLoopForIO::Watcher {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   public:
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    explicit FileWatcherDelegate(LowMemoryListenerImpl* owner)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        : owner_(owner) {}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual ~FileWatcherDelegate() {}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Overrides for MessageLoopForIO::Watcher
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(WARNING) << "Low memory condition detected.  Discarding a tab.";
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We can only discard tabs on the UI thread.
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      BrowserThread::PostTask(BrowserThread::UI,
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              FROM_HERE,
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                              owner_->low_memory_callback_);
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      owner_->ScheduleNextObservation();
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   private:
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    LowMemoryListenerImpl* owner_;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DISALLOW_COPY_AND_ASSIGN(FileWatcherDelegate);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
97868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> watcher_;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  FileWatcherDelegate watcher_delegate_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int file_descriptor_;
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::OneShotTimer<LowMemoryListenerImpl> timer_;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::Closure low_memory_callback_;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(LowMemoryListenerImpl);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListenerImpl::StartObservingOnFileThread(
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const base::Closure& low_memory_callback) {
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  low_memory_callback_ = low_memory_callback;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LE(file_descriptor_, 0)
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      << "Attempted to start observation when it was already started.";
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(watcher_.get() == NULL);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(base::MessageLoopForIO::current());
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_descriptor_ = ::open(kLowMemFile, O_RDONLY);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't report this error unless we're really running on ChromeOS
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // to avoid testing spam.
1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (file_descriptor_ < 0 && base::SysInfo::IsRunningOnChromeOS()) {
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PLOG(ERROR) << "Unable to open " << kLowMemFile;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
122868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  StartWatchingDescriptor();
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListenerImpl::StopObservingOnFileThread() {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If StartObserving failed, StopObserving will still get called.
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timer_.Stop();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (file_descriptor_ >= 0) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    watcher_.reset(NULL);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::close(file_descriptor_);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file_descriptor_ = -1;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListenerImpl::ScheduleNextObservation() {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  timer_.Start(FROM_HERE,
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               base::TimeDelta::FromMilliseconds(kLowMemoryCheckTimeoutMs),
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               this,
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               &LowMemoryListenerImpl::StartWatchingDescriptor);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListenerImpl::StartWatchingDescriptor() {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(watcher_.get());
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK(base::MessageLoopForIO::current());
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (file_descriptor_ < 0)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          file_descriptor_,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          false,  // persistent=false: We want it to fire once and reschedule.
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)          base::MessageLoopForIO::WATCH_READ,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          watcher_.get(),
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          &watcher_delegate_)) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Unable to watch " << kLowMemFile;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// LowMemoryListener
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)LowMemoryListener::LowMemoryListener(LowMemoryListenerDelegate* delegate)
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : observer_(new LowMemoryListenerImpl),
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      delegate_(delegate),
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_factory_(this) {
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)LowMemoryListener::~LowMemoryListener() {
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  Stop();
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListener::Start() {
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::Closure memory_low_callback =
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&LowMemoryListener::OnMemoryLow, weak_factory_.GetWeakPtr());
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&LowMemoryListenerImpl::StartObservingOnFileThread,
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 observer_.get(),
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 memory_low_callback));
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListener::Stop() {
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  weak_factory_.InvalidateWeakPtrs();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      FROM_HERE,
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&LowMemoryListenerImpl::StopObservingOnFileThread,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 observer_.get()));
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LowMemoryListener::OnMemoryLow() {
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  delegate_->OnMemoryLow();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace chromeos
199