From 6f4d89045a2129f1aef73fd86d60c4d3d673e036 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 21 Feb 2023 20:24:07 +0530 Subject: [PATCH] A nicer implementation of sync.Once Doesnt require storing the result of the function in a dedicated global variable with a dedicated getter function --- tools/utils/once.go | 35 +++++++++++++++++++++++++++++++++++ tools/utils/once_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tools/utils/once.go create mode 100644 tools/utils/once_test.go 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) + } +}