diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/tests/gtest/TestTaskController.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestTaskController.cpp b/xpcom/tests/gtest/TestTaskController.cpp new file mode 100644 index 0000000000..c7d71ae0f8 --- /dev/null +++ b/xpcom/tests/gtest/TestTaskController.cpp @@ -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 |