Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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