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 <atomic>
18 #include <cstdint>
19 #include <thread>
20 
21 #include <folly/Memory.h>
22 #include <folly/io/async/EventBase.h>
23 #include <folly/io/async/Request.h>
24 #include <folly/io/async/test/RequestContextHelper.h>
25 #include <folly/portability/GTest.h>
26 #include <folly/system/ThreadName.h>
27 
28 #include <boost/thread/barrier.hpp>
29 
30 using namespace folly;
31 
32 RequestToken testtoken("test");
33 
34 class RequestContextTest : public ::testing::Test {
35  protected:
SetUp()36   void SetUp() override {
37     // Make sure each test starts out using the default context, and not some
38     // other context left over by a previous test.
39     RequestContext::setContext(nullptr);
40 
41     // Make sure no data is set for the "test" key when we start.  There could
42     // be left over data in the default context from a previous test.  If we
43     // don't clear it out future calls to setContextData() won't actually work,
44     // and will reset the data to null instead of properly setting the new
45     // desired data.
46     //
47     // (All of the tests generally want the behavior of overwriteContextData()
48     // rather than setContextData(), but that method is private.)
49     //
50     // We ideally want to clear out data for any keys that may be set, not just
51     // the "test" key, but there also isn't a RequestContext API to do this.
52     clearData();
53   }
54 
getContext()55   RequestContext& getContext() {
56     auto* ctx = RequestContext::get();
57     EXPECT_TRUE(ctx != nullptr);
58     return *ctx;
59   }
60 
setData(int data=0,std::string key="test")61   void setData(int data = 0, std::string key = "test") {
62     getContext().setContextData(key, std::make_unique<TestData>(data));
63   }
64 
hasData(std::string key="test")65   bool hasData(std::string key = "test") {
66     return getContext().hasContextData(key);
67   }
68 
getData(std::string key="test")69   const TestData& getData(std::string key = "test") {
70     auto* ptr = dynamic_cast<TestData*>(getContext().getContextData(key));
71     EXPECT_TRUE(ptr != nullptr);
72     return *ptr;
73   }
74 
clearData(std::string key="test")75   void clearData(std::string key = "test") {
76     getContext().clearContextData(key);
77   }
78 
getRootIdsFromAllThreads()79   std::vector<intptr_t> getRootIdsFromAllThreads() {
80     auto rootids = RequestContext::getRootIdsFromAllThreads();
81     std::vector<intptr_t> result;
82     std::transform(
83         rootids.begin(), rootids.end(), std::back_inserter(result), [](auto e) {
84           return e.id;
85         });
86     return result;
87   }
88 };
89 
TEST_F(RequestContextTest,SimpleTest)90 TEST_F(RequestContextTest, SimpleTest) {
91   EventBase base;
92 
93   // There should always be a default context with get()
94   EXPECT_TRUE(RequestContext::get() != nullptr);
95   // but fallback context should have rootid set to 0
96   EXPECT_EQ(RequestContext::get()->getRootId(), 0);
97 
98   // but not with saveContext()
99   EXPECT_EQ(RequestContext::saveContext(), nullptr);
100   RequestContext::create();
101   EXPECT_NE(RequestContext::saveContext(), nullptr);
102   auto rootids = getRootIdsFromAllThreads();
103   EXPECT_EQ(1, rootids.size());
104   EXPECT_EQ(RequestContext::get()->getRootId(), rootids[0]);
105   EXPECT_EQ(reinterpret_cast<intptr_t>(RequestContext::get()), rootids[0]);
106   EXPECT_NE(RequestContext::get()->getRootId(), 0);
107   RequestContext::create();
108   EXPECT_NE(RequestContext::saveContext(), nullptr);
109   EXPECT_NE(RequestContext::get()->getRootId(), rootids[0]);
110 
111   EXPECT_EQ(nullptr, RequestContext::get()->getContextData("test"));
112 
113   RequestContext::get()->setContextData("test", std::make_unique<TestData>(10));
114   base.runInEventBaseThread([&]() {
115     EXPECT_TRUE(RequestContext::get() != nullptr);
116     auto data = dynamic_cast<TestData*>(
117                     RequestContext::get()->getContextData(testtoken))
118                     ->data_;
119     EXPECT_EQ(10, data);
120     rootids = getRootIdsFromAllThreads();
121     EXPECT_EQ(2, rootids.size());
122     EXPECT_EQ(RequestContext::get()->getRootId(), rootids[0]);
123     EXPECT_EQ(RequestContext::get()->getRootId(), rootids[1]);
124     base.terminateLoopSoon();
125   });
126   auto th = std::thread([&]() { base.loopForever(); });
127   th.join();
128   EXPECT_TRUE(RequestContext::get() != nullptr);
129   auto a =
130       dynamic_cast<TestData*>(RequestContext::get()->getContextData("test"));
131   auto data = a->data_;
132   EXPECT_EQ(10, data);
133 
134   RequestContext::setContext(std::shared_ptr<RequestContext>());
135   // There should always be a default context
136   EXPECT_TRUE(nullptr != RequestContext::get());
137 }
138 
TEST_F(RequestContextTest,RequestContextScopeGuard)139 TEST_F(RequestContextTest, RequestContextScopeGuard) {
140   RequestContextScopeGuard g0;
141   setData(10);
142   {
143     RequestContextScopeGuard g1;
144     EXPECT_FALSE(hasData());
145     setData(20);
146     EXPECT_EQ(20, getData().data_);
147     EXPECT_EQ(1, getData().set_);
148     EXPECT_EQ(0, getData().unset_);
149   }
150   EXPECT_EQ(10, getData().data_);
151   EXPECT_EQ(2, getData().set_);
152   EXPECT_EQ(1, getData().unset_);
153 }
154 
TEST_F(RequestContextTest,defaultContext)155 TEST_F(RequestContextTest, defaultContext) {
156   // Don't create a top level guard
157   setData(10);
158   {
159     RequestContextScopeGuard g1;
160     EXPECT_FALSE(hasData());
161   }
162   EXPECT_EQ(10, getData().data_);
163   EXPECT_EQ(1, getData().set_);
164   EXPECT_EQ(0, getData().unset_);
165 }
166 
TEST_F(RequestContextTest,setIfAbsentTest)167 TEST_F(RequestContextTest, setIfAbsentTest) {
168   EXPECT_TRUE(RequestContext::get() != nullptr);
169 
170   RequestContext::get()->setContextData("test", std::make_unique<TestData>(10));
171   EXPECT_FALSE(RequestContext::get()->setContextDataIfAbsent(
172       "test", std::make_unique<TestData>(20)));
173   EXPECT_EQ(
174       10,
175       dynamic_cast<TestData*>(RequestContext::get()->getContextData(testtoken))
176           ->data_);
177 
178   EXPECT_TRUE(RequestContext::get()->setContextDataIfAbsent(
179       "test2", std::make_unique<TestData>(20)));
180   EXPECT_EQ(
181       20,
182       dynamic_cast<TestData*>(RequestContext::get()->getContextData("test2"))
183           ->data_);
184 
185   RequestContext::setContext(std::shared_ptr<RequestContext>());
186   EXPECT_TRUE(nullptr != RequestContext::get());
187 }
188 
TEST_F(RequestContextTest,testSetUnset)189 TEST_F(RequestContextTest, testSetUnset) {
190   RequestContext::create();
191   auto ctx1 = RequestContext::saveContext();
192   ctx1->setContextData("test", std::make_unique<TestData>(10));
193   auto testData1 = dynamic_cast<TestData*>(ctx1->getContextData("test"));
194 
195   // onSet called in setContextData
196   EXPECT_EQ(1, testData1->set_);
197   EXPECT_EQ(ctx1.get(), testData1->onSetRctx);
198 
199   // Override RequestContext
200   RequestContext::create();
201   auto ctx2 = RequestContext::saveContext();
202   ctx2->setContextData(testtoken, std::make_unique<TestData>(20));
203   auto testData2 = dynamic_cast<TestData*>(ctx2->getContextData(testtoken));
204 
205   // onSet called in setContextData
206   EXPECT_EQ(1, testData2->set_);
207   EXPECT_EQ(ctx2.get(), testData2->onSetRctx);
208 
209   // Check ctx1->onUnset was called
210   EXPECT_EQ(1, testData1->unset_);
211   EXPECT_EQ(ctx1.get(), testData1->onUnSetRctx);
212 
213   RequestContext::setContext(ctx1);
214   EXPECT_EQ(2, testData1->set_);
215   EXPECT_EQ(1, testData1->unset_);
216   EXPECT_EQ(1, testData2->unset_);
217   EXPECT_EQ(ctx1.get(), testData1->onSetRctx);
218   EXPECT_EQ(ctx1.get(), testData1->onUnSetRctx);
219   EXPECT_EQ(ctx2.get(), testData2->onUnSetRctx);
220 
221   RequestContext::setContext(ctx2);
222   EXPECT_EQ(2, testData1->set_);
223   EXPECT_EQ(2, testData1->unset_);
224   EXPECT_EQ(2, testData2->set_);
225   EXPECT_EQ(1, testData2->unset_);
226 }
227 
TEST_F(RequestContextTest,deadlockTest)228 TEST_F(RequestContextTest, deadlockTest) {
229   class DeadlockTestData : public RequestData {
230    public:
231     explicit DeadlockTestData(const std::string& val) : val_(val) {}
232 
233     ~DeadlockTestData() override {
234       RequestContext::get()->setContextData(
235           val_, std::make_unique<TestData>(1));
236     }
237 
238     bool hasCallback() override { return false; }
239 
240     std::string val_;
241   };
242 
243   RequestContext::get()->setContextData(
244       "test", std::make_unique<DeadlockTestData>("test1"));
245   RequestContext::get()->clearContextData(testtoken);
246 }
247 
248 // A common use case is to use set/unset to maintain a thread global
249 // Regression test to ensure that unset is always called before set
TEST_F(RequestContextTest,sharedGlobalTest)250 TEST_F(RequestContextTest, sharedGlobalTest) {
251   static bool global = false;
252 
253   class GlobalTestData : public RequestData {
254    public:
255     void onSet() override {
256       ASSERT_FALSE(global);
257       global = true;
258     }
259 
260     void onUnset() override {
261       ASSERT_TRUE(global);
262       global = false;
263     }
264 
265     bool hasCallback() override { return true; }
266   };
267 
268   intptr_t root = 0;
269   {
270     RequestContextScopeGuard g0;
271     RequestContext::get()->setContextData(
272         "test", std::make_unique<GlobalTestData>());
273     auto root0 = RequestContext::saveContext().get()->getRootId();
274     EXPECT_EQ(getRootIdsFromAllThreads()[0], root0);
275     {
276       RequestContextScopeGuard g1;
277       RequestContext::get()->setContextData(
278           "test", std::make_unique<GlobalTestData>());
279       auto root1 = RequestContext::saveContext().get()->getRootId();
280       EXPECT_EQ(getRootIdsFromAllThreads()[0], root1);
281     }
282     EXPECT_EQ(getRootIdsFromAllThreads()[0], root0);
283   }
284   EXPECT_EQ(getRootIdsFromAllThreads()[0], root);
285 }
286 
TEST_F(RequestContextTest,ShallowCopyBasic)287 TEST_F(RequestContextTest, ShallowCopyBasic) {
288   ShallowCopyRequestContextScopeGuard g0;
289   setData(123, "immutable");
290   EXPECT_EQ(123, getData("immutable").data_);
291   EXPECT_FALSE(hasData());
292   EXPECT_EQ(0, getRootIdsFromAllThreads()[0]);
293 
294   {
295     ShallowCopyRequestContextScopeGuard g1;
296     EXPECT_EQ(123, getData("immutable").data_);
297     setData(789);
298     EXPECT_EQ(789, getData().data_);
299     EXPECT_EQ(0, getRootIdsFromAllThreads()[0]);
300   }
301 
302   EXPECT_FALSE(hasData());
303   EXPECT_EQ(123, getData("immutable").data_);
304   EXPECT_EQ(1, getData("immutable").set_);
305   EXPECT_EQ(0, getData("immutable").unset_);
306   EXPECT_EQ(0, getRootIdsFromAllThreads()[0]);
307 }
308 
TEST_F(RequestContextTest,ShallowCopyOverwrite)309 TEST_F(RequestContextTest, ShallowCopyOverwrite) {
310   RequestContextScopeGuard g0;
311   setData(123);
312   EXPECT_EQ(123, getData().data_);
313   auto rootid = RequestContext::get()->getRootId();
314   EXPECT_EQ(rootid, getRootIdsFromAllThreads()[0]);
315   {
316     ShallowCopyRequestContextScopeGuard g1(
317         "test", std::make_unique<TestData>(789));
318     EXPECT_EQ(789, getData().data_);
319     EXPECT_EQ(1, getData().set_);
320     EXPECT_EQ(0, getData().unset_);
321     // should have inherited parent's rootid
322     EXPECT_EQ(rootid, getRootIdsFromAllThreads()[0]);
323 
324     {
325       // rootId is preserved for shallow copies of shallow copies
326       ShallowCopyRequestContextScopeGuard g2;
327       EXPECT_EQ(rootid, getRootIdsFromAllThreads()[0]);
328     }
329     EXPECT_EQ(rootid, getRootIdsFromAllThreads()[0]);
330   }
331   EXPECT_EQ(123, getData().data_);
332   EXPECT_EQ(2, getData().set_);
333   EXPECT_EQ(1, getData().unset_);
334   EXPECT_EQ(rootid, getRootIdsFromAllThreads()[0]);
335 }
336 
TEST_F(RequestContextTest,ShallowCopyDefaultContext)337 TEST_F(RequestContextTest, ShallowCopyDefaultContext) {
338   // Don't set global scope guard
339   setData(123);
340   EXPECT_EQ(123, getData().data_);
341   {
342     ShallowCopyRequestContextScopeGuard g1(
343         "test", std::make_unique<TestData>(789));
344     EXPECT_EQ(789, getData().data_);
345   }
346   EXPECT_EQ(123, getData().data_);
347   EXPECT_EQ(1, getData().set_);
348   EXPECT_EQ(0, getData().unset_);
349 }
350 
TEST_F(RequestContextTest,ShallowCopyClear)351 TEST_F(RequestContextTest, ShallowCopyClear) {
352   RequestContextScopeGuard g0;
353   setData(123);
354   EXPECT_EQ(123, getData().data_);
355   {
356     ShallowCopyRequestContextScopeGuard g1;
357     EXPECT_EQ(123, getData().data_);
358     clearData();
359     setData(789);
360     EXPECT_EQ(789, getData().data_);
361   }
362   EXPECT_EQ(123, getData().data_);
363   EXPECT_EQ(2, getData().set_);
364   EXPECT_EQ(1, getData().unset_);
365 }
366 
TEST_F(RequestContextTest,ShallowCopyMulti)367 TEST_F(RequestContextTest, ShallowCopyMulti) {
368   RequestContextScopeGuard g0;
369   setData(1, "test1");
370   setData(2, "test2");
371   EXPECT_EQ(1, getData("test1").data_);
372   EXPECT_EQ(2, getData("test2").data_);
373   {
374     ShallowCopyRequestContextScopeGuard g1(
375         RequestDataItem{"test1", std::make_unique<TestData>(2)},
376         RequestDataItem{"test2", std::make_unique<TestData>(4)});
377 
378     EXPECT_EQ(2, getData("test1").data_);
379     EXPECT_EQ(4, getData("test2").data_);
380     clearData("test1");
381     clearData("test2");
382     setData(4, "test1");
383     setData(8, "test2");
384     EXPECT_EQ(4, getData("test1").data_);
385     EXPECT_EQ(8, getData("test2").data_);
386   }
387   EXPECT_EQ(1, getData("test1").data_);
388   EXPECT_EQ(2, getData("test2").data_);
389 }
390 
TEST_F(RequestContextTest,RootIdOnCopy)391 TEST_F(RequestContextTest, RootIdOnCopy) {
392   auto ctxBase = std::make_shared<RequestContext>(0xab);
393   EXPECT_EQ(0xab, ctxBase->getRootId());
394   {
395     auto ctx = RequestContext::copyAsRoot(*ctxBase, 0xabc);
396     EXPECT_EQ(0xabc, ctx->getRootId());
397   }
398   {
399     auto ctx = RequestContext::copyAsChild(*ctxBase);
400     EXPECT_EQ(0xab, ctx->getRootId());
401   }
402 }
403 
TEST_F(RequestContextTest,ThreadId)404 TEST_F(RequestContextTest, ThreadId) {
405   folly::setThreadName("DummyThread");
406   RequestContextScopeGuard g;
407   auto ctxBase = std::make_shared<RequestContext>();
408   auto rootids = RequestContext::getRootIdsFromAllThreads();
409   EXPECT_EQ(*folly::getThreadName(rootids[0].tid), "DummyThread");
410   EXPECT_EQ(rootids[0].tidOS, folly::getOSThreadID());
411 
412   EventBase base;
413   base.runInEventBaseThread([&]() {
414     RequestContextScopeGuard g;
415     folly::setThreadName("DummyThread2");
416     rootids = RequestContext::getRootIdsFromAllThreads();
417     base.terminateLoopSoon();
418   });
419 
420   auto th = std::thread([&]() { base.loopForever(); });
421   th.join();
422 
423   std::sort(rootids.begin(), rootids.end(), [](const auto& a, const auto& b) {
424     auto aname = folly::getThreadName(a.tid);
425     auto bname = folly::getThreadName(b.tid);
426     return (aname ? *aname : "zzz") < (bname ? *bname : "zzz");
427   });
428 
429   EXPECT_EQ(*folly::getThreadName(rootids[0].tid), "DummyThread");
430   EXPECT_FALSE(folly::getThreadName(rootids[1].tid));
431 }
432 
TEST_F(RequestContextTest,Clear)433 TEST_F(RequestContextTest, Clear) {
434   struct Foo : public RequestData {
435     bool& cleared;
436     bool& deleted;
437     Foo(bool& c, bool& d) : cleared(c), deleted(d) {}
438     ~Foo() override {
439       EXPECT_TRUE(cleared);
440       deleted = true;
441     }
442     bool hasCallback() override { return false; }
443     void onClear() override {
444       EXPECT_FALSE(cleared);
445       cleared = true;
446     }
447   };
448 
449   std::string key = "clear";
450   {
451     bool cleared = false;
452     bool deleted = false;
453     {
454       RequestContextScopeGuard g;
455       RequestContext::get()->setContextData(
456           key, std::make_unique<Foo>(cleared, deleted));
457       EXPECT_FALSE(cleared);
458       RequestContext::get()->clearContextData(key);
459       EXPECT_TRUE(cleared);
460     }
461     EXPECT_TRUE(deleted);
462   }
463   {
464     bool cleared = false;
465     bool deleted = false;
466     {
467       RequestContextScopeGuard g;
468       RequestContext::get()->setContextData(
469           key, std::make_unique<Foo>(cleared, deleted));
470       EXPECT_FALSE(cleared);
471       EXPECT_FALSE(deleted);
472     }
473     EXPECT_TRUE(cleared);
474     EXPECT_TRUE(deleted);
475   }
476 }
477 
TEST_F(RequestContextTest,OverwriteNullData)478 TEST_F(RequestContextTest, OverwriteNullData) {
479   folly::ShallowCopyRequestContextScopeGuard g0("token", nullptr);
480   {
481     folly::ShallowCopyRequestContextScopeGuard g1(
482         "token", std::make_unique<TestData>(0));
483     EXPECT_NE(folly::RequestContext::get()->getContextData("token"), nullptr);
484   }
485 }
486 
TEST_F(RequestContextTest,ConcurrentDataRefRelease)487 TEST_F(RequestContextTest, ConcurrentDataRefRelease) {
488   for (int i = 0; i < 100; ++i) {
489     std::atomic<int> step{0};
490     std::shared_ptr<folly::RequestContext> sp1;
491     auto th1 = std::thread([&]() {
492       folly::RequestContextScopeGuard g0; // Creates ctx0.
493       setData(); // Creates data0 with one reference in ctx0.
494       {
495         folly::ShallowCopyRequestContextScopeGuard g1;
496         // g1 created ctx1 with second reference to data0.
497         EXPECT_NE(&getData(), nullptr);
498         // Keep shared_ptr to ctx1 to pass to th2
499         sp1 = folly::RequestContext::saveContext();
500         step.store(1); // sp1 is ready.
501         while (step.load() < 2)
502           /* Wait for th2 to clear reference to data0. */;
503       }
504       // End of g2 released shared_ptr to ctx1, switched back to ctx0
505       // At this point:
506       // - One shared_ptr to ctx0, held by th1.
507       // - One shared_ptr to ctx1, help by th2.
508       // - data0 has one clear count (for reference from ctx0) and
509       //   two delete counts (one each from ctx0 and ctx1).
510       step.store(3);
511       // End of g1 will destroy ctx0, release clear/delete counts for data0.
512     });
513     auto th2 = std::thread([&]() {
514       while (step.load() < 1)
515         /* Wait for th1 to set sp1. */;
516       folly::RequestContextScopeGuard g2(std::move(sp1));
517       // g2 set context to ctx1.
518       EXPECT_EQ(sp1.get(), nullptr);
519       EXPECT_NE(&getData(), nullptr);
520       clearData();
521       step.store(2); // th2 cleared reference to data0 in ctx1.
522       while (step.load() < 3)
523         /* Wait for th1 to release shared_ptr to ctx1. */;
524       // End of g2 will destroy ctx1, release delete count for data0.
525     });
526     th1.join();
527     th2.join();
528   }
529 }
530 
TEST_F(RequestContextTest,AccessAllThreadsDestructionGuard)531 TEST_F(RequestContextTest, AccessAllThreadsDestructionGuard) {
532   constexpr auto kNumThreads = 128;
533 
534   std::vector<std::thread> threads{kNumThreads};
535   boost::barrier barrier{kNumThreads + 1};
536 
537   std::atomic<std::size_t> count{0};
538   for (auto& thread : threads) {
539     thread = std::thread([&] {
540       // Force creation of thread local
541       RequestContext::get();
542       ++count;
543       // Wait for all other threads to do the same
544       barrier.wait();
545       // Wait until signaled to die
546       barrier.wait();
547     });
548   }
549 
550   barrier.wait();
551   // Sanity check
552   EXPECT_EQ(count.load(), kNumThreads);
553 
554   {
555     auto accessor = RequestContext::accessAllThreads();
556     // Allow threads to die (but they should not as long as we hold accessor!)
557     barrier.wait();
558     auto accessorsCount = std::distance(accessor.begin(), accessor.end());
559     EXPECT_EQ(accessorsCount, kNumThreads + 1);
560     for (RequestContext::StaticContext& staticContext : accessor) {
561       EXPECT_EQ(staticContext.requestContext, nullptr);
562     }
563   }
564 
565   for (auto& thread : threads) {
566     thread.join();
567   }
568 }
569 
TEST(RequestContextTryGetTest,TryGetTest)570 TEST(RequestContextTryGetTest, TryGetTest) {
571   // try_get() should not create a default RequestContext object if none exists.
572   EXPECT_EQ(RequestContext::try_get(), nullptr);
573   // Explicitly create a new instance so that subsequent calls to try_get()
574   // return it.
575   RequestContext::create();
576   EXPECT_NE(RequestContext::saveContext(), nullptr);
577   EXPECT_NE(RequestContext::try_get(), nullptr);
578   // Make sure that the pointers returned by both get() and try_get() point to
579   // the same underlying instance.
580   EXPECT_EQ(RequestContext::try_get(), RequestContext::get());
581   // Set some context data and read it out via try_get() accessor.
582   RequestContext::get()->setContextData("test", std::make_unique<TestData>(10));
583   auto rc = RequestContext::try_get();
584   EXPECT_TRUE(rc->hasContextData("test"));
585   auto* dataPtr = dynamic_cast<TestData*>(rc->getContextData("test"));
586   EXPECT_EQ(dataPtr->data_, 10);
587 
588   auto thread = std::thread([&] {
589     auto accessor = RequestContext::accessAllThreads();
590     // test there is no deadlock with try_get()
591     RequestContext::try_get();
592   });
593   thread.join();
594 
595   thread = std::thread([&] {
596     RequestContext::get();
597     auto accessor = RequestContext::accessAllThreads();
598     // test there is no deadlock with get()
599     RequestContext::get();
600   });
601   thread.join();
602 }
603 
TEST(ImmutableRequestTest,simple)604 TEST(ImmutableRequestTest, simple) {
605   ImmutableRequestData<int> ird(4);
606   EXPECT_EQ(ird.value(), 4);
607 }
608 
TEST(ImmutableRequestTest,type_traits)609 TEST(ImmutableRequestTest, type_traits) {
610   using IRDI = ImmutableRequestData<int>;
611 
612   auto c1 = std::is_constructible<IRDI, int>::value;
613   EXPECT_TRUE(c1);
614   auto n1 = std::is_nothrow_constructible<IRDI, int>::value;
615   EXPECT_TRUE(n1);
616 
617   auto c2 = std::is_constructible<IRDI, int, int>::value;
618   EXPECT_FALSE(c2);
619 }
620