wa-lang.org/wazero@v1.0.2/internal/wasm/namespace.go (about)

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