1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_async2/join.h"
16
17 #include "pw_async2/dispatcher.h"
18 #include "pw_unit_test/framework.h"
19
20 namespace {
21
22 using ::pw::async2::Context;
23 using ::pw::async2::Dispatcher;
24 using ::pw::async2::Join;
25 using ::pw::async2::Pending;
26 using ::pw::async2::Poll;
27 using ::pw::async2::Waker;
28
29 // Windows GCC emits a bogs uninitialized error for the
30 // move constructor below.
31 PW_MODIFY_DIAGNOSTICS_PUSH();
32 PW_MODIFY_DIAGNOSTIC_GCC(ignored, "-Wmaybe-uninitialized");
33 struct SomeMoveOnlyValue {
SomeMoveOnlyValue__anon0eae9da30111::SomeMoveOnlyValue34 explicit SomeMoveOnlyValue(int result) : result_(result), move_count_(0) {}
35 SomeMoveOnlyValue(const SomeMoveOnlyValue&) = delete;
36 SomeMoveOnlyValue& operator=(const SomeMoveOnlyValue&) = delete;
SomeMoveOnlyValue__anon0eae9da30111::SomeMoveOnlyValue37 SomeMoveOnlyValue(SomeMoveOnlyValue&& other)
38 : result_(other.result_), move_count_(other.move_count_ + 1) {
39 other.result_ = -1;
40 }
operator =__anon0eae9da30111::SomeMoveOnlyValue41 SomeMoveOnlyValue& operator=(SomeMoveOnlyValue&& other) {
42 result_ = other.result_;
43 other.result_ = -1;
44 move_count_ = other.move_count_ + 1;
45 return *this;
46 }
47 ~SomeMoveOnlyValue() = default;
48 int result_;
49 int move_count_;
50 };
51 PW_MODIFY_DIAGNOSTICS_POP();
52
53 struct PendableController {
PendableController__anon0eae9da30111::PendableController54 PendableController() : poll_count_(0), allow_completion_(false), waker_() {}
55 PendableController(PendableController&) = delete;
56 PendableController(PendableController&&) = delete;
57 PendableController& operator=(const PendableController&) = delete;
58 PendableController& operator=(PendableController&&) = delete;
59 ~PendableController() = default;
60
61 int poll_count_;
62 bool allow_completion_;
63 Waker waker_;
64 };
65
66 struct StructWithPendMethod {
StructWithPendMethod__anon0eae9da30111::StructWithPendMethod67 StructWithPendMethod(int result, PendableController& controller)
68 : result_(result), controller_(&controller) {}
69
70 StructWithPendMethod(const StructWithPendMethod&) = delete;
71 StructWithPendMethod& operator=(const StructWithPendMethod&) = delete;
72 StructWithPendMethod(StructWithPendMethod&&) = default;
73 StructWithPendMethod& operator=(StructWithPendMethod&&) = default;
74 ~StructWithPendMethod() = default;
75
Pend__anon0eae9da30111::StructWithPendMethod76 Poll<SomeMoveOnlyValue> Pend(Context& cx) {
77 ++controller_->poll_count_;
78 if (controller_->allow_completion_) {
79 return Poll<SomeMoveOnlyValue>(result_);
80 }
81 PW_ASYNC_STORE_WAKER(
82 cx,
83 controller_->waker_,
84 "StructWithPendMethod is waiting for PendableController's waker");
85 return Pending();
86 }
87
88 int result_;
89 PendableController* controller_;
90 };
91
TEST(Join,PendDelegatesToPendables)92 TEST(Join, PendDelegatesToPendables) {
93 Dispatcher dispatcher;
94
95 PendableController controller_1;
96 PendableController controller_2;
97 StructWithPendMethod pendable_1(1, controller_1);
98 StructWithPendMethod pendable_2(2, controller_2);
99 Join join(std::move(pendable_1), std::move(pendable_2));
100
101 EXPECT_EQ(dispatcher.RunPendableUntilStalled(join), Pending());
102 controller_2.allow_completion_ = true;
103 std::move(controller_2.waker_).Wake();
104 EXPECT_EQ(dispatcher.RunPendableUntilStalled(join), Pending());
105 controller_1.allow_completion_ = true;
106 std::move(controller_1.waker_).Wake();
107 auto&& result = dispatcher.RunPendableUntilStalled(join);
108 ASSERT_TRUE(result.IsReady());
109 auto&& [v1, v2] = std::move(*result);
110 EXPECT_EQ(v1.result_, 1);
111 EXPECT_EQ(v2.result_, 2);
112 EXPECT_EQ(v1.move_count_, 1);
113 EXPECT_EQ(v2.move_count_, 1);
114 }
115
TEST(Join,BindsDirectly)116 TEST(Join, BindsDirectly) {
117 Dispatcher dispatcher;
118 PendableController controller_1;
119 controller_1.allow_completion_ = true;
120 PendableController controller_2;
121 controller_2.allow_completion_ = true;
122 StructWithPendMethod pendable_1(1, controller_1);
123 StructWithPendMethod pendable_2(2, controller_2);
124 Join join(std::move(pendable_1), std::move(pendable_2));
125
126 auto&& [v1, v2] = dispatcher.RunPendableToCompletion(join);
127 EXPECT_EQ(v1.result_, 1);
128 EXPECT_EQ(v2.result_, 2);
129 EXPECT_EQ(v1.move_count_, 1);
130 EXPECT_EQ(v2.move_count_, 1);
131 }
132
133 } // namespace
134