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