From 0ebb1a2e3eff33a76bc6599eecaf84b57cb4cae3 Mon Sep 17 00:00:00 2001 From: zhangyunhao Date: Fri, 16 Jul 2021 15:42:50 +0800 Subject: [PATCH] support LoadOrStoreLazy --- collection/skipmap/skipmap.go | 59 ++ collection/skipmap/skipmap_test.go | 29 + collection/skipmap/types.go | 1125 +++++++++++++++++++++++++++- 3 files changed, 1211 insertions(+), 2 deletions(-) diff --git a/collection/skipmap/skipmap.go b/collection/skipmap/skipmap.go index d4da3ed8..59fd05a6 100644 --- a/collection/skipmap/skipmap.go +++ b/collection/skipmap/skipmap.go @@ -242,6 +242,7 @@ func (s *Int64Map) Load(key int64) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Int64Map) LoadAndDelete(key int64) (value interface{}, loaded bool) { var ( nodeToDelete *int64Node @@ -307,6 +308,7 @@ func (s *Int64Map) LoadAndDelete(key int64) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Int64Map) LoadOrStore(key int64, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*int64Node @@ -360,6 +362,63 @@ func (s *Int64Map) LoadOrStore(key int64, value interface{}) (actual interface{} } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Int64Map) LoadOrStoreLazy(key int64, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*int64Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *int64Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt64(preds, highestLocked) + continue + } + value := f() + nn := newInt64Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt64(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Int64Map) Delete(key int64) bool { var ( diff --git a/collection/skipmap/skipmap_test.go b/collection/skipmap/skipmap_test.go index f471e4f2..6383f0eb 100644 --- a/collection/skipmap/skipmap_test.go +++ b/collection/skipmap/skipmap_test.go @@ -230,6 +230,35 @@ func TestSkipMap(t *testing.T) { if added != 1 { t.Fatal("Only one LoadAndDelete can successfully get a value") } + + // Correntness 5. (LoadOrStoreLazy) + mp = NewInt() + tmpmap = NewInt64() + samekey = 123 + added = 0 + var fcalled int64 + valuef := func() interface{} { + atomic.AddInt64(&fcalled, 1) + return fastrand.Int63() + } + for i := 1; i < 1000; i++ { + wg.Add(1) + go func() { + actual, loaded := mp.LoadOrStoreLazy(samekey, valuef) + if !loaded { + atomic.AddInt64(&added, 1) + } + tmpmap.Store(actual.(int64), nil) + wg.Done() + }() + } + wg.Wait() + if added != 1 || fcalled != 1 { + t.Fatal("only one LoadOrStoreLazy can successfully insert a key and value") + } + if tmpmap.Len() != 1 { + t.Fatal("only one value can be returned from LoadOrStoreLazy") + } } func TestSkipMapDesc(t *testing.T) { diff --git a/collection/skipmap/types.go b/collection/skipmap/types.go index 2be978f2..91d46a32 100644 --- a/collection/skipmap/types.go +++ b/collection/skipmap/types.go @@ -241,6 +241,7 @@ func (s *Float32Map) Load(key float32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Float32Map) LoadAndDelete(key float32) (value interface{}, loaded bool) { var ( nodeToDelete *float32Node @@ -306,6 +307,7 @@ func (s *Float32Map) LoadAndDelete(key float32) (value interface{}, loaded bool) // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Float32Map) LoadOrStore(key float32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*float32Node @@ -359,6 +361,63 @@ func (s *Float32Map) LoadOrStore(key float32, value interface{}) (actual interfa } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Float32Map) LoadOrStoreLazy(key float32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*float32Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *float32Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockFloat32(preds, highestLocked) + continue + } + value := f() + nn := newFloat32Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockFloat32(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Float32Map) Delete(key float32) bool { var ( @@ -667,6 +726,7 @@ func (s *Float32MapDesc) Load(key float32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Float32MapDesc) LoadAndDelete(key float32) (value interface{}, loaded bool) { var ( nodeToDelete *float32NodeDesc @@ -732,6 +792,7 @@ func (s *Float32MapDesc) LoadAndDelete(key float32) (value interface{}, loaded b // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Float32MapDesc) LoadOrStore(key float32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*float32NodeDesc @@ -785,6 +846,63 @@ func (s *Float32MapDesc) LoadOrStore(key float32, value interface{}) (actual int } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Float32MapDesc) LoadOrStoreLazy(key float32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*float32NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *float32NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockFloat32Desc(preds, highestLocked) + continue + } + value := f() + nn := newFloat32NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockFloat32Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Float32MapDesc) Delete(key float32) bool { var ( @@ -1093,6 +1211,7 @@ func (s *Float64Map) Load(key float64) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Float64Map) LoadAndDelete(key float64) (value interface{}, loaded bool) { var ( nodeToDelete *float64Node @@ -1158,6 +1277,7 @@ func (s *Float64Map) LoadAndDelete(key float64) (value interface{}, loaded bool) // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Float64Map) LoadOrStore(key float64, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*float64Node @@ -1211,6 +1331,63 @@ func (s *Float64Map) LoadOrStore(key float64, value interface{}) (actual interfa } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Float64Map) LoadOrStoreLazy(key float64, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*float64Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *float64Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockFloat64(preds, highestLocked) + continue + } + value := f() + nn := newFloat64Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockFloat64(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Float64Map) Delete(key float64) bool { var ( @@ -1519,6 +1696,7 @@ func (s *Float64MapDesc) Load(key float64) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Float64MapDesc) LoadAndDelete(key float64) (value interface{}, loaded bool) { var ( nodeToDelete *float64NodeDesc @@ -1584,6 +1762,7 @@ func (s *Float64MapDesc) LoadAndDelete(key float64) (value interface{}, loaded b // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Float64MapDesc) LoadOrStore(key float64, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*float64NodeDesc @@ -1637,6 +1816,63 @@ func (s *Float64MapDesc) LoadOrStore(key float64, value interface{}) (actual int } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Float64MapDesc) LoadOrStoreLazy(key float64, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*float64NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *float64NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockFloat64Desc(preds, highestLocked) + continue + } + value := f() + nn := newFloat64NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockFloat64Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Float64MapDesc) Delete(key float64) bool { var ( @@ -1945,6 +2181,7 @@ func (s *Int32Map) Load(key int32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Int32Map) LoadAndDelete(key int32) (value interface{}, loaded bool) { var ( nodeToDelete *int32Node @@ -2010,6 +2247,7 @@ func (s *Int32Map) LoadAndDelete(key int32) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Int32Map) LoadOrStore(key int32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*int32Node @@ -2063,6 +2301,63 @@ func (s *Int32Map) LoadOrStore(key int32, value interface{}) (actual interface{} } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Int32Map) LoadOrStoreLazy(key int32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*int32Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *int32Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt32(preds, highestLocked) + continue + } + value := f() + nn := newInt32Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt32(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Int32Map) Delete(key int32) bool { var ( @@ -2371,6 +2666,7 @@ func (s *Int32MapDesc) Load(key int32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Int32MapDesc) LoadAndDelete(key int32) (value interface{}, loaded bool) { var ( nodeToDelete *int32NodeDesc @@ -2436,6 +2732,7 @@ func (s *Int32MapDesc) LoadAndDelete(key int32) (value interface{}, loaded bool) // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Int32MapDesc) LoadOrStore(key int32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*int32NodeDesc @@ -2489,7 +2786,64 @@ func (s *Int32MapDesc) LoadOrStore(key int32, value interface{}) (actual interfa } } -// Delete deletes the value for a key. +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Int32MapDesc) LoadOrStoreLazy(key int32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*int32NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *int32NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt32Desc(preds, highestLocked) + continue + } + value := f() + nn := newInt32NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt32Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + +// Delete deletes the value for a key. func (s *Int32MapDesc) Delete(key int32) bool { var ( nodeToDelete *int32NodeDesc @@ -2797,6 +3151,7 @@ func (s *Int16Map) Load(key int16) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Int16Map) LoadAndDelete(key int16) (value interface{}, loaded bool) { var ( nodeToDelete *int16Node @@ -2862,6 +3217,7 @@ func (s *Int16Map) LoadAndDelete(key int16) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Int16Map) LoadOrStore(key int16, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*int16Node @@ -2915,6 +3271,63 @@ func (s *Int16Map) LoadOrStore(key int16, value interface{}) (actual interface{} } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Int16Map) LoadOrStoreLazy(key int16, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*int16Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *int16Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt16(preds, highestLocked) + continue + } + value := f() + nn := newInt16Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt16(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Int16Map) Delete(key int16) bool { var ( @@ -3223,6 +3636,7 @@ func (s *Int16MapDesc) Load(key int16) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Int16MapDesc) LoadAndDelete(key int16) (value interface{}, loaded bool) { var ( nodeToDelete *int16NodeDesc @@ -3288,6 +3702,7 @@ func (s *Int16MapDesc) LoadAndDelete(key int16) (value interface{}, loaded bool) // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Int16MapDesc) LoadOrStore(key int16, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*int16NodeDesc @@ -3341,6 +3756,63 @@ func (s *Int16MapDesc) LoadOrStore(key int16, value interface{}) (actual interfa } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Int16MapDesc) LoadOrStoreLazy(key int16, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*int16NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *int16NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt16Desc(preds, highestLocked) + continue + } + value := f() + nn := newInt16NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt16Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Int16MapDesc) Delete(key int16) bool { var ( @@ -3649,6 +4121,7 @@ func (s *IntMap) Load(key int) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *IntMap) LoadAndDelete(key int) (value interface{}, loaded bool) { var ( nodeToDelete *intNode @@ -3714,6 +4187,7 @@ func (s *IntMap) LoadAndDelete(key int) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *IntMap) LoadOrStore(key int, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*intNode @@ -3767,6 +4241,63 @@ func (s *IntMap) LoadOrStore(key int, value interface{}) (actual interface{}, lo } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *IntMap) LoadOrStoreLazy(key int, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*intNode + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *intNode + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockInt(preds, highestLocked) + continue + } + value := f() + nn := newIntNode(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockInt(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *IntMap) Delete(key int) bool { var ( @@ -4075,6 +4606,7 @@ func (s *IntMapDesc) Load(key int) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *IntMapDesc) LoadAndDelete(key int) (value interface{}, loaded bool) { var ( nodeToDelete *intNodeDesc @@ -4140,6 +4672,7 @@ func (s *IntMapDesc) LoadAndDelete(key int) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *IntMapDesc) LoadOrStore(key int, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*intNodeDesc @@ -4193,6 +4726,63 @@ func (s *IntMapDesc) LoadOrStore(key int, value interface{}) (actual interface{} } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *IntMapDesc) LoadOrStoreLazy(key int, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*intNodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *intNodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockIntDesc(preds, highestLocked) + continue + } + value := f() + nn := newIntNodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockIntDesc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *IntMapDesc) Delete(key int) bool { var ( @@ -4501,6 +5091,7 @@ func (s *Uint64Map) Load(key uint64) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint64Map) LoadAndDelete(key uint64) (value interface{}, loaded bool) { var ( nodeToDelete *uint64Node @@ -4566,6 +5157,7 @@ func (s *Uint64Map) LoadAndDelete(key uint64) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint64Map) LoadOrStore(key uint64, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint64Node @@ -4619,6 +5211,63 @@ func (s *Uint64Map) LoadOrStore(key uint64, value interface{}) (actual interface } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint64Map) LoadOrStoreLazy(key uint64, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint64Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint64Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint64(preds, highestLocked) + continue + } + value := f() + nn := newUuint64Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint64(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Uint64Map) Delete(key uint64) bool { var ( @@ -4927,6 +5576,7 @@ func (s *Uint64MapDesc) Load(key uint64) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint64MapDesc) LoadAndDelete(key uint64) (value interface{}, loaded bool) { var ( nodeToDelete *uint64NodeDesc @@ -4992,6 +5642,7 @@ func (s *Uint64MapDesc) LoadAndDelete(key uint64) (value interface{}, loaded boo // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint64MapDesc) LoadOrStore(key uint64, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint64NodeDesc @@ -5045,6 +5696,63 @@ func (s *Uint64MapDesc) LoadOrStore(key uint64, value interface{}) (actual inter } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint64MapDesc) LoadOrStoreLazy(key uint64, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint64NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint64NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint64Desc(preds, highestLocked) + continue + } + value := f() + nn := newUuint64NodeDescDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint64Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Uint64MapDesc) Delete(key uint64) bool { var ( @@ -5353,6 +6061,7 @@ func (s *Uint32Map) Load(key uint32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint32Map) LoadAndDelete(key uint32) (value interface{}, loaded bool) { var ( nodeToDelete *uint32Node @@ -5418,6 +6127,7 @@ func (s *Uint32Map) LoadAndDelete(key uint32) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint32Map) LoadOrStore(key uint32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint32Node @@ -5471,6 +6181,63 @@ func (s *Uint32Map) LoadOrStore(key uint32, value interface{}) (actual interface } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint32Map) LoadOrStoreLazy(key uint32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint32Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint32Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint32(preds, highestLocked) + continue + } + value := f() + nn := newUint32Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint32(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Uint32Map) Delete(key uint32) bool { var ( @@ -5779,6 +6546,7 @@ func (s *Uint32MapDesc) Load(key uint32) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint32MapDesc) LoadAndDelete(key uint32) (value interface{}, loaded bool) { var ( nodeToDelete *uint32NodeDesc @@ -5844,6 +6612,7 @@ func (s *Uint32MapDesc) LoadAndDelete(key uint32) (value interface{}, loaded boo // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint32MapDesc) LoadOrStore(key uint32, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint32NodeDesc @@ -5884,7 +6653,64 @@ func (s *Uint32MapDesc) LoadOrStore(key uint32, value interface{}) (actual inter unlockUint32Desc(preds, highestLocked) continue } - + + nn := newUint32NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint32Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint32MapDesc) LoadOrStoreLazy(key uint32, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint32NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint32NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint32Desc(preds, highestLocked) + continue + } + value := f() nn := newUint32NodeDesc(key, value, level) for layer := 0; layer < level; layer++ { nn.storeNext(layer, succs[layer]) @@ -6205,6 +7031,7 @@ func (s *Uint16Map) Load(key uint16) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint16Map) LoadAndDelete(key uint16) (value interface{}, loaded bool) { var ( nodeToDelete *uint16Node @@ -6270,6 +7097,7 @@ func (s *Uint16Map) LoadAndDelete(key uint16) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint16Map) LoadOrStore(key uint16, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint16Node @@ -6323,6 +7151,63 @@ func (s *Uint16Map) LoadOrStore(key uint16, value interface{}) (actual interface } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint16Map) LoadOrStoreLazy(key uint16, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint16Node + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint16Node + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint16(preds, highestLocked) + continue + } + value := f() + nn := newUint16Node(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint16(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Uint16Map) Delete(key uint16) bool { var ( @@ -6631,6 +7516,7 @@ func (s *Uint16MapDesc) Load(key uint16) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *Uint16MapDesc) LoadAndDelete(key uint16) (value interface{}, loaded bool) { var ( nodeToDelete *uint16NodeDesc @@ -6696,6 +7582,7 @@ func (s *Uint16MapDesc) LoadAndDelete(key uint16) (value interface{}, loaded boo // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *Uint16MapDesc) LoadOrStore(key uint16, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uint16NodeDesc @@ -6749,6 +7636,63 @@ func (s *Uint16MapDesc) LoadOrStore(key uint16, value interface{}) (actual inter } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *Uint16MapDesc) LoadOrStoreLazy(key uint16, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uint16NodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uint16NodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint16Desc(preds, highestLocked) + continue + } + value := f() + nn := newUint16NodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint16Desc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *Uint16MapDesc) Delete(key uint16) bool { var ( @@ -7057,6 +8001,7 @@ func (s *UintMap) Load(key uint) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *UintMap) LoadAndDelete(key uint) (value interface{}, loaded bool) { var ( nodeToDelete *uintNode @@ -7122,6 +8067,7 @@ func (s *UintMap) LoadAndDelete(key uint) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *UintMap) LoadOrStore(key uint, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uintNode @@ -7175,6 +8121,63 @@ func (s *UintMap) LoadOrStore(key uint, value interface{}) (actual interface{}, } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *UintMap) LoadOrStoreLazy(key uint, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uintNode + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uintNode + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUint(preds, highestLocked) + continue + } + value := f() + nn := newUintNode(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUint(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *UintMap) Delete(key uint) bool { var ( @@ -7483,6 +8486,7 @@ func (s *UintMapDesc) Load(key uint) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *UintMapDesc) LoadAndDelete(key uint) (value interface{}, loaded bool) { var ( nodeToDelete *uintNodeDesc @@ -7548,6 +8552,7 @@ func (s *UintMapDesc) LoadAndDelete(key uint) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *UintMapDesc) LoadOrStore(key uint, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*uintNodeDesc @@ -7601,6 +8606,63 @@ func (s *UintMapDesc) LoadOrStore(key uint, value interface{}) (actual interface } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *UintMapDesc) LoadOrStoreLazy(key uint, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*uintNodeDesc + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *uintNodeDesc + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockUintDesc(preds, highestLocked) + continue + } + value := f() + nn := newUintNodeDesc(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockUintDesc(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *UintMapDesc) Delete(key uint) bool { var ( @@ -7906,6 +8968,7 @@ func (s *StringMap) Load(key string) (value interface{}, ok bool) { // LoadAndDelete deletes the value for a key, returning the previous value if any. // The loaded result reports whether the key was present. +// (Modified from Delete) func (s *StringMap) LoadAndDelete(key string) (value interface{}, loaded bool) { var ( nodeToDelete *stringNode @@ -7971,6 +9034,7 @@ func (s *StringMap) LoadAndDelete(key string) (value interface{}, loaded bool) { // LoadOrStore returns the existing value for the key if present. // Otherwise, it stores and returns the given value. // The loaded result is true if the value was loaded, false if stored. +// (Modified from Store) func (s *StringMap) LoadOrStore(key string, value interface{}) (actual interface{}, loaded bool) { level := s.randomlevel() var preds, succs [maxLevel]*stringNode @@ -8024,6 +9088,63 @@ func (s *StringMap) LoadOrStore(key string, value interface{}) (actual interface } } +// LoadOrStoreLazy returns the existing value for the key if present. +// Otherwise, it stores and returns the given value from f, f will only be called once. +// The loaded result is true if the value was loaded, false if stored. +// (Modified from LoadOrStore) +func (s *StringMap) LoadOrStoreLazy(key string, f func() interface{}) (actual interface{}, loaded bool) { + level := s.randomlevel() + var preds, succs [maxLevel]*stringNode + for { + nodeFound := s.findNode(key, &preds, &succs) + if nodeFound != nil { // indicating the key is already in the skip-list + if !nodeFound.flags.Get(marked) { + // We don't need to care about whether or not the node is fully linked, + // just return the value. + return nodeFound.loadVal(), true + } + // If the node is marked, represents some other goroutines is in the process of deleting this node, + // we need to add this node in next loop. + continue + } + + // Add this node into skip list. + var ( + highestLocked = -1 // the highest level being locked by this process + valid = true + pred, succ, prevPred *stringNode + ) + for layer := 0; valid && layer < level; layer++ { + pred = preds[layer] // target node's previous node + succ = succs[layer] // target node's next node + if pred != prevPred { // the node in this layer could be locked by previous loop + pred.mu.Lock() + highestLocked = layer + prevPred = pred + } + // valid check if there is another node has inserted into the skip list in this layer during this process. + // It is valid if: + // 1. The previous node and next node both are not marked. + // 2. The previous node's next node is succ in this layer. + valid = !pred.flags.Get(marked) && pred.loadNext(layer) == succ && (succ == nil || !succ.flags.Get(marked)) + } + if !valid { + unlockString(preds, highestLocked) + continue + } + value := f() + nn := newStringNode(key, value, level) + for layer := 0; layer < level; layer++ { + nn.storeNext(layer, succs[layer]) + preds[layer].atomicStoreNext(layer, nn) + } + nn.flags.SetTrue(fullyLinked) + unlockString(preds, highestLocked) + atomic.AddInt64(&s.length, 1) + return value, false + } +} + // Delete deletes the value for a key. func (s *StringMap) Delete(key string) bool { var (