| /** |
| # Copyright 2024 NVIDIA CORPORATION |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| **/ |
| |
| package nvsandboxutils |
| |
| import ( |
| "errors" |
| "fmt" |
| "sync" |
| |
| "github.com/NVIDIA/go-nvml/pkg/dl" |
| ) |
| |
| const ( |
| defaultNvSandboxUtilsLibraryName = "libnvidia-sandboxutils.so.1" |
| defaultNvSandboxUtilsLibraryLoadFlags = dl.RTLD_LAZY | dl.RTLD_GLOBAL |
| ) |
| |
| var errLibraryNotLoaded = errors.New("library not loaded") |
| var errLibraryAlreadyLoaded = errors.New("library already loaded") |
| |
| // dynamicLibrary is an interface for abstacting the underlying library. |
| // This also allows for mocking and testing. |
| |
| //go:generate moq -rm -stub -out dynamicLibrary_mock.go . dynamicLibrary |
| type dynamicLibrary interface { |
| Lookup(string) error |
| Open() error |
| Close() error |
| } |
| |
| // library represents an nvsandboxutils library. |
| // This includes a reference to the underlying DynamicLibrary |
| type library struct { |
| sync.Mutex |
| path string |
| refcount refcount |
| dl dynamicLibrary |
| } |
| |
| // libnvsandboxutils is a global instance of the nvsandboxutils library. |
| var libnvsandboxutils = newLibrary() |
| |
| func New(opts ...LibraryOption) Interface { |
| return newLibrary(opts...) |
| } |
| |
| func newLibrary(opts ...LibraryOption) *library { |
| l := &library{} |
| l.init(opts...) |
| return l |
| } |
| |
| func (l *library) init(opts ...LibraryOption) { |
| o := libraryOptions{} |
| for _, opt := range opts { |
| opt(&o) |
| } |
| |
| if o.path == "" { |
| o.path = defaultNvSandboxUtilsLibraryName |
| } |
| if o.flags == 0 { |
| o.flags = defaultNvSandboxUtilsLibraryLoadFlags |
| } |
| |
| l.path = o.path |
| l.dl = dl.New(o.path, o.flags) |
| } |
| |
| // LookupSymbol checks whether the specified library symbol exists in the library. |
| // Note that this requires that the library be loaded. |
| func (l *library) LookupSymbol(name string) error { |
| if l == nil || l.refcount == 0 { |
| return fmt.Errorf("error looking up %s: %w", name, errLibraryNotLoaded) |
| } |
| return l.dl.Lookup(name) |
| } |
| |
| // load initializes the library and updates the versioned symbols. |
| // Multiple calls to an already loaded library will return without error. |
| func (l *library) load() (rerr error) { |
| l.Lock() |
| defer l.Unlock() |
| |
| defer func() { l.refcount.IncOnNoError(rerr) }() |
| if l.refcount > 0 { |
| return nil |
| } |
| |
| if err := l.dl.Open(); err != nil { |
| return fmt.Errorf("error opening %s: %w", l.path, err) |
| } |
| |
| // Update the errorStringFunc to point to nvsandboxutils.ErrorString |
| errorStringFunc = nvsanboxutilsErrorString |
| |
| // Update all versioned symbols |
| l.updateVersionedSymbols() |
| |
| return nil |
| } |
| |
| // close the underlying library and ensure that the global pointer to the |
| // library is set to nil to ensure that subsequent calls to open will reinitialize it. |
| // Multiple calls to an already closed nvsandboxutils library will return without error. |
| func (l *library) close() (rerr error) { |
| l.Lock() |
| defer l.Unlock() |
| |
| defer func() { l.refcount.DecOnNoError(rerr) }() |
| if l.refcount != 1 { |
| return nil |
| } |
| |
| if err := l.dl.Close(); err != nil { |
| return fmt.Errorf("error closing %s: %w", l.path, err) |
| } |
| |
| // Update the errorStringFunc to point to defaultErrorStringFunc |
| errorStringFunc = defaultErrorStringFunc |
| |
| return nil |
| } |
| |
| // Default all versioned APIs to v1 (to infer the types) |
| var ( |
| // Insert default versions for APIs here. |
| // Example: |
| // nvsandboxUtilsFunction = nvsandboxUtilsFunction_v1 |
| ) |
| |
| // updateVersionedSymbols checks for versioned symbols in the loaded dynamic library. |
| // If newer versioned symbols exist, these replace the default `v1` symbols initialized above. |
| // When new versioned symbols are added, these would have to be initialized above and have |
| // corresponding checks and subsequent assignments added below. |
| func (l *library) updateVersionedSymbols() { |
| // Example: |
| // err := l.dl.Lookup("nvsandboxUtilsFunction_v2") |
| // if err == nil { |
| // nvsandboxUtilsFunction = nvsandboxUtilsFunction_v2 |
| // } |
| } |