Skip to content

Commit 53c2dab

Browse files
node-api: add unit test
1 parent 74717cb commit 53c2dab

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <js_native_api.h>
2+
#include <node_api.h>
3+
#include <node_api_types.h>
4+
5+
#include <cstdio>
6+
#include <cstdlib>
7+
#include <memory>
8+
#include <mutex>
9+
#include <shared_mutex>
10+
#include <thread>
11+
#include <type_traits>
12+
#include <utility>
13+
#include <vector>
14+
15+
template <typename R, auto func, typename... Args>
16+
inline auto call(const char *name, Args &&...args) -> R {
17+
napi_status status;
18+
if constexpr (std::is_same_v<R, void>) {
19+
status = func(std::forward<Args>(args)...);
20+
if (status == napi_ok) {
21+
return;
22+
}
23+
} else {
24+
R ret;
25+
status = func(std::forward<Args>(args)..., &ret);
26+
if (status == napi_ok) {
27+
return ret;
28+
}
29+
}
30+
std::fprintf(stderr, "%s: %d\n", name, status);
31+
std::abort();
32+
}
33+
34+
#define NAPI_CALL(ret_type, func, ...) \
35+
call<ret_type, func>(#func, ##__VA_ARGS__)
36+
37+
void thread_func(napi_threadsafe_function tsfn) {
38+
fprintf(stderr, "thread_func: starting\n");
39+
auto status =
40+
napi_call_threadsafe_function(tsfn, nullptr, napi_tsfn_blocking);
41+
while (status == napi_ok) {
42+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
43+
status = napi_call_threadsafe_function(tsfn, nullptr, napi_tsfn_blocking);
44+
}
45+
fprintf(stderr, "thread_func: Got status %d, exiting...\n", status);
46+
}
47+
48+
void tsfn_callback(napi_env env, napi_value js_cb, void *ctx, void *data) {
49+
if (env == nullptr) {
50+
fprintf(stderr, "tsfn_callback: env=%p\n", env);
51+
}
52+
}
53+
54+
void tsfn_finalize(napi_env env, void *finalize_data, void *finalize_hint) {
55+
fprintf(stderr, "tsfn_finalize: env=%p\n", env);
56+
}
57+
58+
std::vector<std::jthread> threads;
59+
60+
auto run(napi_env env, napi_callback_info info) -> napi_value {
61+
auto global = NAPI_CALL(napi_value, napi_get_global, env);
62+
auto undefined = NAPI_CALL(napi_value, napi_get_undefined, env);
63+
auto n_threads = 32;
64+
auto tsfn =
65+
NAPI_CALL(napi_threadsafe_function, napi_create_threadsafe_function, env,
66+
nullptr, global, undefined, 0, n_threads, nullptr,
67+
tsfn_finalize, nullptr, tsfn_callback);
68+
for (auto i = 0; i < n_threads; ++i) {
69+
threads.emplace_back([tsfn] { thread_func(tsfn); });
70+
}
71+
NAPI_CALL(void, napi_unref_threadsafe_function, env, tsfn);
72+
return NAPI_CALL(napi_value, napi_get_undefined, env);
73+
}
74+
75+
napi_value init(napi_env env, napi_value exports) {
76+
return NAPI_CALL(napi_value, napi_create_function, env, nullptr, 0,
77+
run, nullptr);
78+
}
79+
80+
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"targets": [
3+
{
4+
"target_name": "binding",
5+
"sources": ["binding.cc"],
6+
"cflags_cc": ["--std=c++20"],
7+
'cflags!': [ '-fno-exceptions', '-fno-rtti' ],
8+
'cflags_cc!': [ '-fno-exceptions', '-fno-rtti' ],
9+
}
10+
]
11+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
const common = require('../../common');
4+
const process = require('process')
5+
const assert = require('assert');
6+
const { fork } = require('child_process');
7+
const binding = require(`./build/${common.buildType}/binding`);
8+
9+
if (process.argv[2] === 'child') {
10+
binding();
11+
setTimeout(() => {}, 100);
12+
} else {
13+
const child = fork(__filename, ['child']);
14+
child.on('close', (code) => {
15+
assert.strictEqual(code, 0);
16+
});
17+
}

0 commit comments

Comments
 (0)