1 /*
2 * Copyright (c) 2021 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 <functional>
17 #include <vector>
18 #include "native_parameters_js.h"
19 #include "uv.h"
20
21 using namespace OHOS::system;
22 static constexpr int ARGC_NUMBER = 2;
23 static constexpr int BUF_LENGTH = 128;
24
25 static napi_ref g_paramWatchRef;
26
27 using ParamAsyncContext = struct ParamAsyncContext {
28 napi_env env = nullptr;
29 napi_async_work work = nullptr;
30
31 char key[BUF_LENGTH] = { 0 };
32 size_t keyLen = 0;
33 char value[BUF_LENGTH] = { 0 };
34 size_t valueLen = 0;
35 int32_t timeout = 0;
36 napi_deferred deferred = nullptr;
37 napi_ref callbackRef = nullptr;
38
39 int status = -1;
40 std::string getValue;
41 };
42
43 using ParamWatcher = struct ParamWatcher {
44 napi_env env = nullptr;
45 napi_ref thisVarRef = nullptr;
46 char keyPrefix[BUF_LENGTH] = { 0 };
47 size_t keyLen = 0;
48 bool notifySwitch = false;
49 bool startWatch = false;
50 std::mutex mutex {};
51 napi_ref currCallbackRef = nullptr;
52 std::map<uint32_t, napi_ref> callbackReferences {};
53 };
54
55 using ParamWatcherWork = struct ParamWatcherWork {
56 napi_async_work work = nullptr;
57 ParamWatcher *watcher = nullptr;
58 bool startWatch = false;
59 };
60
61 using ParamChangeValue = struct ParamChangeValue {
62 uv_work_t work;
63 ParamWatcher *watcher;
64 std::string key;
65 std::string value;
66 };
67
68 using ParamAsyncContextPtr = ParamAsyncContext *;
69 using ParamWatcherPtr = ParamWatcher *;
70
NapiGetNull(napi_env env)71 static napi_value NapiGetNull(napi_env env)
72 {
73 napi_value result = 0;
74 napi_get_null(env, &result);
75 return result;
76 }
77
GetNapiValue(napi_env env,int val)78 static napi_value GetNapiValue(napi_env env, int val)
79 {
80 napi_value result = nullptr;
81 napi_create_int32(env, val, &result);
82 return result;
83 }
84
GetParamValue(napi_env env,napi_value arg,napi_valuetype valueType,char * buffer,size_t & buffLen)85 static int GetParamValue(napi_env env, napi_value arg, napi_valuetype valueType, char *buffer, size_t &buffLen)
86 {
87 napi_valuetype type = napi_null;
88 napi_typeof(env, arg, &type);
89 PARAM_JS_CHECK(type == valueType, return -1, "Invalid type %d %d", type, valueType);
90 napi_status status = napi_ok;
91 if (valueType == napi_string) {
92 status = napi_get_value_string_utf8(env, arg, buffer, buffLen, &buffLen);
93 } else if (valueType == napi_number) {
94 status = napi_get_value_int32(env, arg, reinterpret_cast<int *>(buffer));
95 }
96 return status;
97 }
98
WaitCallbackWork(napi_env env,ParamAsyncContextPtr asyncContext)99 static void WaitCallbackWork(napi_env env, ParamAsyncContextPtr asyncContext)
100 {
101 napi_value resource = nullptr;
102 napi_create_string_utf8(env, "JSStartupGet", NAPI_AUTO_LENGTH, &resource);
103 napi_create_async_work(
104 env, nullptr, resource,
105 [](napi_env env, void *data) {
106 ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data);
107 asyncContext->status = WaitParameter(asyncContext->key, asyncContext->value, asyncContext->timeout);
108 PARAM_JS_LOGV("JSApp Wait status: %d, key: %s", asyncContext->status, asyncContext->key);
109 },
110 [](napi_env env, napi_status status, void *data) {
111 ParamAsyncContext *asyncContext = reinterpret_cast<ParamAsyncContext *>(data);
112 napi_value result[ARGC_NUMBER] = { 0 };
113 napi_value message = nullptr;
114 napi_create_object(env, &result[0]);
115 napi_create_int32(env, asyncContext->status, &message);
116 napi_set_named_property(env, result[0], "code", message);
117 napi_get_undefined(env, &result[1]); // only one param
118
119 PARAM_JS_LOGV("JSApp Wait status: %d, key: %s ", asyncContext->status, asyncContext->key);
120 if (asyncContext->deferred) {
121 if (asyncContext->status == 0) {
122 napi_resolve_deferred(env, asyncContext->deferred, result[1]);
123 } else {
124 napi_reject_deferred(env, asyncContext->deferred, result[0]);
125 }
126 } else {
127 napi_value callbackRef = nullptr;
128 napi_value callResult = nullptr;
129 napi_status status = napi_get_reference_value(env, asyncContext->callbackRef, &callbackRef);
130 PARAM_JS_CHECK(status == 0 && callbackRef != nullptr, return, "Failed to get reference ");
131 napi_value undefined;
132 napi_get_undefined(env, &undefined);
133 napi_call_function(env, undefined, callbackRef, ARGC_NUMBER, result, &callResult);
134 napi_delete_reference(env, asyncContext->callbackRef);
135 }
136 napi_delete_async_work(env, asyncContext->work);
137 delete asyncContext;
138 },
139 reinterpret_cast<void *>(asyncContext), &asyncContext->work);
140 napi_queue_async_work(env, asyncContext->work);
141 }
142
ParamWait(napi_env env,napi_callback_info info)143 napi_value ParamWait(napi_env env, napi_callback_info info)
144 {
145 constexpr int PARAM_TIMEOUT_INDEX = 2;
146 constexpr int ARGC_THREE_NUMBER = 3;
147 size_t argc = ARGC_THREE_NUMBER + 1;
148 napi_value argv[ARGC_THREE_NUMBER + 1];
149 napi_value thisVar = nullptr;
150 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
151 PARAM_JS_CHECK(status == napi_ok, return GetNapiValue(env, status), "Failed to get cb info");
152 PARAM_JS_CHECK(argc >= ARGC_THREE_NUMBER, return GetNapiValue(env, status), "Failed to get argc");
153
154 ParamAsyncContextPtr asyncContext = new ParamAsyncContext();
155 PARAM_JS_CHECK(asyncContext != nullptr, return GetNapiValue(env, status), "Failed to create context");
156 asyncContext->env = env;
157
158 // get param key
159 asyncContext->keyLen = BUF_LENGTH - 1;
160 asyncContext->valueLen = BUF_LENGTH - 1;
161 size_t len = sizeof(asyncContext->timeout);
162 int ret = GetParamValue(env, argv[0], napi_string, asyncContext->key, asyncContext->keyLen);
163 PARAM_JS_CHECK(ret == 0, delete asyncContext;
164 return GetNapiValue(env, ret), "Invalid param for wait");
165 ret = GetParamValue(env, argv[1], napi_string, asyncContext->value, asyncContext->valueLen);
166 PARAM_JS_CHECK(ret == 0, delete asyncContext;
167 return GetNapiValue(env, ret), "Invalid param for wait");
168 ret = GetParamValue(env, argv[PARAM_TIMEOUT_INDEX], napi_number, (char *)&asyncContext->timeout, len);
169 PARAM_JS_CHECK(ret == 0, delete asyncContext;
170 return GetNapiValue(env, ret), "Invalid param for wait");
171 if (argc > ARGC_THREE_NUMBER) {
172 napi_valuetype valueType = napi_null;
173 napi_typeof(env, argv[ARGC_THREE_NUMBER], &valueType);
174 PARAM_JS_CHECK(valueType == napi_function, delete asyncContext;
175 return GetNapiValue(env, ret), "Invalid param for wait callbackRef");
176 napi_create_reference(env, argv[ARGC_THREE_NUMBER], 1, &asyncContext->callbackRef);
177 }
178 PARAM_JS_LOGV("JSApp Wait key: %s, value: %s timeout %d.",
179 asyncContext->key, asyncContext->value, asyncContext->timeout);
180
181 napi_value result = nullptr;
182 if (asyncContext->callbackRef == nullptr) {
183 napi_create_promise(env, &asyncContext->deferred, &result);
184 } else {
185 result = GetNapiValue(env, 0);
186 }
187 WaitCallbackWork(env, asyncContext);
188 return result;
189 }
190
GetFristRefence(ParamWatcherPtr watcher,uint32_t & next)191 static bool GetFristRefence(ParamWatcherPtr watcher, uint32_t &next)
192 {
193 std::lock_guard<std::mutex> lock(watcher->mutex);
194 auto iter = watcher->callbackReferences.begin();
195 if (iter != watcher->callbackReferences.end()) {
196 next = watcher->callbackReferences.begin()->first;
197 return true;
198 }
199 return false;
200 }
201
GetWatcherReference(ParamWatcherPtr watcher,uint32_t next)202 static napi_ref GetWatcherReference(ParamWatcherPtr watcher, uint32_t next)
203 {
204 std::lock_guard<std::mutex> lock(watcher->mutex);
205 auto iter = watcher->callbackReferences.find(next);
206 if (iter != watcher->callbackReferences.end()) {
207 return iter->second;
208 }
209 return nullptr;
210 }
211
GetNextRefence(ParamWatcherPtr watcher,uint32_t & next)212 static uint32_t GetNextRefence(ParamWatcherPtr watcher, uint32_t &next)
213 {
214 std::lock_guard<std::mutex> lock(watcher->mutex);
215 auto iter = watcher->callbackReferences.upper_bound(next);
216 if (iter == watcher->callbackReferences.end()) {
217 return false;
218 }
219 next = iter->first;
220 return true;
221 }
222
AddWatcherCallback(ParamWatcherPtr watcher,napi_ref callbackRef)223 static void AddWatcherCallback(ParamWatcherPtr watcher, napi_ref callbackRef)
224 {
225 static uint32_t watcherId = 0;
226 std::lock_guard<std::mutex> lock(watcher->mutex);
227 watcherId++;
228 watcher->callbackReferences[watcherId] = callbackRef;
229 PARAM_JS_LOGV("JSApp watcher add watcher callback %s %u.",
230 watcher->keyPrefix, watcherId);
231 }
232
DelWatcherCallback(ParamWatcherPtr watcher,uint32_t next)233 static void DelWatcherCallback(ParamWatcherPtr watcher, uint32_t next)
234 {
235 PARAM_JS_LOGV("JSApp watcher key %s delete callback %u", watcher->keyPrefix, next);
236 std::lock_guard<std::mutex> lock(watcher->mutex);
237 watcher->callbackReferences.erase(next);
238 }
239
DelCallbackRef(napi_env env,ParamWatcherPtr watcher,napi_ref callbackRef,uint32_t id)240 static void DelCallbackRef(napi_env env, ParamWatcherPtr watcher, napi_ref callbackRef, uint32_t id)
241 {
242 std::lock_guard<std::mutex> lock(watcher->mutex);
243 if (callbackRef == watcher->currCallbackRef) {
244 PARAM_JS_LOGV("JSApp watcher key %s has been callbacked %u.",
245 watcher->keyPrefix, id);
246 watcher->currCallbackRef = nullptr;
247 } else {
248 napi_delete_reference(env, callbackRef);
249 }
250 watcher->callbackReferences.erase(id);
251 }
252
DelCallback(napi_env env,napi_value callback,ParamWatcherPtr watcher)253 static void DelCallback(napi_env env, napi_value callback, ParamWatcherPtr watcher)
254 {
255 bool isEquals = false;
256 uint32_t next = 0;
257 bool ret = GetFristRefence(watcher, next);
258 while (ret) {
259 napi_ref callbackRef = GetWatcherReference(watcher, next);
260 if (callbackRef == nullptr) {
261 PARAM_JS_LOGV("JSApp watcher key %s callbackRef has been deleted %d.", watcher->keyPrefix, next);
262 DelWatcherCallback(watcher, next);
263 } else if (callback != nullptr) {
264 napi_value handler = nullptr;
265 napi_get_reference_value(env, callbackRef, &handler);
266 napi_strict_equals(env, handler, callback, &isEquals);
267 if (isEquals) {
268 DelCallbackRef(env, watcher, callbackRef, next);
269 break;
270 }
271 } else {
272 DelCallbackRef(env, watcher, callbackRef, next);
273 }
274 ret = GetNextRefence(watcher, next);
275 }
276 }
277
CheckCallbackEqual(napi_env env,napi_value callback,ParamWatcherPtr watcher)278 static bool CheckCallbackEqual(napi_env env, napi_value callback, ParamWatcherPtr watcher)
279 {
280 bool isEquals = false;
281 uint32_t next = 0;
282 bool ret = GetFristRefence(watcher, next);
283 while (ret) {
284 napi_ref callbackRef = GetWatcherReference(watcher, next);
285 if (callbackRef != nullptr) {
286 napi_value handler = nullptr;
287 napi_get_reference_value(env, callbackRef, &handler);
288 napi_strict_equals(env, handler, callback, &isEquals);
289 if (isEquals) {
290 return true;
291 }
292 }
293 ret = GetNextRefence(watcher, next);
294 }
295 return false;
296 }
297
ParamWatchConstructor(napi_env env,napi_callback_info info)298 static napi_value ParamWatchConstructor(napi_env env, napi_callback_info info)
299 {
300 size_t argc = 1;
301 napi_value argv[1];
302 napi_value thisVar = nullptr;
303 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
304 ParamWatcherPtr watcher = new ParamWatcher();
305 PARAM_JS_CHECK(watcher != nullptr, return NapiGetNull(env), "Failed to create param watcher");
306 napi_status status = napi_create_reference(env, thisVar, 1, &watcher->thisVarRef);
307 PARAM_JS_CHECK(status == 0, delete watcher;
308 return NapiGetNull(env), "Failed to create reference %d", status);
309
310 napi_wrap(
311 env, thisVar, watcher,
312 [](napi_env env, void *data, void *hint) {
313 ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(data);
314 if (watcher) {
315 DelCallback(env, nullptr, watcher);
316 WatchParameter(watcher->keyPrefix, nullptr, nullptr);
317 delete watcher;
318 watcher = nullptr;
319 }
320 },
321 nullptr, nullptr);
322 return thisVar;
323 }
324
GetWatcher(napi_env env,napi_callback_info info)325 napi_value GetWatcher(napi_env env, napi_callback_info info)
326 {
327 size_t argc = 1;
328 napi_value argv[1];
329 napi_value thisVar = nullptr;
330 void *data = nullptr;
331 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
332
333 napi_value obj = thisVar;
334 ParamWatcherPtr watcher = nullptr;
335 napi_unwrap(env, thisVar, (void **)&watcher);
336 if (watcher == nullptr) { // check if watcher exist
337 napi_value constructor = nullptr;
338 int status = napi_get_reference_value(env, g_paramWatchRef, &constructor);
339 PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to get reference");
340 status = napi_new_instance(env, constructor, 0, nullptr, &obj);
341 PARAM_JS_CHECK(status == 0, return NapiGetNull(env), "Failed to create instance for watcher");
342 napi_unwrap(env, obj, (void **)&watcher);
343 }
344 if (watcher != nullptr) {
345 watcher->keyLen = BUF_LENGTH;
346 int ret = GetParamValue(env, argv[0], napi_string, watcher->keyPrefix, watcher->keyLen);
347 PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get key prefix");
348 PARAM_JS_LOGV("JSApp watcher keyPrefix = %s ", watcher->keyPrefix);
349 ret = WatchParameter(watcher->keyPrefix, nullptr, watcher);
350 PARAM_JS_CHECK(ret == 0, return NapiGetNull(env), "Failed to get watcher ret %d", ret);
351 }
352 return obj;
353 }
354
GetWatcherInfo(napi_env env,napi_callback_info info,napi_value * callback)355 static ParamWatcherPtr GetWatcherInfo(napi_env env, napi_callback_info info, napi_value *callback)
356 {
357 size_t argc = ARGC_NUMBER;
358 napi_value argv[ARGC_NUMBER];
359 napi_value thisVar = nullptr;
360 void *data = nullptr;
361 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
362
363 size_t typeLen = BUF_LENGTH - 1;
364 std::vector<char> eventType(typeLen, 0);
365 int ret = GetParamValue(env, argv[0], napi_string, eventType.data(), typeLen);
366 PARAM_JS_CHECK(ret == 0, return nullptr, "Failed to get event type");
367 if (strcmp(eventType.data(), "valueChange") != 0) {
368 return nullptr;
369 }
370 // argv[1]:callbackRef
371 if (argc > 1) {
372 napi_valuetype valuetype;
373 napi_status status = napi_typeof(env, argv[1], &valuetype);
374 PARAM_JS_CHECK(status == 0, return nullptr, "Failed to get type");
375 PARAM_JS_CHECK(valuetype == napi_function, return nullptr, "Invalid type %d", valuetype);
376 *callback = argv[1];
377 }
378 ParamWatcherPtr watcher = nullptr;
379 napi_unwrap(env, thisVar, (void **)&watcher);
380 return watcher;
381 }
382
NotifyValueChange(ParamWatcherPtr watcher,uint32_t id,napi_value thisVar,const napi_value result[])383 static void NotifyValueChange(ParamWatcherPtr watcher, uint32_t id, napi_value thisVar, const napi_value result[])
384 {
385 napi_ref callbackRef = GetWatcherReference(watcher, id);
386 PARAM_JS_CHECK(callbackRef != nullptr, return,
387 "Failed to get callback for %s %d", watcher->keyPrefix, id);
388 napi_value callbackFunc = nullptr;
389 napi_status status = napi_get_reference_value(watcher->env, callbackRef, &callbackFunc);
390 PARAM_JS_CHECK(status == 0 && callbackFunc != nullptr, return,
391 "Failed to get callback for %s %d", watcher->keyPrefix, id);
392 {
393 std::lock_guard<std::mutex> lock(watcher->mutex);
394 watcher->currCallbackRef = callbackRef;
395 }
396
397 napi_value callbackResult = nullptr;
398 napi_call_function(watcher->env, thisVar, callbackFunc, ARGC_NUMBER, result, &callbackResult);
399
400 {
401 std::lock_guard<std::mutex> lock(watcher->mutex);
402 if (watcher->currCallbackRef == nullptr) {
403 PARAM_JS_LOGV("JSApp watcher notify key %s callback deleted watcherId %u",
404 watcher->keyPrefix, id);
405 napi_delete_reference(watcher->env, callbackRef);
406 }
407 watcher->currCallbackRef = nullptr;
408 }
409 }
410
HandleParameterChange(ParamWatcherPtr watcher,const char * key,const char * value)411 static void HandleParameterChange(ParamWatcherPtr watcher, const char *key, const char *value)
412 {
413 napi_handle_scope scope = nullptr;
414 napi_status status = napi_open_handle_scope(watcher->env, &scope);
415 PARAM_JS_CHECK(status == 0, return, "Failed to get reference ");
416 napi_value result[ARGC_NUMBER] = { 0 };
417 napi_create_string_utf8(watcher->env, key, strlen(key), &result[0]);
418 napi_create_string_utf8(watcher->env, value, strlen(value), &result[1]);
419 napi_value thisVar = nullptr;
420 status = napi_get_reference_value(watcher->env, watcher->thisVarRef, &thisVar);
421 PARAM_JS_CHECK(status == 0 && thisVar != nullptr, napi_close_handle_scope(watcher->env, scope);
422 return, "Failed to get reference ");
423 uint32_t next = 0;
424 bool ret = GetFristRefence(watcher, next);
425 while (ret) {
426 NotifyValueChange(watcher, next, thisVar, result);
427 ret = GetNextRefence(watcher, next);
428 }
429 napi_close_handle_scope(watcher->env, scope);
430 PARAM_JS_LOGV("JSApp watcher ProcessParamChange %s finish", key);
431 }
432
ProcessParamChange(const char * key,const char * value,void * context)433 static void ProcessParamChange(const char *key, const char *value, void *context)
434 {
435 ParamWatcherPtr watcher = static_cast<ParamWatcherPtr>(context);
436 PARAM_JS_CHECK(watcher != nullptr && watcher->env != nullptr, return, "Invalid param");
437 PARAM_JS_CHECK(watcher->callbackReferences.size() > 0, return, "No callback for watcher");
438
439 uv_loop_s *loop = nullptr;
440 napi_get_uv_event_loop(watcher->env, &loop);
441 PARAM_JS_CHECK(loop != nullptr, return, "Failed to get loop for %s", watcher->keyPrefix);
442 ParamChangeValue *change = new (std::nothrow) ParamChangeValue;
443 PARAM_JS_CHECK(change != nullptr, return, "Failed to get change for %s", watcher->keyPrefix);
444 change->work.data = reinterpret_cast<void *>(change);
445 change->watcher = watcher;
446 change->key = std::string(key);
447 change->value = std::string(value);
448 int ret = uv_queue_work(loop, &change->work, [] (uv_work_t *work) {}, [] (uv_work_t *work, int result) {
449 ParamChangeValue *change = reinterpret_cast<ParamChangeValue *>(work->data);
450 HandleParameterChange(change->watcher, change->key.c_str(), change->value.c_str());
451 delete change;
452 change = nullptr;
453 });
454 PARAM_JS_CHECK(ret == 0, delete change;
455 return, "Failed to start work for %s", watcher->keyPrefix);
456 }
457
WatchCallbackWork(napi_env env,ParamWatcherPtr watcher)458 static void WatchCallbackWork(napi_env env, ParamWatcherPtr watcher)
459 {
460 PARAM_JS_LOGV("JSApp WatchCallbackWork key: %s", watcher->keyPrefix);
461 ParamWatcherWork *worker = new ParamWatcherWork();
462 PARAM_JS_CHECK(worker != nullptr, return, "Failed to create worker ");
463 worker->watcher = watcher;
464 worker->work = nullptr;
465 worker->startWatch = watcher->startWatch;
466
467 napi_value resource = nullptr;
468 napi_create_string_utf8(env, "JSStartupWatch", NAPI_AUTO_LENGTH, &resource);
469 napi_create_async_work(env, nullptr, resource,
470 [](napi_env env, void *data) {
471 ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data);
472 PARAM_JS_CHECK(worker != nullptr && worker->watcher != nullptr, return, "Invalid worker ");
473 int status = WatchParameter(worker->watcher->keyPrefix,
474 worker->startWatch ? ProcessParamChange : nullptr, worker->watcher);
475 PARAM_JS_LOGV("JSApp WatchCallbackWork %s status: %d, key: %s",
476 worker->startWatch ? "on" : "off", status, worker->watcher->keyPrefix);
477 },
478 [](napi_env env, napi_status status, void *data) {
479 ParamWatcherWork *worker = reinterpret_cast<ParamWatcherWork *>(data);
480 PARAM_JS_LOGV("JSApp WatchCallbackWork delete %s key: %s",
481 worker->startWatch ? "on" : "off", worker->watcher->keyPrefix);
482 napi_delete_async_work(env, worker->work);
483 delete worker;
484 },
485 reinterpret_cast<void *>(worker), &worker->work);
486 napi_queue_async_work(env, worker->work);
487 }
488
SwithWatchOn(napi_env env,napi_callback_info info)489 static napi_value SwithWatchOn(napi_env env, napi_callback_info info)
490 {
491 napi_value callback = nullptr;
492 ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback);
493 PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher switch param");
494
495 if (CheckCallbackEqual(env, callback, watcher)) {
496 PARAM_JS_LOGE("JSApp watcher repeater switch on %s", watcher->keyPrefix);
497 return 0;
498 }
499 PARAM_JS_LOGV("JSApp watcher on %s", watcher->keyPrefix);
500 // save callback
501 napi_ref callbackRef;
502 napi_create_reference(env, callback, 1, &callbackRef);
503 AddWatcherCallback(watcher, callbackRef);
504 watcher->env = env;
505 {
506 std::lock_guard<std::mutex> lock(watcher->mutex);
507 if (watcher->startWatch) {
508 return GetNapiValue(env, 0);
509 }
510 watcher->startWatch = true;
511 }
512
513 PARAM_JS_LOGV("JSApp watcher add %s", watcher->keyPrefix);
514 WatchCallbackWork(env, watcher);
515 PARAM_JS_LOGV("JSApp watcher on %s finish", watcher->keyPrefix);
516 return GetNapiValue(env, 0);
517 }
518
SwithWatchOff(napi_env env,napi_callback_info info)519 static napi_value SwithWatchOff(napi_env env, napi_callback_info info)
520 {
521 napi_value callback = nullptr;
522 ParamWatcherPtr watcher = GetWatcherInfo(env, info, &callback);
523 PARAM_JS_CHECK(watcher != nullptr, return GetNapiValue(env, -1), "Failed to get watcher");
524 PARAM_JS_LOGV("JSApp watcher off %s", watcher->keyPrefix);
525 DelCallback(env, callback, watcher);
526 {
527 std::lock_guard<std::mutex> lock(watcher->mutex);
528 if (watcher->callbackReferences.size() == 0) {
529 watcher->startWatch = false;
530 WatchCallbackWork(env, watcher);
531 }
532 }
533 return GetNapiValue(env, 0);
534 }
535
RegisterWatcher(napi_env env,napi_value exports)536 napi_value RegisterWatcher(napi_env env, napi_value exports)
537 {
538 napi_property_descriptor properties[] = {
539 DECLARE_NAPI_FUNCTION("on", SwithWatchOn),
540 DECLARE_NAPI_FUNCTION("off", SwithWatchOff),
541 };
542
543 napi_value result = nullptr;
544 NAPI_CALL(env,
545 napi_define_class(env,
546 "paramWatcher",
547 NAPI_AUTO_LENGTH,
548 ParamWatchConstructor,
549 nullptr,
550 sizeof(properties) / sizeof(*properties),
551 properties,
552 &result));
553 napi_set_named_property(env, exports, "paramWatcher", result);
554 napi_create_reference(env, result, 1, &g_paramWatchRef);
555 return exports;
556 }
557