Files
sousa-gecko/xpcom/tests/gtest/TestTargetShutdownTask.cpp
Andreas Pehrson 4bfa0a6de2 Bug 1993981 - Introduce an atomicity policy for tail dispatch. r=karlt,xpcom-reviewers,nika,webrtc-reviewers,bwc,media-playback-reviewers
For MediaTrackGraph to be able to switch its manual batching of control
messages to tail dispatch, the message atomicity guarantee it provides needs to
be retained. This patch introduces an atomicity mode for tail dispatch to
support the graph.

To tidy up the AbstractThread API, the old boolean aSupportsTailDispatch flag
passed at construction is changed to a new TailDispatchPolicy enum. Converting
callers which passed that flag is the bulk of the change.

Differential Revision: https://phabricator.services.mozilla.com/D277181
2026-08-17 22:16:23 +00:00

154 lines
5.6 KiB
C++

/* 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 "gmock/gmock.h"
#include "gtest/gtest-spi.h"
#include "gtest/gtest.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/TaskQueue.h"
#include "nsITargetShutdownTask.h"
namespace TestTargetShutdownTask {
using namespace mozilla;
class DidRunTask final : public nsITargetShutdownTask {
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit DidRunTask(nsIEventTarget* aTarget) : mTarget(aTarget) {}
void TargetShutdown() override {
mCorrect = mTarget->IsOnCurrentThread();
mRan = true;
// Dispatch something to our target, should be still allowed and run.
mDispatched = NS_SUCCEEDED(mTarget->Dispatch(NS_NewRunnableFunction(
"RunFromShutdown", [self = nsCOMPtr<DidRunTask>{this}]() {
self->mDispatchedRan = true;
})));
}
bool DidRunOnTarget() { return mRan && mCorrect; }
bool DidDispatchOnTarget() { return mDispatched && mDispatchedRan; }
private:
~DidRunTask() = default;
Atomic<bool> mRan{false};
Atomic<bool> mCorrect{false};
Atomic<bool> mDispatched{false};
Atomic<bool> mDispatchedRan{false};
nsCOMPtr<nsIEventTarget> mTarget;
};
NS_IMPL_ISUPPORTS(DidRunTask, nsITargetShutdownTask)
TEST(TestTargetShutdownTask, Thread)
{
nsCOMPtr<nsIThread> thread;
nsresult rv = NS_NewNamedThread("nsIThread", getter_AddRefs(thread));
EXPECT_EQ(rv, NS_OK);
// Add two tasks, remove one, leave one for shutdown.
auto threadTask1 = MakeRefPtr<DidRunTask>(thread);
auto threadTask2 = MakeRefPtr<DidRunTask>(thread);
EXPECT_EQ(thread->RegisterShutdownTask(threadTask1), NS_OK);
EXPECT_EQ(thread->RegisterShutdownTask(threadTask2), NS_OK);
EXPECT_EQ(thread->UnregisterShutdownTask(threadTask1), NS_OK);
EXPECT_EQ(thread->UnregisterShutdownTask(threadTask1), NS_ERROR_UNEXPECTED);
thread->Shutdown();
// Register/unregister after shutdown should fail.
EXPECT_EQ(thread->RegisterShutdownTask(threadTask1), NS_ERROR_UNEXPECTED);
EXPECT_EQ(thread->UnregisterShutdownTask(threadTask2), NS_ERROR_UNEXPECTED);
// Task 1 was removed before shutdown and should not have run.
EXPECT_FALSE(threadTask1->DidRunOnTarget());
EXPECT_FALSE(threadTask1->DidDispatchOnTarget());
EXPECT_TRUE(threadTask2->DidRunOnTarget());
EXPECT_TRUE(threadTask2->DidDispatchOnTarget());
}
TEST(TestTargetShutdownTask, LazyIdleThread)
{
auto target = MakeRefPtr<LazyIdleThread>(
100, "LazyIdleThread", LazyIdleThread::ShutdownMethod::ManualShutdown);
// Add two tasks, remove one, leave one for shutdown.
auto threadTask1 = MakeRefPtr<DidRunTask>(target);
auto threadTask2 = MakeRefPtr<DidRunTask>(target);
EXPECT_EQ(target->RegisterShutdownTask(threadTask1), NS_OK);
EXPECT_EQ(target->RegisterShutdownTask(threadTask2), NS_OK);
EXPECT_EQ(target->UnregisterShutdownTask(threadTask1), NS_OK);
EXPECT_EQ(target->UnregisterShutdownTask(threadTask1), NS_ERROR_UNEXPECTED);
target->Shutdown();
// Register/unregister after shutdown should fail.
EXPECT_EQ(target->RegisterShutdownTask(threadTask1), NS_ERROR_UNEXPECTED);
EXPECT_EQ(target->UnregisterShutdownTask(threadTask2), NS_ERROR_UNEXPECTED);
// Task 1 was removed before shutdown and should not have run.
EXPECT_FALSE(threadTask1->DidRunOnTarget());
EXPECT_FALSE(threadTask1->DidDispatchOnTarget());
EXPECT_TRUE(threadTask2->DidRunOnTarget());
EXPECT_TRUE(threadTask2->DidDispatchOnTarget());
}
TEST(TestTargetShutdownTask, PoolAndTaskQueue)
{
nsCOMPtr<nsIThreadPool> pool = new nsThreadPool();
// Add two tasks, remove one, leave one for shutdown.
auto poolTask1 = MakeRefPtr<DidRunTask>(pool);
auto poolTask2 = MakeRefPtr<DidRunTask>(pool);
EXPECT_EQ(pool->RegisterShutdownTask(poolTask1), NS_OK);
EXPECT_EQ(pool->RegisterShutdownTask(poolTask2), NS_OK);
EXPECT_EQ(pool->UnregisterShutdownTask(poolTask1), NS_OK);
EXPECT_EQ(pool->UnregisterShutdownTask(poolTask1), NS_ERROR_UNEXPECTED);
{
RefPtr<TaskQueue> target = TaskQueue::Create(
do_AddRef(pool), "TaskQueue", TailDispatchPolicy::ConsistentOrdering);
// Add two tasks, remove one, leave one for shutdown.
auto queueTask1 = MakeRefPtr<DidRunTask>(target);
auto queueTask2 = MakeRefPtr<DidRunTask>(target);
EXPECT_EQ(target->RegisterShutdownTask(queueTask1), NS_OK);
EXPECT_EQ(target->RegisterShutdownTask(queueTask2), NS_OK);
EXPECT_EQ(target->UnregisterShutdownTask(queueTask1), NS_OK);
EXPECT_EQ(target->UnregisterShutdownTask(queueTask1), NS_ERROR_UNEXPECTED);
target->BeginShutdown();
// Register/unregister after shutdown should fail.
EXPECT_EQ(target->RegisterShutdownTask(queueTask1), NS_ERROR_UNEXPECTED);
EXPECT_EQ(target->UnregisterShutdownTask(queueTask2), NS_ERROR_UNEXPECTED);
target->AwaitShutdownAndIdle();
// Task 1 was removed before shutdown and should not have run.
EXPECT_FALSE(queueTask1->DidRunOnTarget());
EXPECT_FALSE(queueTask1->DidDispatchOnTarget());
EXPECT_TRUE(queueTask2->DidRunOnTarget());
EXPECT_TRUE(queueTask2->DidDispatchOnTarget());
}
pool->Shutdown();
// Register/unregister after shutdown should fail.
EXPECT_EQ(pool->RegisterShutdownTask(poolTask1), NS_ERROR_UNEXPECTED);
EXPECT_EQ(pool->UnregisterShutdownTask(poolTask2), NS_ERROR_UNEXPECTED);
// Task 1 was removed before shutdown and should not have run.
EXPECT_FALSE(poolTask1->DidRunOnTarget());
EXPECT_FALSE(poolTask1->DidDispatchOnTarget());
EXPECT_TRUE(poolTask2->DidRunOnTarget());
EXPECT_TRUE(poolTask2->DidDispatchOnTarget());
}
} // namespace TestTargetShutdownTask