From 5a1a904b4264c6ee323c9bd433f9ee4da93c984d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sun, 5 Feb 2023 13:02:57 -0700 Subject: typedsync: Bring up to being a mostly-drop-in replacement for Go 1.20 sync --- syncutil/cachemap.go | 4 +--- typedsync/cond.go | 38 +++++++++++++++++++++++++++++++++++++ typedsync/doc.go | 28 +++++++++++++++++++++++++++ typedsync/map.go | 21 +++++++++++++++++---- typedsync/map_go120.go | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ typedsync/pool.go | 5 +++++ typedsync/value.go | 10 +++++++--- 7 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 typedsync/cond.go create mode 100644 typedsync/doc.go 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 +// +// 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 +// +// 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 -- cgit v1.1-4-g5e80