summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-05 13:02:57 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-05 13:33:02 -0700
commit5a1a904b4264c6ee323c9bd433f9ee4da93c984d (patch)
tree035414c8e8f3b94e24b482e5096a3b4e3688e257
parent2d939c9c6e62395ed924fe7c5cd4c4b294e391a9 (diff)
typedsync: Bring up to being a mostly-drop-in replacement for Go 1.20 sync
-rw-r--r--syncutil/cachemap.go4
-rw-r--r--typedsync/cond.go38
-rw-r--r--typedsync/doc.go28
-rw-r--r--typedsync/map.go21
-rw-r--r--typedsync/map_go120.go51
-rw-r--r--typedsync/pool.go5
-rw-r--r--typedsync/value.go10
7 files changed, 147 insertions, 10 deletions
diff --git a/syncutil/cachemap.go b/syncutil/cachemap.go
index 8eab4bc..8c4dfc5 100644
--- a/syncutil/cachemap.go
+++ b/syncutil/cachemap.go
@@ -5,13 +5,11 @@
package syncutil
import (
- "sync"
-
"git.lukeshu.com/go/containers/typedsync"
)
type cacheVal[V any] struct {
- wg sync.WaitGroup
+ wg typedsync.WaitGroup
v V
}
diff --git a/typedsync/cond.go b/typedsync/cond.go
new file mode 100644
index 0000000..00d3164
--- /dev/null
+++ b/typedsync/cond.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package typedsync
+
+import (
+ "sync"
+)
+
+// Cond is a type-safe equivalent of the standard library's sync.Cond.
+//
+// See the [sync.Cond documentation] for full details.
+//
+// [sync.Cond documentation]: https://pkg.go.dev/sync#Cond
+type Cond[T Locker] struct {
+ L T
+ inner sync.Cond
+}
+
+func NewCond[T Locker](l T) *Cond[T] {
+ return &Cond[T]{L: l}
+}
+
+func (c *Cond[T]) Broadcast() {
+ c.inner.L = c.L
+ c.inner.Broadcast()
+}
+
+func (c *Cond[T]) Signal() {
+ c.inner.L = c.L
+ c.inner.Signal()
+}
+
+func (c *Cond[T]) Wait() {
+ c.inner.L = c.L
+ c.inner.Wait()
+}
diff --git a/typedsync/doc.go b/typedsync/doc.go
new file mode 100644
index 0000000..e20d44c
--- /dev/null
+++ b/typedsync/doc.go
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Package typedsync is an alternative to the standard library's sync
+// that uses type-parameters for type safety.
+//
+// This package does not bother to duplicate documentation from the
+// standard library's sync package; see [sync's documentation] for
+// full documentation.
+//
+// Besides requiring type parameters and such, typedsync is a drop-in
+// replacement for sync.
+//
+// [sync's documentation]: https://pkg.go.dev/sync
+package typedsync
+
+import (
+ "sync"
+)
+
+type (
+ Locker = sync.Locker
+ Mutex = sync.Mutex
+ Once = sync.Once
+ RWMutex = sync.RWMutex
+ WaitGroup = sync.WaitGroup
+)
diff --git a/typedsync/map.go b/typedsync/map.go
index 6bb1170..2f94ae2 100644
--- a/typedsync/map.go
+++ b/typedsync/map.go
@@ -10,20 +10,33 @@ import (
// Map is a type-safe equivalent of the standard library's sync.Map.
//
-// With versions of Go prior to Go 1.20, Map is specified too loosely,
-// as
+// See the [sync.Map documentation] for full details.
+//
+// Go 1.20 added sync.Map.Swap method; this method is only available
+// on typedsync.Map if built with Go 1.20 or later.
+//
+// Go 1.20 added sync.Map.CompareAndDelete and sync.Map.CompareAndSwap
+// methods that are only usable if V is a comparable type; these
+// methods are not available on typedsync.Map, but when typedsync is
+// built with Go 1.20 or later they are available on a separate
+// ComparableMap type.
+//
+// When typedsync is built versions of Go prior to Go 1.20,
+// typedsync.Map is specified too loosely, as
//
// Map[K any, V any]
//
-// while with Go 1.20 and later, Map is specified as
+// while with Go 1.20 and later, typedsync.Map is specified as
//
// Map[K comparable, V any]
//
// This is because with Go versions prior to 1.20, 'comparable' was
// overly strict, disallowing many types that are valid map-keys (see
// https://github.com/golang/go/issues/56548). The type used as K in
-// a Map older versions of Go must be a valid map-key type, even
+// a Map with older versions of Go must be a valid map-key type, even
// though the type specification of Map does not enforce that.
+//
+// [sync.Map documentation]: https://pkg.go.dev/sync#Map
type Map[K mapkey, V any] struct {
inner sync.Map
}
diff --git a/typedsync/map_go120.go b/typedsync/map_go120.go
index 0d4ff5b..6885e2a 100644
--- a/typedsync/map_go120.go
+++ b/typedsync/map_go120.go
@@ -7,3 +7,54 @@
package typedsync
type mapkey = comparable
+
+func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool) {
+ _previous, loaded := m.inner.Swap(key, value)
+ if loaded {
+ //nolint:forcetypeassert // Typed wrapper around untyped lib.
+ previous = _previous.(V)
+ }
+ return previous, loaded
+}
+
+// ComparableMap is a variant of Map with a comparable type for V;
+// affording additional CompareAndDelete and CompareAndSwap methods.
+//
+// See the [sync.Map documentation] for full details.
+//
+// [sync.Map documentation]: https://pkg.go.dev/sync#Map
+type ComparableMap[K comparable, V comparable] struct {
+ inner Map[K, V]
+}
+
+func (m *ComparableMap[K, V]) Delete(key K) {
+ m.inner.Delete(key)
+}
+
+func (m *ComparableMap[K, V]) Load(key K) (value V, ok bool) {
+ return m.inner.Load(key)
+}
+
+func (m *ComparableMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
+ return m.inner.LoadAndDelete(key)
+}
+
+func (m *ComparableMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
+ return m.inner.LoadOrStore(key, value)
+}
+
+func (m *ComparableMap[K, V]) Range(f func(key K, value V) bool) {
+ m.inner.Range(f)
+}
+
+func (m *ComparableMap[K, V]) Store(key K, value V) {
+ m.inner.Store(key, value)
+}
+
+func (m *ComparableMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
+ return m.inner.inner.CompareAndDelete(key, old)
+}
+
+func (m *ComparableMap[K, V]) CompareAndSwap(key K, oldV, newV V) bool {
+ return m.inner.inner.CompareAndSwap(key, oldV, newV)
+}
diff --git a/typedsync/pool.go b/typedsync/pool.go
index c196085..370d70a 100644
--- a/typedsync/pool.go
+++ b/typedsync/pool.go
@@ -8,6 +8,11 @@ import (
"sync"
)
+// Pool is a type-safe equivalent of the standard library's sync.Pool.
+//
+// See the [sync.Pool documentation] for full details.
+//
+// [sync.Pool documentation]: https://pkg.go.dev/sync#Pool
type Pool[T any] struct {
New func() T
diff --git a/typedsync/value.go b/typedsync/value.go
index 99c8876..90c8b55 100644
--- a/typedsync/value.go
+++ b/typedsync/value.go
@@ -8,11 +8,15 @@ import (
"sync"
)
-// Value is a typed equivalent of sync/atomic.Value.
+// Value is a type-safe equivalent of the standard library's
+// sync/atomic.Value.
//
-// It is not actually a wrapper around sync/atomic.Value for
-// allocation-performance reasons.
+// See the [sync/atomic.Value documentation] for full details.
+//
+// [sync/atomic.Value documentation]: https://pkg.go.dev/sync/atomic#Value
type Value[T comparable] struct {
+ // It is not actually a wrapper around sync/atomic.Value for
+ // allocation-performance reasons.
mu sync.Mutex
ok bool
val T