CallbackList improvements, part 4: OnceCallback support
This is implemented as OnceCallbackList vs. RepeatingCallbackList. It
might in theory be nice to simply have a single CallbackList that
supports simultaneously containing both Once and Repeating callbacks.
However, I don't personally need it, and while I suspect it's possible,
I didn't want to try and figure it out.
Bug: none
Change-Id: Ib04d39adedca5b15e002b1b3d5df8957b40f2254
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2174162
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Jan Wilken Dörrie <[email protected]>
Commit-Queue: Peter Kasting <[email protected]>
Cr-Commit-Position: refs/heads/master@{#766187}
diff --git a/base/callback_list_unittest.cc b/base/callback_list_unittest.cc
index a706eb4c..2f15a8a 100644
--- a/base/callback_list_unittest.cc
+++ b/base/callback_list_unittest.cc
@@ -33,6 +33,7 @@
int scaler_ = 1;
};
+template <typename T>
class Remover {
public:
Remover() = default;
@@ -45,8 +46,7 @@
removal_subscription_.reset();
}
- void SetSubscriptionToRemove(
- std::unique_ptr<CallbackList<void(void)>::Subscription> sub) {
+ void SetSubscriptionToRemove(std::unique_ptr<typename T::Subscription> sub) {
removal_subscription_ = std::move(sub);
}
@@ -54,12 +54,12 @@
private:
int total_ = 0;
- std::unique_ptr<CallbackList<void(void)>::Subscription> removal_subscription_;
+ std::unique_ptr<typename T::Subscription> removal_subscription_;
};
class Adder {
public:
- explicit Adder(CallbackList<void(void)>* cb_reg) : cb_reg_(cb_reg) {}
+ explicit Adder(RepeatingClosureList* cb_reg) : cb_reg_(cb_reg) {}
Adder(const Adder&) = delete;
Adder& operator=(const Adder&) = delete;
~Adder() = default;
@@ -80,8 +80,8 @@
private:
bool added_ = false;
int total_ = 0;
- CallbackList<void(void)>* cb_reg_;
- std::unique_ptr<CallbackList<void(void)>::Subscription> subscription_;
+ RepeatingClosureList* cb_reg_;
+ std::unique_ptr<RepeatingClosureList::Subscription> subscription_;
};
class Summer {
@@ -127,47 +127,50 @@
TEST(CallbackListTest, ArityTest) {
Summer s;
- CallbackList<void(int)> c1;
- std::unique_ptr<CallbackList<void(int)>::Subscription> subscription1 =
- c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
+ RepeatingCallbackList<void(int)> c1;
+ std::unique_ptr<RepeatingCallbackList<void(int)>::Subscription>
+ subscription1 =
+ c1.Add(BindRepeating(&Summer::AddOneParam, Unretained(&s)));
c1.Notify(1);
EXPECT_EQ(1, s.value());
- CallbackList<void(int, int)> c2;
- std::unique_ptr<CallbackList<void(int, int)>::Subscription> subscription2 =
- c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
+ RepeatingCallbackList<void(int, int)> c2;
+ std::unique_ptr<RepeatingCallbackList<void(int, int)>::Subscription>
+ subscription2 =
+ c2.Add(BindRepeating(&Summer::AddTwoParam, Unretained(&s)));
c2.Notify(1, 2);
EXPECT_EQ(3, s.value());
- CallbackList<void(int, int, int)> c3;
- std::unique_ptr<CallbackList<void(int, int, int)>::Subscription>
+ RepeatingCallbackList<void(int, int, int)> c3;
+ std::unique_ptr<RepeatingCallbackList<void(int, int, int)>::Subscription>
subscription3 =
c3.Add(BindRepeating(&Summer::AddThreeParam, Unretained(&s)));
c3.Notify(1, 2, 3);
EXPECT_EQ(6, s.value());
- CallbackList<void(int, int, int, int)> c4;
- std::unique_ptr<CallbackList<void(int, int, int, int)>::Subscription>
+ RepeatingCallbackList<void(int, int, int, int)> c4;
+ std::unique_ptr<RepeatingCallbackList<void(int, int, int, int)>::Subscription>
subscription4 =
c4.Add(BindRepeating(&Summer::AddFourParam, Unretained(&s)));
c4.Notify(1, 2, 3, 4);
EXPECT_EQ(10, s.value());
- CallbackList<void(int, int, int, int, int)> c5;
- std::unique_ptr<CallbackList<void(int, int, int, int, int)>::Subscription>
+ RepeatingCallbackList<void(int, int, int, int, int)> c5;
+ std::unique_ptr<
+ RepeatingCallbackList<void(int, int, int, int, int)>::Subscription>
subscription5 =
c5.Add(BindRepeating(&Summer::AddFiveParam, Unretained(&s)));
c5.Notify(1, 2, 3, 4, 5);
EXPECT_EQ(15, s.value());
- CallbackList<void(int, int, int, int, int, int)> c6;
+ RepeatingCallbackList<void(int, int, int, int, int, int)> c6;
std::unique_ptr<
- CallbackList<void(int, int, int, int, int, int)>::Subscription>
+ RepeatingCallbackList<void(int, int, int, int, int, int)>::Subscription>
subscription6 =
c6.Add(BindRepeating(&Summer::AddSixParam, Unretained(&s)));
@@ -178,12 +181,12 @@
// Sanity check that closures added to the list will be run, and those removed
// from the list will not be run.
TEST(CallbackListTest, BasicTest) {
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
Listener a, b, c;
- std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
EXPECT_TRUE(a_subscription.get());
@@ -196,7 +199,7 @@
b_subscription.reset();
- std::unique_ptr<CallbackList<void(void)>::Subscription> c_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> c_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&c)));
cb_reg.Notify();
@@ -206,10 +209,45 @@
EXPECT_EQ(1, c.total());
}
+// Similar to BasicTest but with OnceCallbacks instead of Repeating.
+TEST(CallbackListTest, OnceCallbacks) {
+ OnceClosureList cb_reg;
+ Listener a, b, c;
+
+ std::unique_ptr<OnceClosureList::Subscription> a_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
+ std::unique_ptr<OnceClosureList::Subscription> b_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
+
+ EXPECT_TRUE(a_subscription.get());
+ EXPECT_TRUE(b_subscription.get());
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(1, a.total());
+ EXPECT_EQ(1, b.total());
+
+ // OnceCallbacks should auto-remove themselves after calling Notify().
+ EXPECT_TRUE(cb_reg.empty());
+
+ // Destroying a subscription after the callback is canceled should not cause
+ // any problems.
+ b_subscription.reset();
+
+ std::unique_ptr<OnceClosureList::Subscription> c_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&c)));
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(1, a.total());
+ EXPECT_EQ(1, b.total());
+ EXPECT_EQ(1, c.total());
+}
+
// Sanity check that callbacks with details added to the list will be run, with
// the correct details, and those removed from the list will not be run.
TEST(CallbackListTest, BasicTestWithParams) {
- using CallbackListType = CallbackList<void(int)>;
+ using CallbackListType = RepeatingCallbackList<void(int)>;
CallbackListType cb_reg;
Listener a(1), b(-1), c(1);
@@ -241,19 +279,21 @@
// Test the a callback can remove itself or a different callback from the list
// during iteration without invalidating the iterator.
TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
Listener a, b;
- Remover remover_1, remover_2;
+ Remover<RepeatingClosureList> remover_1, remover_2;
- std::unique_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
- cb_reg.Add(BindRepeating(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_1)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
- cb_reg.Add(BindRepeating(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_2)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> remover_1_sub =
+ cb_reg.Add(
+ BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_1)));
+ std::unique_ptr<RepeatingClosureList::Subscription> remover_2_sub =
+ cb_reg.Add(
+ BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_2)));
+ std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&a)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
// |remover_1| will remove itself.
@@ -279,16 +319,57 @@
EXPECT_EQ(2, b.total());
}
+// Similar to RemoveCallbacksDuringIteration but with OnceCallbacks instead of
+// Repeating.
+TEST(CallbackListTest, RemoveOnceCallbacksDuringIteration) {
+ OnceClosureList cb_reg;
+ Listener a, b;
+ Remover<OnceClosureList> remover_1, remover_2;
+
+ std::unique_ptr<OnceClosureList::Subscription> remover_1_sub =
+ cb_reg.Add(BindOnce(&Remover<OnceClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_1)));
+ std::unique_ptr<OnceClosureList::Subscription> remover_2_sub =
+ cb_reg.Add(BindOnce(&Remover<OnceClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_2)));
+ std::unique_ptr<OnceClosureList::Subscription> a_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
+ std::unique_ptr<OnceClosureList::Subscription> b_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&b)));
+
+ // |remover_1| will remove itself.
+ remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
+ // |remover_2| will remove a.
+ remover_2.SetSubscriptionToRemove(std::move(a_subscription));
+
+ cb_reg.Notify();
+
+ // |remover_1| runs once (and removes itself), |remover_2| runs once (and
+ // removes a), |a| never runs, and |b| runs once.
+ EXPECT_EQ(1, remover_1.total());
+ EXPECT_EQ(1, remover_2.total());
+ EXPECT_EQ(0, a.total());
+ EXPECT_EQ(1, b.total());
+
+ cb_reg.Notify();
+
+ // Nothing runs this time.
+ EXPECT_EQ(1, remover_1.total());
+ EXPECT_EQ(1, remover_2.total());
+ EXPECT_EQ(0, a.total());
+ EXPECT_EQ(1, b.total());
+}
+
// Test that a callback can add another callback to the list durning iteration
// without invalidating the iterator. The newly added callback should be run on
// the current iteration as will all other callbacks in the list.
TEST(CallbackListTest, AddCallbacksDuringIteration) {
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
Adder a(&cb_reg);
Listener b;
- std::unique_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> a_subscription =
cb_reg.Add(BindRepeating(&Adder::AddCallback, Unretained(&a)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> b_subscription =
cb_reg.Add(BindRepeating(&Listener::IncrementTotal, Unretained(&b)));
cb_reg.Notify();
@@ -305,18 +386,18 @@
// Sanity check: notifying an empty list is a no-op.
TEST(CallbackListTest, EmptyList) {
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
cb_reg.Notify();
}
TEST(CallbackListTest, RemovalCallback) {
Counter remove_count;
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
cb_reg.set_removal_callback(
BindRepeating(&Counter::Increment, Unretained(&remove_count)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> subscription =
+ std::unique_ptr<RepeatingClosureList::Subscription> subscription =
cb_reg.Add(DoNothing());
// Removing a subscription outside of iteration signals the callback.
@@ -325,13 +406,15 @@
EXPECT_EQ(1, remove_count.value());
// Configure two subscriptions to remove themselves.
- Remover remover_1, remover_2;
- std::unique_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
- cb_reg.Add(BindRepeating(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_1)));
- std::unique_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
- cb_reg.Add(BindRepeating(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_2)));
+ Remover<RepeatingClosureList> remover_1, remover_2;
+ std::unique_ptr<RepeatingClosureList::Subscription> remover_1_sub =
+ cb_reg.Add(
+ BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_1)));
+ std::unique_ptr<RepeatingClosureList::Subscription> remover_2_sub =
+ cb_reg.Add(
+ BindRepeating(&Remover<RepeatingClosureList>::IncrementTotalAndRemove,
+ Unretained(&remover_2)));
remover_1.SetSubscriptionToRemove(std::move(remover_1_sub));
remover_2.SetSubscriptionToRemove(std::move(remover_2_sub));
@@ -344,9 +427,9 @@
TEST(CallbackListTest, AbandonSubscriptions) {
Listener listener;
- std::unique_ptr<CallbackList<void(void)>::Subscription> subscription;
+ std::unique_ptr<RepeatingClosureList::Subscription> subscription;
{
- CallbackList<void(void)> cb_reg;
+ RepeatingClosureList cb_reg;
subscription = cb_reg.Add(
BindRepeating(&Listener::IncrementTotal, Unretained(&listener)));
// Make sure the callback is signaled while cb_reg is in scope.
@@ -359,5 +442,22 @@
subscription.reset();
}
+TEST(CallbackListTest, CancelBeforeRunning) {
+ OnceClosureList cb_reg;
+ Listener a;
+
+ std::unique_ptr<OnceClosureList::Subscription> a_subscription =
+ cb_reg.Add(BindOnce(&Listener::IncrementTotal, Unretained(&a)));
+
+ EXPECT_TRUE(a_subscription.get());
+
+ // Canceling a OnceCallback before running it should not cause problems.
+ a_subscription.reset();
+ cb_reg.Notify();
+
+ // |a| should not have received any callbacks.
+ EXPECT_EQ(0, a.total());
+}
+
} // namespace
} // namespace base