type mapExceededItem[V any] struct {
data *V
exceeded time.Time
+ wait sync.RWMutex
}
func (t *MapExceeded[K, V]) Store(k K, v *V, dur time.Duration) {
- t.m.Store(k, mapExceededItem[V]{
+ t.m.Store(k, &mapExceededItem[V]{
data: v,
exceeded: time.Now().Add(dur),
})
}
func (t *MapExceeded[K, V]) Load(k K) (*V, bool) {
- if v, ok := t.m.Load(k); ok {
- if v.(mapExceededItem[V]).exceeded.After(time.Now()) {
- return v.(mapExceededItem[V]).data, true
+ if v, ok := t.m.LoadV(k).(*mapExceededItem[V]); ok {
+ if v.exceeded.After(time.Now()) {
+ return v.data, true
}
t.Delete(k)
}
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()) {
+ if value.(*mapExceededItem[V]).exceeded.After(time.Now()) {
return f(key.(K), value.(*V))
}
t.Delete(key.(K))
func (t *MapExceeded[K, V]) GC() {
t.m.Range(func(key, value any) bool {
- if value.(mapExceededItem[V]).exceeded.Before(time.Now()) {
+ 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
+}
t.Fatal()
}
}
+
+func TestMapExceeded2(t *testing.T) {
+ var m MapExceeded[string, []byte]
+ var data = []byte("1")
+ if v, loaded, f := m.LoadOrStore("1"); v != nil || loaded {
+ t.Fatal()
+ } else {
+ f(&data, time.Second)
+ if v, ok := m.Load("1"); !ok || v == nil || !bytes.Equal(data, *v) {
+ t.Fatal()
+ }
+ }
+
+ var w sync.WaitGroup
+ w.Add(10)
+ for i := 0; i < 10; i++ {
+ go func() {
+ v, loaded, f := m.LoadOrStore("2")
+ if (!loaded && v != nil) || (loaded && !bytes.Equal(data, *v)) {
+ panic("")
+ }
+ f(&data, time.Second)
+ w.Done()
+ }()
+ }
+ w.Wait()
+}