1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "handle.h"
6
7 #include <algorithm>
8
9 #include "third_party/tonic/dart_binding_macros.h"
10 #include "third_party/tonic/dart_class_library.h"
11
12 using tonic::ToDart;
13
14 namespace zircon {
15 namespace dart {
16
17 IMPLEMENT_WRAPPERTYPEINFO(zircon, Handle);
18
Handle(zx_handle_t handle)19 Handle::Handle(zx_handle_t handle) : handle_(handle) {
20 tonic::DartState* state = tonic::DartState::Current();
21 FML_DCHECK(state);
22 Dart_Handle zircon_lib = Dart_LookupLibrary(ToDart("dart:zircon"));
23 FML_DCHECK(!tonic::LogIfError(zircon_lib));
24
25 Dart_Handle on_wait_completer_type =
26 Dart_GetClass(zircon_lib, ToDart("_OnWaitCompleteClosure"));
27 FML_DCHECK(!tonic::LogIfError(on_wait_completer_type));
28 on_wait_completer_type_.Set(state, on_wait_completer_type);
29
30 Dart_Handle async_lib = Dart_LookupLibrary(ToDart("dart:async"));
31 FML_DCHECK(!tonic::LogIfError(async_lib));
32 async_lib_.Set(state, async_lib);
33
34 Dart_Handle closure_string = ToDart("_closure");
35 FML_DCHECK(!tonic::LogIfError(closure_string));
36 closure_string_.Set(state, closure_string);
37
38 Dart_Handle schedule_microtask_string = ToDart("scheduleMicrotask");
39 FML_DCHECK(!tonic::LogIfError(schedule_microtask_string));
40 schedule_microtask_string_.Set(state, schedule_microtask_string);
41 }
42
~Handle()43 Handle::~Handle() {
44 if (is_valid()) {
45 zx_status_t status = Close();
46 FML_DCHECK(status == ZX_OK);
47 }
48 }
49
Create(zx_handle_t handle)50 fml::RefPtr<Handle> Handle::Create(zx_handle_t handle) {
51 return fml::MakeRefCounted<Handle>(handle);
52 }
53
CreateInvalid()54 Dart_Handle Handle::CreateInvalid() {
55 return ToDart(Create(ZX_HANDLE_INVALID));
56 }
57
ReleaseHandle()58 zx_handle_t Handle::ReleaseHandle() {
59 FML_DCHECK(is_valid());
60
61 zx_handle_t handle = handle_;
62 handle_ = ZX_HANDLE_INVALID;
63 while (waiters_.size()) {
64 // HandleWaiter::Cancel calls Handle::ReleaseWaiter which removes the
65 // HandleWaiter from waiters_.
66 FML_DCHECK(waiters_.back()->is_pending());
67 waiters_.back()->Cancel();
68 }
69
70 FML_DCHECK(!is_valid());
71
72 return handle;
73 }
74
Close()75 zx_status_t Handle::Close() {
76 if (is_valid()) {
77 zx_handle_t handle = ReleaseHandle();
78 return zx_handle_close(handle);
79 }
80 return ZX_ERR_BAD_HANDLE;
81 }
82
AsyncWait(zx_signals_t signals,Dart_Handle callback)83 fml::RefPtr<HandleWaiter> Handle::AsyncWait(zx_signals_t signals,
84 Dart_Handle callback) {
85 if (!is_valid()) {
86 FML_LOG(WARNING) << "Attempt to wait on an invalid handle.";
87 return nullptr;
88 }
89
90 fml::RefPtr<HandleWaiter> waiter =
91 HandleWaiter::Create(this, signals, callback);
92 waiters_.push_back(waiter.get());
93
94 return waiter;
95 }
96
ReleaseWaiter(HandleWaiter * waiter)97 void Handle::ReleaseWaiter(HandleWaiter* waiter) {
98 FML_DCHECK(waiter);
99 auto iter = std::find(waiters_.cbegin(), waiters_.cend(), waiter);
100 FML_DCHECK(iter != waiters_.cend());
101 FML_DCHECK(*iter == waiter);
102 waiters_.erase(iter);
103 }
104
Duplicate(uint32_t rights)105 Dart_Handle Handle::Duplicate(uint32_t rights) {
106 if (!is_valid()) {
107 return ToDart(Create(ZX_HANDLE_INVALID));
108 }
109
110 zx_handle_t out_handle;
111 zx_status_t status = zx_handle_duplicate(handle_, rights, &out_handle);
112 if (status != ZX_OK) {
113 return ToDart(Create(ZX_HANDLE_INVALID));
114 }
115 return ToDart(Create(out_handle));
116 }
117
ScheduleCallback(tonic::DartPersistentValue callback,zx_status_t status,const zx_packet_signal_t * signal)118 void Handle::ScheduleCallback(tonic::DartPersistentValue callback,
119 zx_status_t status,
120 const zx_packet_signal_t* signal) {
121 auto state = callback.dart_state().lock();
122 FML_DCHECK(state);
123 tonic::DartState::Scope scope(state);
124
125 // Make a new _OnWaitCompleteClosure(callback, status, signal->observed).
126 FML_DCHECK(!callback.is_empty());
127 std::vector<Dart_Handle> constructor_args{callback.Release(), ToDart(status),
128 ToDart(signal->observed)};
129 Dart_Handle on_wait_complete_closure =
130 Dart_New(on_wait_completer_type_.Get(), Dart_Null(),
131 constructor_args.size(), constructor_args.data());
132 FML_DCHECK(!tonic::LogIfError(on_wait_complete_closure));
133
134 // The _callback field contains the thunk:
135 // () => callback(status, signal->observed)
136 Dart_Handle closure =
137 Dart_GetField(on_wait_complete_closure, closure_string_.Get());
138 FML_DCHECK(!tonic::LogIfError(closure));
139
140 // Put the thunk on the microtask queue by calling scheduleMicrotask().
141 std::vector<Dart_Handle> sm_args{closure};
142 Dart_Handle sm_result =
143 Dart_Invoke(async_lib_.Get(), schedule_microtask_string_.Get(),
144 sm_args.size(), sm_args.data());
145 FML_DCHECK(!tonic::LogIfError(sm_result));
146 }
147
148 // clang-format: off
149
150 #define FOR_EACH_STATIC_BINDING(V) V(Handle, CreateInvalid)
151
152 #define FOR_EACH_BINDING(V) \
153 V(Handle, handle) \
154 V(Handle, is_valid) \
155 V(Handle, Close) \
156 V(Handle, AsyncWait) \
157 V(Handle, Duplicate)
158
159 // clang-format: on
160
161 // Tonic is missing a comma.
162 #define DART_REGISTER_NATIVE_STATIC_(CLASS, METHOD) \
163 DART_REGISTER_NATIVE_STATIC(CLASS, METHOD),
164
165 FOR_EACH_STATIC_BINDING(DART_NATIVE_CALLBACK_STATIC)
FOR_EACH_BINDING(DART_NATIVE_CALLBACK)166 FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
167
168 void Handle::RegisterNatives(tonic::DartLibraryNatives* natives) {
169 natives->Register({FOR_EACH_STATIC_BINDING(DART_REGISTER_NATIVE_STATIC_)
170 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
171 }
172
173 } // namespace dart
174 } // namespace zircon
175