github.com/taubyte/vm-wasm-utils@v1.0.2/namespace.go (about)

     1  package wasm
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/tetratelabs/wazero/api"
     9  )
    10  
    11  // Namespace is a collection of instantiated modules which cannot conflict on name.
    12  type Namespace struct {
    13  	// moduleNames ensures no race conditions instantiating two modules of the same name
    14  	moduleNames []string // guarded by mux
    15  
    16  	// modules holds the instantiated Wasm modules by module name from Instantiate.
    17  	modules map[string]*ModuleInstance // guarded by mux
    18  
    19  	// mux is used to guard the fields from concurrent access.
    20  	mux sync.RWMutex
    21  }
    22  
    23  // newNamespace returns an empty namespace.
    24  func newNamespace() *Namespace {
    25  	return &Namespace{
    26  		moduleNames: nil,
    27  		modules:     map[string]*ModuleInstance{},
    28  	}
    29  }
    30  
    31  // addModule makes the module visible for import.
    32  func (ns *Namespace) addModule(m *ModuleInstance) {
    33  	ns.mux.Lock()
    34  	defer ns.mux.Unlock()
    35  	ns.modules[m.Name] = m
    36  }
    37  
    38  // deleteModule makes the moduleName available for instantiation again.
    39  func (ns *Namespace) deleteModule(moduleName string) {
    40  	ns.mux.Lock()
    41  	defer ns.mux.Unlock()
    42  	delete(ns.modules, moduleName)
    43  	// remove this module name
    44  	for i, n := range ns.moduleNames {
    45  		if n == moduleName {
    46  			ns.moduleNames = append(ns.moduleNames[:i], ns.moduleNames[i+1:]...)
    47  			break
    48  		}
    49  	}
    50  }
    51  
    52  // module returns the module of the given name or nil if not in this namespace
    53  func (ns *Namespace) module(moduleName string) *ModuleInstance {
    54  	ns.mux.RLock()
    55  	defer ns.mux.RUnlock()
    56  	return ns.modules[moduleName]
    57  }
    58  
    59  // requireModules returns all instantiated modules whose names equal the keys in the input, or errs if any are missing.
    60  func (ns *Namespace) requireModules(moduleNames map[string]struct{}) (map[string]*ModuleInstance, error) {
    61  	ns.mux.RLock()
    62  	defer ns.mux.RUnlock()
    63  
    64  	ret := make(map[string]*ModuleInstance, len(moduleNames))
    65  	for n := range moduleNames {
    66  		m, ok := ns.modules[n]
    67  		if !ok {
    68  			return nil, fmt.Errorf("module[%s] not instantiated", n)
    69  		}
    70  		ret[n] = m
    71  	}
    72  	return ret, nil
    73  }
    74  
    75  // requireModuleName is a pre-flight check to reserve a module.
    76  // This must be reverted on error with deleteModule if initialization fails.
    77  func (ns *Namespace) requireModuleName(moduleName string) error {
    78  	ns.mux.Lock()
    79  	defer ns.mux.Unlock()
    80  	for _, n := range ns.moduleNames {
    81  		if n == moduleName {
    82  			return fmt.Errorf("module[%s] has already been instantiated", moduleName)
    83  		}
    84  	}
    85  	ns.moduleNames = append(ns.moduleNames, moduleName)
    86  	return nil
    87  }
    88  
    89  // AliasModule aliases the instantiated module named `src` as `dst`.
    90  //
    91  // Note: This is only used for spectests.
    92  func (ns *Namespace) AliasModule(src, dst string) {
    93  	ns.modules[dst] = ns.modules[src]
    94  }
    95  
    96  // CloseWithExitCode implements the same method as documented on wazero.Namespace.
    97  func (ns *Namespace) CloseWithExitCode(ctx context.Context, exitCode uint32) (err error) {
    98  	ns.mux.Lock()
    99  	defer ns.mux.Unlock()
   100  	// Close modules in reverse initialization order.
   101  	for i := len(ns.moduleNames) - 1; i >= 0; i-- {
   102  		// If closing this module errs, proceed anyway to close the others.
   103  		if m, ok := ns.modules[ns.moduleNames[i]]; ok {
   104  			if _, e := m.CallCtx.close(ctx, exitCode); e != nil && err == nil {
   105  				err = e // first error
   106  			}
   107  		}
   108  	}
   109  	ns.moduleNames = nil
   110  	ns.modules = map[string]*ModuleInstance{}
   111  	return
   112  }
   113  
   114  // Module implements wazero.Namespace Module
   115  func (ns *Namespace) Module(moduleName string) api.Module {
   116  	if m := ns.module(moduleName); m != nil {
   117  		return m.CallCtx
   118  	} else {
   119  		return nil
   120  	}
   121  }