Go реализует sync.Once как таковой:
type Once struct {
m Mutex
done uint32
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
Я пытаюсь понять необходимость мьютекса здесь, в чем будет проблема с его реализацией в таком виде?
func (o *Once) Do(f func()) {
if atomic.CompareAndSwapUInt32(&o.done, 0, 1) {
f()
}
}

Удаление мьютекса нарушает один из задокументированное поведение:
no call to Do returns until the one call to f returns