• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "PromiseBase.h"
17 
18 #include <meta/api/make_callback.h>
19 #include <meta/interface/intf_task_queue_registry.h>
20 #include <napi_api.h>
21 
22 static constexpr BASE_NS::Uid JS_RELEASE_THREAD { "3784fa96-b25b-4e9c-bbf1-e897d36f73af" };
23 
PromiseBase(napi_env env)24 PromiseBase::PromiseBase(napi_env env) : env_(env)
25 {
26     CreateJsPromise();
27     CreateSettleFunc();
28 }
29 
~PromiseBase()30 PromiseBase::~PromiseBase() {}
31 
ToNapiValue() const32 napi_value PromiseBase::ToNapiValue() const
33 {
34     return promise_;
35 }
36 
SettleLater()37 void PromiseBase::SettleLater()
38 {
39     auto task = META_NS::MakeCallback<META_NS::ITaskQueueTask>(BASE_NS::move([this]() {
40         napi_call_threadsafe_function(
41             settleLaterFunc_, nullptr, napi_threadsafe_function_call_mode::napi_tsfn_blocking);
42         return false;
43     }));
44     // An extra task in JS_RELEASE_THREAD to trigger this is required to work around an issue
45     // where SettleLater is called *in* an eventhandler.
46     // There are rare cases where napi_release_threadsafe_function waits for threadsafe function completion
47     // and the "JS function" is waiting for the engine thread (which is blocked releasing the function)
48     META_NS::GetTaskQueueRegistry().GetTaskQueue(JS_RELEASE_THREAD)->AddTask(task);
49 }
50 
SettleNow()51 void PromiseBase::SettleNow()
52 {
53     if (SetResult()) {
54         Resolve();
55     } else {
56         Reject();
57     }
58     ReleaseSettleFunc();
59 }
60 
Reject()61 void PromiseBase::Reject()
62 {
63     if (!result_) {
64         napi_get_undefined(env_, &result_);
65     }
66     napi_reject_deferred(env_, deferred_, result_);
67     ReleaseSettleFunc();
68 }
69 
Resolve()70 void PromiseBase::Resolve()
71 {
72     if (!result_) {
73         napi_get_undefined(env_, &result_);
74     }
75     napi_resolve_deferred(env_, deferred_, result_);
76 }
77 
CreateJsPromise()78 void PromiseBase::CreateJsPromise()
79 {
80     auto status = napi_create_promise(env_, &deferred_, &promise_);
81     if (status != napi_ok) {
82         CORE_LOG_F("Failed to create promise");
83         Abort();
84     }
85 }
86 
CreateSettleFunc()87 void PromiseBase::CreateSettleFunc()
88 {
89     napi_value name;
90     napi_create_string_latin1(env_, "async_promise_settle_func", NAPI_AUTO_LENGTH, &name);
91     auto settleContext = this;
92     auto finalizerDataArg = this;
93     auto status = napi_create_threadsafe_function(
94         env_, nullptr, nullptr, name, 1, 1, finalizerDataArg, FinalizeCB, settleContext, SettleCB, &settleLaterFunc_);
95     if (status != napi_ok) {
96         CORE_LOG_F("Failed to create a threadsafe JS function");
97         Abort();
98         return;
99     }
100     napi_ref_threadsafe_function(env_, settleLaterFunc_);
101 }
102 
ReleaseSettleFunc()103 void PromiseBase::ReleaseSettleFunc()
104 {
105     // The finalizer of the settle later function deletes this promise object.
106     napi_unref_threadsafe_function(env_, settleLaterFunc_);
107     napi_release_threadsafe_function(settleLaterFunc_, napi_threadsafe_function_release_mode::napi_tsfn_release);
108 }
109 
Abort()110 void PromiseBase::Abort()
111 {
112     const napi_extended_error_info* errorInfo { nullptr };
113     napi_get_last_error_info(env_, &errorInfo);
114     auto originalMessage = errorInfo->error_message;
115     bool exceptionPending { false };
116     napi_is_exception_pending(env_, &exceptionPending);
117     if (exceptionPending) {
118         // Not allowed to call most napi functions, so can't clean up.
119         deferred_ = nullptr;
120         promise_ = nullptr;
121     } else {
122         napi_get_undefined(env_, &result_);
123         if (deferred_) {
124             napi_reject_deferred(env_, deferred_, result_);
125         }
126         auto message = (originalMessage != nullptr) ? originalMessage : "Error initializing Promise";
127         napi_throw_error(env_, nullptr, message);
128     }
129     settleLaterFunc_ = nullptr;
130 }
131 
SettleCB(napi_env env,napi_value js_callback,void * context,void * inData)132 void PromiseBase::SettleCB(napi_env env, napi_value js_callback, void* context, void* inData)
133 {
134     // This is run in the JS thread, so be careful with the calls to engine.
135     PromiseBase* self = (PromiseBase*)context;
136     self->SettleNow();
137 }
138 
FinalizeCB(napi_env env,void * finalize_data,void * finalize_hint)139 void PromiseBase::FinalizeCB(napi_env env, void* finalize_data, void* finalize_hint)
140 {
141     PromiseBase* self = (PromiseBase*)finalize_data;
142     delete self;
143 }
144