mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 17:01:04 -06:00
657932261b
The Close methods on shadow.Values require pointer receivers because they contain a sync.Mutex, but that value was being copied through Value.Interface by the closeWalker. Because reflectwalk passes the struct fields to the StructField method as they are defined in the struct, and they may have been read as a value, we can't immediately call Interface() to check the method set without possibly copying the internal mutex values. Use the Implements method to first check if we need to call Interface, and if it's not, then we can check if the value is addressable. Because of this use of reflection, we can't vet for the copying of these locks. The minimal amount of code in the Close method left us only with a race detected within the mutex itself, which leads to a stacktrace pointing to the runtime rather than our code.
84 lines
1.8 KiB
Go
84 lines
1.8 KiB
Go
package shadow
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/mitchellh/reflectwalk"
|
|
)
|
|
|
|
// Close will close all shadow values within the given structure.
|
|
//
|
|
// This uses reflection to walk the structure, find all shadow elements,
|
|
// and close them. Currently this will only find struct fields that are
|
|
// shadow values, and not slice elements, etc.
|
|
func Close(v interface{}) error {
|
|
// We require a pointer so we can address the internal fields
|
|
val := reflect.ValueOf(v)
|
|
if val.Kind() != reflect.Ptr {
|
|
return fmt.Errorf("value must be a pointer")
|
|
}
|
|
|
|
// Walk and close
|
|
var w closeWalker
|
|
if err := reflectwalk.Walk(v, &w); err != nil {
|
|
return err
|
|
}
|
|
|
|
return w.Err
|
|
}
|
|
|
|
type closeWalker struct {
|
|
Err error
|
|
}
|
|
|
|
func (w *closeWalker) Struct(reflect.Value) error {
|
|
// Do nothing. We implement this for reflectwalk.StructWalker
|
|
return nil
|
|
}
|
|
|
|
var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem()
|
|
|
|
func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error {
|
|
// Not sure why this would be but lets avoid some panics
|
|
if !v.IsValid() {
|
|
return nil
|
|
}
|
|
|
|
// Empty for exported, so don't check unexported fields
|
|
if f.PkgPath != "" {
|
|
return nil
|
|
}
|
|
|
|
// Verify the io.Closer is in this package
|
|
typ := v.Type()
|
|
if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" {
|
|
return nil
|
|
}
|
|
|
|
var closer io.Closer
|
|
if v.Type().Implements(closerType) {
|
|
closer = v.Interface().(io.Closer)
|
|
} else if v.CanAddr() {
|
|
// The Close method may require a pointer receiver, but we only have a value.
|
|
v := v.Addr()
|
|
if v.Type().Implements(closerType) {
|
|
closer = v.Interface().(io.Closer)
|
|
}
|
|
}
|
|
|
|
if closer == nil {
|
|
return reflectwalk.SkipEntry
|
|
}
|
|
|
|
// Close it
|
|
if err := closer.Close(); err != nil {
|
|
w.Err = multierror.Append(w.Err, err)
|
|
}
|
|
|
|
// Don't go into the struct field
|
|
return reflectwalk.SkipEntry
|
|
}
|