Commit a4cd0a49 authored by Aleksandr Demakin's avatar Aleksandr Demakin Committed by Robert Griesemer

go/types: fix race-y initialization of Struct.offsets

Use sync.Once to ensure, that 'offsets' field is initialized
once only in a threadsafe way.

Fixes #12887

Change-Id: I90ef929c421ccd3094339c67a39b02d8f2e47211
Reviewed-on: https://go-review.googlesource.com/16013Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 5ff309f4
...@@ -132,13 +132,8 @@ func (s *StdSizes) Sizeof(T Type) int64 { ...@@ -132,13 +132,8 @@ func (s *StdSizes) Sizeof(T Type) int64 {
if n == 0 { if n == 0 {
return 0 return 0
} }
offsets := t.offsets setOffsets(t, s)
if t.offsets == nil { return t.offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
// compute offsets on demand
offsets = s.Offsetsof(t.fields)
t.offsets = offsets
}
return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
case *Interface: case *Interface:
return s.WordSize * 2 return s.WordSize * 2
} }
...@@ -159,11 +154,13 @@ func (conf *Config) alignof(T Type) int64 { ...@@ -159,11 +154,13 @@ func (conf *Config) alignof(T Type) int64 {
} }
func (conf *Config) offsetsof(T *Struct) []int64 { func (conf *Config) offsetsof(T *Struct) []int64 {
offsets := T.offsets var offsets []int64
if offsets == nil && T.NumFields() > 0 { if T.NumFields() > 0 {
// compute offsets on demand // compute offsets on demand
if s := conf.Sizes; s != nil { if s := conf.Sizes; s != nil {
offsets = s.Offsetsof(T.fields) calculated := setOffsets(T, s)
offsets = T.offsets
if calculated {
// sanity checks // sanity checks
if len(offsets) != T.NumFields() { if len(offsets) != T.NumFields() {
panic("Config.Sizes.Offsetsof returned the wrong number of offsets") panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
...@@ -173,10 +170,11 @@ func (conf *Config) offsetsof(T *Struct) []int64 { ...@@ -173,10 +170,11 @@ func (conf *Config) offsetsof(T *Struct) []int64 {
panic("Config.Sizes.Offsetsof returned an offset < 0") panic("Config.Sizes.Offsetsof returned an offset < 0")
} }
} }
}
} else { } else {
offsets = stdSizes.Offsetsof(T.fields) setOffsets(T, &stdSizes)
offsets = T.offsets
} }
T.offsets = offsets
} }
return offsets return offsets
} }
...@@ -209,3 +207,15 @@ func align(x, a int64) int64 { ...@@ -209,3 +207,15 @@ func align(x, a int64) int64 {
y := x + a - 1 y := x + a - 1
return y - y%a return y - y%a
} }
// setOffsets sets the offsets of s for the given sizes if necessary.
// The result is true if the offsets were not set before; otherwise it
// is false.
func setOffsets(s *Struct, sizes Sizes) bool {
var calculated bool
s.offsetsOnce.Do(func() {
calculated = true
s.offsets = sizes.Offsetsof(s.fields)
})
return calculated
}
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
package types package types
import "sort" import (
"sort"
// TODO(gri) Revisit factory functions - make sure they have all relevant parameters. "sync"
)
// A Type represents a type of Go. // A Type represents a type of Go.
// All types implement the Type interface. // All types implement the Type interface.
...@@ -122,8 +123,8 @@ func (s *Slice) Elem() Type { return s.elem } ...@@ -122,8 +123,8 @@ func (s *Slice) Elem() Type { return s.elem }
type Struct struct { type Struct struct {
fields []*Var fields []*Var
tags []string // field tags; nil if there are no tags tags []string // field tags; nil if there are no tags
// TODO(gri) access to offsets is not threadsafe - fix this
offsets []int64 // field offsets in bytes, lazily initialized offsets []int64 // field offsets in bytes, lazily initialized
offsetsOnce sync.Once // for threadsafe lazy initialization of offsets
} }
// NewStruct returns a new struct with the given fields and corresponding field tags. // NewStruct returns a new struct with the given fields and corresponding field tags.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment