• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Working with Cleanup Hooks Using Node-API
2
3## Introduction
4
5Node-API provides APIs for adding and removing cleanup hooks, which are called to release resources when the environment exits.
6
7## Basic Concepts
8
9Before using Node-API to add or remove cleanup hooks, understand the following concepts:
10
11- Resource management<br>In ArkTS, you need to manage system resources, such as memory, file handles, and network connections. Properly creating, using, and releasing these resources during the lifecycle of the Node-API module can prevent resource leaks and application breakdown. Resource management usually includes initializing resources, clearing resources when required, and performing necessary operations when clearing resources, such as closing a file or disconnecting from the network.
12- Hook function<br>A hook function is a callback that is automatically executed at the specified time or upon a specific event. When an environment or a process exits, not all the resources can be automatically reclaimed immediately. In the context of a Node-API module, the cleanup hooks are a supplement that ensures release of all the resources occupied.
13
14So far, you've learnt resource management in ArkTS and cleanup hook functions. Read on to learn the Node-API interfaces that you can use to perform resource management with cleanup hooks.
15
16## Available APIs
17
18The following table lists the APIs for using different types of cleanup hooks.
19
20| API| Description|
21| -------- | -------- |
22| napi_add_env_cleanup_hook | Adds an environment cleanup hook function, which will be called when the Node-API environment exits.|
23| napi_remove_env_cleanup_hook | Removes an environment cleanup hook function.|
24| napi_add_async_cleanup_hook | Adds an async cleanup hook function, which will be executed asynchronously when the Node-API process exits.|
25| napi_remove_async_cleanup_hook | Removes an async cleanup hook function.|
26
27## Example
28
29If you are just starting out with Node-API, see [Node-API Development Process](use-napi-process.md). The following demonstrates only the C++ and ArkTS code involved in the APIs for cleanup hooks.
30
31### napi_add_env_cleanup_hook
32
33Call **napi_add_env_cleanup_hook** to add an environment cleanup hook function, which will be executed when the environment exits. This ensures that resources are released before the environment is destroyed.
34
35Note that **napi_add_env_cleanup_hook** does not support binding multiple callbacks to the same **arg**. If **env** is destroyed but the **cleanup** callback has not been executed, you can locate the failed calls with "AddCleanupHook Failed, data cannot register multiple times." on HiLog if the ArkTS runtime [multi-thread check](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-multi-thread-check) is enabled.
36
37### napi_remove_env_cleanup_hook
38
39Call **napi_remove_env_cleanup_hook** to remove the previously added environment cleanup hook function. You may need to use this API when an addon is uninstalled or resources are reallocated.
40
41CPP code:
42
43```cpp
44#include <hilog/log.h>
45#include <string>
46#include "napi/native_api.h"
47// Define the memory struct, including the pointer to the data and the data size.
48typedef struct {
49    char *data;
50    size_t size;
51} Memory;
52// Callback for clearing the external buffer. It is used to release the allocated memory.
53void ExternalFinalize(napi_env env, void *finalize_data, void *finalize_hint)
54{
55    Memory *wrapper = (Memory *)finalize_hint;
56    free(wrapper->data);
57    free(wrapper);
58    OH_LOG_INFO(LOG_APP, "Node-API napi_add_env_cleanup_hook ExternalFinalize");
59}
60// Perform cleanup operations when the environment is closed, for example, clear global variables or other resources.
61static void Cleanup(void *arg)
62{
63    // Perform the cleanup operation.
64    OH_LOG_INFO(LOG_APP, "Node-API napi_add_env_cleanup_hook cleanuped: %{public}d", *(int *)(arg));
65}
66// Create an external buffer and add the environment cleanup hook function.
67static napi_value NapiEnvCleanUpHook(napi_env env, napi_callback_info info)
68{
69    // Allocate memory and copy the string to the memory.
70    std::string str("Hello from Node-API!");
71    Memory *wrapper = (Memory *)malloc(sizeof(Memory));
72    wrapper->data = static_cast<char *>(malloc(str.size()));
73    strcpy(wrapper->data, str.c_str());
74    wrapper->size = str.size();
75    // Create an external buffer object and specify the cleanup callback function.
76    napi_value buffer = nullptr;
77    napi_create_external_buffer(env, wrapper->size, (void *)wrapper->data, ExternalFinalize, wrapper, &buffer);
78    // Use static variables as hook function parameters.
79    static int hookArg = 42;
80    static int hookParameter = 1;
81    // Add a cleanup hook function for releasing resources when the environment exits.
82    napi_status status = napi_add_env_cleanup_hook(env, Cleanup, &hookArg);
83    if (status != napi_ok) {
84        napi_throw_error(env, nullptr, "Test Node-API napi_add_env_cleanup_hook failed.");
85        return nullptr;
86    }
87    // Add the environment cleanup hook function. The hook function is not removed here to make it called to simulate some cleanup operations, such as releasing resources and closing files, when the Java environment is destroyed.
88    status = napi_add_env_cleanup_hook(env, Cleanup, &hookParameter);
89    if (status != napi_ok) {
90        napi_throw_error(env, nullptr, "Test Node-API napi_add_env_cleanup_hook failed.");
91        return nullptr;
92    }
93    // Remove the environment cleanup hook function immediately.
94    // Generally, use this API when the resource associated with the hook must be released.
95    napi_remove_env_cleanup_hook(env, Cleanup, &hookArg);
96    // Return the created external buffer object.
97    return buffer;
98}
99```
100
101API declaration:
102
103```ts
104// index.d.ts
105export const napiEnvCleanUpHook: () => Object | void;
106```
107
108ArkTS code:
109
110```ts
111// index.ets
112import hilog from '@ohos.hilog';
113import worker from '@ohos.worker';
114
115let wk = new worker.ThreadWorker("entry/ets/workers/worker.ts");
116// Send a message to the worker thread.
117wk.postMessage("test NapiEnvCleanUpHook");
118// Process the message from the worker thread.
119wk.onmessage = (message) => {
120  hilog.info(0x0000, 'testTag', 'Test Node-API message from worker: %{public}s', JSON.stringify(message));
121  wk.terminate();
122};
123```
124
125```ts
126// worker.ts
127import hilog from '@ohos.hilog';
128import worker from '@ohos.worker';
129import testNapi from 'libentry.so';
130
131let parent = worker.workerPort;
132// Process messages from the main thread.
133parent.onmessage = (message) => {
134  hilog.info(0x0000, 'testTag', 'Test Node-API message from main thread: %{public}s', JSON.stringify(message));
135  // Send a message to the main thread.
136  parent.postMessage('Test Node-API worker:' + testNapi.napiEnvCleanUpHook());
137}
138```
139
140For details about the worker development, see [Worker Introduction](../arkts-utils/worker-introduction.md).
141
142### napi_add_async_cleanup_hook
143
144Call **napi_add_async_cleanup_hook** to add an async cleanup hook function, which will be executed asynchronously when the environment exits. Unlike a sync hook, an async hook allows a longer operation without blocking the process exit.
145
146### napi_remove_async_cleanup_hook
147
148Call **napi_remove_async_cleanup_hook** to remove an async cleanup hook function that is no longer required.
149
150CPP code:
151
152```cpp
153#include <malloc.h>
154#include <string.h>
155#include "napi/native_api.h"
156#include "uv.h"
157
158// Async operation content.
159typedef struct {
160    napi_env env;
161    void *testData;
162    uv_async_s asyncUv;
163    napi_async_cleanup_hook_handle cleanupHandle;
164} AsyncContent;
165// Delete the async work object and remove the hook function.
166static void FinalizeWork(uv_handle_s *handle)
167{
168    AsyncContent *asyncData = reinterpret_cast<AsyncContent *>(handle->data);
169    // Remove the hook function from the environment when it is no longer required.
170    napi_status result = napi_remove_async_cleanup_hook(asyncData->cleanupHandle);
171    if (result != napi_ok) {
172        napi_throw_error(asyncData->env, nullptr, "Test Node-API napi_remove_async_cleanup_hook failed");
173    }
174    // Release AsyncContent.
175    free(asyncData);
176}
177// Asynchronously clear the environment.
178static void AsyncWork(uv_async_s *async)
179{
180    // Perform cleanup operations, for example, release the dynamically allocated memory.
181    AsyncContent *asyncData = reinterpret_cast<AsyncContent *>(async->data);
182    if (asyncData->testData != nullptr) {
183        free(asyncData->testData);
184        asyncData->testData = nullptr;
185    }
186    // Close the libuv handle and trigger the FinalizeWork callback to clear the handle.
187    uv_close((uv_handle_s *)async, FinalizeWork);
188}
189// Create and trigger an async cleanup operation in an event loop.
190static void AsyncCleanup(napi_async_cleanup_hook_handle handle, void *info)
191{
192    AsyncContent *data = reinterpret_cast<AsyncContent *>(info);
193    // Obtain a libuv loop instance and initialize a handle for subsequent async work.
194    uv_loop_s *uvLoop;
195    napi_get_uv_event_loop(data->env, &uvLoop);
196    uv_async_init(uvLoop, &data->asyncUv, AsyncWork);
197
198    data->asyncUv.data = data;
199    data->cleanupHandle = handle;
200    // Send an async signal to trigger the AsyncWork function to perform cleanup.
201    uv_async_send(&data->asyncUv);
202}
203
204static napi_value NapiAsyncCleanUpHook(napi_env env, napi_callback_info info)
205{
206    // Allocate the AsyncContent memory.
207    AsyncContent *data = reinterpret_cast<AsyncContent *>(malloc(sizeof(AsyncContent)));
208    data->env = env;
209    data->cleanupHandle = nullptr;
210    // Allocate memory and copy string data.
211    const char *testDataStr = "TestNapiAsyncCleanUpHook";
212    data->testData = strdup(testDataStr);
213    if (data->testData == nullptr) {
214        napi_throw_error(env, nullptr, "Test Node-API data->testData is nullptr");
215    }
216    // Add an async cleanup hook function.
217    napi_status status = napi_add_async_cleanup_hook(env, AsyncCleanup, data, &data->cleanupHandle);
218    if (status != napi_ok) {
219        napi_throw_error(env, nullptr, "Test Node-API napi_add_async_cleanup_hook failed");
220    }
221    napi_value result = nullptr;
222    napi_get_boolean(env, true, &result);
223    return result;
224}
225```
226
227Since the uv.h library is used, add the following configuration to the CMakeLists file:
228```text
229// CMakeLists.txt
230target_link_libraries(entry PUBLIC libuv.so)
231```
232
233API declaration:
234
235```ts
236// index.d.ts
237export const napiAsyncCleanUpHook: () => boolean | void;
238```
239
240ArkTS code:
241
242```ts
243import hilog from '@ohos.hilog';
244import testNapi from 'libentry.so';
245try {
246  hilog.info(0x0000, 'testTag', 'Test Node-API napi_add_async_cleanup_hook: %{public}s', testNapi.napiAsyncCleanUpHook());
247} catch (error) {
248  hilog.error(0x0000, 'testTag', 'Test Node-API napi_add_async_cleanup_hook error.message: %{public}s', error.message);
249}
250```
251
252To print logs in the native CPP, add the following information to the **CMakeLists.txt** file and add the header file by using **#include "hilog/log.h"**.
253
254```text
255// CMakeLists.txt
256add_definitions( "-DLOG_DOMAIN=0xd0d0" )
257add_definitions( "-DLOG_TAG=\"testTag\"" )
258target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
259```
260