github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/sync/once/once.go (about)

     1  // Copyright 2018 GRAIL, Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package once contains utilities for managing actions that
     6  // must be performed exactly once.
     7  package once
     8  
     9  import (
    10  	"sync"
    11  	"sync/atomic"
    12  )
    13  
    14  // Task manages a computation that must be run at most once.
    15  // It's similar to sync.Once, except it also handles and returns errors.
    16  type Task struct {
    17  	mu   sync.Mutex
    18  	done uint32
    19  	err  error
    20  }
    21  
    22  // Do run the function do at most once. Successive invocations of Do
    23  // guarantee exactly one invocation of the function do. Do returns
    24  // the error of do's invocation.
    25  func (o *Task) Do(do func() error) error {
    26  	if atomic.LoadUint32(&o.done) == 1 {
    27  		return o.err
    28  	}
    29  	o.mu.Lock()
    30  	defer o.mu.Unlock()
    31  	if atomic.LoadUint32(&o.done) == 0 {
    32  		o.err = do()
    33  		atomic.StoreUint32(&o.done, 1)
    34  	}
    35  	return o.err
    36  }
    37  
    38  // Done returns whether the task is done.
    39  func (o *Task) Done() bool {
    40  	o.mu.Lock()
    41  	defer o.mu.Unlock()
    42  	return 1 == atomic.LoadUint32(&o.done)
    43  }
    44  
    45  // Reset resets the task effectively making it possible for `Do` to invoke the underlying do func again.
    46  // Reset will only reset the task if it was already completed.
    47  func (o *Task) Reset() {
    48  	o.mu.Lock()
    49  	defer o.mu.Unlock()
    50  	atomic.CompareAndSwapUint32(&o.done, 1, 0)
    51  }
    52  
    53  // Map coordinates actions that must happen exactly once, keyed
    54  // by user-defined keys.
    55  type Map sync.Map
    56  
    57  // Perform the provided action named by a key. Do invokes the action
    58  // exactly once for each key, and returns any errors produced by the
    59  // provided action.
    60  func (m *Map) Do(key interface{}, do func() error) error {
    61  	taskv, _ := (*sync.Map)(m).LoadOrStore(key, new(Task))
    62  	task := taskv.(*Task)
    63  	return task.Do(do)
    64  }
    65  
    66  // Forget forgets past computations associated with the provided key.
    67  func (m *Map) Forget(key interface{}) {
    68  	(*sync.Map)(m).Delete(key)
    69  }