commit d95f8e51faa5bfc46e88f564ae4cf0966f9c4f17
parent b61bbe314a666f0d4cf4acb35d0ab4ae8ef62bdb
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 20 Sep 2026 09:07:56 -0300
lib/dynamic/webp: load the shared object exactly once
Init returned early only when the library had already loaded. On failure
_libwebp stayed zero, so the next call retried, and since Encode and Decode
called Init per call, a process without libwebp installed paid two failed
dlopens on every single operation — precisely the machines that fall through
to a pure-Go backend.
Diffstat:
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/lib/dynamic/webp/lib.go b/lib/dynamic/webp/lib.go
@@ -5,6 +5,7 @@ import (
"image"
"io"
"runtime"
+ "sync"
"git.sr.ht/~jackmordaunt/go-libwebp/v2/lib/common"
"github.com/ebitengine/purego"
@@ -38,11 +39,21 @@ func loadLibrary() (uintptr, error) {
return handle, err
}
-func Init() (err error) {
- if _libwebp != 0 {
- return nil
- }
+// Init loads libwebp and binds the entry points. It is safe to call
+// repeatedly and from multiple goroutines; the attempt is made exactly once,
+// so a process without the shared object does not re-run a failing dlopen on
+// every encode.
+func Init() error {
+ initOnce.Do(func() { initErr = load() })
+ return initErr
+}
+
+var (
+ initOnce sync.Once
+ initErr error
+)
+func load() (err error) {
_libwebp, err = loadLibrary()
if err != nil {
return fmt.Errorf("loading library: %w", err)