1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/io/async/EventBaseLocal.h>
18
19 #include <folly/io/async/EventBaseAtomicNotificationQueue.h>
20 #include <folly/portability/GTest.h>
21 #include <folly/synchronization/Baton.h>
22
23 struct Foo {
FooFoo24 Foo(int n_, std::function<void()> dtorFn_)
25 : n(n_), dtorFn(std::move(dtorFn_)) {}
~FooFoo26 ~Foo() { dtorFn(); }
27
28 int n;
29 std::function<void()> dtorFn;
30 };
31
TEST(EventBaseLocalTest,Basic)32 TEST(EventBaseLocalTest, Basic) {
33 int dtorCnt = 0;
34 folly::EventBase evb1;
35
36 {
37 folly::EventBaseLocal<Foo> foo;
38
39 EXPECT_EQ(foo.get(evb1), nullptr);
40
41 foo.emplace(evb1, 5, [&] { ++dtorCnt; });
42
43 EXPECT_EQ(foo.get(evb1)->n, 5);
44
45 {
46 folly::EventBase evb2;
47 foo.emplace(evb2, 6, [&] { ++dtorCnt; });
48 EXPECT_EQ(foo.get(evb2)->n, 6);
49 foo.erase(evb2);
50 EXPECT_EQ(dtorCnt, 1); // should dtor a Foo when we erase
51 EXPECT_EQ(foo.get(evb2), nullptr);
52 foo.emplace(evb2, 7, [&] { ++dtorCnt; });
53 EXPECT_EQ(foo.get(evb2)->n, 7);
54 }
55
56 EXPECT_EQ(dtorCnt, 2); // should dtor a Foo when evb2 destructs
57 }
58 EXPECT_EQ(dtorCnt, 2); // should schedule Foo destructor, when foo destructs
59 evb1.loop();
60 EXPECT_EQ(dtorCnt, 3); // Foo will be destroyed in EventBase loop
61 }
62
TEST(EventBaseLocalTest,try_emplace)63 TEST(EventBaseLocalTest, try_emplace) {
64 folly::EventBase evb1;
65 folly::EventBaseLocal<int> ints;
66
67 EXPECT_EQ(ints.try_emplace(evb1), 0);
68 EXPECT_EQ(ints.try_emplace(evb1, 5), 0);
69
70 folly::EventBase evb2;
71 EXPECT_EQ(ints.try_emplace(evb2, 5), 5);
72 ints.erase(evb2);
73 EXPECT_EQ(4, ints.try_emplace_with(evb2, [] { return 4; }));
74 }
75
76 using IntPtr = std::unique_ptr<int>;
77
TEST(EventBaseLocalTest,getOrCreateNoncopyable)78 TEST(EventBaseLocalTest, getOrCreateNoncopyable) {
79 folly::EventBase evb1;
80 folly::EventBaseLocal<IntPtr> ints;
81
82 EXPECT_EQ(ints.try_emplace(evb1), IntPtr());
83 EXPECT_EQ(ints.try_emplace(evb1, std::make_unique<int>(5)), IntPtr());
84
85 folly::EventBase evb2;
86 EXPECT_EQ(*ints.try_emplace(evb2, std::make_unique<int>(5)), 5);
87 }
88
TEST(EventBaseLocalTest,emplaceNoncopyable)89 TEST(EventBaseLocalTest, emplaceNoncopyable) {
90 folly::EventBase evb;
91 folly::EventBaseLocal<IntPtr> ints;
92 ints.emplace(evb, std::make_unique<int>(42));
93 EXPECT_EQ(42, **ints.get(evb));
94 }
95
TEST(EventBaseLocalTest,DestructionOrder)96 TEST(EventBaseLocalTest, DestructionOrder) {
97 struct Consumer {
98 void operator()(int) noexcept {}
99 };
100 using Queue = folly::EventBaseAtomicNotificationQueue<int, Consumer>;
101 folly::EventBaseLocal<std::unique_ptr<Queue>> ebl;
102 {
103 // Since queue binds to the underlying event loop, we must ensure
104 // local storage is cleared before event loop is destroyed.
105 folly::EventBase evb;
106 ebl.emplace_with(
107 evb,
108 [&evb] {
109 auto q = std::make_unique<Queue>();
110 q->startConsumingInternal(&evb);
111 return q;
112 })
113 .get();
114 }
115 }
116
TEST(EventBaseLocalTest,DestructorStressTest)117 TEST(EventBaseLocalTest, DestructorStressTest) {
118 std::atomic<folly::EventBase*> currentEvb;
119 folly::Baton<> baton;
120 const int kIterations = 10000;
121 auto thread1 = std::thread([&] {
122 for (int i = 0; i < kIterations; i++) {
123 folly::EventBase evb;
124 currentEvb = &evb;
125 baton.post();
126 evb.loopForever();
127 }
128 });
129 auto thread2 = std::thread([&] {
130 for (int i = 0; i < kIterations; i++) {
131 folly::EventBaseLocal<int> local;
132 baton.wait();
133 baton.reset();
134 auto evb = currentEvb.exchange(nullptr);
135 evb->runInEventBaseThreadAndWait([&] { local.emplace(*evb, 4); });
136 evb->terminateLoopSoon();
137 }
138 });
139
140 thread1.join();
141 thread2.join();
142 }
143