While working and generating protobuf stubs in go I stumbled upon this interesting issue.
Whenever I try and copy a message's struct by value I get this warning:
call of state.world.script.HandleEvent copies lock value: throne/server/messages.PlayerDialogeStatus contains google.golang.org/protobuf/internal/impl.MessageState contains sync.Mutex copylocks
While I understand why copying a mutex lock by value is wrong, I started wondering why are they even there in the first place.
And thus my question: Why does the go generated protobuf files contain mutex locks placed on the message structs, specifically on the MessageState
struct?
Or alternatively: What is the goal of the mutex lock placed in the MessageState
struct found on generated protobuf message structs?
A Mutex guarantees only that if something has locked it, it cannot be locked again by something else until the lock is first released. It is up to you to use it correctly, by ensuring that you obtain a lock before you try to access whatever you want protected by the lock, as you've done in your example main .
There are two requirements for the full process. protoc binary installed on your path go get -u github.com/gogo/protobuf/... You can generate the proto files, the marshal/unmarshal and the rest of protobuf stuff for your Go types, the RPC client and server interface and the RPC server implementation for your packages.
Using protocol buffers with Go To compile the protocol buffer definition, run protoc with the --go_out parameter set to the directory you want to output the Go code to. The generated files will be suffixed . pb.go. See the Test code below for an example using such a file.
This article is all about the go_package option in the Protocol Buffers (Protobuf). The go_package option defines the import path of the package which will contain all the generated code for this file. The Go package name will be the last path component of the import path — Google doc.
The impl.MessageState is embedded in concrete messages only, not in the generated structs that implement a proto message.
It specifically embeds the three pragmas: NoUnkeyedLiterals
, DoNotCompare
, and DoNotCopy
.
The last one, DoNotCopy is a zero-sized array of sync.Mutex
. The sole purpose is to have go vet
complain loudly about shallow copies, as described in the comment:
DoNotCopy can be embedded in a struct to help prevent shallow copies. This does not rely on a Go language feature, but rather a special case within the vet checker.
The summary of it all: impl.MessageState
is not supposed to be copied and the mutex is there only to catch copying. If you do so, it is because you are using something the wrong way.
As far as I can tell, there are three reasons the Go protobuf API includes the DoNotCopy mutex:
msg.Marshal()
on a message, then overwriting it with *msg = MyMessage{...}
mixes atomic and non-atomic accesses. Even if this works with today's implementation on x86, there is no guarantee this will work on other systems in the future. (See a long Go issue about this).ProtoReflect()
on a message, then later overwrite the message, it will crash, since the ProtoReflect() result relies on the internal reflection pointer (original issue):d := &durationpb.Duration{Seconds: 1}
protoreflectMessage := d.ProtoReflect()
fmt.Printf("protoreflectMessage.Interface()=%v\n", protoreflectMessage.Interface())
*d = durationpb.Duration{Seconds: 2}
fmt.Printf("protoreflectMessage.Interface()=%v\n", protoreflectMessage.Interface())
This crashes with:
protoreflectMessage.Interface()=seconds:1
panic: invalid nil message info; this suggests memory corruption due to a race or shallow copy on the message struct
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With