1/* 2 * Copyright (c) 2025 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 17class WeakRefPool { 18 // map objects -> weakrefs 19 private static wmap_ = new WeakMap(); 20 // map weakref -> Map<tag, gc-callback> 21 private static fmap_ = new Map(); 22 // 23 private static freg_ = new FinalizationRegistry((weakRef: WeakRef<object>) => { 24 WeakRefPool.getTaggedCallbacks(weakRef).forEach(fn => fn()); 25 WeakRefPool.fmap_.delete(weakRef); 26 }); 27 28 // Create a WeakRef for the given object and put it into the pool, or get 29 // existing WeakRef from the pool if the object is already there. WeakRefs 30 // for the same object are always identical. 31 public static get<T extends object>(obj: T): WeakRef<T> 32 { 33 let weakRef = WeakRefPool.wmap_.get(obj); 34 if (weakRef === undefined) { 35 WeakRefPool.wmap_.set(obj, weakRef = new WeakRef(obj)); 36 } 37 return weakRef; 38 } 39 40 // Add handler to track when the given object is GC'ed. 41 // Tag is used by unregister() only. Pair <obj, tag> should be unique per callback 42 public static register<T extends object>(obj: T, tag: unknown, callback: () => void): void { 43 const weakRef = WeakRefPool.get(obj); 44 const tagMap = WeakRefPool.getTaggedCallbacks(weakRef); 45 tagMap.size || WeakRefPool.freg_.register(obj, weakRef); 46 tagMap.set(tag, callback); 47 } 48 49 public static unregister<T extends object>(obj: T, tag: unknown): void { 50 const weakRef = WeakRefPool.get(obj); 51 const tagMap = WeakRefPool.getTaggedCallbacks(weakRef); 52 tagMap.delete(tag); 53 tagMap.size || WeakRefPool.freg_.unregister(weakRef); 54 tagMap.size || WeakRefPool.fmap_.delete(weakRef); 55 } 56 57 private static getTaggedCallbacks<T extends object>(weakRef: WeakRef<T>): Map<unknown, () => void> { 58 let tagMap = WeakRefPool.fmap_.get(weakRef); 59 if (tagMap === undefined) { 60 WeakRefPool.fmap_.set(weakRef, tagMap = new Map()); 61 } 62 return tagMap; 63 } 64 65 // debug only 66 private static getTaggedCallbacksSize(): number { 67 let size = 0; 68 WeakRefPool.fmap_.forEach(tagMap => size += tagMap.size); 69 return size; 70 } 71}