28c2ffb84f
Reference-count the process-global ONNX Runtime across embedder and routing-head sessions, make close idempotent, and require named proof that both aggregate routing gates executed rather than self-skipped (V-716). Owner explicitly requested direct commits to master.
125 lines
3.5 KiB
Go
125 lines
3.5 KiB
Go
package router
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
ort "github.com/yalue/onnxruntime_go"
|
|
)
|
|
|
|
// ONNXRuntimeLease keeps the process-global ONNX Runtime environment alive.
|
|
//
|
|
// onnxruntime_go exposes the environment as package-global state: it may be
|
|
// initialized only once, and destroying it while any session is alive is
|
|
// unsafe. A lease makes that ownership explicit. Model wrappers hold one for
|
|
// the lifetime of their session; TestMain may hold an additional lease so a
|
|
// package's model-aware gates share one environment instead of repeatedly
|
|
// loading and unloading the shared library.
|
|
type ONNXRuntimeLease struct {
|
|
once sync.Once
|
|
err error
|
|
}
|
|
|
|
var sharedONNXRuntime = struct {
|
|
sync.Mutex
|
|
refs int
|
|
libPath string
|
|
managed bool
|
|
}{}
|
|
|
|
// AcquireONNXRuntime acquires the process-global ONNX Runtime environment.
|
|
// The first caller initializes it; later callers share it. A non-empty library
|
|
// path must agree with the path that initialized the active environment.
|
|
//
|
|
// The external-environment branch is intentional. It keeps this package safe
|
|
// when an executable initialized onnxruntime_go directly before constructing
|
|
// a router model; in that case the lease never destroys state it does not own.
|
|
func AcquireONNXRuntime(libPath string) (*ONNXRuntimeLease, error) {
|
|
libPath = cleanONNXLibraryPath(libPath)
|
|
|
|
sharedONNXRuntime.Lock()
|
|
defer sharedONNXRuntime.Unlock()
|
|
|
|
if sharedONNXRuntime.refs > 0 {
|
|
if err := compatibleONNXLibraryPath(sharedONNXRuntime.libPath, libPath); err != nil {
|
|
return nil, err
|
|
}
|
|
sharedONNXRuntime.refs++
|
|
return &ONNXRuntimeLease{}, nil
|
|
}
|
|
|
|
if ort.IsInitialized() {
|
|
// Some other component owns the already-live environment. Attach to it,
|
|
// but never unload its shared library when our last lease closes.
|
|
sharedONNXRuntime.refs = 1
|
|
sharedONNXRuntime.libPath = libPath
|
|
sharedONNXRuntime.managed = false
|
|
return &ONNXRuntimeLease{}, nil
|
|
}
|
|
|
|
if libPath != "" {
|
|
ort.SetSharedLibraryPath(libPath)
|
|
}
|
|
if err := ort.InitializeEnvironment(); err != nil {
|
|
return nil, fmt.Errorf("onnx: init environment: %w", err)
|
|
}
|
|
sharedONNXRuntime.refs = 1
|
|
sharedONNXRuntime.libPath = libPath
|
|
sharedONNXRuntime.managed = true
|
|
return &ONNXRuntimeLease{}, nil
|
|
}
|
|
|
|
// Close releases one environment lease. The last lease destroys an
|
|
// environment initialized by this package, after every model session that
|
|
// held a lease has already been destroyed.
|
|
func (l *ONNXRuntimeLease) Close() error {
|
|
if l == nil {
|
|
return nil
|
|
}
|
|
l.once.Do(func() {
|
|
l.err = releaseONNXRuntime()
|
|
})
|
|
return l.err
|
|
}
|
|
|
|
func releaseONNXRuntime() error {
|
|
sharedONNXRuntime.Lock()
|
|
defer sharedONNXRuntime.Unlock()
|
|
|
|
if sharedONNXRuntime.refs == 0 {
|
|
return fmt.Errorf("onnx: release environment without a lease")
|
|
}
|
|
sharedONNXRuntime.refs--
|
|
if sharedONNXRuntime.refs != 0 {
|
|
return nil
|
|
}
|
|
|
|
managed := sharedONNXRuntime.managed
|
|
sharedONNXRuntime.libPath = ""
|
|
sharedONNXRuntime.managed = false
|
|
if !managed {
|
|
return nil
|
|
}
|
|
if err := ort.DestroyEnvironment(); err != nil {
|
|
return fmt.Errorf("onnx: destroy environment: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cleanONNXLibraryPath(path string) string {
|
|
if path == "" {
|
|
return ""
|
|
}
|
|
return filepath.Clean(path)
|
|
}
|
|
|
|
func compatibleONNXLibraryPath(active, requested string) error {
|
|
// Empty means "use the environment that is already active". RouterHeads
|
|
// intentionally has no second copy of the embedder's library-path config.
|
|
if active == "" || requested == "" || active == requested {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("onnx: runtime already uses shared library %q, cannot also use %q", active, requested)
|
|
}
|