From 1c2bc060da5fe8255e22474e8b841dc1bd1fe51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Schw=C3=B6rer?= Date: Sat, 6 Dec 2025 11:50:54 +0100 Subject: [PATCH] v0.0.615 MultiMutex --- dataext/multiMutex.go | 93 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 2 + goextVersion.go | 4 +- 4 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 dataext/multiMutex.go diff --git a/dataext/multiMutex.go b/dataext/multiMutex.go new file mode 100644 index 0000000..4a1af54 --- /dev/null +++ b/dataext/multiMutex.go @@ -0,0 +1,93 @@ +package dataext + +import ( + "context" + "sync" + "time" +) + +// MultiMutex is a simple map[key -> mutex] +type MultiMutex[TKey comparable] struct { + mutextMap *SyncMap[TKey, *CASMutex] +} + +func NewMultiMutex[TKey comparable]() *MultiMutex[TKey] { + return &MultiMutex[TKey]{ + mutextMap: NewSyncMap[TKey, *CASMutex](), + } +} + +// TryLockWithContext attempts to acquire the lock, blocking until resources +// are available or ctx is done (timeout or cancellation). +func (mm *MultiMutex[TKey]) TryLockWithContext(ctx context.Context, key TKey) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.TryLockWithContext(ctx) +} + +// Lock acquires the lock. +// If it is currently held by others, Lock will wait until it has a chance to acquire it. +func (mm *MultiMutex[TKey]) Lock(key TKey) { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + lck.Lock() +} + +// TryLock attempts to acquire the lock without blocking. +// Return false if someone is holding it now. +func (mm *MultiMutex[TKey]) TryLock(key TKey) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.TryLock() +} + +// TryLockWithTimeout attempts to acquire the lock within a period of time. +// Return false if spending time is more than duration and no chance to acquire it. +func (mm *MultiMutex[TKey]) TryLockWithTimeout(key TKey, duration time.Duration) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.TryLockWithTimeout(duration) +} + +// Unlock releases the lock. +func (mm *MultiMutex[TKey]) Unlock(key TKey) { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + lck.Unlock() +} + +// RTryLockWithContext attempts to acquire the read lock, blocking until resources +// are available or ctx is done (timeout or cancellation). +func (mm *MultiMutex[TKey]) RTryLockWithContext(ctx context.Context, key TKey) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.RTryLockWithContext(ctx) +} + +// RLock acquires the read lock. +// If it is currently held by others writing, RLock will wait until it has a chance to acquire it. +func (mm *MultiMutex[TKey]) RLock(key TKey) { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + lck.RLock() +} + +// RTryLock attempts to acquire the read lock without blocking. +// Return false if someone is writing it now. +func (mm *MultiMutex[TKey]) RTryLock(key TKey) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.RTryLock() +} + +// RTryLockWithTimeout attempts to acquire the read lock within a period of time. +// Return false if spending time is more than duration and no chance to acquire it. +func (mm *MultiMutex[TKey]) RTryLockWithTimeout(duration time.Duration, key TKey) bool { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.RTryLockWithTimeout(duration) +} + +// RUnlock releases the read lock. +func (mm *MultiMutex[TKey]) RUnlock(key TKey) { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + lck.RUnlock() +} + +// RLocker returns a Locker interface that implements the Lock and Unlock methods +// by calling CASMutex.RLock and CASMutex.RUnlock. +func (mm *MultiMutex[TKey]) RLocker(key TKey) sync.Locker { + lck := mm.mutextMap.GetAndSetIfNotContainsFunc(key, NewCASMutex) + return lck.RLocker() +} diff --git a/go.mod b/go.mod index 949efc0..d9da70a 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/golang/snappy v1.0.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.18.1 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/go.sum b/go.sum index ebe26da..fc4e692 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= diff --git a/goextVersion.go b/goextVersion.go index 96aced3..1849960 100644 --- a/goextVersion.go +++ b/goextVersion.go @@ -1,5 +1,5 @@ package goext -const GoextVersion = "0.0.614" +const GoextVersion = "0.0.615" -const GoextVersionTimestamp = "2025-11-29T12:49:17+0100" +const GoextVersionTimestamp = "2025-12-06T11:50:54+0100"