diff --git a/incremental/common/src/index.ts b/incremental/common/src/index.ts index 11136c33c8f0907ec6872c9b570e3a4eedd8f034..b55a601ef0ac19eeed05b3351c75f126cff2b3dc 100644 --- a/incremental/common/src/index.ts +++ b/incremental/common/src/index.ts @@ -42,6 +42,7 @@ export { int8Array, errorAsString, unsafeCast, + CoroutineLocalValue, scheduleCoroutine, memoryStats, launchJob diff --git a/incremental/compat/src/arkts/utils.ts b/incremental/compat/src/arkts/utils.ts index 0523e835a3140a173c71fe89e9cee790a52a5918..75c8e744f68fc54154f857441693f1883a2985b7 100644 --- a/incremental/compat/src/arkts/utils.ts +++ b/incremental/compat/src/arkts/utils.ts @@ -34,4 +34,18 @@ export function memoryStats(): string { export function launchJob(job: () => void): Promise { throw new Error("unsupported yet: return launch job()") -} \ No newline at end of file +} + +export class CoroutineLocalValue { + private map = new containers.ConcurrentHashMap + get(): T | undefined { + return this.map.get(CoroutineExtras.getWorkerId()) + } + set(value: T | undefined) { + if (value) { + this.map.set(CoroutineExtras.getWorkerId(), value) + } else { + this.map.delete(CoroutineExtras.getWorkerId()) + } + } +} diff --git a/incremental/compat/src/index.ts b/incremental/compat/src/index.ts index 24e868eac187f0e342d7349dbbdb6502f4dfa8dc..e4a0a9c4ddf1de7bc2a7a353f43622f4abd39c3b 100644 --- a/incremental/compat/src/index.ts +++ b/incremental/compat/src/index.ts @@ -61,6 +61,7 @@ export { int8Array, errorAsString, unsafeCast, + CoroutineLocalValue, scheduleCoroutine, memoryStats, launchJob diff --git a/incremental/compat/src/ohos/index.ts b/incremental/compat/src/ohos/index.ts index cafcfb1cd6fed3eb274e33d979849f07e17b5621..3edbe8fe57e06014ac819f591b92c40b3ae2b884 100644 --- a/incremental/compat/src/ohos/index.ts +++ b/incremental/compat/src/ohos/index.ts @@ -59,6 +59,7 @@ export { int8Array, errorAsString, unsafeCast, + CoroutineLocalValue, scheduleCoroutine, memoryStats, launchJob diff --git a/incremental/compat/src/typescript/utils.ts b/incremental/compat/src/typescript/utils.ts index 1d72692be8e3240af81231ee0389f62ee023758b..c51ff17f4c2bc746ff380b72afc34b7c67447b40 100644 --- a/incremental/compat/src/typescript/utils.ts +++ b/incremental/compat/src/typescript/utils.ts @@ -33,4 +33,14 @@ export function launchJob(job: () => void): Promise { job() }, 0) ) -} \ No newline at end of file +} + +export class CoroutineLocalValue { + private value: T | undefined = undefined + get(): T | undefined { + return this.value + } + set(value: T | undefined) { + this.value = value + } +} diff --git a/incremental/runtime/src/states/GlobalStateManager.ts b/incremental/runtime/src/states/GlobalStateManager.ts index fed60f1cf51534e7c25fbd4e18488c2621f10127..7c1285cf61fbcd0498a16d67d541060b9735df3a 100644 --- a/incremental/runtime/src/states/GlobalStateManager.ts +++ b/incremental/runtime/src/states/GlobalStateManager.ts @@ -13,6 +13,7 @@ * limitations under the License. */ +import { CoroutineLocalValue, KoalaCallsiteKey } from "@koalaui/common" import { ArrayState, Equivalent, MutableState, StateManager, ValueTracker, createStateManager } from "./State" /** @@ -20,7 +21,12 @@ import { ArrayState, Equivalent, MutableState, StateManager, ValueTracker, creat * @internal */ export class GlobalStateManager { - private static current: StateManager | undefined = undefined + private static localManager = new CoroutineLocalValue() + private static sharedManager: StateManager | undefined = undefined + + private static get current(): StateManager | undefined { + return GlobalStateManager.GetLocalManager() ?? GlobalStateManager.sharedManager + } /** * The current instance of a global state manager. @@ -30,7 +36,7 @@ export class GlobalStateManager { let current = GlobalStateManager.current if (current === undefined) { current = createStateManager() - GlobalStateManager.current = current + GlobalStateManager.sharedManager = current } return current } @@ -42,6 +48,30 @@ export class GlobalStateManager { static reset() { GlobalStateManager.current?.reset() } + + /** + * Get state manager by coroutine id. + * @internal + */ + static GetLocalManager(): StateManager | undefined { + return GlobalStateManager.localManager.get() + } + + /** + * Store state manager by coroutine id. + * @internal + */ + static SetLocalManager(manager: StateManager | undefined): void { + GlobalStateManager.localManager.set(manager) + } + + /** + * @return callsite key for a current context or `undefined` for global context + * @internal + */ + public static getCurrentScopeId(): KoalaCallsiteKey | undefined { + return GlobalStateManager.instance.currentScopeId + } } /** diff --git a/incremental/runtime/src/states/State.ts b/incremental/runtime/src/states/State.ts index f5afc5290c595da0656c708db3ca26cce037f2ae..3c924bc2446fd4162df99c89975371198dd864ba 100644 --- a/incremental/runtime/src/states/State.ts +++ b/incremental/runtime/src/states/State.ts @@ -44,6 +44,7 @@ export function createStateManager(): StateManager { * applications. */ export interface StateManager extends StateContext { + readonly currentScopeId: KoalaCallsiteKey | undefined syncChanges(): void isUpdateNeeded(): boolean updateSnapshot(): uint32 @@ -547,7 +548,8 @@ class StateManagerImpl implements StateManager { private readonly callbacks: MarkableQueue = markableQueue() readonly journal: Journal = new Journal() - constructor() { + get currentScopeId(): KoalaCallsiteKey | undefined { + return this.current?.id } reset(): void {