internal/filedesc: move message options to L1 initialization
Avoid a deadlock when registering a legacy ExtensionType, caused by
initialization of the "internal/impl".ExtensionInfo calling IsMessageSet
on the MessageDescriptor of the type being extended.
We can avoid this deadlock either by initializing the ExtensionType
outside of the GlobalTypes mutex, or by moving IsMessageSet to L1
initialization of the MessageDescriptor so that it doesn't trigger lazy
init.
CL 204804 takes the former approach; this CL takes the latter.
Change-Id: Idfc1ed36a23a139839290ea32492142a17f68cf5
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/205957
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/filedesc/desc.go b/internal/filedesc/desc.go
index 72a45e2..8185758 100644
--- a/internal/filedesc/desc.go
+++ b/internal/filedesc/desc.go
@@ -180,14 +180,14 @@
L2 *MessageL2 // protected by fileDesc.once
}
MessageL1 struct {
- Enums Enums
- Messages Messages
- Extensions Extensions
+ Enums Enums
+ Messages Messages
+ Extensions Extensions
+ IsMapEntry bool // promoted from google.protobuf.MessageOptions
+ IsMessageSet bool // promoted from google.protobuf.MessageOptions
}
MessageL2 struct {
Options func() pref.ProtoMessage
- IsMapEntry bool // promoted from google.protobuf.MessageOptions
- IsMessageSet bool // promoted from google.protobuf.MessageOptions
Fields Fields
Oneofs Oneofs
ReservedNames Names
@@ -234,7 +234,7 @@
}
return descopts.Message
}
-func (md *Message) IsMapEntry() bool { return md.lazyInit().IsMapEntry }
+func (md *Message) IsMapEntry() bool { return md.L1.IsMapEntry }
func (md *Message) Fields() pref.FieldDescriptors { return &md.lazyInit().Fields }
func (md *Message) Oneofs() pref.OneofDescriptors { return &md.lazyInit().Oneofs }
func (md *Message) ReservedNames() pref.Names { return &md.lazyInit().ReservedNames }
@@ -263,7 +263,7 @@
// WARNING: This method is exempt from the compatibility promise and may be
// removed in the future without warning.
func (md *Message) IsMessageSet() bool {
- return md.lazyInit().IsMessageSet
+ return md.L1.IsMessageSet
}
func (fd *Field) Options() pref.ProtoMessage {
diff --git a/internal/filedesc/desc_init.go b/internal/filedesc/desc_init.go
index 1b6daf0..249021f 100644
--- a/internal/filedesc/desc_init.go
+++ b/internal/filedesc/desc_init.go
@@ -314,6 +314,8 @@
posExtensions = len(b0) - len(b) - n - m
}
numExtensions++
+ case fieldnum.DescriptorProto_Options:
+ md.unmarshalSeedOptions(v)
}
prevField = num
default:
@@ -364,6 +366,27 @@
}
}
+func (md *Message) unmarshalSeedOptions(b []byte) {
+ for len(b) > 0 {
+ num, typ, n := wire.ConsumeTag(b)
+ b = b[n:]
+ switch typ {
+ case wire.VarintType:
+ v, m := wire.ConsumeVarint(b)
+ b = b[m:]
+ switch num {
+ case fieldnum.MessageOptions_MapEntry:
+ md.L1.IsMapEntry = wire.DecodeBool(v)
+ case fieldnum.MessageOptions_MessageSetWireFormat:
+ md.L1.IsMessageSet = wire.DecodeBool(v)
+ }
+ default:
+ m := wire.ConsumeFieldValue(num, typ, b)
+ b = b[m:]
+ }
+ }
+}
+
func (xd *Extension) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd pref.Descriptor, i int) {
xd.L0.ParentFile = pf
xd.L0.Parent = pd
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
index 86720a0..309d739 100644
--- a/internal/filedesc/desc_lazy.go
+++ b/internal/filedesc/desc_lazy.go
@@ -348,9 +348,9 @@
b = b[m:]
switch num {
case fieldnum.MessageOptions_MapEntry:
- md.L2.IsMapEntry = wire.DecodeBool(v)
+ md.L1.IsMapEntry = wire.DecodeBool(v)
case fieldnum.MessageOptions_MessageSetWireFormat:
- md.L2.IsMessageSet = wire.DecodeBool(v)
+ md.L1.IsMessageSet = wire.DecodeBool(v)
}
default:
m := wire.ConsumeFieldValue(num, typ, b)
diff --git a/internal/impl/legacy_message.go b/internal/impl/legacy_message.go
index dbc99ed..e078045 100644
--- a/internal/impl/legacy_message.go
+++ b/internal/impl/legacy_message.go
@@ -297,7 +297,7 @@
md2.L0.Parent = md
md2.L0.Index = n
- md2.L2.IsMapEntry = true
+ md2.L1.IsMapEntry = true
md2.L2.Options = func() pref.ProtoMessage {
opts := descopts.Message.ProtoReflect().New()
opts.Set(opts.Descriptor().Fields().ByName("map_entry"), protoreflect.ValueOfBool(true))
diff --git a/reflect/protodesc/desc_init.go b/reflect/protodesc/desc_init.go
index 2fe19b0..6f689b1 100644
--- a/reflect/protodesc/desc_init.go
+++ b/reflect/protodesc/desc_init.go
@@ -70,8 +70,8 @@
if opts := md.GetOptions(); opts != nil {
opts = clone(opts).(*descriptorpb.MessageOptions)
m.L2.Options = func() protoreflect.ProtoMessage { return opts }
- m.L2.IsMapEntry = opts.GetMapEntry()
- m.L2.IsMessageSet = opts.GetMessageSetWireFormat()
+ m.L1.IsMapEntry = opts.GetMapEntry()
+ m.L1.IsMessageSet = opts.GetMessageSetWireFormat()
}
for _, s := range md.GetReservedName() {
m.L2.ReservedNames.List = append(m.L2.ReservedNames.List, protoreflect.Name(s))