mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 876029 - Make Gonk memory pressure by-pass the event queue. r=jlebar
This commit is contained in:
parent
7c34932720
commit
6a756a0ea4
@ -11,6 +11,7 @@
|
|||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
|
#include "nsMemoryPressure.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -24,37 +25,6 @@ using namespace mozilla;
|
|||||||
|
|
||||||
namespace {
|
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
|
* MemoryPressureWatcher watches sysfs from its own thread to notice when the
|
||||||
* system is under memory pressure. When we observe memory pressure, we use
|
* 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
|
// 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.
|
// and thus there is no need for the main process to forward this event.
|
||||||
Dispatch("memory-pressure",
|
rv = NS_DispatchMemoryPressure(MemPressure_New);
|
||||||
NS_LITERAL_STRING("low-memory-no-forward").get());
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Manually check lowMemFd until we observe that memory pressure is over.
|
// Manually check lowMemFd until we observe that memory pressure is over.
|
||||||
// We won't fire any more low-memory events until we observe that
|
// We won't fire any more low-memory events until we observe that
|
||||||
@ -219,8 +189,8 @@ public:
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (memoryPressure) {
|
if (memoryPressure) {
|
||||||
Dispatch("memory-pressure",
|
rv = NS_DispatchMemoryPressure(MemPressure_Ongoing);
|
||||||
NS_LITERAL_STRING("low-memory-ongoing-no-forward").get());
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} while (false);
|
} while (false);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
#include "nsISupports.h"
|
#include "nsISupports.h"
|
||||||
|
#include "nsMemoryPressure.h"
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "nsThread.h"
|
#include "nsThread.h"
|
||||||
|
|
||||||
@ -187,7 +188,7 @@ bool MaybeScheduleMemoryPressureEvent()
|
|||||||
sLastLowMemoryNotificationTime = PR_IntervalNow();
|
sLastLowMemoryNotificationTime = PR_IntervalNow();
|
||||||
|
|
||||||
LOG("Scheduling memory pressure notification.");
|
LOG("Scheduling memory pressure notification.");
|
||||||
ScheduleMemoryPressureEvent();
|
NS_DispatchEventualMemoryPressure(MemPressure_New);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +213,7 @@ void CheckMemAvailable()
|
|||||||
// so don't worry about firing this notification too often.
|
// so don't worry about firing this notification too often.
|
||||||
LOG("Detected low virtual memory.");
|
LOG("Detected low virtual memory.");
|
||||||
PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents);
|
PR_ATOMIC_INCREMENT(&sNumLowVirtualMemEvents);
|
||||||
ScheduleMemoryPressureEvent();
|
NS_DispatchEventualMemoryPressure(MemPressure_New);
|
||||||
}
|
}
|
||||||
else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
|
else if (stat.ullAvailPageFile < sLowCommitSpaceThreshold * 1024 * 1024) {
|
||||||
LOG("Detected low available page file space.");
|
LOG("Detected low available page file space.");
|
||||||
|
@ -24,6 +24,7 @@ MODULE = 'xpcom'
|
|||||||
|
|
||||||
EXPORTS += [
|
EXPORTS += [
|
||||||
'nsEventQueue.h',
|
'nsEventQueue.h',
|
||||||
|
'nsMemoryPressure.h',
|
||||||
'nsProcess.h',
|
'nsProcess.h',
|
||||||
'nsThread.h',
|
'nsThread.h',
|
||||||
]
|
]
|
||||||
@ -40,6 +41,7 @@ CPP_SOURCES += [
|
|||||||
'TimerThread.cpp',
|
'TimerThread.cpp',
|
||||||
'nsEnvironment.cpp',
|
'nsEnvironment.cpp',
|
||||||
'nsEventQueue.cpp',
|
'nsEventQueue.cpp',
|
||||||
|
'nsMemoryPressure.cpp',
|
||||||
'nsProcessCommon.cpp',
|
'nsProcessCommon.cpp',
|
||||||
'nsThread.cpp',
|
'nsThread.cpp',
|
||||||
'nsThreadManager.cpp',
|
'nsThreadManager.cpp',
|
||||||
|
48
xpcom/threads/nsMemoryPressure.cpp
Normal file
48
xpcom/threads/nsMemoryPressure.cpp
Normal file
@ -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);
|
||||||
|
}
|
76
xpcom/threads/nsMemoryPressure.h
Normal file
76
xpcom/threads/nsMemoryPressure.h
Normal file
@ -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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/ReentrantMonitor.h"
|
#include "mozilla/ReentrantMonitor.h"
|
||||||
|
#include "nsMemoryPressure.h"
|
||||||
#include "nsThread.h"
|
#include "nsThread.h"
|
||||||
#include "nsThreadManager.h"
|
#include "nsThreadManager.h"
|
||||||
#include "nsIClassInfoImpl.h"
|
#include "nsIClassInfoImpl.h"
|
||||||
@ -57,23 +58,6 @@ NS_DECL_CI_INTERFACE_GETTER(nsThread)
|
|||||||
|
|
||||||
nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
|
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
|
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
|
||||||
// somewhat manually.
|
// 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
|
// Fire a memory pressure notification, if we're the main thread and one is
|
||||||
// pending.
|
// pending.
|
||||||
if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
|
if (MAIN_THREAD == mIsMainThread && !ShuttingDown()) {
|
||||||
bool mpPending = PR_ATOMIC_SET(&sMemoryPressurePending, 0);
|
MemoryPressureState mpPending = NS_GetPendingMemoryPressure();
|
||||||
if (mpPending) {
|
if (mpPending != MemPressure_None) {
|
||||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
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) {
|
if (os) {
|
||||||
os->NotifyObservers(nullptr, "memory-pressure",
|
os->NotifyObservers(nullptr, "memory-pressure",
|
||||||
NS_LITERAL_STRING("low-memory").get());
|
mpPending == MemPressure_New ? lowMem.get() :
|
||||||
}
|
lowMemOngoing.get());
|
||||||
else {
|
} else {
|
||||||
NS_WARNING("Can't get observer service!");
|
NS_WARNING("Can't get observer service!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,16 +128,4 @@ private:
|
|||||||
nsresult mResult;
|
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__
|
#endif // nsThread_h__
|
||||||
|
Loading…
Reference in New Issue
Block a user