Quelle TestAvailableMemoryWatcherWin.cpp
Sprache: C
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
// The message queue can be empty and the loop stops // waiting for a new event before detecting timeout. // Creating a timer to fire a timeout event.
nsCOMPtr<nsITimer> timer;
NS_NewTimerWithFuncCallback(
getter_AddRefs(timer),
[](nsITimer*, void* isTimeout) {
*reinterpret_cast<bool*>(isTimeout) = true;
},
&isTimeout, aTimeoutMs, nsITimer::TYPE_ONE_SHOT, __func__);
SpinEventLoopUntil("xpcom-tests:WaitUntil"_ns, [&]() -> bool { if (isTimeout) { returntrue;
}
bool done = aCondition(); if (done) {
fprintf(stderr, "Done in %llu msec\n", ::GetTickCount64() - t0);
} return done;
});
return !isTimeout;
}
class Spinner final : public nsIObserver {
nsCOMPtr<nsIObserverService> mObserverSvc;
nsDependentCString mTopicToWatch;
Maybe<nsDependentString> mSubTopicToWatch; bool mTopicObserved;
// Force the loop to move in case that there is no event in the queue.
nsCOMPtr<nsIRunnable> dummyEvent = new Runnable(__func__);
NS_DispatchToMainThread(dummyEvent);
}
} else {
fprintf(stderr, "Unexpected topic: %s\n", aTopic);
}
/** * Starts a new thread with a message queue to process * memory allocation/free requests
*/ class MemoryEater { using PageT = UniquePtr<void, VirtualFreeDeleter>;
staticvoid TouchMemory(void* aAddr, size_t aSize) {
constexpr uint32_t kPageSize = 4096; volatile uint8_t x = 0; auto base = reinterpret_cast<uint8_t*>(aAddr); for (int64_t i = 0, pages = aSize / kPageSize; i < pages; ++i) { // Pick a random place in every allocated page // and dereference it.
x ^= *(base + i * kPageSize + rand() % kPageSize);
}
(void)x;
}
size_t currentSize = aSize; while (aSize >= kMinGranularity) { if (!GetAvailablePhysicalMemoryInMb()) { // If the available physical memory is less than 1MB, we finish // allocation though there may be still the available commit space.
fprintf(stderr, "No enough physical memory.\n"); returnfalse;
}
// Try again with a smaller allocation size.
currentSize /= 2; continue;
}
aSize -= currentSize;
// VirtualAlloc consumes the commit space, but we need to *touch* memory // to consume physical memory
TouchMemory(page.get(), currentSize);
Unused << aOutput.emplaceBack(std::move(page));
} returntrue;
}
// The value data is REG_MULTI_SZ and each element is "<path> <min> <max>". // If the page file size is automatically managed for all drives, the <path> // is set to "?:\pagefile.sys". // If the page file size is configured per drive, for a drive whose page // file is set to "system managed size", both <min> and <max> are set to 0. return !pagingFiles.IsEmpty() &&
(pagingFiles[0] == u'?' || FindInReadable(u" 0 0"_ns, pagingFiles));
}
static size_t GetAllocationSizeToTriggerMemoryNotification() { // The percentage of the used physical memory to the total physical memory // size which is big enough to trigger a memory resource notification.
constexpr uint32_t kThresholdPercentage = 98; // If the page file is not expandable, leave a little commit space. const uint32_t kMinimumSafeCommitSpaceMb =
IsPageFileExpandable() ? 0 : 1024;
// How much memory needs to be used to trigger the notification const size_t targetUsedTotalMb =
(statex.ullTotalPhys / kBytesInMB) * kThresholdPercentage / 100;
// How much memory is currently consumed const size_t currentConsumedMb =
(statex.ullTotalPhys - statex.ullAvailPhys) / kBytesInMB;
if (currentConsumedMb >= targetUsedTotalMb) {
fprintf(stderr, "The available physical memory is already low.\n"); return 0;
}
// How much memory we need to allocate to trigger the notification const uint32_t allocMb = targetUsedTotalMb - currentConsumedMb;
// If we allocate the target amount, how much commit space will be // left available. const uint32_t estimtedAvailCommitSpace = std::max(
0, static_cast<int32_t>((statex.ullAvailPageFile / kBytesInMB) - allocMb));
// If the available commit space will be too low, we should not continue if (estimtedAvailCommitSpace < kMinimumSafeCommitSpaceMb) {
fprintf(stderr, "The available commit space will be short - %d\n",
estimtedAvailCommitSpace); return 0;
}
fprintf(stderr, "Total physical memory = %ul\n" "Available commit space = %ul\n" "Amount to allocate = %ul\n" "Future available commit space after allocation = %d\n", static_cast<uint32_t>(statex.ullTotalPhys / kBytesInMB), static_cast<uint32_t>(statex.ullAvailPageFile / kBytesInMB),
allocMb, estimtedAvailCommitSpace); return allocMb * kBytesInMB;
}
// We set the threshold to 50% of the current available commit space. // This means we declare low-memory when the available commit space // gets lower than this threshold, otherwise we declare high-memory.
SetThresholdAsPercentageOfCommitSpace(50);
}
// Since this test does not involve TabUnloader, the first two numbers // are always expected to be zero.
EXPECT_STREQ(tokens[0].get(), L"0");
EXPECT_STREQ(tokens[1].get(), L"0");
// The third token should be a valid floating number.
nsresult rv;
tokens[2].ToDouble(&rv);
EXPECT_NS_SUCCEEDED(rv);
}
};
const size_t allocSize = GetAllocationSizeToTriggerMemoryNotification(); if (!allocSize) { // Not enough memory to safely create a low-memory situation. // Aborting the test without failure. return;
}
mTabUnloader->ResetCounter();
mMemEater.RequestAlloc(allocSize); if (!WaitForMemoryResourceNotification()) { // If the notification was not triggered, abort the test without failure // because it's not a fault in nsAvailableMemoryWatcher. return;
}
// skip-if asan, Bug 1940978 #if !(defined(MOZ_ASAN))
TEST_F(AvailableMemoryWatcherFixture, InactiveToActive) {
AutoJSContextWithGlobal cx(mCleanGlobal);
MemoryWatcherTelemetryEvent telemetryEvent(cx.GetJSContext()); const size_t allocSize = GetAllocationSizeToTriggerMemoryNotification(); if (!allocSize) { // Not enough memory to safely create a low-memory situation. // Aborting the test without failure. return;
}
mTabUnloader->ResetCounter();
mMemEater.RequestAlloc(allocSize); if (!WaitForMemoryResourceNotification()) { // If the notification was not triggered, abort the test without failure // because it's not a fault in nsAvailableMemoryWatcher. return;
}
// OnHighMemory should not be triggered during no user interaction // eve after all memory was freed. Expecting false.
EXPECT_FALSE(mHighMemoryObserver->Wait(3000));
StartUserInteraction();
// After user is active, we expect true.
EXPECT_TRUE(mHighMemoryObserver->Wait(kStateChangeTimeoutMs));
TEST_F(AvailableMemoryWatcherFixture, HighCommitSpace_AlwaysActive) { // Setting a low threshold simulates a high commit space.
SetThresholdAsPercentageOfCommitSpace(1);
StartUserInteraction();
const size_t allocSize = GetAllocationSizeToTriggerMemoryNotification(); if (!allocSize) { // Not enough memory to safely create a low-memory situation. // Aborting the test without failure. return;
}
mTabUnloader->ResetCounter();
mMemEater.RequestAlloc(allocSize); if (!WaitForMemoryResourceNotification()) { // If the notification was not triggered, abort the test without failure // because it's not a fault in nsAvailableMemoryWatcher. return;
}
// Tab unload will not be triggered because the commit space is not low.
EXPECT_FALSE(WaitUntil([this]() { return mTabUnloader->GetCounter() >= 1; },
kStateChangeTimeoutMs / 2));
TEST_F(AvailableMemoryWatcherFixture, HighCommitSpace_InactiveToActive) { // Setting a low threshold simulates a high commit space.
SetThresholdAsPercentageOfCommitSpace(1);
const size_t allocSize = GetAllocationSizeToTriggerMemoryNotification(); if (!allocSize) { // Not enough memory to safely create a low-memory situation. // Aborting the test without failure. return;
}
mTabUnloader->ResetCounter();
mMemEater.RequestAlloc(allocSize); if (!WaitForMemoryResourceNotification()) { // If the notification was not triggered, abort the test without failure // because it's not a fault in nsAvailableMemoryWatcher. return;
}
// Tab unload will not be triggered because the commit space is not low.
EXPECT_FALSE(WaitUntil([this]() { return mTabUnloader->GetCounter() >= 1; },
kStateChangeTimeoutMs / 2));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.