A nicer implementation of sync.Once

Doesnt require storing the result of the function in a dedicated global
variable with a dedicated getter function
This commit is contained in:
Kovid Goyal 2023-02-21 20:24:07 +05:30
parent fbaaca1be9
commit 6f4d89045a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 59 additions and 0 deletions

35
tools/utils/once.go Normal file
View File

@ -0,0 +1,35 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package utils
import (
"fmt"
"sync"
"sync/atomic"
)
var _ = fmt.Print
type Once[T any] struct {
done uint32
mutex sync.Mutex
cached_val T
Run func() T
}
func (self *Once[T]) Get() T {
if atomic.LoadUint32(&self.done) == 0 {
self.do_slow()
}
return self.cached_val
}
func (self *Once[T]) do_slow() {
self.mutex.Lock()
defer self.mutex.Unlock()
if atomic.LoadUint32(&self.done) == 0 {
defer atomic.StoreUint32(&self.done, 1)
self.cached_val = self.Run()
}
}

24
tools/utils/once_test.go Normal file
View File

@ -0,0 +1,24 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package utils
import (
"fmt"
"testing"
)
var _ = fmt.Print
func TestOnce(t *testing.T) {
num := 0
var G = (&Once[string]{Run: func() string {
num++
return fmt.Sprintf("%d", num)
}}).Get
G()
G()
G()
if num != 1 {
t.Fatalf("num unexpectedly: %d", num)
}
}