mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1605119 - Add NS_NewCancelableRunnableFunction. r=froydnj
Differential Revision: https://phabricator.services.mozilla.com/D57799 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
cc63a32cca
commit
4170b9c2da
@ -5,6 +5,7 @@
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/IdleTaskRunner.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
@ -186,6 +187,10 @@ struct TestCopyMove {
|
||||
int* mMoveCounter;
|
||||
};
|
||||
|
||||
struct TestRefCounted : RefCounted<TestRefCounted> {
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(TestRefCounted);
|
||||
};
|
||||
|
||||
static void Expect(const char* aContext, int aCounter, int aMaxExpected) {
|
||||
EXPECT_LE(aCounter, aMaxExpected) << aContext;
|
||||
}
|
||||
@ -198,24 +203,44 @@ static void ExpectRunnableName(Runnable* aRunnable, const char* aExpectedName) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static void TestNewRunnableFunction(bool aNamed) {
|
||||
// Test NS_NewRunnableFunction with copyable-only function object.
|
||||
struct BasicRunnableFactory {
|
||||
static constexpr bool SupportsCopyWithDeletedMove = true;
|
||||
|
||||
template <typename Function>
|
||||
static auto Create(const char* aName, Function&& aFunc) {
|
||||
return NS_NewRunnableFunction(aName, std::forward<Function>(aFunc));
|
||||
}
|
||||
};
|
||||
|
||||
struct CancelableRunnableFactory {
|
||||
static constexpr bool SupportsCopyWithDeletedMove = false;
|
||||
|
||||
template <typename Function>
|
||||
static auto Create(const char* aName, Function&& aFunc) {
|
||||
return NS_NewCancelableRunnableFunction(aName,
|
||||
std::forward<Function>(aFunc));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename RunnableFactory>
|
||||
static void TestRunnableFactory(bool aNamed) {
|
||||
// Test RunnableFactory with copyable-only function object.
|
||||
{
|
||||
int copyCounter = 0;
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||
{
|
||||
TestCopyWithNoMove tracker(©Counter);
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused", tracker)
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
|
||||
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
|
||||
: RunnableFactory::Create(
|
||||
"TestNewRunnableFunction", tracker);
|
||||
// Original 'tracker' is destroyed here.
|
||||
}
|
||||
// Verify that the runnable contains a non-destroyed function object.
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable-only (and no move) function, "
|
||||
"RunnableFactory with copyable-only (and no move) function, "
|
||||
"copies",
|
||||
copyCounter, 1);
|
||||
}
|
||||
@ -227,37 +252,37 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
// Passing as rvalue, but using copy.
|
||||
// (TestCopyWithDeletedMove wouldn't allow this.)
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused",
|
||||
TestCopyWithNoMove(©Counter))
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
TestCopyWithNoMove(©Counter));
|
||||
aNamed ? RunnableFactory::Create("unused",
|
||||
TestCopyWithNoMove(©Counter))
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
TestCopyWithNoMove(©Counter));
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable-only (and no move) function "
|
||||
"RunnableFactory with copyable-only (and no move) function "
|
||||
"rvalue, copies",
|
||||
copyCounter, 1);
|
||||
}
|
||||
{
|
||||
if constexpr (RunnableFactory::SupportsCopyWithDeletedMove) {
|
||||
int copyCounter = 0;
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||
{
|
||||
TestCopyWithDeletedMove tracker(©Counter);
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused", tracker)
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction", tracker);
|
||||
trackedRunnable = aNamed ? RunnableFactory::Create("unused", tracker)
|
||||
: RunnableFactory::Create(
|
||||
"TestNewRunnableFunction", tracker);
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable-only (and deleted move) "
|
||||
"RunnableFactory with copyable-only (and deleted move) "
|
||||
"function, copies",
|
||||
copyCounter, 1);
|
||||
}
|
||||
|
||||
// Test NS_NewRunnableFunction with movable-only function object.
|
||||
// Test RunnableFactory with movable-only function object.
|
||||
{
|
||||
int moveCounter = 0;
|
||||
{
|
||||
@ -265,14 +290,13 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
{
|
||||
TestMove tracker(&moveCounter);
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
std::move(tracker));
|
||||
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
std::move(tracker));
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect("NS_NewRunnableFunction with movable-only function, moves",
|
||||
moveCounter, 1);
|
||||
Expect("RunnableFactory with movable-only function, moves", moveCounter, 1);
|
||||
}
|
||||
{
|
||||
int moveCounter = 0;
|
||||
@ -280,17 +304,17 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||
{
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused", TestMove(&moveCounter))
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
TestMove(&moveCounter));
|
||||
aNamed ? RunnableFactory::Create("unused", TestMove(&moveCounter))
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
TestMove(&moveCounter));
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect("NS_NewRunnableFunction with movable-only function rvalue, moves",
|
||||
Expect("RunnableFactory with movable-only function rvalue, moves",
|
||||
moveCounter, 1);
|
||||
}
|
||||
|
||||
// Test NS_NewRunnableFunction with copyable&movable function object.
|
||||
// Test RunnableFactory with copyable&movable function object.
|
||||
{
|
||||
int copyCounter = 0;
|
||||
int moveCounter = 0;
|
||||
@ -299,16 +323,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
{
|
||||
TestCopyMove tracker(©Counter, &moveCounter);
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused", std::move(tracker))
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
std::move(tracker));
|
||||
aNamed ? RunnableFactory::Create("unused", std::move(tracker))
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
std::move(tracker));
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect("NS_NewRunnableFunction with copyable&movable function, copies",
|
||||
Expect("RunnableFactory with copyable&movable function, copies",
|
||||
copyCounter, 0);
|
||||
Expect("NS_NewRunnableFunction with copyable&movable function, moves",
|
||||
moveCounter, 1);
|
||||
Expect("RunnableFactory with copyable&movable function, moves", moveCounter,
|
||||
1);
|
||||
}
|
||||
{
|
||||
int copyCounter = 0;
|
||||
@ -317,23 +341,21 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
nsCOMPtr<nsIRunnable> trackedRunnable;
|
||||
{
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction(
|
||||
aNamed ? RunnableFactory::Create(
|
||||
"unused", TestCopyMove(©Counter, &moveCounter))
|
||||
: NS_NewRunnableFunction(
|
||||
: RunnableFactory::Create(
|
||||
"TestNewRunnableFunction",
|
||||
TestCopyMove(©Counter, &moveCounter));
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable&movable function rvalue, copies",
|
||||
copyCounter, 0);
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable&movable function rvalue, moves",
|
||||
moveCounter, 1);
|
||||
Expect("RunnableFactory with copyable&movable function rvalue, copies",
|
||||
copyCounter, 0);
|
||||
Expect("RunnableFactory with copyable&movable function rvalue, moves",
|
||||
moveCounter, 1);
|
||||
}
|
||||
|
||||
// Test NS_NewRunnableFunction with copyable-only lambda capture.
|
||||
// Test RunnableFactory with copyable-only lambda capture.
|
||||
{
|
||||
int copyCounter = 0;
|
||||
{
|
||||
@ -342,15 +364,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
TestCopyWithNoMove tracker(©Counter);
|
||||
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
aNamed
|
||||
? RunnableFactory::Create("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable-only (and no move) capture, "
|
||||
"RunnableFactory with copyable-only (and no move) capture, "
|
||||
"copies",
|
||||
copyCounter, 2);
|
||||
}
|
||||
@ -362,15 +385,16 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
TestCopyWithDeletedMove tracker(©Counter);
|
||||
// Expect 2 copies (here -> local lambda -> runnable lambda).
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
aNamed
|
||||
? RunnableFactory::Create("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect(
|
||||
"NS_NewRunnableFunction with copyable-only (and deleted move) capture, "
|
||||
"RunnableFactory with copyable-only (and deleted move) capture, "
|
||||
"copies",
|
||||
copyCounter, 2);
|
||||
}
|
||||
@ -378,7 +402,7 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
// Note: Not possible to use move-only captures.
|
||||
// (Until we can use C++14 generalized lambda captures)
|
||||
|
||||
// Test NS_NewRunnableFunction with copyable&movable lambda capture.
|
||||
// Test RunnableFactory with copyable&movable lambda capture.
|
||||
{
|
||||
int copyCounter = 0;
|
||||
int moveCounter = 0;
|
||||
@ -387,29 +411,30 @@ static void TestNewRunnableFunction(bool aNamed) {
|
||||
{
|
||||
TestCopyMove tracker(©Counter, &moveCounter);
|
||||
trackedRunnable =
|
||||
aNamed ? NS_NewRunnableFunction("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: NS_NewRunnableFunction("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
aNamed
|
||||
? RunnableFactory::Create("unused",
|
||||
[tracker]() mutable { tracker(); })
|
||||
: RunnableFactory::Create("TestNewRunnableFunction",
|
||||
[tracker]() mutable { tracker(); });
|
||||
// Expect 1 copy (here -> local lambda) and 1 move (local -> runnable
|
||||
// lambda).
|
||||
}
|
||||
trackedRunnable->Run();
|
||||
}
|
||||
Expect("NS_NewRunnableFunction with copyable&movable capture, copies",
|
||||
copyCounter, 1);
|
||||
Expect("NS_NewRunnableFunction with copyable&movable capture, moves",
|
||||
moveCounter, 1);
|
||||
Expect("RunnableFactory with copyable&movable capture, copies", copyCounter,
|
||||
1);
|
||||
Expect("RunnableFactory with copyable&movable capture, moves", moveCounter,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThreadUtils, NewRunnableFunction)
|
||||
{ TestNewRunnableFunction(/*aNamed*/ false); }
|
||||
{ TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ false); }
|
||||
|
||||
TEST(ThreadUtils, NewNamedRunnableFunction)
|
||||
{
|
||||
// The named overload shall behave identical to the non-named counterpart.
|
||||
TestNewRunnableFunction(/*aNamed*/ true);
|
||||
TestRunnableFactory<BasicRunnableFactory>(/*aNamed*/ true);
|
||||
|
||||
// Test naming.
|
||||
{
|
||||
@ -420,6 +445,38 @@ TEST(ThreadUtils, NewNamedRunnableFunction)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThreadUtils, NewCancelableRunnableFunction)
|
||||
{ TestRunnableFactory<CancelableRunnableFactory>(/*aNamed*/ false); }
|
||||
|
||||
TEST(ThreadUtils, NewNamedCancelableRunnableFunction)
|
||||
{
|
||||
// The named overload shall behave identical to the non-named counterpart.
|
||||
TestRunnableFactory<CancelableRunnableFactory>(/*aNamed*/ true);
|
||||
|
||||
// Test naming.
|
||||
{
|
||||
const char* expectedName = "NamedRunnable";
|
||||
RefPtr<Runnable> NamedRunnable =
|
||||
NS_NewCancelableRunnableFunction(expectedName, [] {});
|
||||
ExpectRunnableName(NamedRunnable, expectedName);
|
||||
}
|
||||
|
||||
// Test release on cancelation.
|
||||
{
|
||||
auto foo = MakeRefPtr<TestRefCounted>();
|
||||
bool ran = false;
|
||||
|
||||
RefPtr<CancelableRunnable> func =
|
||||
NS_NewCancelableRunnableFunction("unused", [foo, &ran] { ran = true; });
|
||||
|
||||
EXPECT_EQ(foo->refCount(), 2u);
|
||||
func->Cancel();
|
||||
|
||||
EXPECT_EQ(foo->refCount(), 1u);
|
||||
EXPECT_FALSE(ran);
|
||||
}
|
||||
}
|
||||
|
||||
static void TestNewRunnableMethod(bool aNamed) {
|
||||
memset(gRunnableExecuted, false, MAX_TESTS * sizeof(bool));
|
||||
// Scope the smart ptrs so that the runnables need to hold on to whatever they
|
||||
|
@ -659,6 +659,47 @@ already_AddRefed<mozilla::Runnable> NS_NewRunnableFunction(
|
||||
aName, std::forward<Function>(aFunction)));
|
||||
}
|
||||
|
||||
// Creates a new object implementing nsIRunnable and nsICancelableRunnable,
|
||||
// which runs a given function on Run and clears the stored function object on a
|
||||
// call to `Cancel` (and thus destroys all objects it holds).
|
||||
template <typename Function>
|
||||
already_AddRefed<mozilla::CancelableRunnable> NS_NewCancelableRunnableFunction(
|
||||
const char* aName, Function&& aFunc) {
|
||||
class FuncCancelableRunnable final : public mozilla::CancelableRunnable {
|
||||
public:
|
||||
static_assert(std::is_void_v<decltype(
|
||||
std::declval<std::remove_reference_t<Function>>()())>);
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING_INHERITED(FuncCancelableRunnable,
|
||||
CancelableRunnable)
|
||||
|
||||
explicit FuncCancelableRunnable(const char* aName, Function&& aFunc)
|
||||
: CancelableRunnable{aName},
|
||||
mFunc{mozilla::Some(std::forward<Function>(aFunc))} {}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
MOZ_ASSERT(mFunc);
|
||||
|
||||
(*mFunc)();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Cancel() override {
|
||||
mFunc.reset();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~FuncCancelableRunnable() = default;
|
||||
|
||||
mozilla::Maybe<std::remove_reference_t<Function>> mFunc;
|
||||
};
|
||||
|
||||
return mozilla::MakeAndAddRef<FuncCancelableRunnable>(
|
||||
aName, std::forward<Function>(aFunc));
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user