| // Copyright 2023 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. | |
| #pragma once | |
| #include "pw_async_bench/base.h" | |
| #include "pw_async_bench/bivariant.h" | |
| #include "pw_async_bench/poll_echo.h" | |
| #include "pw_result/result.h" | |
| namespace pw::async_bench { | |
| /// User implementation of the `Echo` service which delegates to a remote | |
| /// server. | |
| class ProxyEchoImpl { | |
| public: | |
| ProxyEchoImpl(RemoteEcho& remote) : remote_(&remote) {} | |
| /// A future returned by the `Echo` method. | |
| /// | |
| /// The autogenerated RPC stubs will invoke `Poll` on this type until | |
| /// it returns a `pw::async::Ready` message containing the result. | |
| /// | |
| /// This type is responsible for ensuring that `waker.wake()` is invoked | |
| /// when it can make progress. When this happens, the autogenerated RPC | |
| /// stubs will invoke `Poll` again looking for a result. | |
| /// | |
| /// Note: this wrapper future is used to demonstrate how futures can | |
| /// be composed, but it is unnecessary: `Echo` could simply return the | |
| /// future from `RemoteEcho` directly, avoiding any extra overhead or | |
| /// boilerplate. | |
| /// | |
| /// Similarly, the future need not take in a `RemoteEcho` or deal with the | |
| /// `Echo` call itself-- that could be done in the implementation of `Echo` | |
| /// for minimal boilerplate and maximal efficiency. However, this is intended | |
| /// as a demonstration of what a compositional `Poll`-able might look like. | |
| class EchoFuture { | |
| public: | |
| EchoFuture(EchoRequest request, RemoteEcho& remote) | |
| : state_(BeforeRemoteCall{std::move(request), &remote}) {} | |
| /// Advances the `EchoFuture`, possibly returning a result. | |
| /// | |
| /// If `pw::async::Ready(result)` is returned, the result will be sent back | |
| /// via the RPC stub, and `Poll` will no longer be called. | |
| /// | |
| /// If `pw::async::Pending` is returned, `EchoFuture::Poll` arranges for | |
| /// `waker.wake()` to be invoked when more progress can be made. | |
| pw::async::Poll<pw::Result<EchoResponse>> Poll(pw::async::Waker& waker); | |
| private: | |
| // ------ | |
| // STATES | |
| // ------ | |
| // | |
| // `BeforeRemoteCall` and `WaitingOnRemote` are the two possible states | |
| // that the `EchoFuture` can be in. | |
| /// State 1: `RemoteEcho::Echo` has not yet been invoked. | |
| struct BeforeRemoteCall { | |
| EchoRequest request; | |
| RemoteEcho* remote; | |
| }; | |
| /// State 2: `RemoteEcho::Echo` has been invoked, and we're waiting for the | |
| /// result. | |
| struct WaitingOnRemote { | |
| RemoteEcho::EchoFuture remote_future; | |
| }; | |
| /// A `std::variant` containing the possible states of this future. | |
| /// | |
| /// `std::variant` is particularly useful for building future-style state | |
| /// machines because it stores the tag for our current state, as well as | |
| /// only the data that is valid and needed for the current state. This | |
| /// minimizes the in-memory footprint of our future and prevents mistaken | |
| /// accesses to fields that are not currently valid. | |
| bivariant<BeforeRemoteCall, WaitingOnRemote> state_; | |
| }; | |
| /// Returns a future for the `Echo` method. | |
| /// | |
| /// Returned futures will all have a `Poll` method which accepts a `Waker&` | |
| /// and returns `Poll<Result<RpcResultType>>`. The RPC system is then | |
| /// responsible for invoking `Poll` until the future completes. | |
| /// | |
| /// Note that, unlike in the callback example, the type of the `Echo` return | |
| /// value may vary between implementations of the RPC `Echo` interface, so | |
| /// we can't use virtual dispatch (at least, not without an intermediate layer | |
| /// that stores the `EchoFuture` somewhere). | |
| EchoFuture Echo(EchoRequest request) { | |
| return EchoFuture(std::move(request), *remote_); | |
| } | |
| private: | |
| RemoteEcho* remote_; | |
| }; | |
| } // namespace pw::async_bench |