go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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:
Mlib/dynamic/webp/lib.go | 19+++++++++++++++----
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)