blob: 7f0be96e8f009a1852238c67db836244a2909dcf [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_async2/select.h"
#include "public/pw_async2/size_report/size_report.h"
#include "pw_assert/check.h"
#include "pw_async2/await.h"
#include "pw_async2/basic_dispatcher.h"
#include "pw_async2/dispatcher.h"
#include "pw_async2/func_task.h"
#include "pw_async2/size_report/size_report.h"
#include "pw_async2/value_future.h"
#include "pw_bloat/bloat_this_binary.h"
#include "pw_log/log.h"
namespace pw::async2::size_report {
namespace {
BasicDispatcher dispatcher;
}
#ifdef _PW_ASYNC2_SIZE_REPORT_SELECT
int SingleTypeSelect(uint32_t mask) {
ValueFuture<int> value_1 = ValueFuture<int>::Resolved(47);
ValueFuture<int> value_2 = ValueFuture<int>::Resolved(52);
ValueFuture<int> value_3 = ValueFuture<int>::Resolved(57);
auto future =
Select(std::move(value_1), std::move(value_2), std::move(value_3));
int value = -1;
FuncTask task([&](Context& cx) -> Poll<> {
PW_AWAIT(auto result, future, cx);
if (result.has_value<0>()) {
value = result.value<0>();
} else if (result.has_value<1>()) {
value = result.value<1>();
} else if (result.has_value<2>()) {
value = result.value<2>();
}
return Ready();
});
dispatcher.Post(task);
dispatcher.RunUntilStalled();
PW_BLOAT_COND(value != -1, mask);
return value;
}
#ifdef _PW_ASYNC2_SIZE_REPORT_SELECT_INCREMENTAL
int MultiTypeSelect(uint32_t mask) {
ValueFuture<int> value_1 = ValueFuture<int>::Resolved(47);
ValueFuture<uint32_t> value_2 = ValueFuture<uint32_t>::Resolved(0xffffffff);
ValueFuture<char> value_3 = ValueFuture<char>::Resolved('c');
auto future =
Select(std::move(value_1), std::move(value_2), std::move(value_3));
int value = -1;
FuncTask task([&](Context& cx) -> Poll<> {
PW_AWAIT(auto result, future, cx);
if (result.has_value<0>()) {
value = result.value<0>();
} else if (result.has_value<1>()) {
value = static_cast<int>(result.value<1>());
} else if (result.has_value<2>()) {
value = static_cast<int>(result.value<2>());
}
return Ready();
});
dispatcher.Post(task);
dispatcher.RunUntilStalled();
PW_BLOAT_COND(value != -1, mask);
return value;
}
#endif // _PW_ASYNC2_SIZE_REPORT_SELECT_INCREMENTAL
#endif // _PW_ASYNC2_SIZE_REPORT_SELECT
#ifdef _PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_MANUAL
class SelectComparisonTask : public Task {
public:
SelectComparisonTask()
: value_1_(ValueFuture<int>::Resolved(47)),
value_2_(ValueFuture<uint32_t>::Resolved(0xffffffff)),
value_3_(ValueFuture<char>::Resolved('c')) {}
// Implements the logic of Select() by manually polling each future.
Poll<> DoPend(Context& cx) override {
Poll<int> poll_1 = value_1_.Pend(cx);
if (poll_1.IsReady()) {
PW_LOG_INFO("Value 1 ready: %d", *poll_1);
return Ready();
}
Poll<uint32_t> poll_2 = value_2_.Pend(cx);
if (poll_2.IsReady()) {
PW_LOG_INFO("Value 2 ready: %u", *poll_2);
return Ready();
}
Poll<char> poll_3 = value_3_.Pend(cx);
if (poll_3.IsReady()) {
PW_LOG_INFO("Value 3 ready: %c", *poll_3);
return Ready();
}
return Pending();
}
private:
ValueFuture<int> value_1_;
ValueFuture<uint32_t> value_2_;
ValueFuture<char> value_3_;
};
#endif // _PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_MANUAL
#ifdef _PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_HELPER
class SelectComparisonTask : public Task {
public:
SelectComparisonTask()
: value_1_(ValueFuture<int>::Resolved(47)),
value_2_(ValueFuture<uint32_t>::Resolved(0xffffffff)),
value_3_(ValueFuture<char>::Resolved('c')) {}
// Calls Select() with the pendables.
Poll<> DoPend(Context& cx) override {
auto future =
Select(std::move(value_1_), std::move(value_2_), std::move(value_3_));
PW_AWAIT(auto result, future, cx);
if (result.has_value<0>()) {
PW_LOG_INFO("Value 1 ready: %d", result.value<0>());
} else if (result.has_value<1>()) {
PW_LOG_INFO("Value 2 ready: %u", result.value<1>());
} else if (result.has_value<2>()) {
PW_LOG_INFO("Value 3 ready: %c", result.value<2>());
}
return Ready();
}
private:
ValueFuture<int> value_1_;
ValueFuture<uint32_t> value_2_;
ValueFuture<char> value_3_;
};
#endif // _PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_HELPER
// Ensure all ValueFuture types and their core operations (construct/move/Pend)
// are in the base binary.
void SetBaselineValueFutures(uint32_t mask) {
ValueFuture<int> value_1 = ValueFuture<int>::Resolved(47);
ValueFuture<uint32_t> value_2 = ValueFuture<uint32_t>::Resolved(0x00ff00ffu);
ValueFuture<char> value_3 = ValueFuture<char>::Resolved('c');
FuncTask task([v1 = std::move(value_1),
v2 = std::move(value_2),
v3 = std::move(value_3),
&mask](Context& cx) mutable -> Poll<> {
auto result_1 = v1.Pend(cx);
auto result_2 = v2.Pend(cx);
auto result_3 = v3.Pend(cx);
bool all_ready =
result_1.IsReady() && result_2.IsReady() && result_3.IsReady();
PW_BLOAT_COND(all_ready, mask);
if (all_ready) {
return Ready();
}
return Pending();
});
dispatcher.Post(task);
dispatcher.RunUntilStalled();
}
int Measure() {
volatile uint32_t mask = bloat::kDefaultMask;
SetBaseline(mask);
SetBaselineValueFutures(mask);
MockTask task;
dispatcher.Post(task);
// Move the waker onto the stack to call its operator= before waking the task.
Waker waker;
PW_BLOAT_EXPR((waker = std::move(task.last_waker)), mask);
waker.Wake();
dispatcher.RunToCompletion();
int result = -1;
#ifdef _PW_ASYNC2_SIZE_REPORT_SELECT
result = SingleTypeSelect(mask);
#ifdef _PW_ASYNC2_SIZE_REPORT_SELECT_INCREMENTAL
result += MultiTypeSelect(mask);
#endif //_PW_ASYNC2_SIZE_REPORT_SELECT_INCREMENTAL
#endif // _PW_ASYNC2_SIZE_REPORT_SELECT
#if defined(_PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_MANUAL) || \
defined(_PW_ASYNC_2_SIZE_REPORT_COMPARE_SELECT_HELPER)
ValueFuture<int> pendable_int = ValueFuture<int>::Resolved(47);
FuncTask task2(
[&](Context& cx) { return pendable_int.Pend(cx).Readiness(); });
dispatcher.Post(task2);
dispatcher.RunUntilStalled();
SelectComparisonTask comparison;
dispatcher.Post(comparison);
dispatcher.RunUntilStalled();
#endif
return result;
}
} // namespace pw::async2::size_report
int main() { return pw::async2::size_report::Measure(); }