Bug 876029 - Make Gonk memory pressure by-pass the event queue. r=jlebar

This commit is contained in:
Nicolas B. Pierron 2013-07-07 15:59:50 -07:00
Родитель c9c3f11e7c
Коммит 912277b213
7 изменённых файлов: 146 добавлений и 71 удалений

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/Services.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsMemoryPressure.h"
#include "nsThreadUtils.h"
#include <errno.h>
#include <fcntl.h>
@ -24,37 +25,6 @@ using namespace mozilla;
namespace {
class MemoryPressureRunnable : public nsRunnable
{
const char *mTopic;
const PRUnichar *mData;
public:
MemoryPressureRunnable(const char *aTopic, const PRUnichar *aData) :
mTopic(aTopic), mData(aData)
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
LOG("Dispatching low-memory memory-pressure event");
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(nullptr, mTopic, mData);
}
return NS_OK;
}
};
static void
Dispatch(const char *aTopic, const PRUnichar *aData)
{
nsRefPtr<MemoryPressureRunnable> memoryPressureRunnable =
new MemoryPressureRunnable(aTopic, aData);
NS_DispatchToMainThread(memoryPressureRunnable);
}
/**
* MemoryPressureWatcher watches sysfs from its own thread to notice when the
* system is under memory pressure. When we observe memory pressure, we use
@ -186,8 +156,8 @@ public:
// We use low-memory-no-forward because each process has its own watcher
// and thus there is no need for the main process to forward this event.
Dispatch("memory-pressure",
NS_LITERAL_STRING("low-memory-no-forward").get());
rv = NS_DispatchMemoryPressure(MemPressure_New);
NS_ENSURE_SUCCESS(rv, rv);
// Manually check lowMemFd until we observe that memory pressure is over.
// We won't fire any more low-memory events until we observe that
@ -219,8 +189,8 @@ public:
NS_ENSURE_SUCCESS(rv, rv);
if (memoryPressure) {
Dispatch("memory-pressure",
NS_LITERAL_STRING("low-memory-ongoing-no-forward").get());
rv = NS_DispatchMemoryPressure(MemPressure_Ongoing);
NS_ENSURE_SUCCESS(rv, rv);
continue;
}
} while (false);

Просмотреть файл

@ -15,6 +15,7 @@
#include "nsIObserverService.h"
#include "nsIRunnable.h"
#include "nsISupports.h"
#include "nsMemoryPressure.h"
#include "nsPrintfCString.h"
#include "nsThread.h"
@ -187,7 +188,7 @@ bool MaybeScheduleMemoryPressureEvent()
sLastLowMemoryNotificationTime = PR_IntervalNow();
LOG("Scheduling memory pressure notification.");
ScheduleMemoryPressureEvent();
NS_DispatchEventualMemoryPressure(MemPressure_New);
return true;
}
@ -212,7 +213,7 @@ void CheckMemAvailable()
// so don't worry about firing this notification too often.
LOG("Detected low virtual memory.");
PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents);
ScheduleMemoryPressureEvent();
NS_DispatchEventualMemoryPressure(MemPressure_New);
}
else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
LOG("Detected low available page file space.");

Просмотреть файл

@ -24,6 +24,7 @@ MODULE = 'xpcom'
EXPORTS += [
'nsEventQueue.h',
'nsMemoryPressure.h',
'nsProcess.h',
'nsThread.h',
]
@ -40,6 +41,7 @@ CPP_SOURCES += [
'TimerThread.cpp',
'nsEnvironment.cpp',
'nsEventQueue.cpp',
'nsMemoryPressure.cpp',
'nsProcessCommon.cpp',
'nsThread.cpp',
'nsThreadManager.cpp',

Просмотреть файл

@ -0,0 +1,48 @@
#include "nsMemoryPressure.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "nsThreadUtils.h"
using namespace mozilla;
static Atomic<int32_t, Relaxed> sMemoryPressurePending;
MOZ_STATIC_ASSERT(MemPressure_None == 0,
"Bad static initialization with the default constructor.");
MemoryPressureState
NS_GetPendingMemoryPressure()
{
int32_t value = sMemoryPressurePending.exchange(MemPressure_None);
return MemoryPressureState(value);
}
void
NS_DispatchEventualMemoryPressure(MemoryPressureState state)
{
/*
* A new memory pressure event erases an ongoing memory pressure, but an
* existing "new" memory pressure event takes precedence over a new "ongoing"
* memory pressure event.
*/
switch (state) {
case MemPressure_None:
sMemoryPressurePending = MemPressure_None;
break;
case MemPressure_New:
sMemoryPressurePending = MemPressure_New;
break;
case MemPressure_Ongoing:
sMemoryPressurePending.compareExchange(MemPressure_None, MemPressure_Ongoing);
break;
}
}
nsresult
NS_DispatchMemoryPressure(MemoryPressureState state)
{
NS_DispatchEventualMemoryPressure(state);
nsCOMPtr<nsIRunnable> event = new nsRunnable;
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return NS_DispatchToMainThread(event);
}

