From 0adf540a6fbdff60bc8f13d9570c667d6beabf2d Mon Sep 17 00:00:00 2001 From: qydysky Date: Sun, 13 Apr 2025 00:49:08 +0800 Subject: [PATCH] 1 (#43) * 1 * 1 * 1 * 1 * 1 --- file/FileWR.go | 2 +- file/FileWR_test.go | 8 +- go.mod | 2 +- go.sum | 4 +- sync/LoadOrStoreFunc.go | 61 ++++++++++++++ sync/Map.go | 178 +++++++++------------------------------- sync/MapExceeded.go | 102 +++++++++++++++++++++++ sync/Map_test.go | 14 ++-- web/Web.go | 2 +- 9 files changed, 219 insertions(+), 154 deletions(-) create mode 100644 sync/LoadOrStoreFunc.go create mode 100644 sync/MapExceeded.go diff --git a/file/FileWR.go b/file/FileWR.go index 4c82e38..59ab056 100644 --- a/file/FileWR.go +++ b/file/FileWR.go @@ -176,7 +176,7 @@ func (t *File) CheckRoot(root string) *File { remove: os.Remove, removeAll: os.RemoveAll, stat: os.Stat, - }, t.Config.root+"/.t", fs.ModeDir) + }, t.Config.root+"/.t", fs.ModePerm|fs.ModeDir) return t } diff --git a/file/FileWR_test.go b/file/FileWR_test.go index 3d1dc13..a361320 100644 --- a/file/FileWR_test.go +++ b/file/FileWR_test.go @@ -17,18 +17,18 @@ import ( ) func TestDir(t *testing.T) { - Open("test2").Delete() - if Open("test2").IsExist() { + Open("./test2").Delete() + if Open("./test2").IsExist() { t.Fatal() } - if f, e := DirFS("test2").Open("FileWR.go"); e != nil { + if f, e := DirFS("./test2").Open("1.txt"); e != nil { t.Fatal(e) } else if _, e := f.(*File).Write([]byte{'1'}, false); e != nil { t.Fatal(e) } else { f.(*File).Delete() } - if !Open("test2").IsExist() { + if !Open("./test2").IsExist() { t.Fatal() } } diff --git a/go.mod b/go.mod index c25f430..e055b72 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/jackc/pgx/v5 v5.7.1 github.com/stretchr/testify v1.10.0 // indirect golang.org/x/net v0.37.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/sys v0.32.0 // indirect modernc.org/sqlite v1.34.2 ) diff --git a/go.sum b/go.sum index 407c346..671e268 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= diff --git a/sync/LoadOrStoreFunc.go b/sync/LoadOrStoreFunc.go new file mode 100644 index 0000000..e28c949 --- /dev/null +++ b/sync/LoadOrStoreFunc.go @@ -0,0 +1,61 @@ +package part + +import "sync" + +type LoadOrInitFunc[T any] struct { + Map interface { + LoadOrStore(k, v any) (actual any, loaded bool) + } + Init func() *T + cache *T + l sync.Mutex +} + +func NewLoadOrInitFunc[T any](m interface { + LoadOrStore(k, v any) (actual any, loaded bool) +}) *LoadOrInitFunc[T] { + return &LoadOrInitFunc[T]{ + Map: m, + } +} + +func (l *LoadOrInitFunc[T]) SetInit(init func() *T) *LoadOrInitFunc[T] { + l.l.Lock() + defer l.l.Unlock() + l.Init = init + return l +} + +func (l *LoadOrInitFunc[T]) LoadOrInit(k any) (actual T, loaded bool) { + l.l.Lock() + defer l.l.Unlock() + a, b := l.loadOrInitP(k) + return *a, b +} + +func (l *LoadOrInitFunc[T]) LoadOrInitPThen(k any) func(func(actual *T, loaded bool) (*T, bool)) (*T, bool) { + return func(f func(actual *T, loaded bool) (*T, bool)) (*T, bool) { + l.l.Lock() + defer l.l.Unlock() + return f(l.loadOrInitP(k)) + } +} + +func (l *LoadOrInitFunc[T]) LoadOrInitP(k any) (actual *T, loaded bool) { + l.l.Lock() + defer l.l.Unlock() + + return l.loadOrInitP(k) +} + +func (l *LoadOrInitFunc[T]) loadOrInitP(k any) (actual *T, loaded bool) { + if l.cache == nil { + l.cache = l.Init() + } + if actual, loaded := l.Map.LoadOrStore(k, l.cache); !loaded { + l.cache = nil + return actual.(*T), false + } else { + return actual.(*T), true + } +} diff --git a/sync/Map.go b/sync/Map.go index bcbcd5a..10eb6d4 100644 --- a/sync/Map.go +++ b/sync/Map.go @@ -3,9 +3,23 @@ package part import ( "sync" "sync/atomic" - "time" ) +type MapFunc[T, E any] interface { + Clear() + CompareAndDelete(key T, old E) (deleted bool) + CompareAndSwap(key T, old E, new E) (swapped bool) + Delete(key T) + Load(key T) (value E, ok bool) + LoadAndDelete(key T) (value E, loaded bool) + LoadOrStore(key T, value E) (actual E, loaded bool) + Range(f func(key T, value E) bool) + Store(key T, value E) + Swap(key T, value E) (previous E, loaded bool) +} + +var _ = MapFunc[any, any](&Map{}) + type Map struct { size atomic.Int64 m sync.Map @@ -17,52 +31,36 @@ func (t *Map) Store(k, v any) { } } -func (t *Map) LoadOrStore(k, v any) (actual any, loaded bool) { - actual, loaded = t.m.LoadOrStore(k, v) - if !loaded { - t.size.Add(1) - } - return +func (t *Map) CompareAndSwap(key any, old any, new any) (swapped bool) { + return t.m.CompareAndSwap(key, old, new) } -type LoadOrStoreFunc[T any] struct { - Init func() *T - cache *T - l sync.Mutex +func (t *Map) CompareAndDelete(key any, old any) (deleted bool) { + deleted = t.m.CompareAndDelete(key, old) + if deleted { + t.size.Add(-1) + } + return } -func (l *LoadOrStoreFunc[T]) LoadOrStore(t interface { - LoadOrStore(k, v any) (actual any, loaded bool) -}, k any) (actual T, loaded bool) { - l.l.Lock() - defer l.l.Unlock() - - if l.cache == nil { - l.cache = l.Init() - } - if actual, loaded := t.LoadOrStore(k, l.cache); !loaded { - l.cache = nil - return *(actual.(*T)), false - } else { - return *(actual.(*T)), true +func (t *Map) LoadAndDelete(key any) (value any, loaded bool) { + value, loaded = t.m.LoadAndDelete(key) + if loaded { + t.size.Add(-1) } + return } -func (l *LoadOrStoreFunc[T]) LoadOrStoreP(t interface { - LoadOrStore(k, v any) (actual any, loaded bool) -}, k any) (actual *T, loaded bool) { - l.l.Lock() - defer l.l.Unlock() +func (t *Map) Swap(key any, value any) (previous any, loaded bool) { + return t.m.Swap(key, value) +} - if l.cache == nil { - l.cache = l.Init() - } - if actual, loaded := t.LoadOrStore(k, l.cache); !loaded { - l.cache = nil - return actual.(*T), false - } else { - return actual.(*T), true +func (t *Map) LoadOrStore(k, v any) (actual any, loaded bool) { + actual, loaded = t.m.LoadOrStore(k, v) + if !loaded { + t.size.Add(1) } + return } func (t *Map) Load(k any) (any, bool) { @@ -78,12 +76,14 @@ func (t *Map) Range(f func(key, value any) bool) { t.m.Range(f) } -func (t *Map) Delete(k any) (ok bool) { +func (t *Map) Delete(k any) { if _, ok := t.m.LoadAndDelete(k); ok { t.size.Add(-1) - return true } - return false +} + +func (t *Map) Clear() { + t.ClearAll() } func (t *Map) ClearAll() { @@ -122,99 +122,3 @@ func Copy[T comparable, S any](s map[T]S) map[T]S { } return t } - -type MapExceeded[K, V any] struct { - m Map -} - -type mapExceededItem[V any] struct { - data V - exceeded time.Time - wait sync.RWMutex -} - -func (t *MapExceeded[K, V]) Copy() (m *MapExceeded[K, V]) { - m = &MapExceeded[K, V]{} - t.m.Range(func(key, value any) bool { - if value.(*mapExceededItem[V]).exceeded.After(time.Now()) { - m.m.Store(key, value) - } - return true - }) - return -} - -func (t *MapExceeded[K, V]) Store(k K, v V, dur time.Duration) { - t.m.Store(k, &mapExceededItem[V]{ - data: v, - exceeded: time.Now().Add(dur), - }) -} - -func (t *MapExceeded[K, V]) Load(k K) (v V, ok bool) { - if v, ok := t.m.LoadV(k).(*mapExceededItem[V]); ok { - if v.exceeded.After(time.Now()) { - return v.data, true - } - t.Delete(k) - } - return -} - -func (t *MapExceeded[K, V]) Range(f func(key K, value *V) bool) { - t.m.Range(func(key, value any) bool { - if value.(*mapExceededItem[V]).exceeded.After(time.Now()) { - return f(key.(K), value.(*V)) - } - t.Delete(key.(K)) - return true - }) -} - -func (t *MapExceeded[K, V]) Len() int { - return t.m.Len() -} - -func (t *MapExceeded[K, V]) GC() { - t.m.Range(func(key, value any) bool { - if value.(*mapExceededItem[V]).exceeded.Before(time.Now()) { - t.Delete(key.(K)) - } - return true - }) -} - -func (t *MapExceeded[K, V]) Delete(k K) { - t.m.Delete(k) -} - -func (t *MapExceeded[K, V]) LoadOrStore(k K) (vr V, loaded bool, store func(v1 V, dur time.Duration)) { - store = func(v1 V, dur time.Duration) {} - var actual any - actual, loaded = t.m.LoadOrStore(k, &mapExceededItem[V]{}) - v := actual.(*mapExceededItem[V]) - v.wait.RLock() - exp := v.exceeded - vr = v.data - v.wait.RUnlock() - if loaded && time.Now().Before(exp) { - return - } - if !loaded || (loaded && !exp.IsZero()) { - store = func(v1 V, dur time.Duration) { - v.wait.Lock() - v.data = v1 - v.exceeded = time.Now().Add(dur) - v.wait.Unlock() - } - return - } - for loaded && exp.IsZero() { - time.Sleep(time.Millisecond * 20) - v.wait.RLock() - exp = v.exceeded - vr = v.data - v.wait.RUnlock() - } - return -} diff --git a/sync/MapExceeded.go b/sync/MapExceeded.go new file mode 100644 index 0000000..0f4ed32 --- /dev/null +++ b/sync/MapExceeded.go @@ -0,0 +1,102 @@ +package part + +import ( + "sync" + "time" +) + +type MapExceeded[K, V any] struct { + m Map +} + +type mapExceededItem[V any] struct { + data V + exceeded time.Time + wait sync.RWMutex +} + +func (t *MapExceeded[K, V]) Copy() (m *MapExceeded[K, V]) { + m = &MapExceeded[K, V]{} + t.m.Range(func(key, value any) bool { + if value.(*mapExceededItem[V]).exceeded.After(time.Now()) { + m.m.Store(key, value) + } + return true + }) + return +} + +func (t *MapExceeded[K, V]) Store(k K, v V, dur time.Duration) { + t.m.Store(k, &mapExceededItem[V]{ + data: v, + exceeded: time.Now().Add(dur), + }) +} + +func (t *MapExceeded[K, V]) Load(k K) (v V, ok bool) { + if v, ok := t.m.LoadV(k).(*mapExceededItem[V]); ok { + if v.exceeded.After(time.Now()) { + return v.data, true + } + t.Delete(k) + } + return +} + +func (t *MapExceeded[K, V]) Range(f func(key K, value *V) bool) { + t.m.Range(func(key, value any) bool { + if value.(*mapExceededItem[V]).exceeded.After(time.Now()) { + return f(key.(K), value.(*V)) + } + t.Delete(key.(K)) + return true + }) +} + +func (t *MapExceeded[K, V]) Len() int { + return t.m.Len() +} + +func (t *MapExceeded[K, V]) GC() { + t.m.Range(func(key, value any) bool { + if value.(*mapExceededItem[V]).exceeded.Before(time.Now()) { + t.Delete(key.(K)) + } + return true + }) +} + +func (t *MapExceeded[K, V]) Delete(k K) { + t.m.Delete(k) +} + +func (t *MapExceeded[K, V]) LoadOrStore(k K) (vr V, loaded bool, store func(v1 V, dur time.Duration)) { + store = func(v1 V, dur time.Duration) {} + var actual any + actual, loaded = t.m.LoadOrStore(k, &mapExceededItem[V]{}) + v := actual.(*mapExceededItem[V]) + v.wait.RLock() + exp := v.exceeded + vr = v.data + v.wait.RUnlock() + if loaded && time.Now().Before(exp) { + return + } + if !loaded || (loaded && !exp.IsZero()) { + store = func(v1 V, dur time.Duration) { + v.wait.Lock() + v.data = v1 + v.exceeded = time.Now().Add(dur) + v.wait.Unlock() + } + return + } + for loaded && exp.IsZero() { + time.Sleep(time.Millisecond * 20) + v.wait.RLock() + exp = v.exceeded + vr = v.data + v.wait.RUnlock() + } + return +} diff --git a/sync/Map_test.go b/sync/Map_test.go index 805e892..d5475b5 100644 --- a/sync/Map_test.go +++ b/sync/Map_test.go @@ -13,17 +13,15 @@ type tmp struct { func TestLS(t *testing.T) { var c Map - var ls = LoadOrStoreFunc[int]{ - Init: func() *int { - var i = 1 - return &i - }, - } - a0, l0 := ls.LoadOrStore(&c, `1`) + var ls = NewLoadOrInitFunc[int](&c).SetInit(func() *int { + var i = 1 + return &i + }) + a0, l0 := ls.LoadOrInit(`1`) if l0 { t.Fatal() } - a1, l1 := ls.LoadOrStore(&c, `1`) + a1, l1 := ls.LoadOrInit(`1`) if !l1 { t.Fatal() } diff --git a/web/Web.go b/web/Web.go index bf22f38..8a36ca0 100644 --- a/web/Web.go +++ b/web/Web.go @@ -674,7 +674,7 @@ func (t *Exprier) Reg(dur time.Duration, reNewKey ...string) (string, error) { for { select { case key1 := <-t.mc: - if t.m.Delete(key1) { + if _, ok := t.m.LoadAndDelete(key1); ok { t.mc <- newkey t.m.Store(newkey, time.Now().Add(dur)) return newkey, nil -- 2.39.2