1
0
mirror of https://github.com/golang/go synced 2024-11-21 22:44:40 -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._pendingEvent = null;
this._scheduledTimeouts = new Map(); this._scheduledTimeouts = new Map();
this._nextCallbackTimeoutID = 1; this._nextCallbackTimeoutID = 1;
this._cleanup = null;
this._registry = new FinalizationRegistry((func, id) => {
if (this._cleanup) {
this._cleanup(id);
}
})
const setInt64 = (addr, v) => { const setInt64 = (addr, v) => {
this.mem.setUint32(addr + 0, v, true); this.mem.setUint32(addr + 0, v, true);
@ -563,12 +569,17 @@
_makeFuncWrapper(id) { _makeFuncWrapper(id) {
const go = this; const go = this;
return function () { const f = function () {
const event = { id: id, this: this, args: arguments }; const event = { id: id, this: this, args: arguments };
go._pendingEvent = event; go._pendingEvent = event;
go._resume(); go._resume();
return event.result; return event.result;
}; };
// Should never remove the cleanup function
if (id !== 1) {
this._registry.register(f, id);
}
return f;
} }
} }
})(); })();

View File

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