Просмотреть файл

@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsMemoryPressure_h__
#define nsMemoryPressure_h__
#include "nscore.h"
enum MemoryPressureState {
/*
* No memory pressure.
*/
MemPressure_None = 0,
/*
* New memory pressure deteced.
*
* On a new memory pressure, we stop everything to start cleaning
* aggresively the memory used, in order to free as much memory as
* possible.
*/
MemPressure_New,
/*
* Repeated memory pressure.
*
* A repeated memory pressure implies to clean softly recent allocations.
* It is supposed to happen after a new memory pressure which already
* cleaned aggressivley. So there is no need to damage the reactivity of
* Gecko by stopping the world again.
*
* In case of conflict with an new memory pressue, the new memory pressure
* takes precedence over an ongoing memory pressure. The reason being
* that if no events are processed between 2 notifications (new followed
* by ongoing, or ongoing followed by a new) we want to be as aggresive as
* possible on the clean-up of the memory. After all, we are trying to
* keep Gecko alive as long as possible.
*/
MemPressure_Ongoing
};
/**
* Return and erase the latest state of the memory pressure event set by any of
* the corresponding dispatch function.
*/
MemoryPressureState
NS_GetPendingMemoryPressure();
/**
* This function causes the main thread to fire a memory pressure event
* before processing the next event, but if there are no events pending in
* the main thread's event queue, the memory pressure event would not be
* dispatched until one is enqueued. It is infallible and does not allocate
* any memory.
*
* You may call this function from any thread.
*/
void
NS_DispatchEventualMemoryPressure(MemoryPressureState state);
/**
* This function causes the main thread to fire a memory pressure event
* before processing the next event. We wake up the main thread by adding a
* dummy event to its event loop, so, unlike with
* NS_DispatchEventualMemoryPressure, this memory-pressure event is always
* fired relatively quickly, even if the event loop is otherwise empty.
*
* You may call this function from any thread.
*/
nsresult
NS_DispatchMemoryPressure(MemoryPressureState state);
#endif // nsMemoryPressure_h__

Просмотреть файл

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ReentrantMonitor.h"
#include "nsMemoryPressure.h"
#include "nsThread.h"
#include "nsThreadManager.h"
#include "nsIClassInfoImpl.h"
@ -57,23 +58,6 @@ NS_DECL_CI_INTERFACE_GETTER(nsThread)
nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
namespace mozilla {
// Fun fact: Android's GCC won't convert bool* to int32_t*, so we can't
// PR_ATOMIC_SET a bool.
static int32_t sMemoryPressurePending = 0;
/*
* It's important that this function not acquire any locks, nor do anything
* which might cause malloc to run.
*/
void ScheduleMemoryPressureEvent()
{
PR_ATOMIC_SET(&sMemoryPressurePending, 1);
}
} // namespace mozilla
//-----------------------------------------------------------------------------
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
// somewhat manually.
@ -580,14 +564,20 @@ nsThread::ProcessNextEvent(bool mayWait, bool *result)
// Fire a memory pressure notification, if we're the main thread and one is
// pending.
if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
bool mpPending = PR_ATOMIC_SET(&sMemoryPressurePending, 0);
if (mpPending) {
MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
if (mpPending != MemPressure_None) {
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
// Use no-forward to prevent the notifications from being transferred to
// the children of this process.
NS_NAMED_LITERAL_STRING(lowMem, "low-memory-no-forward");
NS_NAMED_LITERAL_STRING(lowMemOngoing, "low-memory-ongoing-no-forward");
if (os) {
os->NotifyObservers(nullptr, "memory-pressure",
NS_LITERAL_STRING("low-memory").get());
}
else {
mpPending == MemPressure_New ? lowMem.get() :
lowMemOngoing.get());
} else {
NS_WARNING("Can't get observer service!");
}
}

Просмотреть файл

@ -128,16 +128,4 @@ private:
nsresult mResult;
};
namespace mozilla {
/**
* This function causes the main thread to fire a memory pressure event at its
* next available opportunity.
*
* You may call this function from any thread.
*/
void ScheduleMemoryPressureEvent();
} // namespace mozilla
#endif // nsThread_h__