1/*
2 * Copyright (c) 2022 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 <thread>
17
18#include "napi/native_api.h"
19#include "napi/native_node_api.h"
20
21#include "uv.h"
22
23struct CallbackContext {
24    napi_env env = nullptr;
25    napi_ref callbackRef = nullptr;
26    int retData = 0;
27};
28
29void callbackTest(CallbackContext* context)
30{
31    uv_loop_s* loop = nullptr;
32    napi_get_uv_event_loop(context->env, &loop);
33
34    uv_work_t* work = new uv_work_t;
35    context->retData = 1;
36    work->data = (void*)context;
37
38    uv_queue_work(
39        loop, work, [](uv_work_t* work) {},
40        // using callback function back to JS thread
41        [](uv_work_t* work, int status) {
42            CallbackContext* context = (CallbackContext*)work->data;
43            napi_handle_scope scope = nullptr;
44            napi_open_handle_scope(context->env, &scope);
45            if (scope == nullptr) {
46                return;
47            }
48
49            napi_value callback = nullptr;
50            napi_get_reference_value(context->env, context->callbackRef, &callback);
51            napi_value retArg;
52            napi_create_int32(context->env, context->retData, &retArg);
53            napi_value ret;
54            napi_call_function(context->env, nullptr, callback, 1, &retArg, &ret);
55            napi_delete_reference(context->env, context->callbackRef);
56
57            napi_close_handle_scope(context->env, scope);
58
59            if (work != nullptr) {
60                delete work;
61            }
62
63            delete context;
64        }
65    );
66}
67
68static napi_value JSTest(napi_env env, napi_callback_info info)
69{
70    size_t argc = 1;
71    napi_value argv[1] = { 0 };
72    napi_value thisVar = nullptr;
73    void* data = nullptr;
74    napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
75
76    napi_valuetype valueType = napi_undefined;
77    napi_typeof(env, argv[0], &valueType);
78    if (valueType != napi_function) {
79        return nullptr;
80    }
81    auto asyncContext = new CallbackContext();
82    asyncContext->env = env;
83    napi_create_reference(env, argv[0], 1, &asyncContext->callbackRef);
84    // using callback function on other thread
85    std::thread testThread(callbackTest, asyncContext);
86    testThread.detach();
87
88    return nullptr;
89}
90
91/***********************************************
92 * Module export and register
93 ***********************************************/
94static napi_value CallbackExport(napi_env env, napi_value exports)
95{
96    static napi_property_descriptor desc[] = {
97        DECLARE_NAPI_FUNCTION("test", JSTest)
98    };
99    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
100    return exports;
101}
102
103// callback module define
104static napi_module callbackModule = {
105    .nm_version = 1,
106    .nm_flags = 0,
107    .nm_filename = nullptr,
108    .nm_register_func = CallbackExport,
109    .nm_modname = "callback",
110    .nm_priv = ((void*)0),
111    .reserved = { 0 },
112};
113
114// callback module register
115extern "C" __attribute__((constructor)) void CallbackTestRegister()
116{
117    napi_module_register(&callbackModule);
118}