From 42f95dee847f0fa758a9d2e7c8061b27e1114cdf Mon Sep 17 00:00:00 2001 From: Keeyou Date: Tue, 30 Jan 2024 20:31:10 +0800 Subject: [PATCH] harmony: implement startWorker/stopWorker Ref: #698. --- .../src/main/cpp/types/libyass/index.d.ts | 6 +- .../src/main/ets/entryability/EntryAbility.ts | 16 +- .../entry/src/main/ets/pages/DetailPage.ets | 48 +++- src/harmony/yass.cpp | 217 +++++------------- 4 files changed, 113 insertions(+), 174 deletions(-) diff --git a/harmony/entry/src/main/cpp/types/libyass/index.d.ts b/harmony/entry/src/main/cpp/types/libyass/index.d.ts index 508886904..cbdce3607 100644 --- a/harmony/entry/src/main/cpp/types/libyass/index.d.ts +++ b/harmony/entry/src/main/cpp/types/libyass/index.d.ts @@ -7,5 +7,7 @@ export const getPassword: () => string; export const getCipher: () => string; export const getCipherStrings: () => string[]; export const getTimeout: () => number; -export const init: () => undefined; -export const destroy: () => undefined; \ No newline at end of file +export const init: () => void; +export const destroy: () => void; +export const startWorker: (cb: (err_code: number) => void) => void; +export const stopWorker: (cb: () => void) => void; \ No newline at end of file diff --git a/harmony/entry/src/main/ets/entryability/EntryAbility.ts b/harmony/entry/src/main/ets/entryability/EntryAbility.ts index 2d2d1ed8b..a5aeaa2c7 100644 --- a/harmony/entry/src/main/ets/entryability/EntryAbility.ts +++ b/harmony/entry/src/main/ets/entryability/EntryAbility.ts @@ -10,40 +10,40 @@ import yass from 'libyass.so'; */ export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onCreate'); yass.init(); } onDestroy(): void { - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onDestroy'); yass.destroy(); } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent("pages/DetailPage", (err, data) => { if (err.code) { - hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + hilog.error(0x0000, 'yass', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } - hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + hilog.info(0x0000, 'yass', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + hilog.info(0x0000, 'yass', '%{public}s', 'Ability onBackground'); } } \ No newline at end of file diff --git a/harmony/entry/src/main/ets/pages/DetailPage.ets b/harmony/entry/src/main/ets/pages/DetailPage.ets index 013c333ac..8dcc05be1 100644 --- a/harmony/entry/src/main/ets/pages/DetailPage.ets +++ b/harmony/entry/src/main/ets/pages/DetailPage.ets @@ -3,16 +3,27 @@ import { DetailListComponent } from '../view/DetailListComponent'; import { CommonConstants } from '../constants/CommonConstants'; import { DataItem } from '../viewmodel/DataItem'; import AboutViewModel from '../viewmodel/AboutViewModel'; +import hilog from '@ohos.hilog'; +import yass from 'libyass.so'; /** * Detail page. Click the item on about page to jump to the detail page. */ +enum StartState { + STOPPED, + STOPPING, + STARTED, + STARTING +} + @Entry @Component struct DetailPage { private titleParam: Resource = $r('app.string.title_name'); private dataParam: Array; + private state: StartState = StartState.STOPPED; + aboutToAppear() { let appInfo: DataItem[] = AboutViewModel.getYassInfo() this.titleParam = appInfo[0].title @@ -52,9 +63,19 @@ struct DetailPage { right: $r('app.float.grid_row_margin_right') }) Row() { - Button($r('app.string.start_button')) + Button($r('app.string.start_button'), { type: ButtonType.Capsule, + stateEffect: this.state == StartState.STOPPED }).onClick((event: ClickEvent) => { + if (this.state == StartState.STOPPED) { + this.onStartClicked(); + } + }) Blank() - Button($r('app.string.stop_button')) + Button($r('app.string.stop_button'), { type: ButtonType.Capsule, + stateEffect: this.state == StartState.STARTED }).onClick((event: ClickEvent) => { + if (this.state == StartState.STARTED) { + this.onStopClicked(); + } + }) } } .width(CommonConstants.DETAIL_COLUMN_WIDTH_PERCENT) @@ -83,4 +104,27 @@ struct DetailPage { .width(CommonConstants.FULL_WIDTH_PERCENT) .height($r('app.float.title_height')) } + + onStartClicked() { + this.state = StartState.STARTING; + yass.startWorker((error_code: number) => { + hilog.info(0x0000, 'yass', 'started %d', error_code); + if (error_code == 0) { + this.state = StartState.STARTED; + AlertDialog.show({ message: 'The start is successful' }) + } else { + this.state = StartState.STOPPED; + AlertDialog.show({ message: 'The start failed' }) + } + }) + } + + onStopClicked() { + this.state = StartState.STOPPING; + yass.stopWorker(() => { + hilog.info(0x0000, 'yass', 'stopped %d'); + this.state = StartState.STOPPED; + AlertDialog.show({ message: 'The stop is successful' }) + }) + } } \ No newline at end of file diff --git a/src/harmony/yass.cpp b/src/harmony/yass.cpp index 5099b5a09..f086d088c 100644 --- a/src/harmony/yass.cpp +++ b/src/harmony/yass.cpp @@ -433,15 +433,8 @@ static napi_value stopTun2proxy(napi_env env, napi_callback_info info) { static constexpr char kAsyncStartWorkerResourceName[] = "Thread-safe StartWorker"; -struct AsyncStartWorkerEx_t { - napi_threadsafe_function ts_func; - napi_async_work work_item; - int error_code; -}; - static void startWorkerCallingJS(napi_env env, napi_value js_cb, void *context, void *data) { - // This parameter is not used. - (void)context; + napi_ref thiz_ref = reinterpret_cast(context); int err_code = reinterpret_cast(data); @@ -450,10 +443,10 @@ static void startWorkerCallingJS(napi_env env, napi_value js_cb, void *context, return; } - napi_value undefined; - napi_status status = napi_get_undefined(env, &undefined); + napi_value thiz; + napi_status status = napi_get_reference_value(env, thiz_ref, &thiz); if (status != napi_ok) { - LOG(WARNING) << "napi_get_undefined: " << status; + LOG(WARNING) << "napi_get_reference_value: " << status; return; } @@ -464,68 +457,31 @@ static void startWorkerCallingJS(napi_env env, napi_value js_cb, void *context, return; } + napi_value result; napi_value argv[] = { error_code }; - status = napi_call_function(env, undefined, js_cb, std::size(argv), argv, nullptr); + status = napi_call_function(env, thiz, js_cb, std::size(argv), argv, &result); if (status != napi_ok) { LOG(WARNING) << "napi_call_function: " << status; return; } -} - -static void startWorkerExecuteWork(napi_env env, void *data) { - AsyncStartWorkerEx_t *async_start_worker_ex = (AsyncStartWorkerEx_t *)data; - napi_threadsafe_function startWorkerCallbackFunc = async_start_worker_ex->ts_func; - - void* ctx = (void*)(uintptr_t)async_start_worker_ex->error_code; - - auto status = napi_acquire_threadsafe_function(startWorkerCallbackFunc); - if (status != napi_ok) { - LOG(WARNING) << "napi_acquire_threadsafe_function: " << status; - return; - } - - status = napi_call_threadsafe_function(startWorkerCallbackFunc, ctx, napi_tsfn_blocking); - if (status != napi_ok) { - LOG(WARNING) << "napi_call_threadsafe_function: " << status; - return; - } - - status = napi_release_threadsafe_function(startWorkerCallbackFunc, napi_tsfn_release); + status = napi_delete_reference(env, thiz_ref); if (status != napi_ok) { - LOG(WARNING) << "napi_release_threadsafe_function: " << status; + LOG(WARNING) << "napi_delete_reference: " << status; return; } } -// This function runs on the main thread after `ExecuteWork` exited. -static void startWorkerOnWorkComplete(napi_env env, napi_status status, void *data) { - AsyncStartWorkerEx_t *async_start_worker_ex = (AsyncStartWorkerEx_t *)data; - napi_async_work work_item = async_start_worker_ex->work_item; - napi_threadsafe_function startWorkerCallbackFunc = async_start_worker_ex->ts_func; - delete async_start_worker_ex; - - status = napi_delete_async_work(env, work_item); - if (status != napi_ok) { - LOG(WARNING) << "napi_delete_async_work: " << status; - } - - // Clean up the thread-safe function and the work item associated with this - status = napi_release_threadsafe_function(startWorkerCallbackFunc, napi_tsfn_release); - if (status != napi_ok) { - LOG(WARNING) << "napi_release_threadsafe_function: " << status; - } -} - static std::unique_ptr g_worker; static napi_value startWorker(napi_env env, napi_callback_info info) { napi_status status; - size_t argc = 1; - napi_value args[1] = {nullptr}; - status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); - if (status != napi_ok || argc != 1) { + napi_value args[1] {}; + size_t argc = std::size(args); + napi_value thiz; + status = napi_get_cb_info(env, info, &argc, args, &thiz, nullptr); + if (status != napi_ok || argc != std::size(args)) { napi_throw_error(env, nullptr, "napi_get_cb_info failed"); return nullptr; } @@ -543,6 +499,13 @@ static napi_value startWorker(napi_env env, napi_callback_info info) { return nullptr; } + napi_ref thiz_ref; + status = napi_create_reference(env, thiz, 1, &thiz_ref); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "napi_create_reference failed"); + return nullptr; + } + napi_value work_name; // Specify a name to describe this asynchronous operation. status = napi_create_string_utf8(env, kAsyncStartWorkerResourceName, NAPI_AUTO_LENGTH, &work_name); @@ -555,7 +518,7 @@ static napi_value startWorker(napi_env env, napi_callback_info info) { // Create a thread-safe N-API callback function correspond to the C/C++ callback function status = napi_create_threadsafe_function(env, cb, nullptr, work_name, 0, 1, nullptr, - nullptr, nullptr, startWorkerCallingJS, // the C/C++ callback function + nullptr, thiz_ref, startWorkerCallingJS, // the C/C++ callback function &startWorkerCallbackFunc // out: the asynchronous thread-safe JavaScript function ); if (status != napi_ok) { @@ -563,41 +526,24 @@ static napi_value startWorker(napi_env env, napi_callback_info info) { return nullptr; } - g_worker->Start([env, startWorkerCallbackFunc](asio::error_code ec) { + g_worker->Start([startWorkerCallbackFunc](asio::error_code ec) { if (!ec) { config::SaveConfig(); } - AsyncStartWorkerEx_t *async_start_worker_ex = new AsyncStartWorkerEx_t; - napi_async_work work_item; - - napi_value work_name; - // Specify a name to describe this asynchronous operation. - auto status = napi_create_string_utf8(env, kAsyncStartWorkerResourceName, NAPI_AUTO_LENGTH, &work_name); + auto status = napi_acquire_threadsafe_function(startWorkerCallbackFunc); if (status != napi_ok) { - LOG(WARNING) << "napi_create_string_utf8: " << status; - return; + LOG(WARNING) << "napi_acquire_threadsafe_function: " << status; } - status = napi_create_async_work(env, nullptr, work_name, - startWorkerExecuteWork, startWorkerOnWorkComplete, async_start_worker_ex, - &work_item); // OUT: THE handle to the async work item + status = napi_call_threadsafe_function(startWorkerCallbackFunc, (void*)(uintptr_t)ec.value(), napi_tsfn_blocking); if (status != napi_ok) { - delete async_start_worker_ex; - LOG(WARNING) << "napi_create_async_work: " << status; - return; + LOG(WARNING) << "napi_call_threadsafe_function: " << status; } - async_start_worker_ex->ts_func = startWorkerCallbackFunc; - async_start_worker_ex->work_item = work_item; - async_start_worker_ex->error_code = ec.value(); - - // Queue the work item for execution. - status = napi_queue_async_work(env, work_item); + status = napi_release_threadsafe_function(startWorkerCallbackFunc, napi_tsfn_release); if (status != napi_ok) { - delete async_start_worker_ex; // TODO better cleanup? - LOG(WARNING) << "napi_create_async_work: " << status; - return; + LOG(WARNING) << "napi_release_threadsafe_function: " << status; } }); return nullptr; @@ -605,86 +551,42 @@ static napi_value startWorker(napi_env env, napi_callback_info info) { static constexpr char kAsyncStopWorkerResourceName[] = "Thread-safe StopWorker"; -struct AsyncStopWorkerEx_t { - napi_threadsafe_function ts_func; - napi_async_work work_item; -}; - static void stopWorkerCallingJS(napi_env env, napi_value js_cb, void *context, void *data) { - // This parameter is not used. - (void)context; + napi_ref thiz_ref = reinterpret_cast(context); if (env == nullptr) { LOG(WARNING) << "null env"; return; } - napi_value undefined; - napi_status status = napi_get_undefined(env, &undefined); + napi_value thiz; + napi_status status = napi_get_reference_value(env, thiz_ref, &thiz); if (status != napi_ok) { - LOG(WARNING) << "napi_get_undefined: " << status; + LOG(WARNING) << "napi_get_reference_value: " << status; return; } - status = napi_call_function(env, undefined, js_cb, 0, nullptr, nullptr); + status = napi_call_function(env, thiz, js_cb, 0, nullptr, nullptr); if (status != napi_ok) { LOG(WARNING) << "napi_call_function: " << status; return; } -} - -static void stopWorkerExecuteWork(napi_env env, void *data) { - AsyncStopWorkerEx_t *async_stop_worker_ex = (AsyncStopWorkerEx_t *)data; - napi_threadsafe_function stopWorkerCallbackFunc = async_stop_worker_ex->ts_func; - - void* ctx = nullptr; - - auto status = napi_acquire_threadsafe_function(stopWorkerCallbackFunc); - if (status != napi_ok) { - LOG(WARNING) << "napi_acquire_threadsafe_function: " << status; - return; - } - - status = napi_call_threadsafe_function(stopWorkerCallbackFunc, ctx, napi_tsfn_blocking); - if (status != napi_ok) { - LOG(WARNING) << "napi_call_threadsafe_function: " << status; - return; - } - - status = napi_release_threadsafe_function(stopWorkerCallbackFunc, napi_tsfn_release); + status = napi_delete_reference(env, thiz_ref); if (status != napi_ok) { - LOG(WARNING) << "napi_release_threadsafe_function: " << status; + LOG(WARNING) << "napi_delete_reference: " << status; return; } } -// This function runs on the main thread after `ExecuteWork` exited. -static void stopWorkerOnWorkComplete(napi_env env, napi_status status, void *data) { - AsyncStopWorkerEx_t *async_stop_worker_ex = (AsyncStopWorkerEx_t *)data; - napi_async_work work_item = async_stop_worker_ex->work_item; - napi_threadsafe_function stopWorkerCallbackFunc = async_stop_worker_ex->ts_func; - delete async_stop_worker_ex; - - status = napi_delete_async_work(env, work_item); - if (status != napi_ok) { - LOG(WARNING) << "napi_delete_async_work: " << status; - } - - // Clean up the thread-safe function and the work item associated with this - status = napi_release_threadsafe_function(stopWorkerCallbackFunc, napi_tsfn_release); - if (status != napi_ok) { - LOG(WARNING) << "napi_release_threadsafe_function: " << status; - } -} - static napi_value stopWorker(napi_env env, napi_callback_info info) { napi_status status; - size_t argc = 1; - napi_value args[1] = {nullptr}; - status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); - if (status != napi_ok || argc != 1) { + napi_value args[1] {}; + size_t argc = std::size(args); + napi_value thiz; + status = napi_get_cb_info(env, info, &argc, args, &thiz, nullptr); + if (status != napi_ok || argc != std::size(args)) { napi_throw_error(env, nullptr, "napi_get_cb_info failed"); return nullptr; } @@ -702,6 +604,13 @@ static napi_value stopWorker(napi_env env, napi_callback_info info) { return nullptr; } + napi_ref thiz_ref; + status = napi_create_reference(env, thiz, 1, &thiz_ref); + if (status != napi_ok) { + napi_throw_error(env, nullptr, "napi_create_reference failed"); + return nullptr; + } + napi_value work_name; // Specify a name to describe this asynchronous operation. status = napi_create_string_utf8(env, kAsyncStopWorkerResourceName, NAPI_AUTO_LENGTH, &work_name); @@ -714,7 +623,7 @@ static napi_value stopWorker(napi_env env, napi_callback_info info) { // Create a thread-safe N-API callback function correspond to the C/C++ callback function status = napi_create_threadsafe_function(env, cb, nullptr, work_name, 0, 1, nullptr, - nullptr, nullptr, stopWorkerCallingJS, // the C/C++ callback function + nullptr, thiz_ref, stopWorkerCallingJS, // the C/C++ callback function &stopWorkerCallbackFunc // out: the asynchronous thread-safe JavaScript function ); if (status != napi_ok) { @@ -722,36 +631,20 @@ static napi_value stopWorker(napi_env env, napi_callback_info info) { return nullptr; } - g_worker->Stop([env, stopWorkerCallbackFunc]() { - AsyncStopWorkerEx_t *async_stop_worker_ex = new AsyncStopWorkerEx_t; - napi_async_work work_item; - - napi_value work_name; - // Specify a name to describe this asynchronous operation. - auto status = napi_create_string_utf8(env, kAsyncStopWorkerResourceName, NAPI_AUTO_LENGTH, &work_name); + g_worker->Stop([stopWorkerCallbackFunc]() { + auto status = napi_acquire_threadsafe_function(stopWorkerCallbackFunc); if (status != napi_ok) { - LOG(WARNING) << "napi_create_string_utf8: " << status; - return; + LOG(WARNING) << "napi_acquire_threadsafe_function: " << status; } - status = napi_create_async_work(env, nullptr, work_name, - stopWorkerExecuteWork, stopWorkerOnWorkComplete, async_stop_worker_ex, - &work_item); // OUT: THE handle to the async work item + status = napi_call_threadsafe_function(stopWorkerCallbackFunc, nullptr, napi_tsfn_blocking); if (status != napi_ok) { - delete async_stop_worker_ex; - LOG(WARNING) << "napi_create_async_work: " << status; - return; + LOG(WARNING) << "napi_call_threadsafe_function: " << status; } - async_stop_worker_ex->ts_func = stopWorkerCallbackFunc; - async_stop_worker_ex->work_item = work_item; - - // Queue the work item for execution. - status = napi_queue_async_work(env, work_item); + status = napi_release_threadsafe_function(stopWorkerCallbackFunc, napi_tsfn_release); if (status != napi_ok) { - delete async_stop_worker_ex; // TODO better cleanup? - LOG(WARNING) << "napi_create_async_work: " << status; - return; + LOG(WARNING) << "napi_release_threadsafe_function: " << status; } }); return nullptr;