Bug 1860969 - Add testcase for incomplete task and rescheduling in TaskController. r=bas

Also fix the null-check.

Differential Revision: https://phabricator.services.mozilla.com/D198504
This commit is contained in:
Tooru Fujisawa 2024-03-19 03:59:15 +00:00
parent 7ebfa02780
commit 2896f9ef0e
4 changed files with 220 additions and 1 deletions

View File

@ -9,6 +9,7 @@
#include "JSOracleParent.h"
#include "js/CallAndConstruct.h" // JS::Call
#include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin
#include "js/friend/DumpFunctions.h"
#include "js/CharacterEncoding.h"
#include "js/Date.h" // JS::IsISOStyleDate
#include "js/Object.h" // JS::GetClass
@ -633,6 +634,7 @@ static mozJSModuleLoader* GetModuleLoaderForCurrentGlobal(
RefPtr targetModuleLoader = global->GetModuleLoader(aCx);
if (!targetModuleLoader) {
// Sandbox without associated window returns nullptr for GetModuleLoader.
js::DumpBacktrace(aCx);
JS_ReportErrorASCII(aCx, "No ModuleLoader found for the current context");
return nullptr;
}

View File

@ -0,0 +1,214 @@
/* -*- 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/. */
#include "gtest/gtest.h"
#include <stdint.h> // uint32_t
#include "nsString.h" // nsACString
#include "nsThreadUtils.h" // NS_ProcessNextEvent
#include "mozilla/Atomics.h" // Atomic
#include "mozilla/EventQueue.h" // EventQueuePriority
#include "mozilla/Mutex.h" // Mutex, MutexAutoLock
#include "mozilla/RefPtr.h" // RefPtr, do_AddRef
#include "mozilla/TaskController.h" // TaskController, Task
#include "prthread.h" // PR_Sleep
using namespace mozilla;
namespace TestTaskController {
class Logger {
public:
Logger() : mMutex("Logger") {}
void Add(const char* aText) {
MutexAutoLock lock(mMutex);
mLog += aText;
}
const nsAutoCString& GetLog() const { return mLog; }
private:
nsAutoCString mLog;
Mutex mMutex;
};
class ReschedulingTask : public Task {
static constexpr uint32_t LoopCount = 3;
public:
explicit ReschedulingTask(Kind aKind, Logger* aLogger, const char* aName)
: Task(aKind, EventQueuePriority::Normal),
mCount(0),
mIsDone(false),
mLogger(aLogger),
mName(aName) {}
TaskResult Run() override {
mLogger->Add(mName);
mCount++;
if (mCount < LoopCount) {
return TaskResult::Incomplete;
}
mIsDone = true;
return TaskResult::Complete;
}
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
bool GetName(nsACString& aName) override {
aName.AssignLiteral("AsyncScriptCompileTask");
return true;
}
#endif
bool IsDone() const { return mIsDone; }
private:
Atomic<uint32_t> mCount;
Atomic<bool> mIsDone;
Logger* mLogger;
const char* mName;
};
using namespace mozilla;
TEST(TaskController, RescheduleOnMainThread)
{
Logger logger;
RefPtr<ReschedulingTask> mainThreadTask =
new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "1");
TaskController::Get()->AddTask(do_AddRef(mainThreadTask));
while (NS_ProcessNextEvent(nullptr, false)) {
}
ASSERT_TRUE(mainThreadTask->IsDone());
ASSERT_TRUE(logger.GetLog() == "111");
}
TEST(TaskController, RescheduleOffMainThread)
{
Logger logger;
RefPtr<ReschedulingTask> offThreadTask =
new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger, "1");
TaskController::Get()->AddTask(do_AddRef(offThreadTask));
uint32_t count = 0;
while (!offThreadTask->IsDone() && count < 100) {
PR_Sleep(PR_MillisecondsToInterval(100));
count++;
}
ASSERT_TRUE(offThreadTask->IsDone());
ASSERT_TRUE(logger.GetLog() == "111");
}
TEST(TaskController, RescheduleMainAndOffMainThreads)
{
Logger logger;
RefPtr<ReschedulingTask> offThreadTask =
new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger, "1");
RefPtr<ReschedulingTask> mainThreadTask =
new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "2");
mainThreadTask->AddDependency(offThreadTask.get());
TaskController::Get()->AddTask(do_AddRef(offThreadTask));
TaskController::Get()->AddTask(do_AddRef(mainThreadTask));
uint32_t count = 0;
while (!offThreadTask->IsDone() && count < 100) {
PR_Sleep(PR_MillisecondsToInterval(100));
count++;
}
ASSERT_TRUE(offThreadTask->IsDone());
// At this point, the main thread task shouldn't have run.
ASSERT_TRUE(logger.GetLog() == "111");
while (NS_ProcessNextEvent(nullptr, false)) {
}
ASSERT_TRUE(mainThreadTask->IsDone());
ASSERT_TRUE(logger.GetLog() == "111222");
}
TEST(TaskController, RescheduleOrder)
{
Logger logger;
RefPtr<ReschedulingTask> mainThreadTask1 =
new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "1");
RefPtr<ReschedulingTask> mainThreadTask2 =
new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "2");
RefPtr<ReschedulingTask> mainThreadTask3 =
new ReschedulingTask(Task::Kind::MainThreadOnly, &logger, "3");
TaskController::Get()->AddTask(do_AddRef(mainThreadTask1));
TaskController::Get()->AddTask(do_AddRef(mainThreadTask2));
TaskController::Get()->AddTask(do_AddRef(mainThreadTask3));
while (NS_ProcessNextEvent(nullptr, false)) {
}
ASSERT_TRUE(mainThreadTask1->IsDone());
ASSERT_TRUE(mainThreadTask2->IsDone());
ASSERT_TRUE(mainThreadTask3->IsDone());
// Rescheduled tasks should be added to the beginning of the queue.
ASSERT_TRUE(logger.GetLog() == "111222333");
}
TEST(TaskController, RescheduleOrderOffMainThread)
{
Logger logger1;
Logger logger2;
Logger logger3;
RefPtr<ReschedulingTask> offThreadTask1 =
new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger1, "1");
RefPtr<ReschedulingTask> offThreadTask2 =
new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger2, "2");
RefPtr<ReschedulingTask> offThreadTask3 =
new ReschedulingTask(Task::Kind::OffMainThreadOnly, &logger3, "3");
TaskController::Get()->AddTask(do_AddRef(offThreadTask1));
TaskController::Get()->AddTask(do_AddRef(offThreadTask2));
TaskController::Get()->AddTask(do_AddRef(offThreadTask3));
uint32_t count = 0;
while (!(offThreadTask1->IsDone() && offThreadTask2->IsDone() &&
offThreadTask3->IsDone()) &&
count < 100) {
PR_Sleep(PR_MillisecondsToInterval(100));
count++;
}
ASSERT_TRUE(offThreadTask1->IsDone());
ASSERT_TRUE(offThreadTask2->IsDone());
ASSERT_TRUE(offThreadTask3->IsDone());
// Rescheduled tasks should be enqueued.
// The order between off-thread tasks are not deterministic.
ASSERT_TRUE(logger1.GetLog() == "111");
ASSERT_TRUE(logger2.GetLog() == "222");
ASSERT_TRUE(logger3.GetLog() == "333");
}
} // namespace TestTaskController

View File

@ -61,6 +61,7 @@ UNIFIED_SOURCES += [
"TestSynchronization.cpp",
"TestTArray.cpp",
"TestTArray2.cpp",
"TestTaskController.cpp",
"TestTaskQueue.cpp",
"TestTextFormatter.cpp",
"TestThreadManager.cpp",

View File

@ -927,7 +927,9 @@ bool TaskController::DoExecuteNextTaskOnlyMainThreadInternal(
mMainThreadTasks.insert(std::move(mCurrentTasksMT.top()));
MOZ_ASSERT(insertion.second);
task->mIterator = insertion.first;
manager->WillRunTask();
if (manager) {
manager->WillRunTask();
}
} else {
task->mCompleted = true;
#ifdef DEBUG