github.com/coocood/badger@v1.5.1-0.20200528065104-c02ac3616d04/epoch/manager.go (about)

     1  package epoch
     2  
     3  import (
     4  	"time"
     5  	"unsafe"
     6  
     7  	"github.com/coocood/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  }