widget/gonk/GonkMemoryPressureMonitoring.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "GonkMemoryPressureMonitoring.h"
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/FileUtils.h"
    10 #include "mozilla/Monitor.h"
    11 #include "mozilla/Preferences.h"
    12 #include "mozilla/ProcessPriorityManager.h"
    13 #include "mozilla/Services.h"
    14 #include "nsIObserver.h"
    15 #include "nsIObserverService.h"
    16 #include "nsMemoryPressure.h"
    17 #include "nsThreadUtils.h"
    18 #include <errno.h>
    19 #include <fcntl.h>
    20 #include <poll.h>
    21 #include <android/log.h>
    23 #define LOG(args...)  \
    24   __android_log_print(ANDROID_LOG_INFO, "GonkMemoryPressure" , ## args)
    26 #ifdef MOZ_NUWA_PROCESS
    27 #include "ipc/Nuwa.h"
    28 #endif
    30 using namespace mozilla;
    32 namespace {
    34 /**
    35  * MemoryPressureWatcher watches sysfs from its own thread to notice when the
    36  * system is under memory pressure.  When we observe memory pressure, we use
    37  * MemoryPressureRunnable to notify observers that they should release memory.
    38  *
    39  * When the system is under memory pressure, we don't want to constantly fire
    40  * memory-pressure events.  So instead, we try to detect when sysfs indicates
    41  * that we're no longer under memory pressure, and only then start firing events
    42  * again.
    43  *
    44  * (This is a bit problematic because we can't poll() to detect when we're no
    45  * longer under memory pressure; instead we have to periodically read the sysfs
    46  * node.  If we remain under memory pressure for a long time, this means we'll
    47  * continue waking up to read from the node for a long time, potentially wasting
    48  * battery life.  Hopefully we don't hit this case in practice!  We write to
    49  * logcat each time we go around this loop so it's at least noticable.)
    50  *
    51  * Shutting down safely is a bit of a chore.  XPCOM won't shut down until all
    52  * threads exit, so we need to exit the Run() method below on shutdown.  But our
    53  * thread might be blocked in one of two situations: We might be poll()'ing the
    54  * sysfs node waiting for memory pressure to occur, or we might be asleep
    55  * waiting to read() the sysfs node to see if we're no longer under memory
    56  * pressure.
    57  *
    58  * To let us wake up from the poll(), we poll() not just the sysfs node but also
    59  * a pipe, which we write to on shutdown.  To let us wake up from sleeping
    60  * between read()s, we sleep by Wait()'ing on a monitor, which we notify on
    61  * shutdown.
    62  */
    63 class MemoryPressureWatcher
    64   : public nsIRunnable
    65   , public nsIObserver
    66 {
    67 public:
    68   MemoryPressureWatcher()
    69     : mMonitor("MemoryPressureWatcher")
    70     , mShuttingDown(false)
    71   {
    72   }
    74   NS_DECL_THREADSAFE_ISUPPORTS
    76   nsresult Init()
    77   {
    78     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    79     NS_ENSURE_STATE(os);
    81     // The observer service holds us alive.
    82     os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, /* holdsWeak */ false);
    84     // While we're under memory pressure, we periodically read()
    85     // notify_trigger_active to try and see when we're no longer under memory
    86     // pressure.  mPollMS indicates how many milliseconds we wait between those
    87     // read()s.
    88     mPollMS = Preferences::GetUint("gonk.systemMemoryPressureRecoveryPollMS",
    89                                    /* default */ 5000);
    91     int pipes[2];
    92     NS_ENSURE_STATE(!pipe(pipes));
    93     mShutdownPipeRead = pipes[0];
    94     mShutdownPipeWrite = pipes[1];
    95     return NS_OK;
    96   }
    98   NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
    99                      const char16_t* aData)
   100   {
   101     MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
   102     LOG("Observed XPCOM shutdown.");
   104     MonitorAutoLock lock(mMonitor);
   105     mShuttingDown = true;
   106     mMonitor.Notify();
   108     int rv;
   109     do {
   110       // Write something to the pipe; doesn't matter what.
   111       uint32_t dummy = 0;
   112       rv = write(mShutdownPipeWrite, &dummy, sizeof(dummy));
   113     } while(rv == -1 && errno == EINTR);
   115     return NS_OK;
   116   }
   118   NS_IMETHOD Run()
   119   {
   120     MOZ_ASSERT(!NS_IsMainThread());
   122 #ifdef MOZ_NUWA_PROCESS
   123     if (IsNuwaProcess()) {
   124       NS_ASSERTION(NuwaMarkCurrentThread != nullptr,
   125                    "NuwaMarkCurrentThread is undefined!");
   126       NuwaMarkCurrentThread(nullptr, nullptr);
   127     }
   128 #endif
   130     int lowMemFd = open("/sys/kernel/mm/lowmemkiller/notify_trigger_active",
   131                         O_RDONLY | O_CLOEXEC);
   132     NS_ENSURE_STATE(lowMemFd != -1);
   133     ScopedClose autoClose(lowMemFd);
   135     nsresult rv = CheckForMemoryPressure(lowMemFd, nullptr);
   136     NS_ENSURE_SUCCESS(rv, rv);
   138     while (true) {
   139       // Wait for a notification on lowMemFd or for data to be written to
   140       // mShutdownPipeWrite.  (poll(lowMemFd, POLLPRI) blocks until we're under
   141       // memory pressure.)
   142       struct pollfd pollfds[2];
   143       pollfds[0].fd = lowMemFd;
   144       pollfds[0].events = POLLPRI;
   145       pollfds[1].fd = mShutdownPipeRead;
   146       pollfds[1].events = POLLIN;
   148       int pollRv;
   149       do {
   150         pollRv = poll(pollfds, ArrayLength(pollfds), /* timeout */ -1);
   151       } while (pollRv == -1 && errno == EINTR);
   153       if (pollfds[1].revents) {
   154         // Something was written to our shutdown pipe; we're outta here.
   155         LOG("shutting down (1)");
   156         return NS_OK;
   157       }
   159       // If pollfds[1] isn't happening, pollfds[0] ought to be!
   160       if (!(pollfds[0].revents & POLLPRI)) {
   161         LOG("Unexpected revents value after poll(): %d. "
   162             "Shutting down GonkMemoryPressureMonitoring.", pollfds[0].revents);
   163         return NS_ERROR_FAILURE;
   164       }
   166       // POLLPRI on lowMemFd indicates that we're in a low-memory situation.  We
   167       // could read lowMemFd to double-check, but we've observed that the read
   168       // sometimes completes after the memory-pressure event is over, so let's
   169       // just believe the result of poll().
   171       // We use low-memory-no-forward because each process has its own watcher
   172       // and thus there is no need for the main process to forward this event.
   173       rv = DispatchMemoryPressure(MemPressure_New);
   174       NS_ENSURE_SUCCESS(rv, rv);
   176       // Manually check lowMemFd until we observe that memory pressure is over.
   177       // We won't fire any more low-memory events until we observe that
   178       // we're no longer under pressure. Instead, we fire low-memory-ongoing
   179       // events, which cause processes to keep flushing caches but will not
   180       // trigger expensive GCs and other attempts to save memory that are
   181       // likely futile at this point.
   182       bool memoryPressure;
   183       do {
   184         {
   185           MonitorAutoLock lock(mMonitor);
   187           // We need to check mShuttingDown before we wait here, in order to
   188           // catch a shutdown signal sent after we poll()'ed mShutdownPipeRead
   189           // above but before we started waiting on the monitor.  But we don't
   190           // need to check after we wait, because we'll either do another
   191           // iteration of this inner loop, in which case we'll check
   192           // mShuttingDown, or we'll exit this loop and do another iteration
   193           // of the outer loop, in which case we'll check the shutdown pipe.
   194           if (mShuttingDown) {
   195             LOG("shutting down (2)");
   196             return NS_OK;
   197           }
   198           mMonitor.Wait(PR_MillisecondsToInterval(mPollMS));
   199         }
   201         LOG("Checking to see if memory pressure is over.");
   202         rv = CheckForMemoryPressure(lowMemFd, &memoryPressure);
   203         NS_ENSURE_SUCCESS(rv, rv);
   205         if (memoryPressure) {
   206           rv = DispatchMemoryPressure(MemPressure_Ongoing);
   207           NS_ENSURE_SUCCESS(rv, rv);
   208           continue;
   209         }
   210       } while (false);
   212       LOG("Memory pressure is over.");
   213     }
   215     return NS_OK;
   216   }
   218 private:
   219   /**
   220    * Read from aLowMemFd, which we assume corresponds to the
   221    * notify_trigger_active sysfs node, and determine whether we're currently
   222    * under memory pressure.
   223    *
   224    * We don't expect this method to block.
   225    */
   226   nsresult CheckForMemoryPressure(int aLowMemFd, bool* aOut)
   227   {
   228     if (aOut) {
   229       *aOut = false;
   230     }
   232     lseek(aLowMemFd, 0, SEEK_SET);
   234     char buf[2];
   235     int nread;
   236     do {
   237       nread = read(aLowMemFd, buf, sizeof(buf));
   238     } while(nread == -1 && errno == EINTR);
   239     NS_ENSURE_STATE(nread == 2);
   241     // The notify_trigger_active sysfs node should contain either "0\n" or
   242     // "1\n".  The latter indicates memory pressure.
   243     if (aOut) {
   244       *aOut = buf[0] == '1' && buf[1] == '\n';
   245     }
   246     return NS_OK;
   247   }
   249   /**
   250    * Dispatch the specified memory pressure event unless a high-priority
   251    * process is present. If a high-priority process is present then it's likely
   252    * responding to an urgent event (an incoming call or message for example) so
   253    * avoid wasting CPU time responding to low-memory events.
   254    */
   255   nsresult DispatchMemoryPressure(MemoryPressureState state)
   256   {
   257     if (ProcessPriorityManager::AnyProcessHasHighPriority()) {
   258       return NS_OK;
   259     }
   261     return NS_DispatchMemoryPressure(state);
   262   }
   264   Monitor mMonitor;
   265   uint32_t mPollMS;
   266   bool mShuttingDown;
   268   ScopedClose mShutdownPipeRead;
   269   ScopedClose mShutdownPipeWrite;
   270 };
   272 NS_IMPL_ISUPPORTS(MemoryPressureWatcher, nsIRunnable, nsIObserver);
   274 } // anonymous namespace
   276 namespace mozilla {
   278 void
   279 InitGonkMemoryPressureMonitoring()
   280 {
   281   // memoryPressureWatcher is held alive by the observer service.
   282   nsRefPtr<MemoryPressureWatcher> memoryPressureWatcher =
   283     new MemoryPressureWatcher();
   284   NS_ENSURE_SUCCESS_VOID(memoryPressureWatcher->Init());
   286   nsCOMPtr<nsIThread> thread;
   287   NS_NewThread(getter_AddRefs(thread), memoryPressureWatcher);
   288 }
   290 } // namespace mozilla

mercurial