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 }