1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
// Copyright (c) 2025 Karl Gaissmaier
// SPDX-License-Identifier: MIT
package bart_test
import (
"sync"
"sync/atomic"
"github.com/gaissmai/bart"
)
// We use *testVal as the generic payload type V (a pointer type).
type testVal struct {
data int
}
// Clone enables deep copying for ...Persist operations.
// Detected via structural typing (presence of a matching Clone method).
func (v *testVal) Clone() *testVal {
if v == nil {
return nil
}
return &testVal{data: v.data}
}
// #######################################
// ExampleTable_concurrent demonstrates safe concurrent usage of bart.Table.
// This example is intended to be run with the Go race detector enabled
// (use `go test -race -run=ExampleTable_concurrent`)
// to verify that concurrent access is safe and free of data races.
//
// This example demonstrates how multiple goroutines perform lock-free, concurrent reads
// via an atomic pointer, while synchronizing writers with a mutex to ensure exclusive access.
// This concurrency pattern is useful when reads are frequent and writes are rare
// or take a long time in comparison to reads,
// providing high performance for concurrent workloads.
//
// If the payload V either contains pointers or is a pointer,
// implement a Clone method (structural typing is used).
func ExampleTable_concurrent() {
var tblAtomicPtr atomic.Pointer[bart.Table[*testVal]]
var tblMutex sync.Mutex
baseTbl := new(bart.Table[*testVal])
tblAtomicPtr.Store(baseTbl)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for range 100_000 {
for _, ip := range exampleIPs {
_, _ = tblAtomicPtr.Load().Lookup(ip)
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for range 1_000 {
tblMutex.Lock()
cur := tblAtomicPtr.Load()
// batch of inserts
next := cur
for _, pfx := range examplePrefixes {
next = next.InsertPersist(pfx, &testVal{data: 0})
}
tblAtomicPtr.Store(next)
tblMutex.Unlock()
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for range 1_000 {
tblMutex.Lock()
cur := tblAtomicPtr.Load()
// batch of deletes
next := cur
for _, pfx := range examplePrefixes {
next = next.DeletePersist(pfx)
}
tblAtomicPtr.Store(next)
tblMutex.Unlock()
}
}()
wg.Wait()
// Output:
}
|