From f598c06d42bd8eac0452a00562e5a7fc4daa4960 Mon Sep 17 00:00:00 2001 From: 18157154025 Date: Thu, 12 Dec 2024 15:29:48 +0800 Subject: [PATCH 1/3] test Signed-off-by: 18157154025 Change-Id: I4155af22a151d4605c62991e7518e114877550be --- js_concurrent_module/utils/BUILD.gn | 72 +++- .../utils/native_utils_module.cpp | 36 +- js_concurrent_module/utils/src/utils_js.ts | 371 ++++++++++++++++++ js_concurrent_module/utils/tsconfig.json | 16 + 4 files changed, 483 insertions(+), 12 deletions(-) create mode 100644 js_concurrent_module/utils/src/utils_js.ts create mode 100644 js_concurrent_module/utils/tsconfig.json diff --git a/js_concurrent_module/utils/BUILD.gn b/js_concurrent_module/utils/BUILD.gn index d5bbd5b9..ba6bbed7 100644 --- a/js_concurrent_module/utils/BUILD.gn +++ b/js_concurrent_module/utils/BUILD.gn @@ -13,6 +13,63 @@ import("//build/ohos.gni") import("//commonlibrary/ets_utils/ets_utils_config.gni") +import("//build/config/components/ets_frontend/es2abc_config.gni") + +# compile .ts to .js. +action("build_ts_js") { + script = "$util_module/build_ts_js.py" + outFile_Path = target_out_dir + "/" + current_cpu + args = [ + "--dst-file", + rebase_path(target_out_dir + "/utils_js.js"), + "--module-path", + rebase_path("/commonlibrary/ets_utils/js_concurrent_module/utils"), + "--out-file", + rebase_path(outFile_Path + "/utils_js.js"), + "--out-filePath", + rebase_path(outFile_Path), + "--relative-path", + rebase_path("//", root_build_dir), + ] + outputs = [ target_out_dir + "/utils_js.js" ] +} + +# compile .js to .abc. +es2abc_gen_abc("gen_utils_abc") { + extra_visibility = [ ":*" ] + src_js = rebase_path(target_out_dir + "/utils_js.js") + dst_file = rebase_path(target_out_dir + "/utils.abc") + in_puts = [ target_out_dir + "/utils_js.js" ] + out_puts = [ target_out_dir + "/utils.abc" ] + extra_args = [ "--module" ] + extra_dependencies = [ ":build_ts_js" ] +} + +abc_output_path = get_label_info(":utils_abc", "target_out_dir") + +gen_js_obj("utils_js") { + input = "$target_out_dir/utils_js.js" + if (is_arkui_x) { + utils_js_obj_path = abc_output_path + "/utils.c" + } else { + utils_js_obj_path = abc_output_path + "/utils.o" + } + output = utils_js_obj_path + dep = ":build_ts_js" + #snapshot_dep = [ ":build_ts_js" ] +} + +gen_js_obj("utils_abc") { + input = "$target_out_dir/utils.abc" + if (is_arkui_x) { + utils_js_obj_path = abc_output_path + "/utils_abc.c" + } else { + utils_js_obj_path = abc_output_path + "/utils_abc.o" + } + output = utils_js_obj_path + dep = ":gen_utils_abc" + #snapshot_dep = [ ":gen_utils_abc" ] +} locks_sources = [ "json/json_manager.cpp", @@ -34,16 +91,9 @@ ohos_shared_library("utils") { cfi_cross_dso = true debug = false } - include_dirs = [ - js_concurrent_module_common, - ets_util_path, - ] - - sources = locks_sources - sources += concurrent_helper_sources defines = [] - deps = [ "${ets_util_path}/js_sys_module/timer:timer" ] + deps = [ ":utils_static" ] external_deps = [ "napi:ace_napi" ] if (is_ohos) { @@ -91,7 +141,11 @@ ohos_source_set("utils_static") { sources += concurrent_helper_sources defines = [] - deps = [ "${ets_util_path}/js_sys_module/timer:timer" ] + deps = [ + "${ets_util_path}/js_sys_module/timer:timer", + ":utils_abc", + ":utils_js", + ] external_deps = [ "napi:ace_napi" ] if (is_arkui_x) { diff --git a/js_concurrent_module/utils/native_utils_module.cpp b/js_concurrent_module/utils/native_utils_module.cpp index 26b9d118..65be9a93 100644 --- a/js_concurrent_module/utils/native_utils_module.cpp +++ b/js_concurrent_module/utils/native_utils_module.cpp @@ -14,18 +14,48 @@ */ #include "utils.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" -static napi_module g_utilsModule = { +extern const char _binary_utils_js_js_start[]; +extern const char _binary_utils_js_js_end[]; +extern const char _binary_utils_abc_start[]; +extern const char _binary_utils_abc_end[]; + +// utils JS register +extern "C" __attribute__((visibility("default"))) void NAPI_utils_GetJSCode(const char **buf, int *buflen) +{ + if (buf != nullptr) { + *buf = _binary_utils_js_js_start; + } + if (buflen != nullptr) { + *buflen = _binary_utils_js_js_end - _binary_utils_js_js_start; + } +} + +extern "C" __attribute__((visibility("default"))) void NAPI_utils_GetABCCode(const char** buf, int* buflen) +{ + if (buf != nullptr) { + *buf = _binary_utils_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_utils_abc_end - _binary_utils_abc_start; + } +} + + +static napi_module_with_js g_utilsModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Commonlibrary::Concurrent::Utils::Init, .nm_modname = "arkts.utils", .nm_priv = reinterpret_cast(0), - .reserved = {0}, + .nm_get_abc_code = NAPI_utils_GetABCCode, + .nm_get_js_code = NAPI_utils_GetJSCode, }; extern "C" __attribute__((constructor)) void LocksRegister() { - napi_module_register(&g_utilsModule); + napi_module_with_js_register(&g_utilsModule); } diff --git a/js_concurrent_module/utils/src/utils_js.ts b/js_concurrent_module/utils/src/utils_js.ts new file mode 100644 index 00000000..db71c3f2 --- /dev/null +++ b/js_concurrent_module/utils/src/utils_js.ts @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +type AnyType = Object | null | undefined | unknown; +type AsyncLockCallback = () => T | Promise; +interface NativeAsyncLock { + new(): NativeAsyncLock; + lockAsync(callback: AsyncLockCallback): Promise; +} + +interface NativeLocks { + AsyncLock: NativeAsyncLock; +} + +interface IUtils { + locks: NativeLocks; + isSendable(value: Object | null | undefined): boolean; +} +declare function requireInternal(napiModuleName: string): IUtils; +let helpUtil = requireInternal('arkts.utils'); + +declare function print(value: any): string; + +interface NativeMap { + //[Symbol.iterator](): MapIterator<[K, V]>; + new(): NativeMap; + new(entries?: readonly (readonly [AnyType, AnyType])[] | null): NativeMap; + clear(): void; + size: number; + delete(key: Object): boolean; + get(key: Object): Object | undefined; + set(key: Object, value: Object): Map; + entries(): IterableIterator<[Object, Object]>; + keys(): IterableIterator<[Object, Object]>; + values(): IterableIterator<[Object, Object]>; + has(key: Object): boolean; +} + +interface NativeArray { + new(): NativeArray; + new(first: Object, ...left: Object[]): NativeArray; + push(...items: T[]): number; + pop(): T | undefined; + join(separator?: string): string; + shift(): T | undefined; + unshift(...items: T[]): number; + slice(start?: number, end?: number): Array; + sort(compareFn?: (a: T, b: T) => number): Array; + indexOf(searchElement: T, fromIndex?: number): number; + forEach(callbackFn: (value: T, index: number, array: NativeArray) => void): void; + map(callbackFn: (value: T, index: number, array: NativeArray) => U): NativeArray; + filter(predicate: (value: T, index: number, array: NativeArray) => boolean): NativeArray; + reduce(callbackFn: (previousValue: T, currentValue: T, currentIndex: number, array: NativeArray) => T): T; + at(index: number): T | undefined; + entries(): IterableIterator<[number, T]>; + keys(): IterableIterator; + values(): IterableIterator; + find(predicate: (value: T, index: number, obj: NativeArray) => boolean): T | undefined; + includes(searchElement: T, fromIndex?: number): boolean; + findIndex(predicate: (value: T, index: number, obj: NativeArray) => boolean): number; + fill(value: T, start?: number, end?: number): NativeArray; + shrinkTo(arrayLength: number): void; + extendTo(arrayLength: number, initialValue: T): void; + [index: number]: T; + concat(...items: ConcatArray[]): NativeArray; + splice(start: number): NativeArray; + splice(start: number, deleteCount: number, ...items: T[]): NativeArray; +} + +interface ICollections { + Map: NativeMap; + Array: NativeArray; +} + +declare function requireNapi(napiModuleName: string): ICollections; +let collections = requireNapi('arkts.collections'); + +const typeErrorCode = 401; +class BusinessError extends Error { + code: number; + constructor(msg: string) { + super(msg); + this.name = 'BusinessError'; + this.code = typeErrorCode; + } +} + +class SendableLRUCache { + private cache: collections.Map = new collections.Map(); + private lockCache = new helpUtil.locks.AsyncLock(); + // Default current size + private maxSize: number = 64; + // Default maximum size + private maxNumber: number = 2147483647; + private putCount: number = 0; + private createCount: number = 0; + private evictionCount: number = 0; + private hitCount: number = 0; + private missCount: number = 0; + public length: number = 0; + + public constructor(capacity?: number) { + "use sendable" + if (capacity !== undefined && capacity !== null) { + if (capacity <= 0 || capacity % 1 !== 0 || capacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${capacity} must be small integer`); + throw error; + } + this.maxSize = capacity; + } + } + + private async changeCapacity(newCapacity: number): Promise { + await this.lockCache.lockAsync(() => { + while (this.cache.size > newCapacity) { + this.cache.delete(this.cache.keys().next().value); + this.evictionCount++; + this.afterRemoval(true, this.cache.keys(), this.cache.values(), null); + } + this.length = this.cache.size; + this.maxSize = newCapacity; + print("changeCapcity length: ") + print(this.length) + }); + } + + protected afterRemoval(isEvict: boolean, key: Object | undefined | null, value: Object | undefined | null, + newValue: Object | undefined | null): void { + } + + protected createDefault(key: Object): Object | undefined { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + return undefined; + } + + public async updateCapacity(newCapacity: number): Promise { + if (typeof newCapacity !== 'number') { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be number`); + throw error; + } + if (newCapacity <= 0 || newCapacity % 1 !== 0 || newCapacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be small integer`); + throw error; + } else if (this.cache.size > newCapacity) { + await this.changeCapacity(newCapacity); + } + } + + public async get(key: Object): Promise { + if (typeof (key as Object) === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let value: Object; + if (this.cache.has(key)) { + value = this.cache.get(key); + this.hitCount++; + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + return value; + } + + this.missCount++; + let createValue: Object | undefined = this.createDefault(key); + if (createValue === undefined) { + return undefined; + } else { + value = await this.put(key, createValue); + this.createCount++; + if (value !== undefined) { + await this.put(key, value); + this.afterRemoval(false, key, createValue, value); + return value; + } + return createValue; + } + } + + public async put(key: Object, value: Object): Promise { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (typeof (value as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${value} must be Object`); + throw error; + } + if (key === null || value === null) { + let error = new BusinessError(`Parameter error. The type of key and value must be Object`); + throw error; + } + // check key and value is Sendable object, otherwise return error + if ((!helpUtil.isSendable(key)) && (!helpUtil.isSendable(value))) { + let error = new BusinessError(`Parameter error. The type of key and value must be Sendable`); + throw error; + } + let former: Object | undefined = undefined; + this.putCount++; + if (this.cache.has(key)) { + former = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + this.afterRemoval(false, key, former, value); + } else if (this.cache.size >= this.maxSize) { + this.afterRemoval(true, this.cache.keys().next().value, this.cache.values().next().value, null); + await this.lockCache.lockAsync(() => { + this.cache.delete(this.cache.keys().next().value); + }); + this.evictionCount++; + } + await this.lockCache.lockAsync(() => { + this.cache.set(key, value); + }); + this.length = this.cache.size; + former = this.cache.get(key); + return former; + } + + public async remove(key: Object): Promise { + if (typeof (key as Object) === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + if (this.cache.has(key)) { + let former: Object = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + if (former !== null) { + this.afterRemoval(false, key, former, null); + this.length = this.cache.size; + return former; + } + } + this.length = this.cache.size; + return undefined; + } + + public async contains(key: Object): Promise { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let flag: boolean = false; + if (this.cache.has(key)) { + flag = true; + this.hitCount++; + let value: Object = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + this.length = this.cache.size; + return flag; + } + this.missCount++; + return flag; + } + + public getCreateCount(): number { + return this.createCount; + } + + public getMissCount(): number { + return this.missCount; + } + + public getRemovalCount(): number { + return this.evictionCount; + } + + public getMatchCount(): number { + return this.hitCount; + } + + public getPutCount(): number { + return this.putCount; + } + + public getCapacity(): number { + return this.maxSize; + } + + public async clear(): Promise { + this.afterRemoval(false, this.cache.keys(), this.cache.values(), null); + await this.lockCache.lockAsync(() => { + this.cache.clear(); + }); + this.length = this.cache.size; + } + + public isEmpty(): boolean { + return this.cache.size === 0; + } + + public toString(): string { + let peek: number = 0; + let hitRate: number = 0; + peek = this.hitCount + this.missCount; + if (peek !== 0) { + // The value is 100 times larger + hitRate = 100 * this.hitCount / peek; + } + let str: string = ''; + str = 'SendableLRUCache[ maxSize = ' + this.maxSize + ', hits = ' + this.hitCount + + ', misses = ' + this.missCount + ', hitRate = ' + hitRate + '% ]'; + return str; + } + + public values(): Object { + let arr = new collections.Array(); + for (let value of this.cache.values()) { + arr.push(value); + } + return arr; + } + + public keys(): Object { + let arr = new collections.Array(); + for (let key of this.cache.keys()) { + arr.push(key); + } + return arr; + } + + public entries(): IterableIterator<[Object, Object]> { + return this.cache.entries(); + } + + public [Symbol.iterator](): IterableIterator<[Object, Object]> { + return this.cache.entries(); + } +} + + +export default { + SendableLRUCache: SendableLRUCache +}; \ No newline at end of file diff --git a/js_concurrent_module/utils/tsconfig.json b/js_concurrent_module/utils/tsconfig.json new file mode 100644 index 00000000..0c1250c0 --- /dev/null +++ b/js_concurrent_module/utils/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "es6", + "rootDir": "./src", + //"outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./out", /* Specify an output folder for all emitted files. */ + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "noImplicitThis": false, + "suppressImplicitAnyIndexErrors": true, + "strictNullChecks": false, + } + } \ No newline at end of file -- Gitee From 50c269b585eed82f01b7fa4fa86a5d04fb29de12 Mon Sep 17 00:00:00 2001 From: 18157154025 Date: Thu, 12 Dec 2024 15:51:51 +0800 Subject: [PATCH 2/3] q Signed-off-by: 18157154025 Change-Id: I59ecaa3906b2e1b194f34f8c232bd284cb19d4ef --- js_concurrent_module/utils/BUILD.gn | 47 +-- .../utils/native_utils_module.cpp | 16 +- js_concurrent_module/utils/src/utils_js.ts | 15 +- js_concurrent_module/utils/tsconfig.json | 30 +- js_concurrent_module/utils/utils_js.ts | 372 ++++++++++++++++++ 5 files changed, 413 insertions(+), 67 deletions(-) create mode 100644 js_concurrent_module/utils/utils_js.ts diff --git a/js_concurrent_module/utils/BUILD.gn b/js_concurrent_module/utils/BUILD.gn index ba6bbed7..8e3e0eef 100644 --- a/js_concurrent_module/utils/BUILD.gn +++ b/js_concurrent_module/utils/BUILD.gn @@ -15,47 +15,29 @@ import("//build/ohos.gni") import("//commonlibrary/ets_utils/ets_utils_config.gni") import("//build/config/components/ets_frontend/es2abc_config.gni") -# compile .ts to .js. -action("build_ts_js") { - script = "$util_module/build_ts_js.py" - outFile_Path = target_out_dir + "/" + current_cpu - args = [ - "--dst-file", - rebase_path(target_out_dir + "/utils_js.js"), - "--module-path", - rebase_path("/commonlibrary/ets_utils/js_concurrent_module/utils"), - "--out-file", - rebase_path(outFile_Path + "/utils_js.js"), - "--out-filePath", - rebase_path(outFile_Path), - "--relative-path", - rebase_path("//", root_build_dir), - ] - outputs = [ target_out_dir + "/utils_js.js" ] -} - +print("start to convert ts to abc") # compile .js to .abc. -es2abc_gen_abc("gen_utils_abc") { - extra_visibility = [ ":*" ] - src_js = rebase_path(target_out_dir + "/utils_js.js") +es2abc_gen_abc("gen_utils_abc") { + src_js = rebase_path("utils_js.ts") dst_file = rebase_path(target_out_dir + "/utils.abc") - in_puts = [ target_out_dir + "/utils_js.js" ] + in_puts = [ "utils_js.ts" ] out_puts = [ target_out_dir + "/utils.abc" ] extra_args = [ "--module" ] - extra_dependencies = [ ":build_ts_js" ] } abc_output_path = get_label_info(":utils_abc", "target_out_dir") +print("Debug info abc_output_path file path: ", abc_output_path) gen_js_obj("utils_js") { - input = "$target_out_dir/utils_js.js" + print("lhc lhc") + input = "utils_js.ts" if (is_arkui_x) { utils_js_obj_path = abc_output_path + "/utils.c" } else { utils_js_obj_path = abc_output_path + "/utils.o" } output = utils_js_obj_path - dep = ":build_ts_js" + #dep = ":build_ts_js" #snapshot_dep = [ ":build_ts_js" ] } @@ -68,6 +50,7 @@ gen_js_obj("utils_abc") { } output = utils_js_obj_path dep = ":gen_utils_abc" + print("Debug info gen_utils_abc file path: ", input) #snapshot_dep = [ ":gen_utils_abc" ] } @@ -93,7 +76,9 @@ ohos_shared_library("utils") { } defines = [] - deps = [ ":utils_static" ] + deps = [ + ":utils_static" + ] external_deps = [ "napi:ace_napi" ] if (is_ohos) { @@ -141,11 +126,9 @@ ohos_source_set("utils_static") { sources += concurrent_helper_sources defines = [] - deps = [ - "${ets_util_path}/js_sys_module/timer:timer", - ":utils_abc", - ":utils_js", - ] + deps = [ "${ets_util_path}/js_sys_module/timer:timer", + ":utils_abc", + ":utils_js", ] external_deps = [ "napi:ace_napi" ] if (is_arkui_x) { diff --git a/js_concurrent_module/utils/native_utils_module.cpp b/js_concurrent_module/utils/native_utils_module.cpp index 65be9a93..6fc65511 100644 --- a/js_concurrent_module/utils/native_utils_module.cpp +++ b/js_concurrent_module/utils/native_utils_module.cpp @@ -17,21 +17,10 @@ #include "napi/native_api.h" #include "napi/native_node_api.h" -extern const char _binary_utils_js_js_start[]; -extern const char _binary_utils_js_js_end[]; extern const char _binary_utils_abc_start[]; extern const char _binary_utils_abc_end[]; -// utils JS register -extern "C" __attribute__((visibility("default"))) void NAPI_utils_GetJSCode(const char **buf, int *buflen) -{ - if (buf != nullptr) { - *buf = _binary_utils_js_js_start; - } - if (buflen != nullptr) { - *buflen = _binary_utils_js_js_end - _binary_utils_js_js_start; - } -} + extern "C" __attribute__((visibility("default"))) void NAPI_utils_GetABCCode(const char** buf, int* buflen) { @@ -51,8 +40,7 @@ static napi_module_with_js g_utilsModule = { .nm_register_func = Commonlibrary::Concurrent::Utils::Init, .nm_modname = "arkts.utils", .nm_priv = reinterpret_cast(0), - .nm_get_abc_code = NAPI_utils_GetABCCode, - .nm_get_js_code = NAPI_utils_GetJSCode, + .nm_get_abc_code = NAPI_utils_GetABCCode, }; extern "C" __attribute__((constructor)) void LocksRegister() diff --git a/js_concurrent_module/utils/src/utils_js.ts b/js_concurrent_module/utils/src/utils_js.ts index db71c3f2..70fab0b9 100644 --- a/js_concurrent_module/utils/src/utils_js.ts +++ b/js_concurrent_module/utils/src/utils_js.ts @@ -96,9 +96,11 @@ class BusinessError extends Error { } } -class SendableLRUCache { - private cache: collections.Map = new collections.Map(); - private lockCache = new helpUtil.locks.AsyncLock(); +class SendableLRUCache { + // private cache = new collections.Map(); + // private lockCache = new helpUtil.locks.AsyncLock(); + private cache : collections.Map = new collections.Map(); + private lockCache: helpUtil.locks.AsyncLock = new helpUtil.locks.AsyncLock(); // Default current size private maxSize: number = 64; // Default maximum size @@ -110,8 +112,8 @@ class SendableLRUCache { private missCount: number = 0; public length: number = 0; - public constructor(capacity?: number) { - "use sendable" + public constructor(capacity?: number) { + 'use sendable'; if (capacity !== undefined && capacity !== null) { if (capacity <= 0 || capacity % 1 !== 0 || capacity > this.maxNumber) { let error = new BusinessError(`Parameter error. The type of ${capacity} must be small integer`); @@ -119,6 +121,7 @@ class SendableLRUCache { } this.maxSize = capacity; } + console.log("dxg end function") } private async changeCapacity(newCapacity: number): Promise { @@ -368,4 +371,4 @@ class SendableLRUCache { export default { SendableLRUCache: SendableLRUCache -}; \ No newline at end of file +}; diff --git a/js_concurrent_module/utils/tsconfig.json b/js_concurrent_module/utils/tsconfig.json index 0c1250c0..16b97531 100644 --- a/js_concurrent_module/utils/tsconfig.json +++ b/js_concurrent_module/utils/tsconfig.json @@ -1,16 +1,16 @@ { - "compilerOptions": { - "target": "ES2017", - "module": "es6", - "rootDir": "./src", - //"outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./out", /* Specify an output folder for all emitted files. */ - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true, - "noImplicitThis": false, - "suppressImplicitAnyIndexErrors": true, - "strictNullChecks": false, - } - } \ No newline at end of file +"compilerOptions": { + "target": "ES2017", + "module": "es6", + "rootDir": "./src", + //"outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./out", /* Specify an output folder for all emitted files. */ + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "noImplicitThis": false, + "suppressImplicitAnyIndexErrors": true, + "strictNullChecks": false, + } +} diff --git a/js_concurrent_module/utils/utils_js.ts b/js_concurrent_module/utils/utils_js.ts new file mode 100644 index 00000000..d4319428 --- /dev/null +++ b/js_concurrent_module/utils/utils_js.ts @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +type AnyType = Object | null | undefined | unknown; +type AsyncLockCallback = () => T | Promise; +interface NativeAsyncLock { + new(): NativeAsyncLock; + lockAsync(callback: AsyncLockCallback): Promise; +} + +interface NativeLocks { + AsyncLock: NativeAsyncLock; +} + +interface IUtils { + locks: NativeLocks; + isSendable(value: Object | null | undefined): boolean; +} +declare function requireInternal(napiModuleName: string): IUtils; +let helpUtil = requireInternal('arkts.utils'); + +declare function print(value: any): string; + +interface NativeMap { + //[Symbol.iterator](): MapIterator<[K, V]>; + new(): NativeMap; + new(entries?: readonly (readonly [AnyType, AnyType])[] | null): NativeMap; + clear(): void; + size: number; + delete(key: Object): boolean; + get(key: Object): Object | undefined; + set(key: Object, value: Object): Map; + entries(): IterableIterator<[Object, Object]>; + keys(): IterableIterator<[Object, Object]>; + values(): IterableIterator<[Object, Object]>; + has(key: Object): boolean; +} + +interface NativeArray { + new(): NativeArray; + new(first: Object, ...left: Object[]): NativeArray; + push(...items: T[]): number; + pop(): T | undefined; + join(separator?: string): string; + shift(): T | undefined; + unshift(...items: T[]): number; + slice(start?: number, end?: number): Array; + sort(compareFn?: (a: T, b: T) => number): Array; + indexOf(searchElement: T, fromIndex?: number): number; + forEach(callbackFn: (value: T, index: number, array: NativeArray) => void): void; + map(callbackFn: (value: T, index: number, array: NativeArray) => U): NativeArray; + filter(predicate: (value: T, index: number, array: NativeArray) => boolean): NativeArray; + reduce(callbackFn: (previousValue: T, currentValue: T, currentIndex: number, array: NativeArray) => T): T; + at(index: number): T | undefined; + entries(): IterableIterator<[number, T]>; + keys(): IterableIterator; + values(): IterableIterator; + find(predicate: (value: T, index: number, obj: NativeArray) => boolean): T | undefined; + includes(searchElement: T, fromIndex?: number): boolean; + findIndex(predicate: (value: T, index: number, obj: NativeArray) => boolean): number; + fill(value: T, start?: number, end?: number): NativeArray; + shrinkTo(arrayLength: number): void; + extendTo(arrayLength: number, initialValue: T): void; + [index: number]: T; + concat(...items: ConcatArray[]): NativeArray; + splice(start: number): NativeArray; + splice(start: number, deleteCount: number, ...items: T[]): NativeArray; +} + +interface ICollections { + Map: NativeMap; + Array: NativeArray; +} + +declare function requireNapi(napiModuleName: string): ICollections; +let collections = requireNapi('arkts.collections'); + +const typeErrorCode = 401; +class BusinessError extends Error { + code: number; + constructor(msg: string) { + super(msg); + this.name = 'BusinessError'; + this.code = typeErrorCode; + } +} + +class SendableLRUCache { + private cache = new collections.Map(); + private lockCache = new helpUtil.locks.AsyncLock(); + // Default current size + private maxSize: number = 64; + // Default maximum size + private maxNumber: number = 2147483647; + private putCount: number = 0; + private createCount: number = 0; + private evictionCount: number = 0; + private hitCount: number = 0; + private missCount: number = 0; + public length: number = 0; + + public constructor(capacity?: number) { + 'use sendable'; + if (capacity !== undefined && capacity !== null) { + if (capacity <= 0 || capacity % 1 !== 0 || capacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${capacity} must be small integer`); + throw error; + } + this.maxSize = capacity; + } + console.log("dxg end function") + } + + private async changeCapacity(newCapacity: number): Promise { + await this.lockCache.lockAsync(() => { + while (this.cache.size > newCapacity) { + this.cache.delete(this.cache.keys().next().value); + this.evictionCount++; + this.afterRemoval(true, this.cache.keys(), this.cache.values(), null); + } + this.length = this.cache.size; + this.maxSize = newCapacity; + print("changeCapcity length: ") + print(this.length) + }); + } + + protected afterRemoval(isEvict: boolean, key: Object | undefined | null, value: Object | undefined | null, + newValue: Object | undefined | null): void { + } + + protected createDefault(key: Object): Object | undefined { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + return undefined; + } + + public async updateCapacity(newCapacity: number): Promise { + if (typeof newCapacity !== 'number') { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be number`); + throw error; + } + if (newCapacity <= 0 || newCapacity % 1 !== 0 || newCapacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be small integer`); + throw error; + } else if (this.cache.size > newCapacity) { + await this.changeCapacity(newCapacity); + } + } + + public async get(key: Object): Promise { + if (typeof (key as Object) === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let value: Object; + if (this.cache.has(key)) { + value = this.cache.get(key); + this.hitCount++; + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + return value; + } + + this.missCount++; + let createValue: Object | undefined = this.createDefault(key); + if (createValue === undefined) { + return undefined; + } else { + value = await this.put(key, createValue); + this.createCount++; + if (value !== undefined) { + await this.put(key, value); + this.afterRemoval(false, key, createValue, value); + return value; + } + return createValue; + } + } + + public async put(key: Object, value: Object): Promise { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (typeof (value as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${value} must be Object`); + throw error; + } + if (key === null || value === null) { + let error = new BusinessError(`Parameter error. The type of key and value must be Object`); + throw error; + } + // check key and value is Sendable object, otherwise return error + if ((!helpUtil.isSendable(key)) && (!helpUtil.isSendable(value))) { + let error = new BusinessError(`Parameter error. The type of key and value must be Sendable`); + throw error; + } + let former: Object | undefined = undefined; + this.putCount++; + if (this.cache.has(key)) { + former = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + this.afterRemoval(false, key, former, value); + } else if (this.cache.size >= this.maxSize) { + this.afterRemoval(true, this.cache.keys().next().value, this.cache.values().next().value, null); + await this.lockCache.lockAsync(() => { + this.cache.delete(this.cache.keys().next().value); + }); + this.evictionCount++; + } + await this.lockCache.lockAsync(() => { + this.cache.set(key, value); + }); + this.length = this.cache.size; + former = this.cache.get(key); + return former; + } + + public async remove(key: Object): Promise { + if (typeof (key as Object) === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + if (this.cache.has(key)) { + let former: Object = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + if (former !== null) { + this.afterRemoval(false, key, former, null); + this.length = this.cache.size; + return former; + } + } + this.length = this.cache.size; + return undefined; + } + + public async contains(key: Object): Promise { + if (typeof (key as Object) === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let flag: boolean = false; + if (this.cache.has(key)) { + flag = true; + this.hitCount++; + let value: Object = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + this.length = this.cache.size; + return flag; + } + this.missCount++; + return flag; + } + + public getCreateCount(): number { + return this.createCount; + } + + public getMissCount(): number { + return this.missCount; + } + + public getRemovalCount(): number { + return this.evictionCount; + } + + public getMatchCount(): number { + return this.hitCount; + } + + public getPutCount(): number { + return this.putCount; + } + + public getCapacity(): number { + return this.maxSize; + } + + public async clear(): Promise { + this.afterRemoval(false, this.cache.keys(), this.cache.values(), null); + await this.lockCache.lockAsync(() => { + this.cache.clear(); + }); + this.length = this.cache.size; + } + + public isEmpty(): boolean { + return this.cache.size === 0; + } + + public toString(): string { + let peek: number = 0; + let hitRate: number = 0; + peek = this.hitCount + this.missCount; + if (peek !== 0) { + // The value is 100 times larger + hitRate = 100 * this.hitCount / peek; + } + let str: string = ''; + str = 'SendableLRUCache[ maxSize = ' + this.maxSize + ', hits = ' + this.hitCount + + ', misses = ' + this.missCount + ', hitRate = ' + hitRate + '% ]'; + return str; + } + + public values(): Object { + let arr = new collections.Array(); + for (let value of this.cache.values()) { + arr.push(value); + } + return arr; + } + + public keys(): Object { + let arr = new collections.Array(); + for (let key of this.cache.keys()) { + arr.push(key); + } + return arr; + } + + public entries(): IterableIterator<[Object, Object]> { + return this.cache.entries(); + } + + public [Symbol.iterator](): IterableIterator<[Object, Object]> { + return this.cache.entries(); + } +} + + +export default { + SendableLRUCache: SendableLRUCache +}; -- Gitee From 350b4131a265da8702b62aeee10864ac289e09ef Mon Sep 17 00:00:00 2001 From: 18157154025 Date: Tue, 24 Dec 2024 21:28:58 +0800 Subject: [PATCH 3/3] test test Signed-off-by: 18157154025 Change-Id: I6cf3122fe9aca1f0c95efcf2be69b737f1182f55 --- .vscode/c_cpp_properties.json | 18 ++ .vscode/launch.json | 140 ++++++++++ .vscode/settings.json | 65 +++++ js_concurrent_module/utils/BUILD.gn | 1 + .../utils/cache/SendableLruCache.cpp | 158 +++++++++++ .../utils/cache/SendableLruCache.h | 73 +++++ js_concurrent_module/utils/utils.cpp | 5 +- js_concurrent_module/utils/utils_js.js | 262 ++++++++++++++++++ 8 files changed, 721 insertions(+), 1 deletion(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 js_concurrent_module/utils/cache/SendableLruCache.cpp create mode 100644 js_concurrent_module/utils/cache/SendableLruCache.h create mode 100644 js_concurrent_module/utils/utils_js.js diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..c2098a2d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "linux-gcc-x64", + "includePath": [ + "${workspaceFolder}/**" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "${default}", + "cppStandard": "${default}", + "intelliSenseMode": "linux-gcc-x64", + "compilerArgs": [ + "" + ] + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..7d113c05 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,140 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "debug ut", + "type": "cppdbg", + "request": "launch", + "program": "/home/lhc/Openharmony/out/rk3568/clang_x64/exe.unstripped/clang_x64/tests/unittest/arkcompiler/ets_runtime/BuiltinsNatural_001_Test", + // "program": "/home/lhc/Openharmony/out/rk3568/clang_x64/obj/arkcompiler/ets_runtime/ecmascript/tests/EcmaVm_054_Test", + // "args": ["~/openHarmony/openHarmonyMaster/out/rk3568/clang_x64/exe.unstripped/clang_x64/tests/unittest/arkcompiler/ets_runtime/"], + "stopAtEntry": true, + "cwd": "${fileDirname}", + "externalConsole": false, + "environment": [ + { + "name": "LD_LIBRARY_PATH", + // "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/cjson:prebuilts/clang/ohos/linux-x86_64/llvm/lib", + // "value": "/home/lhc/Openharmony/out/third_party/cJSON/cjson:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + } + ], + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, + { + "name": "ets utils debug ut", + "type": "cppdbg", + "request": "launch", + "program": "/home/lhc/Openharmony/out/rk3568/tests/unittest/js_concurrent_module/utils/test_utils_unittest", + + // "args": ["~/openHarmony/openHarmonyMaster/out/rk3568/clang_x64/exe.unstripped/clang_x64/tests/unittest/arkcompiler/ets_runtime/"], + "stopAtEntry": true, + "cwd": "${fileDirname}", + "externalConsole": false, + "environment": [ + { + "name": "LD_LIBRARY_PATH", + // "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/cjson:prebuilts/clang/ohos/linux-x86_64/llvm/lib", + // "value": "/home/lhc/Openharmony/out/third_party/cJSON/cjson:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + } + ], + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + }, + { + "name": "constructorMap", + "type": "cppdbg", + "request": "launch", + "program": "/home/lhc/Openharmony/out/rk3568/clang_x64/exe.unstripped/clang_x64/arkcompiler/ets_runtime/ark_js_vm", + "args": ["--enable-force-gc=false", "--asm-interpreter=false", "/home/lhc/Openharmony/constructorMap.abc"], + // "args": ["--enable-force-gc=false --asm-interpreter=false /home/lhc/Openharmony/fuzz.abc"], + "stopAtEntry": true, + "cwd": "${fileDirname}", + "environment": [ + { + "name": "LD_LIBRARY_PATH", + // "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + // "value": "/home/lhc/Openharmony/out/third_party/cJSON/cjson:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/test/test:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + // "preLaunchTask": "B_BUILT_TS" + }, + { + "name": "(gdb) abc Launch", + "type": "cppdbg", + "request": "launch", + // "program": "/home/lhc/standAlone/out/x64.release/exe.unstripped/arkcompiler/ets_runtime/ark_js_vm", + "program": "/home/lhc/Openharmony/out/rk3568/clang_x64/exe.unstripped/clang_x64/arkcompiler/ets_runtime/ark_js_vm", + // "args": ["--enable-force-gc=false", "--asm-interpreter=false", "/home/lhc/Openharmony/helloworld.abc"], + "args": ["--icu-data-path=\"/home/lhc/Openharmony/third_party/icu/ohos_icu4j/data\"", "--enable-force-gc=false", "--asm-interpreter=false", "/home/lhc/Openharmony/helloworld.abc"], + "stopAtEntry": true, + "cwd": "${fileDirname}", + "environment": [ + { + "name": "LD_LIBRARY_PATH", + // "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime/:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/icu:/home/lhc/Openharmony/out/rk3568/clang_x64/thirdparty/zlib:prebuilts/clang/ohos/linux-x86_64/llvm/lib:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu/", + // "value": "/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/arkcompiler/ets_runtime:/home/lhc/Openharmony/out/rk3568/clang_x64/lib.unstripped/clang_x64/thirdparty/icu", + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + // "preLaunchTask": "B_BUILT_TS" + } + + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..5f7e1309 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,65 @@ +{ + "C_Cpp_Runner.cCompilerPath": "gcc", + "C_Cpp_Runner.cppCompilerPath": "g++", + "C_Cpp_Runner.debuggerPath": "gdb", + "C_Cpp_Runner.cStandard": "", + "C_Cpp_Runner.cppStandard": "", + "C_Cpp_Runner.msvcBatchPath": "", + "C_Cpp_Runner.useMsvc": false, + "C_Cpp_Runner.warnings": [ + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wshadow", + "-Wformat=2", + "-Wcast-align", + "-Wconversion", + "-Wsign-conversion", + "-Wnull-dereference" + ], + "C_Cpp_Runner.msvcWarnings": [ + "/W4", + "/permissive-", + "/w14242", + "/w14287", + "/w14296", + "/w14311", + "/w14826", + "/w44062", + "/w44242", + "/w14905", + "/w14906", + "/w14263", + "/w44265", + "/w14928" + ], + "C_Cpp_Runner.enableWarnings": true, + "C_Cpp_Runner.warningsAsError": false, + "C_Cpp_Runner.compilerArgs": [], + "C_Cpp_Runner.linkerArgs": [], + "C_Cpp_Runner.includePaths": [], + "C_Cpp_Runner.includeSearch": [ + "/home/lhc/Openharmony/commonlibrary/*", + "/home/lhc/Openharmony/foundation/*", + "/home/lhc/Openharmony/arkcompiler/*", + "/home/lhc/Openharmony/interface/*" + ], + "C_Cpp_Runner.excludeSearch": [ + "**/build", + "**/build/**", + "**/.*", + "**/.*/**", + "**/.vscode", + "**/.vscode/**" + ], + "C_Cpp_Runner.useAddressSanitizer": false, + "C_Cpp_Runner.useUndefinedSanitizer": false, + "C_Cpp_Runner.useLeakSanitizer": false, + "C_Cpp_Runner.showCompilationTime": false, + "C_Cpp_Runner.useLinkTimeOptimization": false, + "C_Cpp_Runner.msvcSecureNoWarnings": false, + "C_Cpp.intelliSenseEngine": "default", + "files.associations": { + "typeinfo": "cpp" + } +} \ No newline at end of file diff --git a/js_concurrent_module/utils/BUILD.gn b/js_concurrent_module/utils/BUILD.gn index 8e3e0eef..c5355ceb 100644 --- a/js_concurrent_module/utils/BUILD.gn +++ b/js_concurrent_module/utils/BUILD.gn @@ -61,6 +61,7 @@ locks_sources = [ "locks/deadlock_helpers.cpp", "locks/graph.cpp", "locks/lock_request.cpp", + "cache/SendableLruCache.cpp", "native_utils_module.cpp", "utils.cpp", ] diff --git a/js_concurrent_module/utils/cache/SendableLruCache.cpp b/js_concurrent_module/utils/cache/SendableLruCache.cpp new file mode 100644 index 00000000..762d5e9d --- /dev/null +++ b/js_concurrent_module/utils/cache/SendableLruCache.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SendableLruCache.h" + +#include +#include + +#include "helper/napi_helper.h" +#include "js_native_api_types.h" +#include "native_engine/native_value.h" +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "helper/object_helper.h" + +namespace Commonlibrary::Concurrent::LruCache { + +using namespace Commonlibrary::Concurrent::Common::Helper; +constexpr uint32_t CACHE_DEFAULT_CAPACITY = 64; + +napi_value SendableLruCache::Init(napi_env env, napi_value exports) +{ + // instance properties + // napi_value name; + // NAPI_CALL(env, napi_create_string_utf8(env, "", 0, &name)); + + napi_property_descriptor properties[] = { + DECLARE_NAPI_STATIC_FUNCTION("put", Put), + // DECLARE_NAPI_STATIC_FUNCTION("get", Get) + }; + + // napi_define_properties(env, thisVar, sizeof(properties) / sizeof(properties[0], properties)); + napi_value SendableLruCacheClass = nullptr; + napi_define_sendable_class(env, "SendableLruCache", NAPI_AUTO_LENGTH, Constructor, nullptr, + sizeof(properties) / sizeof(properties[0]), properties, nullptr, &SendableLruCacheClass); + + napi_set_named_property(env, exports, "SendableLruCache", SendableLruCacheClass); + + return exports; +} + +napi_value SendableLruCache::Constructor(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); + NAPI_ASSERT(env, argc == 0 || argc == 1, "SendableLruCache::Constructor: the number of params must be zero or one"); + + // auto args = std::make_unique(); + napi_value args; + napi_value thisVar; + NAPI_CALL(env, napi_get_cb_info(env, cbinfo, &argc, &args, &thisVar, nullptr)); + + uint32_t cap = CACHE_DEFAULT_CAPACITY; + if (argc == 1) { + NAPI_CALL(env, napi_get_value_uint32(env, args, &cap)); + } + + SendableLruCache* cache = new SendableLruCache(); + + // napi_property_descriptor properties[] = { + // DECLARE_NAPI_FUNCTION("put", Put), + // DECLARE_NAPI_FUNCTION("get", Get), + // } + + // // napi_define_properties(env, thisVar, sizeof(properties) / sizeof(properties[0], properties)); + + // napi_define_sendable_class(env, "SendableLruCache", NAPI_AUTO_LENGTH, Constructor, nullptr, + // sizeof(properties) / sizeof(properties[0]), properties, nullptr, &asyncLockManagerClass); + + NAPI_CALL(env, napi_wrap_sendable(env, thisVar, cache, Destructor, nullptr)); + + // napi_ref ref; + // napi_create_reference(evn, this, 0, ref) + + return thisVar; +} + +// 判断入参是否为2 +// 获取入参和thisVar +// 使用napi_coerce_to_string将key转换为string +// 转换为napi_unwrap_sendable SendableLruCache +// 加锁 +// find +// 查找到,则需要修改时间,确保下次移除时只移除最久未使用的 +// 未查找到,insert +napi_value SendableLruCache::Put(napi_env env, napi_callback_info cbinfo) +{ + size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); + NAPI_ASSERT(env, argc == 2, "SendableLruCache::Put: the number of params must be two"); + + auto args = std::make_unique(argc); + napi_value thisVar; + NAPI_CALL(env, napi_get_cb_info(env, cbinfo, &argc, args.get(), &thisVar, nullptr)); + + napi_value keyStr; + NAPI_CALL(env, napi_coerce_to_string(env, args[0], &keyStr)); + std::string str(NapiHelper::GetString(env, keyStr)); + SendableLruCache* cache = nullptr; + NAPI_CALL(env, napi_unwrap_sendable(env, thisVar, reinterpret_cast(&cache))); + { + std::lock_guard lock(cache->cacheMutex_); + auto it = cache->cache_.find(str); + if (it != cache->cache_.end()) { + cache->accessList_.splice(cache->accessList_.end(), cache->accessList_, it->second); + } else { + StoreObject valObj(env, args[1]); + cache->accessList_.push_back({str, valObj}); + cache->cache_.insert({str, --(cache->accessList_.end())}); + } + } + + return args[1]; +} + +// napi_value SendableLruCache::Get(napi_env env, napi_callback_info cbinfo) +// { +// size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); +// NAPI_ASSERT(env, argc == 1, "SendableLruCache::Put: the number of params must be one"); + +// auto args = std::make_unique(); +// napi_value thisVar; +// NAPI_CALL(env, napi_get_cb_info(env, cbinfo, &argc, args.get(), &thisVar, nullptr)); + +// napi_value keyStr; +// NAPI_CALL(env, napi_coerce_to_string(env, args[0], &keyStr)); +// std::string str(NapiHelper::GetString(env, keyStr)); +// SendableLruCache* cache = nullptr; +// NAPI_CALL(env, napi_unwrap_sendable(env, thisVar, reinterpret_cast&cache)); +// { +// std::lock_guard lock(cache->cacheMutex_); +// auto it = cache->cache_.find(str); +// if (it != cache->cache_.end()) { +// accessList_.splice(accessList_.end(), accessList_, it->second); +// return it->second; +// } +// } + +// return args; +// } + +void SendableLruCache::Destructor(napi_env env, void *data, [[maybe_unused]] void *hint) +{ + SendableLruCache* cache = static_cast(data); + delete cache; +} + +} // namespace Commonlibrary::Concurrent::LocksModule diff --git a/js_concurrent_module/utils/cache/SendableLruCache.h b/js_concurrent_module/utils/cache/SendableLruCache.h new file mode 100644 index 00000000..7650a347 --- /dev/null +++ b/js_concurrent_module/utils/cache/SendableLruCache.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JS_CACHE_UTILS_SENDABLE_LRU_CACHE_H +#define JS_CACHE_UTILS_SENDABLE_LRU_CACHE_H + +#include +#include +#include + +#include "helper/error_helper.h" +#include "helper/napi_helper.h" +#include "helper/object_helper.h" + +namespace Commonlibrary::Concurrent::LruCache { + +class SendableLruCache { +public: + static napi_value Init(napi_env env, napi_value exports); + static napi_value Constructor(napi_env env, napi_callback_info cbinfo); + static void Destructor(napi_env env, void *nativeObject, void *finalize); + static napi_value Put(napi_env env, napi_callback_info cbinfo); + static napi_value Get(napi_env env, napi_callback_info cbinfo); + + SendableLruCache() = default; + SendableLruCache(const SendableLruCache &) = delete; + SendableLruCache &operator=(const SendableLruCache &) = delete; + SendableLruCache(SendableLruCache &&) = delete; + SendableLruCache &operator=(SendableLruCache &&) = delete; + ~SendableLruCache() = default; + +private: + typedef struct StoreObjectStruct { + napi_value val_; + napi_ref valRef_; + napi_env env_; + + StoreObjectStruct(napi_env env, napi_value val) + { + val_ = val; + env_ = env; + napi_create_reference(env, val_, 1, &valRef_); + } + + ~StoreObjectStruct() + { + napi_delete_reference(env_, valRef_); + } + } StoreObject; + + +private: + std::mutex cacheMutex_; + std::list> accessList_; + using ListIter = std::list>::iterator; + std::unordered_map cache_; +}; + +} // namespace Commonlibrary::Cache::LruCache + +#endif diff --git a/js_concurrent_module/utils/utils.cpp b/js_concurrent_module/utils/utils.cpp index 308d5175..96eba263 100644 --- a/js_concurrent_module/utils/utils.cpp +++ b/js_concurrent_module/utils/utils.cpp @@ -18,6 +18,7 @@ #include "locks/async_lock_manager.h" #include "json/json_manager.h" +#include "cache/SendableLruCache.h" #include "tools/log.h" #include "utils.h" @@ -49,8 +50,10 @@ napi_value Utils::Init(napi_env env, napi_value exports) { Commonlibrary::Concurrent::LocksModule::AsyncLockManager::Init(env, exports); Commonlibrary::Concurrent::JsonManager::Init(env, exports); + Commonlibrary::Concurrent::LruCache::SendableLruCache::Init(env, exports); InitGlobal(env, IS_SENDABLE_NAME, true, exports); - + // ArkTools.print("lhc lhc ", exports); + // exports->Dump(); return exports; } } // namespace Commonlibrary::Concurrent \ No newline at end of file diff --git a/js_concurrent_module/utils/utils_js.js b/js_concurrent_module/utils/utils_js.js new file mode 100644 index 00000000..7126d651 --- /dev/null +++ b/js_concurrent_module/utils/utils_js.js @@ -0,0 +1,262 @@ +let helpUtil = requireInternal('arkts.utils'); +let collections = requireNapi('arkts.collections'); +const typeErrorCode = 401; +class BusinessError extends Error { + constructor(msg) { + super(msg); + this.name = 'BusinessError'; + this.code = typeErrorCode; + } +} +class SendableLRUCache { + constructor(capacity) { + 'use sendable'; + // the following cache is a sendable instance of sendable map + this.cache = new collections.Map(); + this.lockCache = new helpUtil.locks.AsyncLock(); + // Default current size + this.maxSize = 64; + // Default maximum size + this.maxNumber = 2147483647; + this.putCount = 0; + this.createCount = 0; + this.evictionCount = 0; + this.hitCount = 0; + this.missCount = 0; + this.length = 0; + if (capacity !== undefined && capacity !== null) { + if (capacity <= 0 || capacity % 1 !== 0 || capacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${capacity} must be small integer`); + throw error; + } + this.maxSize = capacity; + } + console.log("dxg end function"); + } + async changeCapacity(newCapacity) { + await this.lockCache.lockAsync(() => { + while (this.cache.size > newCapacity) { + this.cache.delete(this.cache.keys().next().value); + this.evictionCount++; + this.afterRemoval(true, this.cache.keys(), this.cache.values(), null); + } + this.length = this.cache.size; + this.maxSize = newCapacity; + print("changeCapcity length: "); + print(this.length); + }); + } + afterRemoval(isEvict, key, value, newValue) { + } + createDefault(key) { + if (typeof key === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + return undefined; + } + async updateCapacity(newCapacity) { + if (typeof newCapacity !== 'number') { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be number`); + throw error; + } + if (newCapacity <= 0 || newCapacity % 1 !== 0 || newCapacity > this.maxNumber) { + let error = new BusinessError(`Parameter error. The type of ${newCapacity} must be small integer`); + throw error; + } + else if (this.cache.size > newCapacity) { + await this.changeCapacity(newCapacity); + } + } + async get(key) { + if (typeof key === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let value; + if (this.cache.has(key)) { + value = this.cache.get(key); + this.hitCount++; + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + return value; + } + this.missCount++; + let createValue = this.createDefault(key); + if (createValue === undefined) { + return undefined; + } + else { + value = await this.put(key, createValue); + this.createCount++; + if (value !== undefined) { + await this.put(key, value); + this.afterRemoval(false, key, createValue, value); + return value; + } + return createValue; + } + } + async put(key, value) { + if (typeof key === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (typeof value === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${value} must be Object`); + throw error; + } + if (key === null || value === null) { + let error = new BusinessError(`Parameter error. The type of key and value must be Object`); + throw error; + } + // check key and value is Sendable object, otherwise return error + if ((!helpUtil.isSendable(key)) && (!helpUtil.isSendable(value))) { + let error = new BusinessError(`Parameter error. The type of key and value must be Sendable`); + throw error; + } + let former = undefined; + this.putCount++; + if (this.cache.has(key)) { + former = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + this.afterRemoval(false, key, former, value); + } + else if (this.cache.size >= this.maxSize) { + this.afterRemoval(true, this.cache.keys().next().value, this.cache.values().next().value, null); + await this.lockCache.lockAsync(() => { + this.cache.delete(this.cache.keys().next().value); + }); + this.evictionCount++; + } + await this.lockCache.lockAsync(() => { + this.cache.set(key, value); + }); + this.length = this.cache.size; + former = this.cache.get(key); + return former; + } + async remove(key) { + if (typeof key === 'undefined' || key === null) { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + if (this.cache.has(key)) { + let former = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + }); + if (former !== null) { + this.afterRemoval(false, key, former, null); + this.length = this.cache.size; + return former; + } + } + this.length = this.cache.size; + return undefined; + } + async contains(key) { + if (typeof key === 'undefined') { + let error = new BusinessError(`Parameter error. The type of ${key} must be Object`); + throw error; + } + if (!helpUtil.isSendable(key)) { + let error = new BusinessError(`Parameter error. The type of key must be Sendable`); + throw error; + } + let flag = false; + if (this.cache.has(key)) { + flag = true; + this.hitCount++; + let value = this.cache.get(key); + await this.lockCache.lockAsync(() => { + this.cache.delete(key); + this.cache.set(key, value); + }); + this.length = this.cache.size; + return flag; + } + this.missCount++; + return flag; + } + getCreateCount() { + return this.createCount; + } + getMissCount() { + return this.missCount; + } + getRemovalCount() { + return this.evictionCount; + } + getMatchCount() { + return this.hitCount; + } + getPutCount() { + return this.putCount; + } + getCapacity() { + return this.maxSize; + } + async clear() { + this.afterRemoval(false, this.cache.keys(), this.cache.values(), null); + await this.lockCache.lockAsync(() => { + this.cache.clear(); + }); + this.length = this.cache.size; + } + isEmpty() { + return this.cache.size === 0; + } + toString() { + let peek = 0; + let hitRate = 0; + peek = this.hitCount + this.missCount; + if (peek !== 0) { + // The value is 100 times larger + hitRate = 100 * this.hitCount / peek; + } + let str = ''; + str = 'SendableLRUCache[ maxSize = ' + this.maxSize + ', hits = ' + this.hitCount + + ', misses = ' + this.missCount + ', hitRate = ' + hitRate + '% ]'; + return str; + } + values() { + let arr = new collections.Array(); + for (let value of this.cache.values()) { + arr.push(value); + } + return arr; + } + keys() { + let arr = new collections.Array(); + for (let key of this.cache.keys()) { + arr.push(key); + } + return arr; + } + entries() { + return this.cache.entries(); + } + [Symbol.iterator]() { + return this.cache.entries(); + } +} +export default { + SendableLRUCache: SendableLRUCache +}; -- Gitee