(ref string, fn func() (*moduleDef, error))
| 941 | } |
| 942 | |
| 943 | func (h *shellCallHandler) getOrInitDef(ref string, fn func() (*moduleDef, error)) (*moduleDef, error) { |
| 944 | // Fast path |
| 945 | if def := h.loadModDef(ref); def != nil { |
| 946 | return def, nil |
| 947 | } |
| 948 | if fn == nil { |
| 949 | return nil, fmt.Errorf("module %q not loaded", ref) |
| 950 | } |
| 951 | |
| 952 | // Each ref can go through multiple initialization functions until the |
| 953 | // module is found but we don't want to duplicate the effort in running |
| 954 | // the same function for the same ref, so we cache by ref+fn. |
| 955 | v, _ := h.modDefs.LoadOrStore(ref, &sync.Map{}) |
| 956 | |
| 957 | switch t := v.(type) { |
| 958 | case *moduleDef: |
| 959 | // Some other goroutine has already stored the initialized module. |
| 960 | return t, nil |
| 961 | case *sync.Map: |
| 962 | // A map means this ref is in the process of initialization. |
| 963 | // Ensure this function is only called once. |
| 964 | fnKey := fmt.Sprintf("%p", fn) |
| 965 | once, _ := t.LoadOrStore(fnKey, sync.OnceValues(fn)) |
| 966 | switch f := once.(type) { |
| 967 | case func() (*moduleDef, error): |
| 968 | def, err := f() |
| 969 | if err != nil || def == nil { |
| 970 | return nil, err |
| 971 | } |
| 972 | // Module found. If multiple goroutines reach this point they should |
| 973 | // have the same result from the onced function anyway. |
| 974 | h.modDefs.Store(def.SourceDigest, def) |
| 975 | return def, nil |
| 976 | default: |
| 977 | return nil, fmt.Errorf("unexpected initialization type %T for module definitions: %s", once, ref) |
| 978 | } |
| 979 | default: |
| 980 | return nil, fmt.Errorf("unexpected loaded type %T for module definitions: %s", v, ref) |
| 981 | } |
| 982 | } |
| 983 | |
| 984 | // loadModDef returns the module definition for a module ref, if it exists |
| 985 | func (h *shellCallHandler) loadModDef(ref string) *moduleDef { |
no test coverage detected