NOTE

2.7 sync.Once

What sync.Once is, singleton usage, its atomic+Mutex implementation, and a modified version that returns an error.

GoCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What It Is

Once is an object that guarantees that an action is executed only once. Put simply, it makes a method execute only once.

2. Usage

2.1. Singleton Pattern

Singleton Pattern.md (related note is not yet published)

type singleton struct {
}

// private
var instance *singleton
var once sync.Once

func TestSync1(t *testing.T) {
	instance1 := GetInstance()
	instance2 := GetInstance()
	fmt.Println(instance1 == instance2)

}
func GetInstance() *singleton {
	once.Do(func() {
		instance = &singleton{}
	})
	return instance
}

3. Implementation

Implemented through sync.atomic + sync.Mutex.

type Once struct {
	done uint32
	m    Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 {
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done == 0 {
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}
  • Why does the Do method use atomic.LoadUint32(&o.done) == 0 instead of directly using o.done == 0? If o.done == 0 is used directly, changes to o.done made by doSlow may not be observed in time.

  • Why does the doSlow method directly use o.done == 0? It is already locked and is inside the critical section.

  • Since Lock is already used, why not directly use o.done = 1? Why is atomic.StoreUint32(&o.done, 1) still needed?

4. Modification: Return error


// copy from sync.Once, add return error
type Once struct {
	done uint32
	m    sync.Mutex
}

func (o *Once) Do(f func() error) error {
	if atomic.LoadUint32(&o.done) == 0 {
		return o.doSlow(f)
	}
	return nil
}

func (o *Once) doSlow(f func() error) error {
	o.m.Lock()
	defer o.m.Unlock()
	var err error
	if o.done == 0 {
		err = f()
		if err == nil {
			defer atomic.StoreUint32(&o.done, 1)
		}
	}

	return err
}

5. References

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub