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
154 lines
5.6 KiB
C++
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
|