| // Copyright 2017 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. |
| |
| #include "dart-pkg/zircon/sdk_ext/handle.h" |
| |
| #include <algorithm> |
| |
| #include "third_party/tonic/dart_binding_macros.h" |
| #include "third_party/tonic/dart_class_library.h" |
| |
| using tonic::ToDart; |
| |
| namespace zircon { |
| namespace dart { |
| |
| IMPLEMENT_WRAPPERTYPEINFO(zircon, Handle); |
| |
| Handle::Handle(zx_handle_t handle) : handle_(handle) { |
| tonic::DartState* state = tonic::DartState::Current(); |
| FXL_DCHECK(state); |
| Dart_Handle zircon_lib = Dart_LookupLibrary(ToDart("dart:zircon")); |
| FXL_DCHECK(!tonic::LogIfError(zircon_lib)); |
| |
| Dart_Handle on_wait_completer_type = Dart_GetClass( |
| zircon_lib, ToDart("_OnWaitCompleteClosure")); |
| FXL_DCHECK(!tonic::LogIfError(on_wait_completer_type)); |
| on_wait_completer_type_.Set(state, on_wait_completer_type); |
| |
| Dart_Handle async_lib = Dart_LookupLibrary(ToDart("dart:async")); |
| FXL_DCHECK(!tonic::LogIfError(async_lib)); |
| async_lib_.Set(state, async_lib); |
| |
| Dart_Handle closure_string = ToDart("_closure"); |
| FXL_DCHECK(!tonic::LogIfError(closure_string)); |
| closure_string_.Set(state, closure_string); |
| |
| Dart_Handle schedule_microtask_string = ToDart("scheduleMicrotask"); |
| FXL_DCHECK(!tonic::LogIfError(schedule_microtask_string)); |
| schedule_microtask_string_.Set(state, schedule_microtask_string); |
| } |
| |
| Handle::~Handle() { |
| if (is_valid()) { |
| zx_status_t status = Close(); |
| FXL_DCHECK(status == ZX_OK); |
| } |
| } |
| |
| fxl::RefPtr<Handle> Handle::Create(zx_handle_t handle) { |
| return fxl::MakeRefCounted<Handle>(handle); |
| } |
| |
| Dart_Handle Handle::CreateInvalid() { |
| return ToDart(Create(ZX_HANDLE_INVALID)); |
| } |
| |
| zx_handle_t Handle::ReleaseHandle() { |
| FXL_DCHECK(is_valid()); |
| |
| zx_handle_t handle = handle_; |
| handle_ = ZX_HANDLE_INVALID; |
| while (waiters_.size()) { |
| // HandleWaiter::Cancel calls Handle::ReleaseWaiter which removes the |
| // HandleWaiter from waiters_. |
| FXL_DCHECK(waiters_.back()->is_pending()); |
| waiters_.back()->Cancel(); |
| } |
| |
| FXL_DCHECK(!is_valid()); |
| |
| return handle; |
| } |
| |
| zx_status_t Handle::Close() { |
| if (is_valid()) { |
| zx_handle_t handle = ReleaseHandle(); |
| return zx_handle_close(handle); |
| } |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| fxl::RefPtr<HandleWaiter> Handle::AsyncWait(zx_signals_t signals, |
| Dart_Handle callback) { |
| if (!is_valid()) { |
| FXL_LOG(WARNING) << "Attempt to wait on an invalid handle."; |
| return nullptr; |
| } |
| |
| fxl::RefPtr<HandleWaiter> waiter = |
| HandleWaiter::Create(this, signals, callback); |
| waiters_.push_back(waiter.get()); |
| |
| return waiter; |
| } |
| |
| void Handle::ReleaseWaiter(HandleWaiter* waiter) { |
| FXL_DCHECK(waiter); |
| auto iter = std::find(waiters_.cbegin(), waiters_.cend(), waiter); |
| FXL_DCHECK(iter != waiters_.cend()); |
| FXL_DCHECK(*iter == waiter); |
| waiters_.erase(iter); |
| } |
| |
| Dart_Handle Handle::Duplicate(uint32_t rights) { |
| if (!is_valid()) { |
| return ToDart(Create(ZX_HANDLE_INVALID)); |
| } |
| |
| zx_handle_t out_handle; |
| zx_status_t status = zx_handle_duplicate(handle_, rights, &out_handle); |
| if (status != ZX_OK) { |
| return ToDart(Create(ZX_HANDLE_INVALID)); |
| } |
| return ToDart(Create(out_handle)); |
| } |
| |
| void Handle::ScheduleCallback(tonic::DartPersistentValue callback, |
| zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| auto state = callback.dart_state().lock(); |
| FXL_DCHECK(state); |
| tonic::DartState::Scope scope(state); |
| |
| // Make a new _OnWaitCompleteClosure(callback, status, signal->observed). |
| FXL_DCHECK(!callback.is_empty()); |
| std::vector<Dart_Handle> constructor_args{ |
| callback.Release(), ToDart(status), ToDart(signal->observed)}; |
| Dart_Handle on_wait_complete_closure = Dart_New(on_wait_completer_type_.Get(), |
| Dart_Null(), constructor_args.size(), constructor_args.data()); |
| FXL_DCHECK(!tonic::LogIfError(on_wait_complete_closure)); |
| |
| // The _callback field contains the thunk: |
| // () => callback(status, signal->observed) |
| Dart_Handle closure = Dart_GetField(on_wait_complete_closure, |
| closure_string_.Get()); |
| FXL_DCHECK(!tonic::LogIfError(closure)); |
| |
| // Put the thunk on the microtask queue by calling scheduleMicrotask(). |
| std::vector<Dart_Handle> sm_args{closure}; |
| Dart_Handle sm_result = Dart_Invoke(async_lib_.Get(), |
| schedule_microtask_string_.Get(), sm_args.size(), sm_args.data()); |
| FXL_DCHECK(!tonic::LogIfError(sm_result)); |
| } |
| |
| // clang-format: off |
| |
| #define FOR_EACH_STATIC_BINDING(V) V(Handle, CreateInvalid) |
| |
| #define FOR_EACH_BINDING(V) \ |
| V(Handle, handle) \ |
| V(Handle, is_valid) \ |
| V(Handle, Close) \ |
| V(Handle, AsyncWait) \ |
| V(Handle, Duplicate) |
| |
| // clang-format: on |
| |
| // Tonic is missing a comma. |
| #define DART_REGISTER_NATIVE_STATIC_(CLASS, METHOD) \ |
| DART_REGISTER_NATIVE_STATIC(CLASS, METHOD), |
| |
| FOR_EACH_STATIC_BINDING(DART_NATIVE_CALLBACK_STATIC) |
| FOR_EACH_BINDING(DART_NATIVE_CALLBACK) |
| |
| void Handle::RegisterNatives(tonic::DartLibraryNatives* natives) { |
| natives->Register({FOR_EACH_STATIC_BINDING(DART_REGISTER_NATIVE_STATIC_) |
| FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); |
| } |
| |
| } // namespace dart |
| } // namespace zircon |