I have a struct with various fields. It needs to be protected for concurrent access. It looks like this:
type S struct {
SomeBigSlice []int
}
func (s *S) Read(scanner io.RuneScanner) error {
// Read from scanner, then write an update to S. Called frequently.
}
I also intend to export the state of this struct through a debugging endpoint in the DefaultServerMux.
There are a few ways I could go about protecting a struct like this. In general, the approaches are mutex-in or mutex-out.
Mutex Out
Mutex-out is simple: the structure is totally unguarded. The owner has to lock it before each operation.
This complicates ReadOnce. You can't hold the mutex during the entire read, because then the http handler will be blocked if you are waiting for IO. So you need to do something like splitting the reader into two separate functions. One of these would read some sort of update object from the scanner. The other would, with the lock, update S.
It also complicates the http handler, as you have to pass the mutex-to-be-locked to the registration function as a parameter.
Mutex In
The mutex in approach has a private and a public subtype. In the public subtype, the mutex is an exposed field in the struct. The struct's receivers lock it before modifying the struct, and external code locks it before reading the struct.
In the private subtype, the receivers and other in-package elements can handle the issue the same as the public subtype. To the interface you add a Do() function, which gives you locked access to the struct. This seems to more or less be the approach of the
expvar package.
There's another variant of the private subtype where members of the struct are only exposed via getters. This seems most like other languages, although, for my purposes, it would not be as good as iteration over slices is involved. So getting values index by index would be very slow under a pure-getter approach.
Advice
What is your advice for me? Which among these approaches is most Go-idiomatic? I'm leaning toward mutex-out at the moment, but this is a toy project for learning about Go, so doing what's most idiomatic is more important than which way I lean.