| // 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 |