• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 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
16package std.core;
17
18export final class FinalizationRegistry<T> {
19
20    private callbackWithArg: (value: T) => void = (value: T) => { return };
21    private callbackWithoutArg: () => void = () => { return };
22    private isCallbackWithArg: boolean = true;
23    // Can be changed to local trimplet class when it will be implemented
24    private obj_token_refs: (Object | null)[] = [];
25    private callback_arguments: (T | null)[] = [];
26    // Contains chains of used and free trimplet indexes, [0] serves as uselist head
27    private idxarr: int[] = [];
28    private freeidx: int = -1;
29
30    private static readonly FIRST_SIZE = 32;
31
32    private init_arrays(size: int): void {
33        assert size > 1;
34        this.idxarr = new int[size];
35        this.idxarr[0] = -1; // first elem reserved for useidx head
36        this.enlarge_freeidx(1);
37
38        this.obj_token_refs = new Object[2 * size];
39        this.callback_arguments = new T[size];
40    }
41
42    private enlarge_arrays(newsize: int): void {
43        let old_idxarr = this.idxarr;
44        this.idxarr = new int[newsize];
45        for (let i = 0; i < old_idxarr.length; ++i) {
46            this.idxarr[i] = old_idxarr[i];
47        }
48        this.enlarge_freeidx(old_idxarr.length);
49
50        let old_obj_token_refs = this.obj_token_refs;
51        this.obj_token_refs = new Object[newsize * 2];
52        for (let i = 0; i < old_obj_token_refs.length; ++i) {
53            this.obj_token_refs[i] = old_obj_token_refs[i];
54        }
55        let old_callback_arguments = this.callback_arguments;
56        this.callback_arguments = new T[newsize];
57        for (let i = 0; i < old_callback_arguments.length; ++i) {
58            this.callback_arguments[i] = old_callback_arguments[i];
59        }
60    }
61
62    private enlarge_freeidx(oldsize: int): void {
63        // push new idxarr elements to freelist
64        let last_free = this.idxarr.length - 1;
65        this.idxarr[last_free] = this.freeidx;
66        for (let i = oldsize; i < last_free; i++) {
67            this.idxarr[i] = i + 1;
68        }
69        this.freeidx = oldsize;
70    }
71
72    /**
73     * Returns size of the FinalizationRegistry
74     *
75     * @returns size of the FinalizationRegistry
76     */
77    public getSize(): int {
78        let count: int;
79        for (let i = this.idxarr[0]; i != -1; i = this.idxarr[i]) {
80            count++;
81        }
82        return count;
83    }
84
85    /**
86     * Creates FinalizationRegistry object with the specific callback function.
87     * One can register an object to this queue and after reclamation of this object
88     * the callback function will be called.
89     *
90     * @param callback_value callback function which will be called
91     * after registered object reclamation.
92     */
93    public constructor(callback_value: (value: T) => void) {
94        this.init_arrays(FinalizationRegistry.FIRST_SIZE);
95        this.callbackWithArg = callback_value;
96        this.isCallbackWithArg = true;
97        FinalizationRegistry.registerInstance(this);
98    }
99
100    /**
101     * Creates FinalizationRegistry object with the specific callback function,
102     * which doesn't have any argument
103     * One can register an object to this queue and after reclamation of this object
104     * the callback function will be called.
105     *
106     * @param callback_value callback function which will be called
107     * after registered object reclamation.
108     */
109    public constructor(callback_value: () => void) {
110        this.init_arrays(FinalizationRegistry.FIRST_SIZE);
111        this.callbackWithoutArg = callback_value;
112        this.isCallbackWithArg = false;
113        FinalizationRegistry.registerInstance(this);
114    }
115
116    /**
117     * Registers an object with specific callback argument and
118     * with token object - an object which can be used to remove
119     * this registred callback. @see unregister
120     * After the object will be removed, the callback method
121     * will be called with the specific argument.
122     *
123     * @param object a tracking object
124     *
125     * @param callbackArg callback argument for callback method
126     *
127     * @param token an object which can be used to remove
128     * this registred callback if provided
129     */
130    public register(object: Object, callbackArg: T, token?: Object): void {
131        let refObject = new WeakRef<Object>(object);
132        let refToken: WeakRef<Object> | null = null;
133        if (token != null) {
134            refToken = new WeakRef<Object>(token);
135        }
136        let free_index = this.allocTrimplet();
137        // NOTE: GC and thus cleanup function can be triggered here after JIT/AOT optimizations
138        this.setTrimplet(free_index, refObject, callbackArg, refToken);
139    }
140
141    /**
142     * Removes all registered callbacks with the specific token value
143     *
144     * @param token all registered callbacks with this token will be removed
145     */
146    public unregister(token: Object): void {
147        let prev = 0;
148        for (let i = this.idxarr[prev]; i != -1; prev = i, i = this.idxarr[i]) {
149            let refToken = this.getToken(i);
150            if (refToken != null && refToken.deref() == token) {
151                this.freeNextTrimplet(prev);
152                i = prev; // iterator rollback
153            }
154        }
155    }
156
157    private cleanup(): void {
158        let prev = 0;
159        for (let i = this.idxarr[prev]; i != -1; prev = i, i = this.idxarr[i]) {
160            let object = this.getObject(i);
161            if (object != null && object.deref() == undefined) {
162                // Remove from trimplet first to prevent
163                // calling it again in separate thread
164                let arg: T | null = this.getCallbackArg(i);
165                this.freeNextTrimplet(prev);
166                i = prev; // iterator rollback
167                if (this.isCallbackWithArg) {
168                    this.callbackWithArg(arg!);
169                } else {
170                    this.callbackWithoutArg();
171                }
172            }
173        }
174    }
175
176    private allocTrimplet(): int {
177        if (this.freeidx == -1) {
178            this.enlarge_arrays(this.idxarr.length * 2);
179        }
180        // pop elem from freelist
181        let new_idx = this.freeidx;
182        this.freeidx = this.idxarr[new_idx];
183
184        // push elem to uselist
185        this.idxarr[new_idx] = this.idxarr[0];
186        this.idxarr[0] = new_idx;
187        return new_idx;
188    }
189
190    private freeNextTrimplet(index: int): void {
191        // remove next elem from uselist
192        let freed_idx = this.idxarr[index];
193        this.idxarr[index] = this.idxarr[freed_idx];
194
195        // push freed elem to freelist
196        this.idxarr[freed_idx] = this.freeidx;
197        this.freeidx = freed_idx;
198
199        // clean data
200        this.setTrimplet(freed_idx, null, null, null);
201    }
202
203    private getObject(index: int): WeakRef<Object> | null {
204        return this.obj_token_refs[2 * index] as (WeakRef<Object> | null);
205    }
206
207    private getCallbackArg(index: int): T | null {
208        return this.callback_arguments[index];
209    }
210
211    private getToken(index: int): WeakRef<Object> | null {
212        return this.obj_token_refs[2 * index + 1] as (WeakRef<Object> | null);
213    }
214
215    private setTrimplet(index: int, obj: WeakRef<Object> | null, cbArg: T | null, token: WeakRef<Object> | null): void {
216        this.obj_token_refs[2 * index] = obj;
217        this.obj_token_refs[2 * index + 1] = token;
218        this.callback_arguments[index] = cbArg;
219    }
220
221    private static native registerInstance(object: Object): void;
222
223}
224