| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_CLIENT_H_ |
| #define LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_CLIENT_H_ |
| |
| #include <lib/fidl/llcpp/client_base.h> |
| #include <lib/fidl/llcpp/internal/endpoints.h> |
| #include <lib/fidl/llcpp/internal/thenable.h> |
| #include <lib/fidl/llcpp/wire_messaging_declarations.h> |
| |
| namespace fidl { |
| |
| // |
| // Note: when updating the documentation below, please make similar updates to |
| // the one in //src/lib/fidl/cpp/include/lib/fidl/cpp/client.h and |
| // sdk/lib/fidl_driver/include/lib/fidl_driver/natural_client.h. |
| // |
| |
| // |WireClient| is a client for sending and receiving FIDL wire messages, that |
| // is bound to a single fixed thread. See |WireSharedClient| for a client that |
| // may be moved or cloned to a different thread. |
| // |
| // Generated FIDL APIs are accessed by 'dereferencing' the client value: |
| // |
| // // Creates a client that speaks over |client_end|, on the |my_dispatcher| dispatcher. |
| // fidl::WireClient client(std::move(client_end), my_dispatcher); |
| // |
| // // Call the |Foo| method asynchronously, passing in a callback that will be |
| // // invoked on a dispatcher thread when the server response arrives. |
| // auto status = client->Foo(args, [] (Result result) {}); |
| // |
| // ## Lifecycle |
| // |
| // A client must be **bound** to an endpoint before it could be used. This |
| // association between the endpoint and the client is called a "binding". |
| // Binding a client to an endpoint starts the monitoring of incoming messages. |
| // Those messages are appropriately dispatched: to response callbacks, to event |
| // handlers, etc. FIDL methods (asynchronous or synchronous) may only be invoked |
| // on a bound client. |
| // |
| // Internally, a client is a lightweight reference to the binding, performing |
| // its duties indirectly through that object, as illustrated by the simplified |
| // diagram below: |
| // |
| // references makes |
| // client -------------> binding --------> FIDL call |
| // |
| // This means that the client _object_ and the binding have overlapping but |
| // slightly different lifetimes. For example, the binding may terminate in |
| // response to fatal communication errors, leaving the client object alive but |
| // unable to make any calls. |
| // |
| // To stop the monitoring of incoming messages, one may **teardown** the |
| // binding. When teardown is initiated, the client will not monitor new messages |
| // on the endpoint. Ongoing callbacks will be allowed to run to completion. When |
| // teardown is complete, further calls on the same client will fail. Unfulfilled |
| // response callbacks will be dropped. |
| // |
| // Destruction of a client object will initiate teardown. |
| // |
| // Teardown will also be initiated when the binding encounters a terminal error: |
| // |
| // - The server-end of the channel was closed. |
| // - An epitaph was received. |
| // - Decoding or encoding failed. |
| // - An invalid or unknown message was encountered. |
| // - Error waiting on, reading from, or writing to the channel. |
| // |
| // In this case, the user will be notified of the detailed error via the |
| // |on_fidl_error| method on the event handler. |
| // |
| // ## Thread safety |
| // |
| // |WireClient| provides an easier to use API in exchange of a more restrictive |
| // threading model: |
| // |
| // - There must only ever be one thread executing asynchronous operations for |
| // the provided |async_dispatcher_t|, termed "the dispatcher thread". |
| // - The client must be bound on the dispatcher thread. |
| // - The client must be destroyed on the dispatcher thread. |
| // - FIDL method calls may be made on other threads, but the response is always |
| // delivered on the dispatcher thread, as are event callbacks. |
| // |
| // The above rules are checked in debug builds at run-time. In short, the client |
| // is local to a thread. |
| // |
| // Note that FIDL method calls must be synchronized with operations that consume |
| // or mutate the |WireClient| itself: |
| // |
| // - Assigning a new value to the |WireClient| variable. |
| // - Moving the |WireClient| to a different location. |
| // - Destroying the |WireClient|. |
| // |
| // |WireClient| is suitable for systems with stronger sequential threading |
| // guarantees. It is intended to be used as a local variable with fixed |
| // lifetime, or as a member of a larger class where it is uniquely owned by |
| // instances of that class. Destroying the |WireClient| is guaranteed to stop |
| // message dispatch: since the client is destroyed on the dispatcher thread, |
| // there is no opportunity of parallel callbacks to user code, and |
| // use-after-free of user objects is naturally avoided during teardown. |
| // |
| // See |WireSharedClient| for a client that supports binding and destroying on |
| // arbitrary threads, at the expense of requiring two-phase shutdown. |
| template <typename Protocol> |
| class WireClient { |
| public: |
| // Create an initialized client which manages the binding of the client end of |
| // a channel to a dispatcher, as if that client had been default-constructed |
| // then later bound to that endpoint via |Bind|. |
| // |
| // It is a logic error to use a dispatcher that is shutting down or already |
| // shut down. Doing so will result in a panic. |
| // |
| // If any other error occurs during initialization, the |
| // |event_handler->on_fidl_error| handler will be invoked asynchronously with |
| // the reason, if specified. |
| template <typename AsyncEventHandler = fidl::WireAsyncEventHandler<Protocol>> |
| WireClient(fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| AsyncEventHandler* event_handler = nullptr) { |
| Bind(std::move(client_end), dispatcher, event_handler); |
| } |
| |
| // Create an uninitialized client. The client may then be bound to an endpoint |
| // later via |Bind|. |
| // |
| // Prefer using the constructor overload that binds the client to a channel |
| // atomically during construction. Use this default constructor only when the |
| // client must be constructed first before a channel could be obtained (for |
| // example, if the client is an instance variable). |
| WireClient() = default; |
| |
| // Returns if the |WireClient| is initialized. |
| bool is_valid() const { return controller_.is_valid(); } |
| explicit operator bool() const { return is_valid(); } |
| |
| // The destructor of |WireClient| will initiate binding teardown. |
| // |
| // When the client destructs: |
| // - The channel will be closed. |
| // - Pointers obtained via |get| will be invalidated. |
| // - Binding teardown will happen, implying: |
| // * In-progress calls will be forgotten. Async callbacks will be dropped. |
| ~WireClient() = default; |
| |
| // |WireClient|s can be safely moved without affecting any in-flight FIDL |
| // method calls. Note that calling methods on a client should be serialized |
| // with respect to operations that consume the client, such as moving it or |
| // destroying it. |
| WireClient(WireClient&& other) noexcept = default; |
| WireClient& operator=(WireClient&& other) noexcept = default; |
| |
| // Initializes the client by binding the |client_end| endpoint to the |
| // dispatcher. |
| // |
| // It is a logic error to invoke |Bind| on a dispatcher that is shutting down |
| // or already shut down. Doing so will result in a panic. |
| // |
| // When other errors occur during binding, the |event_handler->on_fidl_error| |
| // handler will be asynchronously invoked with the reason, if specified. |
| // |
| // It is not allowed to call |Bind| on an initialized client. To rebind a |
| // |WireClient| to a different endpoint, simply replace the |WireClient| |
| // variable with a new instance. |
| void Bind(fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| fidl::WireAsyncEventHandler<Protocol>* event_handler = nullptr) { |
| controller_.Bind(internal::MakeAnyTransport(client_end.TakeChannel()), dispatcher, |
| internal::MakeAnyEventDispatcher(event_handler), event_handler, |
| fidl::AnyTeardownObserver::Noop(), |
| fidl::internal::ThreadingPolicy::kCreateAndTeardownFromDispatcherThread); |
| } |
| |
| // Returns the interface for making outgoing FIDL calls with managed memory |
| // allocation. The client must be initialized first. |
| // |
| // If the binding has been torn down, calls on the interface return error with |
| // status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|. |
| // |
| // Persisting this pointer to a local variable is discouraged, since that |
| // results in unsafe borrows. Always prefer making calls directly via the |
| // |WireClient| reference-counting type. |
| auto operator->() const { |
| return internal::Arrow<internal::WireWeakAsyncClientImpl<Protocol>>{&get()}; |
| } |
| |
| // Returns a veneer object which exposes the caller-allocating API, using the |
| // provided |resource| to allocate buffers necessary for each call. Requests |
| // will live on those buffers. Responses on the other hand do not live on |
| // those buffers; they are separately managed and may become invalid after the |
| // corresponding async response/result callback returns. |
| // |
| // Examples of supported memory resources are: |
| // |
| // * |fidl::BufferSpan|, referencing a range of bytes. |
| // * |fidl::AnyArena&|, referencing an arena. |
| // * Any type for which there is a |MakeAnyBufferAllocator| specialization. |
| // See |AnyBufferAllocator|. |
| // |
| // This interface is suitable when one needs complete control over memory |
| // allocation. Instead of implicitly heap allocating the necessary bookkeeping |
| // for in-flight operations, the methods take a raw pointer to a |
| // |fidl::WireResponseContext<FidlMethod>|, which may be allocated via any |
| // means as long as it outlives the duration of this async FIDL call. Refer to |
| // documentation on the response context. |
| // |
| // The returned object borrows from this object, hence must not outlive |
| // the client object. |
| // |
| // The returned object may be briefly persisted for use over multiple calls: |
| // |
| // fidl::Arena my_arena; |
| // fidl::WireClient client(std::move(client_end), some_dispatcher); |
| // auto buffered = client.buffer(my_arena); |
| // buffered->FooMethod(args, foo_response_context); |
| // buffered->BarMethod(args, bar_response_context); |
| // ... |
| // |
| // In this situation, those calls will all use the initially provided memory |
| // resource (`my_arena`) to allocate their message buffers. The memory |
| // resource won't be reset/overwritten across calls. Note that if a |
| // |BufferSpan| is provided as the memory resource, sharing memory resource in |
| // this manner may eventually exhaust the capacity of the buffer span since it |
| // represents a single fixed size buffer. To reuse (overwrite) the underlying |
| // buffer across multiple calls, obtain a new caller-allocating veneer object |
| // for each call: |
| // |
| // fidl::BufferSpan span(some_large_buffer, size); |
| // fidl::WireClient client(std::move(client_end), some_dispatcher); |
| // client.buffer(span)->FooMethod(args, foo_response_context); |
| // client.buffer(span)->BarMethod(args, bar_response_context); |
| // |
| template <typename MemoryResource> |
| auto buffer(MemoryResource&& resource) const { |
| ZX_ASSERT(is_valid()); |
| return internal::Arrow<internal::WireWeakAsyncBufferClientImpl<Protocol>>{ |
| &get(), internal::MakeAnyBufferAllocator(std::forward<MemoryResource>(resource))}; |
| } |
| |
| // Returns a veneer object exposing synchronous calls. Example: |
| // |
| // fidl::WireClient client(std::move(client_end), some_dispatcher); |
| // fidl::WireResult result = client.sync()->FooMethod(args); |
| // |
| auto sync() const { |
| ZX_ASSERT(is_valid()); |
| return internal::Arrow<internal::WireWeakSyncClientImpl<Protocol>>{&get()}; |
| } |
| |
| private: |
| // Allow unit tests to peek into the internals of this class. |
| friend ::fidl_testing::ClientChecker; |
| |
| internal::ClientBase& get() const { return controller_.get(); } |
| |
| WireClient(const WireClient& other) noexcept = delete; |
| WireClient& operator=(const WireClient& other) noexcept = delete; |
| |
| internal::ClientController controller_; |
| }; |
| |
| template <typename Protocol, typename AsyncEventHandlerReference> |
| WireClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&) |
| -> WireClient<Protocol>; |
| |
| template <typename Protocol> |
| WireClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*) -> WireClient<Protocol>; |
| |
| // |fidl::ObserveTeardown| is used with |fidl::WireSharedClient| and allows |
| // custom logic to run on teardown completion, represented by a callable |
| // |callback| that takes no parameters and returns |void|. It should be supplied |
| // as the last argument when constructing or binding the client. See lifecycle |
| // notes on |fidl::WireSharedClient|. |
| template <typename Callable> |
| fidl::AnyTeardownObserver ObserveTeardown(Callable&& callback) { |
| static_assert(std::is_convertible<Callable, fit::closure>::value, |
| "|callback| must have the signature `void fn()`."); |
| return fidl::AnyTeardownObserver::ByCallback(std::forward<Callable>(callback)); |
| } |
| |
| // |fidl::ShareUntilTeardown| configures a |fidl::WireSharedClient| to co-own |
| // the supplied |object| until teardown completion. It may be used to extend the |
| // lifetime of user objects responsible for handling messages. It should be |
| // supplied as the last argument when constructing or binding the client. See |
| // lifecycle notes on |fidl::WireSharedClient|. |
| template <typename T> |
| fidl::AnyTeardownObserver ShareUntilTeardown(std::shared_ptr<T> object) { |
| return fidl::AnyTeardownObserver::ByOwning(object); |
| } |
| |
| // |WireSharedClient| is a client for sending and receiving wire messages. It is |
| // suitable for systems with less defined threading guarantees, by providing the |
| // building blocks to implement a two-phase asynchronous shutdown pattern. |
| // |
| // During teardown, |WireSharedClient| exposes a synchronization point beyond |
| // which it will not make any more upcalls to user code. The user may then |
| // arrange any objects that are the recipient of client callbacks to be |
| // destroyed after the synchronization point. As a result, when destroying an |
| // entire subsystem, the teardown of the client may be requested from an |
| // arbitrary thread, in parallel with any callbacks to user code, while |
| // avoiding use-after-free of user objects. |
| // |
| // In addition, |WireSharedClient| supports cloning multiple instances sharing |
| // the same underlying endpoint. |
| // |
| // ## Lifecycle |
| // |
| // See lifecycle notes on |WireClient| for general lifecycle information. Here |
| // we note the additional subtleties and two-phase shutdown features exclusive |
| // to |WireSharedClient|. |
| // |
| // Teardown of the binding is an asynchronous process, to account for the |
| // possibility of in-progress calls to user code. For example, the bindings |
| // runtime could be invoking a response callback from a dispatcher thread, while |
| // the user initiates teardown from an unrelated thread. |
| // |
| // There are a number of ways to monitor the completion of teardown: |
| // |
| // - Owned event handler: transfer the ownership of an event handler to the |
| // bindings as a |std::unique_ptr| when binding the client. After teardown is |
| // complete, the event handler will be destroyed. It is safe to destroy the |
| // user objects referenced by any client callbacks from within the event |
| // handler destructor. |
| // |
| // - Teardown observer: provide an instance of |fidl::AnyTeardownObserver| to |
| // the bindings. The observer will be notified when teardown is complete. |
| // |
| // See |
| // https://fuchsia.dev/fuchsia-src/development/languages/fidl/guides/llcpp-threading |
| // for detailed examples. |
| // |
| // A |WireSharedClient| may be |Clone|d, with the clone referencing the same |
| // endpoint. Automatic teardown occurs when the last clone bound to the |
| // endpoint is destructed. |
| // |
| // |AsyncTeardown| may be called on a |WireSharedClient| to explicitly initiate |
| // teardown. |
| // |
| // ## Thread safety |
| // |
| // FIDL method calls on this class are thread-safe. |AsyncTeardown| and |Clone| |
| // are also thread-safe, and may be invoked in parallel with FIDL method calls. |
| // However, those operations must be synchronized with operations that consume |
| // or mutate the |WireSharedClient| itself: |
| // |
| // - Assigning a new value to the |WireSharedClient| variable. |
| // - Moving the |WireSharedClient| to a different location. |
| // - Destroying the |WireSharedClient| variable. |
| // |
| // When teardown completes, the binding will notify the user from a |dispatcher| |
| // thread, unless the user shuts down the |dispatcher| while there are active |
| // clients associated with it. In that case, those clients will be synchronously |
| // torn down, and the notification (e.g. destroying the event handler) will |
| // happen on the thread invoking dispatcher shutdown. |
| template <typename Protocol> |
| class WireSharedClient final { |
| public: |
| // Creates an initialized |WireSharedClient| which manages the binding of the |
| // client end of a channel to a dispatcher. |
| // |
| // It is a logic error to use a dispatcher that is shutting down or already |
| // shut down. Doing so will result in a panic. |
| // |
| // If any other error occurs during initialization, the |
| // |event_handler->on_fidl_error| handler will be invoked asynchronously with |
| // the reason, if specified. |
| // |
| // |event_handler| will be destroyed when teardown completes. |
| template <typename AsyncEventHandler = fidl::WireAsyncEventHandler<Protocol>> |
| WireSharedClient(fidl::internal::ClientEndType<Protocol> client_end, |
| async_dispatcher_t* dispatcher, |
| std::unique_ptr<AsyncEventHandler> event_handler) { |
| Bind(std::move(client_end), dispatcher, std::move(event_handler)); |
| } |
| |
| // Creates a |WireSharedClient| that supports custom behavior on teardown |
| // completion via |teardown_observer|. Through helpers that return an |
| // |AnyTeardownObserver|, users may link the completion of teardown to the |
| // invocation of a callback or the lifecycle of related business objects. See |
| // for example |fidl::ObserveTeardown| and |fidl::ShareUntilTeardown|. |
| // |
| // This overload does not demand taking ownership of |event_handler| by |
| // |std::unique_ptr|, hence is suitable when the |event_handler| needs to be |
| // managed independently of the client lifetime. |
| // |
| // See |WireSharedClient| above for other behavior aspects of the constructor. |
| template <typename AsyncEventHandler = fidl::WireAsyncEventHandler<Protocol>> |
| WireSharedClient( |
| fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| AsyncEventHandler* event_handler, |
| fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) { |
| Bind(std::move(client_end), dispatcher, event_handler, std::move(teardown_observer)); |
| } |
| |
| // Overload of |WireSharedClient| that omits the |event_handler|, to |
| // workaround C++ limitations on default arguments. |
| // |
| // See |WireSharedClient| above for other behavior aspects of the constructor. |
| template <typename AsyncEventHandler = fidl::WireAsyncEventHandler<Protocol>> |
| WireSharedClient( |
| fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) { |
| Bind(std::move(client_end), dispatcher, nullptr, std::move(teardown_observer)); |
| } |
| |
| // Creates an uninitialized |WireSharedClient|. |
| // |
| // Prefer using the constructor overload that binds the client to a channel |
| // atomically during construction. Use this default constructor only when the |
| // client must be constructed first before a channel could be obtained (for |
| // example, if the client is an instance variable). |
| WireSharedClient() = default; |
| |
| // Returns if the |WireSharedClient| is initialized. |
| bool is_valid() const { return controller_.is_valid(); } |
| explicit operator bool() const { return is_valid(); } |
| |
| // If the current |WireSharedClient| is the last instance controlling the |
| // current connection, the destructor of this |WireSharedClient| will trigger |
| // teardown. |
| // |
| // When the last |WireSharedClient| destructs: |
| // - The channel will be closed. |
| // - Pointers obtained via |get| will be invalidated. |
| // - Teardown will be initiated. See the **Lifecycle** section from the |
| // class documentation of |WireClient|. |
| // |
| // See also: |AsyncTeardown|. |
| ~WireSharedClient() = default; |
| |
| // |fidl::WireSharedClient|s can be safely moved without affecting any in-progress |
| // operations. Note that calling methods on a client should be serialized with |
| // respect to operations that consume the client, such as moving it or |
| // destroying it. |
| WireSharedClient(WireSharedClient&& other) noexcept = default; |
| WireSharedClient& operator=(WireSharedClient&& other) noexcept = default; |
| |
| // Initializes the client by binding the |client_end| endpoint to the dispatcher. |
| // |
| // It is a logic error to invoke |Bind| on a dispatcher that is shutting down |
| // or already shut down. Doing so will result in a panic. |
| // |
| // It is not allowed to call |Bind| on an initialized client. To rebind a |
| // |WireSharedClient| to a different endpoint, simply replace the |
| // |WireSharedClient| variable with a new instance. |
| // |
| // When other error occurs during binding, the |event_handler->on_fidl_error| |
| // handler will be asynchronously invoked with the reason, if specified. |
| // |
| // |event_handler| will be destroyed when teardown completes. |
| void Bind(fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| std::unique_ptr<fidl::WireAsyncEventHandler<Protocol>> event_handler) { |
| auto event_handler_raw = event_handler.get(); |
| Bind(std::move(client_end), dispatcher, event_handler_raw, |
| fidl::AnyTeardownObserver::ByOwning(std::move(event_handler))); |
| } |
| |
| // Overload of |Bind| that supports custom behavior on teardown completion via |
| // |teardown_observer|. Through helpers that return an |AnyTeardownObserver|, |
| // users may link the completion of teardown to the invocation of a callback |
| // or the lifecycle of related business objects. See for example |
| // |fidl::ObserveTeardown| and |fidl::ShareUntilTeardown|. |
| // |
| // This overload does not demand taking ownership of |event_handler| by |
| // |std::unique_ptr|, hence is suitable when the |event_handler| needs to be |
| // managed independently of the client lifetime. |
| // |
| // See |Bind| above for other behavior aspects of the function. |
| void Bind(fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| fidl::WireAsyncEventHandler<Protocol>* event_handler, |
| fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) { |
| controller_.Bind(internal::MakeAnyTransport(client_end.TakeHandle()), dispatcher, |
| internal::MakeAnyEventDispatcher(event_handler), event_handler, |
| std::move(teardown_observer), |
| fidl::internal::ThreadingPolicy::kCreateAndTeardownFromAnyThread); |
| } |
| |
| // Overload of |Bind| that omits the |event_handler|, to |
| // workaround C++ limitations on default arguments. |
| // |
| // See |Bind| above for other behavior aspects of the constructor. |
| void Bind(fidl::internal::ClientEndType<Protocol> client_end, async_dispatcher_t* dispatcher, |
| fidl::AnyTeardownObserver teardown_observer = fidl::AnyTeardownObserver::Noop()) { |
| Bind(std::move(client_end), dispatcher, nullptr, std::move(teardown_observer)); |
| } |
| |
| // Initiates asynchronous teardown of the bindings. See the **Lifecycle** |
| // section from the class documentation. |
| // |
| // |Bind| must have been called before this. |
| // |
| // While it is safe to invoke |AsyncTeardown| from any thread, it is unsafe to |
| // wait for teardown to complete from a dispatcher thread, as that will likely |
| // deadlock. |
| void AsyncTeardown() { controller_.Unbind(); } |
| |
| // Returns another |WireSharedClient| instance sharing the same channel. |
| // |
| // Prefer to |Clone| only when necessary e.g. extending the lifetime of a |
| // |SharedClient| to a different scope. Any clone will prevent the cleanup |
| // of the channel while the binding is alive. |
| WireSharedClient Clone() { return WireSharedClient(*this); } |
| |
| // Returns the interface for making outgoing FIDL calls with managed memory |
| // allocation. The client must be initialized first. |
| // |
| // If the binding has been torn down, calls on the interface return error with |
| // status |ZX_ERR_CANCELED| and reason |fidl::Reason::kUnbind|. |
| // |
| // Persisting this pointer to a local variable is discouraged, since that |
| // results in unsafe borrows. Always prefer making calls directly via the |
| // |WireSharedClient| reference-counting type. A client may be cloned and |
| // handed off through the |Clone| method. |
| auto operator->() const { |
| return internal::Arrow<internal::WireWeakAsyncClientImpl<Protocol>>{&get()}; |
| } |
| |
| // Returns a veneer object which exposes the caller-allocating API, using |
| // the provided |resource| to allocate buffers necessary for each call. |
| // See documentation on |WireClient::buffer| for detailed behavior. |
| template <typename MemoryResource> |
| auto buffer(MemoryResource&& resource) const { |
| ZX_ASSERT(is_valid()); |
| return internal::Arrow<internal::WireWeakAsyncBufferClientImpl<Protocol>>{ |
| &get(), internal::MakeAnyBufferAllocator(std::forward<MemoryResource>(resource))}; |
| } |
| |
| // Returns a veneer object exposing synchronous calls. Example: |
| // |
| // fidl::WireClient client(std::move(client_end), some_dispatcher); |
| // fidl::WireResult result = client.sync()->FooMethod(args); |
| // |
| auto sync() const { |
| ZX_ASSERT(is_valid()); |
| return internal::Arrow<internal::WireWeakSyncClientImpl<Protocol>>{&get()}; |
| } |
| |
| private: |
| // Allow unit tests to peek into the internals of this class. |
| friend ::fidl_testing::ClientChecker; |
| |
| internal::ClientBase& get() const { return controller_.get(); } |
| |
| WireSharedClient(const WireSharedClient& other) noexcept = default; |
| WireSharedClient& operator=(const WireSharedClient& other) noexcept = default; |
| |
| internal::ClientController controller_; |
| }; |
| |
| template <typename Protocol, typename AsyncEventHandlerReference> |
| WireSharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&, |
| fidl::AnyTeardownObserver) -> WireSharedClient<Protocol>; |
| |
| template <typename Protocol, typename AsyncEventHandlerReference> |
| WireSharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*, AsyncEventHandlerReference&&) |
| -> WireSharedClient<Protocol>; |
| |
| template <typename Protocol> |
| WireSharedClient(fidl::ClientEnd<Protocol>, async_dispatcher_t*) -> WireSharedClient<Protocol>; |
| |
| } // namespace fidl |
| |
| #endif // LIB_FIDL_LLCPP_INCLUDE_LIB_FIDL_LLCPP_CLIENT_H_ |