The implementation does not grab the lock,
if Once is already initalized.
Benchmark results on HP Z600 (2 x Xeon E5620, 8 HT cores, 2.40GHz)
are as follows:
benchmark old ns/op new ns/op delta
sync_test.BenchmarkOnce 187.00 14.00 -92.51%
sync_test.BenchmarkOnce-2 909.00 21.40 -97.65%
sync_test.BenchmarkOnce-4 3684.00 20.90 -99.43%
sync_test.BenchmarkOnce-8 5987.00 23.00 -99.62%
sync_test.BenchmarkOnce-16 5051.00 21.60 -99.57%
R=bradfitz, rsc
CC=golang-dev
https://golang.org/cl/
4641066
package sync
+import (
+ "sync/atomic"
+)
+
// Once is an object that will perform exactly one action.
type Once struct {
m Mutex
- done bool
+ done int32
}
// Do calls the function f if and only if the method is being called for the
// Do to be called, it will deadlock.
//
func (o *Once) Do(f func()) {
+ if atomic.AddInt32(&o.done, 0) == 1 {
+ return
+ }
+ // Slow-path.
o.m.Lock()
defer o.m.Unlock()
- if !o.done {
- o.done = true
+ if o.done == 0 {
f()
+ atomic.CompareAndSwapInt32(&o.done, 0, 1)
}
}