◐ Shell
clean mode source ↗

node-api: execute tsfn finalizer after queue drains when aborted · nodejs/node@87443b4

1+

#include <js_native_api.h>

2+

#include <node_api.h>

3+

#include <node_api_types.h>

4+5+

#include <assert.h>

6+

#include <cstdio>

7+

#include <cstdlib>

8+

#include <type_traits>

9+

#include <utility>

10+11+

template <typename R, auto func, typename... Args>

12+

inline auto call(const char* name, Args&&... args) -> R {

13+

napi_status status;

14+

if constexpr (std::is_same_v<R, void>) {

15+

status = func(std::forward<Args>(args)...);

16+

if (status == napi_ok) {

17+

return;

18+

}

19+

} else {

20+

R ret;

21+

status = func(std::forward<Args>(args)..., &ret);

22+

if (status == napi_ok) {

23+

return ret;

24+

}

25+

}

26+

std::fprintf(stderr, "%s: %d\n", name, status);

27+

std::abort();

28+

}

29+30+

#define NAPI_CALL(ret_type, func, ...) \

31+

call<ret_type, func>(#func, ##__VA_ARGS__)

32+33+

class Context {

34+

public:

35+

enum class State { kCreated, kCalled } state = State::kCreated;

36+

};

37+38+

void tsfn_callback(napi_env env,

39+

napi_value js_cb,

40+

void* ctx_p,

41+

void* /* data */) {

42+

auto ctx = static_cast<Context*>(ctx_p);

43+

assert(ctx->state == Context::State::kCreated);

44+

ctx->state = Context::State::kCalled;

45+

}

46+47+

void tsfn_finalize(napi_env env,

48+

void* /* finalize_data */,

49+

void* finalize_hint) {

50+

auto ctx = static_cast<Context*>(finalize_hint);

51+

assert(ctx->state == Context::State::kCalled);

52+

delete ctx;

53+

}

54+55+

auto run(napi_env env, napi_callback_info info) -> napi_value {

56+

auto global = NAPI_CALL(napi_value, napi_get_global, env);

57+

auto undefined = NAPI_CALL(napi_value, napi_get_undefined, env);

58+

auto ctx = new Context();

59+

auto tsfn = NAPI_CALL(napi_threadsafe_function,

60+

napi_create_threadsafe_function,

61+

env,

62+

nullptr,

63+

global,

64+

undefined,

65+

0,

66+

1 /* initial_thread_count */,

67+

nullptr,

68+

tsfn_finalize,

69+

ctx,

70+

tsfn_callback);

71+72+

NAPI_CALL(

73+

void, napi_call_threadsafe_function, tsfn, nullptr, napi_tsfn_blocking);

74+75+

NAPI_CALL(void, napi_unref_threadsafe_function, env, tsfn);

76+77+

NAPI_CALL(void,

78+

napi_release_threadsafe_function,

79+

tsfn,

80+

napi_threadsafe_function_release_mode::napi_tsfn_abort);

81+

return NAPI_CALL(napi_value, napi_get_undefined, env);

82+

}

83+84+

napi_value init(napi_env env, napi_value exports) {

85+

return NAPI_CALL(

86+

napi_value, napi_create_function, env, nullptr, 0, run, nullptr);

87+

}

88+89+

NAPI_MODULE(NODE_GYP_MODULE_NAME, init)