1
0
mirror of https://github.com/golang/go synced 2024-11-21 11:34:44 -07:00

syscall/js: auto recycle Func

This commit is contained in:
Zxilly 2024-09-08 17:47:37 +08:00
parent 807e01db48
commit 0f6f58fa9b
No known key found for this signature in database
GPG Key ID: 47AB1DEC841BC6A2
2 changed files with 29 additions and 10 deletions

View File

@ -115,6 +115,12 @@
this._pendingEvent = null;
this._scheduledTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
this._cleanup = null;
this._registry = new FinalizationRegistry((func, id) => {
if (this._cleanup) {
this._cleanup(id);
}
})
const setInt64 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true);
@ -563,12 +569,17 @@
_makeFuncWrapper(id) {
const go = this;
return function () {
const f = function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
// Should never remove the cleanup function
if (id !== 1) {
this._registry.register(f, id);
}
return f;
}
}
})();

View File

@ -9,7 +9,8 @@ package js
import "sync"
var (
funcsMu sync.Mutex
funcsMu sync.Mutex
// funcID 1 is reserved for func garbage collection callback.
funcs = make(map[uint32]func(Value, []Value) any)
nextFuncID uint32 = 1
)
@ -36,8 +37,6 @@ type Func struct {
// API, which requires the event loop, like fetch (http.Client), will cause an
// immediate deadlock. Therefore a blocking function should explicitly start a
// new goroutine.
//
// Func.Release must be called to free up resources when the function will not be invoked any more.
func FuncOf(fn func(this Value, args []Value) any) Func {
funcsMu.Lock()
id := nextFuncID
@ -50,13 +49,9 @@ func FuncOf(fn func(this Value, args []Value) any) Func {
}
}
// Release frees up resources allocated for the function.
// The function must not be invoked after calling Release.
// It is allowed to call Release while the function is still running.
// Release is a no-op method, the function is automatically released
// when it is garbage collected on the JavaScript side.
func (c Func) Release() {
funcsMu.Lock()
delete(funcs, c.id)
funcsMu.Unlock()
}
// setEventHandler is defined in the runtime package.
@ -64,6 +59,19 @@ func setEventHandler(fn func() bool)
func init() {
setEventHandler(handleEvent)
// Set func garbage collection callback.
cleanup := FuncOf(func(this Value, args []Value) any {
id := uint32(args[0].Int())
funcsMu.Lock()
delete(funcs, id)
funcsMu.Unlock()
return Undefined()
})
if cleanup.id != 1 {
panic("bad function id for cleanup")
}
jsGo.Set("_cleanup", cleanup)
}
// handleEvent retrieves the pending event (window._pendingEvent) and calls the js.Func on it.