blob: eb400aa152c6ca2d8b24d75bf570e03e3f276275 [file] [log] [blame]
// 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