зеркало из https://github.com/mozilla/gecko-dev.git
Bug 876029 - Make Gonk memory pressure by-pass the event queue. r=jlebar
This commit is contained in:
Родитель
c9c3f11e7c
Коммит
912277b213
|
@ -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__
|
||||
|
|
Загрузка…
Ссылка в новой задаче