github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/epoch/manager.go (about) 1 package epoch 2 3 import ( 4 "time" 5 "unsafe" 6 7 "github.com/pingcap/badger/y" 8 ) 9 10 type GuardsInspector interface { 11 Begin() 12 Inspect(payload interface{}, active bool) 13 End() 14 } 15 16 type NoOpInspector struct{} 17 18 func (i NoOpInspector) Begin() {} 19 func (i NoOpInspector) Inspect(payload interface{}, active bool) {} 20 func (i NoOpInspector) End() {} 21 22 type Guard struct { 23 localEpoch atomicEpoch 24 mgr *ResourceManager 25 deletions []deletion 26 payload interface{} 27 28 next unsafe.Pointer 29 } 30 31 func (g *Guard) Delete(resources []Resource) { 32 globalEpoch := g.mgr.currentEpoch.load() 33 g.deletions = append(g.deletions, deletion{ 34 epoch: globalEpoch, 35 resources: resources, 36 }) 37 } 38 39 func (g *Guard) Done() { 40 g.localEpoch.store(g.localEpoch.load().deactivate()) 41 } 42 43 func (g *Guard) collect(globalEpoch epoch) bool { 44 ds := g.deletions[:0] 45 for _, d := range g.deletions { 46 if globalEpoch.sub(d.epoch) < 2 { 47 ds = append(ds, d) 48 continue 49 } 50 for _, r := range d.resources { 51 r.Delete() 52 } 53 d.resources = nil 54 } 55 g.deletions = ds 56 return len(ds) == 0 57 } 58 59 type Resource interface { 60 Delete() error 61 } 62 63 type ResourceManager struct { 64 currentEpoch atomicEpoch 65 66 // TODO: cache line size for non x86 67 // cachePad make currentEpoch stay in a separate cache line. 68 cachePad [64]byte 69 guards guardList 70 inspector GuardsInspector 71 } 72 73 func NewResourceManager(c *y.Closer, inspector GuardsInspector) *ResourceManager { 74 rm := &ResourceManager{ 75 currentEpoch: atomicEpoch{epoch: 1 << 1}, 76 inspector: inspector, 77 } 78 c.AddRunning(1) 79 go rm.collectLoop(c) 80 return rm 81 } 82 83 func (rm *ResourceManager) AcquireWithPayload(payload interface{}) *Guard { 84 g := &Guard{ 85 mgr: rm, 86 payload: payload, 87 } 88 g.localEpoch.store(rm.currentEpoch.load().activate()) 89 rm.guards.add(g) 90 return g 91 } 92 93 func (rm *ResourceManager) Acquire() *Guard { 94 return rm.AcquireWithPayload(nil) 95 } 96 97 func (rm *ResourceManager) collectLoop(c *y.Closer) { 98 defer c.Done() 99 ticker := time.NewTicker(100 * time.Millisecond) 100 for { 101 select { 102 case <-ticker.C: 103 rm.collect() 104 case <-c.HasBeenClosed(): 105 return 106 } 107 } 108 } 109 110 func (rm *ResourceManager) collect() { 111 canAdvance := true 112 globalEpoch := rm.currentEpoch.load() 113 114 rm.inspector.Begin() 115 rm.guards.iterate(func(guard *Guard) bool { 116 localEpoch := guard.localEpoch.load() 117 118 isActive := localEpoch.isActive() 119 rm.inspector.Inspect(guard.payload, isActive) 120 121 if isActive { 122 canAdvance = canAdvance && localEpoch.sub(globalEpoch) == 0 123 return false 124 } 125 126 return guard.collect(globalEpoch) 127 }) 128 rm.inspector.End() 129 130 if canAdvance { 131 rm.currentEpoch.store(globalEpoch.successor()) 132 } 133 } 134 135 type deletion struct { 136 epoch epoch 137 resources []Resource 138 }