| // Copyright 2021 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. |
| |
| // clang-format off |
| // The MakeAnyTransport overloads need to be defined before including |
| // message.h, which uses them. |
| #include <lib/fidl_driver/cpp/transport.h> |
| // clang-format on |
| |
| #include <lib/fdf/cpp/channel_read.h> |
| #include <lib/fdf/dispatcher.h> |
| #include <lib/fidl/llcpp/message.h> |
| #include <lib/fidl/llcpp/message_storage.h> |
| #include <lib/fidl/llcpp/status.h> |
| #include <zircon/errors.h> |
| |
| #include <optional> |
| |
| namespace fidl { |
| namespace internal { |
| |
| namespace { |
| |
| zx_status_t driver_write(fidl_handle_t handle, WriteOptions write_options, const WriteArgs& args) { |
| // Note: in order to force the encoder to only output one iovec, only provide an iovec buffer of |
| // 1 element to the encoder. |
| ZX_ASSERT(args.data_count == 1); |
| |
| const zx_channel_iovec_t& iovec = static_cast<const zx_channel_iovec_t*>(args.data)[0]; |
| fdf_arena_t* arena = |
| write_options.outgoing_transport_context.release<internal::DriverTransport>(); |
| void* arena_handles = fdf_arena_allocate(arena, args.handles_count * sizeof(fdf_handle_t)); |
| memcpy(arena_handles, args.handles, args.handles_count * sizeof(fdf_handle_t)); |
| |
| zx_status_t status = |
| fdf_channel_write(handle, 0, arena, const_cast<void*>(iovec.buffer), iovec.capacity, |
| static_cast<fdf_handle_t*>(arena_handles), args.handles_count); |
| return status; |
| } |
| |
| zx_status_t driver_read(fidl_handle_t handle, const ReadOptions& read_options, |
| const ReadArgs& args) { |
| ZX_DEBUG_ASSERT(args.storage_view != nullptr); |
| ZX_DEBUG_ASSERT(args.out_data != nullptr); |
| DriverMessageStorageView* rd_view = static_cast<DriverMessageStorageView*>(args.storage_view); |
| |
| fdf_arena_t* out_arena; |
| zx_status_t status = |
| fdf_channel_read(handle, 0, &out_arena, args.out_data, args.out_data_actual_count, |
| args.out_handles, args.out_handles_actual_count); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *rd_view->arena = fdf::Arena(out_arena); |
| return ZX_OK; |
| } |
| |
| zx_status_t driver_call(fidl_handle_t handle, CallOptions call_options, |
| const CallMethodArgs& args) { |
| ZX_DEBUG_ASSERT(args.rd.storage_view != nullptr); |
| ZX_DEBUG_ASSERT(args.rd.out_data != nullptr); |
| DriverMessageStorageView* rd_view = static_cast<DriverMessageStorageView*>(args.rd.storage_view); |
| |
| // Note: in order to force the encoder to only output one iovec, only provide an iovec buffer of |
| // 1 element to the encoder. |
| ZX_ASSERT(args.wr.data_count == 1); |
| const zx_channel_iovec_t& iovec = static_cast<const zx_channel_iovec_t*>(args.wr.data)[0]; |
| fdf_arena_t* arena = call_options.outgoing_transport_context.release<DriverTransport>(); |
| void* arena_handles = fdf_arena_allocate(arena, args.wr.handles_count * sizeof(fdf_handle_t)); |
| memcpy(arena_handles, args.wr.handles, args.wr.handles_count * sizeof(fdf_handle_t)); |
| |
| fdf_arena_t* rd_arena = nullptr; |
| fdf_channel_call_args fdf_args = { |
| .wr_arena = arena, |
| .wr_data = const_cast<void*>(iovec.buffer), |
| .wr_num_bytes = iovec.capacity, |
| .wr_handles = static_cast<fdf_handle_t*>(arena_handles), |
| .wr_num_handles = args.wr.handles_count, |
| |
| .rd_arena = &rd_arena, |
| .rd_data = args.rd.out_data, |
| .rd_num_bytes = args.rd.out_data_actual_count, |
| .rd_handles = args.rd.out_handles, |
| .rd_num_handles = args.rd.out_handles_actual_count, |
| }; |
| zx_status_t status = fdf_channel_call(handle, 0, ZX_TIME_INFINITE, &fdf_args); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| *rd_view->arena = fdf::Arena(rd_arena); |
| return ZX_OK; |
| } |
| |
| zx_status_t driver_create_waiter(fidl_handle_t handle, async_dispatcher_t* dispatcher, |
| TransportWaitSuccessHandler success_handler, |
| TransportWaitFailureHandler failure_handler, |
| AnyTransportWaiter& any_transport_waiter) { |
| any_transport_waiter.emplace<DriverWaiter>(handle, dispatcher, std::move(success_handler), |
| std::move(failure_handler)); |
| return ZX_OK; |
| } |
| |
| void driver_create_thread_checker(async_dispatcher_t* dispatcher, ThreadingPolicy threading_policy, |
| AnyThreadChecker& any_thread_checker) { |
| class __TA_CAPABILITY("mutex") DriverThreadChecker final : public ThreadChecker { |
| public: |
| explicit DriverThreadChecker(async_dispatcher_t* dispatcher, ThreadingPolicy policy) |
| : ThreadChecker(policy), |
| initial_dispatcher_(fdf_dispatcher_from_async_dispatcher(dispatcher)) { |
| if (policy == ThreadingPolicy::kCreateAndTeardownFromDispatcherThread) { |
| uint32_t options = fdf_dispatcher_get_options(initial_dispatcher_); |
| if (options & FDF_DISPATCHER_OPTION_UNSYNCHRONIZED) { |
| // This error indicates that the user is using a synchronized FIDL |
| // binding (e.g. |fdf::WireClient|) over an unsynchronized dispatcher. |
| // This is not allowed, as it leads to thread safety issues. |
| resumable_panic( |
| "A synchronized fdf_dispatcher_t is required. " |
| "Ensure the fdf_dispatcher_t does not have the " |
| "|FDF_DISPATCHER_OPTION_UNSYNCHRONIZED| option."); |
| } |
| } |
| } |
| |
| // Checks for exclusive access by checking that the current thread is the |
| // same as the constructing thread. |
| void check() const __TA_ACQUIRE() override { |
| if (policy() == ThreadingPolicy::kCreateAndTeardownFromDispatcherThread) { |
| fdf_dispatcher_t* current_dispatcher = fdf_dispatcher_get_current_dispatcher(); |
| if (current_dispatcher == nullptr) { |
| // This error indicates that the user is destroying a synchronized |
| // FIDL binding (e.g. |fdf::WireClient|) on a thread that is not |
| // managed by a driver dispatcher. This is not allowed, as it leads to |
| // thread safety issues. |
| resumable_panic( |
| "Current thread is not managed by a driver dispatcher. " |
| "Ensure binding and teardown occur on a dispatcher managed thread."); |
| return; |
| } |
| if (initial_dispatcher_ != current_dispatcher) { |
| // This error indicates that the user is destroying a synchronized |
| // FIDL binding (e.g. |fdf::WireClient|) on a thread whose dispatcher |
| // is not the same as the one it is bound to. This is not allowed, as |
| // it leads to thread safety issues. |
| resumable_panic( |
| "Currently executing on a different dispatcher than the FIDL binding was bound on. " |
| "Ensure binding and teardown occur from the same dispatcher."); |
| return; |
| } |
| } |
| } |
| |
| // Generates an exception that could be caught in unit testing, then recovered. |
| // |
| // By comparison, `ZX_PANIC` would put the current thread under an infinite loop |
| // of crashing. |
| static void resumable_panic(const char* msg) { |
| fprintf(stderr, "%s\n", msg); |
| fflush(nullptr); |
| // The following logic is similar to `backtrace_request`. |
| // See zircon/system/ulib/backtrace-request/include/lib/backtrace-request/backtrace-request.h |
| #if defined(__aarch64__) |
| __asm__("brk 0"); |
| #elif defined(__x86_64__) |
| __asm__("int3"); |
| #else |
| #error "what machine?" |
| #endif |
| } |
| |
| private: |
| fdf_dispatcher_t* initial_dispatcher_; |
| }; |
| any_thread_checker.emplace<DriverThreadChecker>(dispatcher, threading_policy); |
| } |
| |
| void driver_close(fidl_handle_t handle) { fdf_handle_close(handle); } |
| |
| void driver_close_many(const fidl_handle_t* handles, size_t num_handles) { |
| for (size_t i = 0; i < num_handles; i++) { |
| fdf_handle_close(handles[i]); |
| } |
| } |
| |
| } // namespace |
| |
| const TransportVTable DriverTransport::VTable = { |
| .type = FIDL_TRANSPORT_TYPE_DRIVER, |
| .encoding_configuration = &DriverTransport::EncodingConfiguration, |
| .write = driver_write, |
| .read = driver_read, |
| .call = driver_call, |
| .create_waiter = driver_create_waiter, |
| .create_thread_checker = driver_create_thread_checker, |
| }; |
| |
| zx_status_t DriverWaiter::Begin() { |
| state_.channel_read.emplace( |
| state_.handle, 0 /* options */, |
| [&state = state_](fdf_dispatcher_t* dispatcher, fdf::ChannelRead* channel_read, |
| fdf_status_t status) { |
| if (status != ZX_OK) { |
| fidl::UnbindInfo unbind_info; |
| if (status == ZX_ERR_PEER_CLOSED) { |
| unbind_info = fidl::UnbindInfo::PeerClosed(status); |
| } else { |
| unbind_info = fidl::UnbindInfo::DispatcherError(status); |
| } |
| return state.failure_handler(unbind_info); |
| } |
| |
| fdf::Arena arena; |
| DriverMessageStorageView storage_view{.arena = &arena}; |
| IncomingMessage msg = fidl::MessageRead(fdf::UnownedChannel(state.handle), storage_view); |
| if (!msg.ok()) { |
| return state.failure_handler(fidl::UnbindInfo{msg}); |
| } |
| state.channel_read = std::nullopt; |
| return state.success_handler(msg, &storage_view); |
| }); |
| zx_status_t status = |
| state_.channel_read->Begin(fdf_dispatcher_from_async_dispatcher(state_.dispatcher)); |
| if (status == ZX_ERR_UNAVAILABLE) { |
| // Begin() is called when the dispatcher is shutting down. |
| return ZX_ERR_CANCELED; |
| } |
| return status; |
| } |
| |
| zx_status_t DriverWaiter::Cancel() { |
| fdf_dispatcher_t* dispatcher = fdf_dispatcher_from_async_dispatcher(state_.dispatcher); |
| uint32_t options = fdf_dispatcher_get_options(dispatcher); |
| fdf_status_t status = state_.channel_read->Cancel(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| // When the dispatcher is synchronized, our |ChannelRead| handler won't be |
| // called. When the dispatcher is unsynchronized, our |ChannelRead| handler |
| // will always be called (sometimes with a ZX_OK status and othertimes with a |
| // ZX_ERR_CANCELED status). For the purpose of determining which code should |
| // finish teardown of the |AsyncBinding|, it is as if the cancellation failed. |
| if (options & FDF_DISPATCHER_OPTION_UNSYNCHRONIZED) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| return ZX_OK; |
| } |
| |
| const CodingConfig DriverTransport::EncodingConfiguration = { |
| .max_iovecs_write = 1, |
| .handle_metadata_stride = 0, |
| .close = driver_close, |
| .close_many = driver_close_many, |
| }; |
| |
| } // namespace internal |
| } // namespace fidl |