xref: /aosp_15_r20/external/federated-compute/fcp/base/future_test.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2019 Google LLC
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 "fcp/base/future.h"
18 
19 #include <functional>
20 #include <memory>
21 #include <thread>  // NOLINT(build/c++11)
22 #include <type_traits>
23 #include <utility>
24 
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 #include "absl/base/thread_annotations.h"
28 #include "absl/synchronization/barrier.h"
29 #include "absl/time/time.h"
30 #include "fcp/base/meta.h"
31 #include "fcp/base/move_to_lambda.h"
32 
33 namespace fcp {
34 namespace thread {
35 
36 using ::testing::Eq;
37 
38 // Future::Wait and Future::Take sometimes block. We'd like to test thread
39 // interleavings where these calls block before being woken up. In the absence
40 // of instrumentation of the underlying synchronization primitives, we just use
41 // this arbitrary delay before unblocking operations (the other thread
42 // "probably" has time to block). Note that the other ordering (Promise::Set
43 // before Future::Take etc.) is easy to guarantee.
44 constexpr absl::Duration kArbitraryDelay = absl::Milliseconds(50);
45 
Delay()46 void Delay() { absl::SleepFor(kArbitraryDelay); }
47 
48 // Freely copyable test value. We put in a 'valid' value and hope to find it
49 // again.
50 enum class V { kInvalid, kValid };
51 
52 // Move-only test value (we use this for Promise and Future, to make sure they
53 // are compatible with move-only types - typically the harder case). This
54 // corresponds to V:
55 //   Value present (not moved) <=> kValid
56 //   Value moved <=> kInvalid
57 // The TakeV / SetV wrappers below actually do those conversions, since the test
58 // assertions (e.g. Eq matcher) are difficult to use with move-only types.
59 using UV = UniqueValue<Unit>;
60 
61 static_assert(!std::is_copy_constructible<UV>::value,
62               "Expected to be move-only");
63 
TakeV(Future<UV> future)64 std::optional<V> TakeV(Future<UV> future) {
65   std::optional<UV> maybe_uv = std::move(future).Take();
66   if (maybe_uv.has_value()) {
67     UniqueValue<Unit> uv = *std::move(maybe_uv);
68     return uv.has_value() ? V::kValid : V::kInvalid;
69   } else {
70     return std::nullopt;
71   }
72 }
73 
SetV(Promise<UV> promise)74 void SetV(Promise<UV> promise) { std::move(promise).Set(UV(Unit{})); }
75 
MakeBarrier()76 absl::Barrier MakeBarrier() { return absl::Barrier(2); }
77 
RunThreads(std::vector<std::function<void ()>> fns)78 void RunThreads(std::vector<std::function<void()>> fns) {
79   std::vector<std::thread> threads;
80   for (auto& fn : fns) {
81     threads.push_back(std::thread(std::move(fn)));
82   }
83 
84   for (auto& thread : threads) {
85     thread.join();
86   }
87 }
88 
RunThreadsWithFuture(std::function<void (Promise<UV>)> promise_fn,std::function<void (Future<UV>)> future_fn)89 void RunThreadsWithFuture(std::function<void(Promise<UV>)> promise_fn,
90                           std::function<void(Future<UV>)> future_fn) {
91   FuturePair<UV> pair = MakeFuture<UV>();
92 
93   MoveToLambdaWrapper<Promise<UV>> promise_capture =
94       MoveToLambda(std::move(pair.promise));
95   auto promise_fn_wrapped = [promise_capture, promise_fn]() mutable {
96     promise_fn(std::move(*promise_capture));
97   };
98 
99   MoveToLambdaWrapper<Future<UV>> future_capture =
100       MoveToLambda(std::move(pair.future));
101   auto future_fn_wrapped = [future_capture, future_fn]() mutable {
102     future_fn(std::move(*future_capture));
103   };
104 
105   RunThreads({std::move(promise_fn_wrapped), std::move(future_fn_wrapped)});
106 }
107 
TEST(FutureTest,WaitTimeouts)108 TEST(FutureTest, WaitTimeouts) {
109   absl::Barrier waited = MakeBarrier();
110   absl::Barrier set = MakeBarrier();
111 
112   auto promise_fn = [&](Promise<UV> promise) {
113     waited.Block();
114     SetV(std::move(promise));
115     set.Block();
116   };
117 
118   auto future_fn = [&](Future<UV> future) {
119     // Before set: Timeout should elapse
120     EXPECT_FALSE(future.Wait(absl::Milliseconds(1)))
121         << "Future shouldn't be ready yet";
122     waited.Block();
123     set.Block();
124     // After set: Zero timeout should be sufficient
125     EXPECT_TRUE(future.Wait(absl::ZeroDuration()))
126         << "Future should be ready without waiting";
127   };
128 
129   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
130 }
131 
TEST(FutureTest,TakeAfterSet)132 TEST(FutureTest, TakeAfterSet) {
133   absl::Barrier set = MakeBarrier();
134 
135   auto promise_fn = [&](Promise<UV> promise) {
136     SetV(std::move(promise));
137     set.Block();
138   };
139 
140   auto future_fn = [&](Future<UV> future) {
141     set.Block();
142     EXPECT_THAT(TakeV(std::move(future)), Eq(V::kValid));
143   };
144 
145   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
146 }
147 
TEST(FutureTest,TakeProbablyBeforeSet)148 TEST(FutureTest, TakeProbablyBeforeSet) {
149   auto promise_fn = [](Promise<UV> promise) {
150     Delay();
151     SetV(std::move(promise));
152   };
153 
154   auto future_fn = [](Future<UV> future) {
155     EXPECT_THAT(TakeV(std::move(future)), Eq(V::kValid));
156   };
157 
158   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
159 }
160 
TEST(FutureTest,AbandonWhileProbablyTaking)161 TEST(FutureTest, AbandonWhileProbablyTaking) {
162   auto promise_fn = [](Promise<UV> promise) {
163     Delay();
164     { Promise<UV> dies = std::move(promise); }
165   };
166 
167   auto future_fn = [](Future<UV> future) {
168     EXPECT_THAT(std::move(future).Take(), Eq(std::nullopt));
169   };
170 
171   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
172 }
173 
TEST(FutureTest,SetWhileProbablyWaiting)174 TEST(FutureTest, SetWhileProbablyWaiting) {
175   auto promise_fn = [](Promise<UV> promise) {
176     Delay();
177     SetV(std::move(promise));
178   };
179 
180   auto future_fn = [](Future<UV> future) {
181     EXPECT_TRUE(future.Wait(absl::InfiniteDuration()));
182   };
183 
184   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
185 }
186 
TEST(FutureTest,AbandonWhileProbablyWaiting)187 TEST(FutureTest, AbandonWhileProbablyWaiting) {
188   auto promise_fn = [](Promise<UV> promise) {
189     Delay();
190     { Promise<UV> dies = std::move(promise); }
191   };
192 
193   auto future_fn = [](Future<UV> future) {
194     EXPECT_TRUE(future.Wait(absl::InfiniteDuration()));
195   };
196 
197   RunThreadsWithFuture(std::move(promise_fn), std::move(future_fn));
198 }
199 
200 }  // namespace thread
201 }  // namespace fcp
202