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.

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

mercurial