diff --git a/tools/utils/once.go b/tools/utils/once.go new file mode 100644 index 000000000..03734448d --- /dev/null +++ b/tools/utils/once.go @@ -0,0 +1,35 @@ +// License: GPLv3 Copyright: 2023, Kovid Goyal, + +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() + } +} diff --git a/tools/utils/once_test.go b/tools/utils/once_test.go new file mode 100644 index 000000000..c748dd16a --- /dev/null +++ b/tools/utils/once_test.go @@ -0,0 +1,24 @@ +// License: GPLv3 Copyright: 2023, Kovid Goyal, + +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) + } +}