github.com/vipernet-xyz/tm@v0.34.24/libs/clist/clist.go (about)

     1  package clist
     2  
     3  /*
     4  
     5  The purpose of CList is to provide a goroutine-safe linked-list.
     6  This list can be traversed concurrently by any number of goroutines.
     7  However, removed CElements cannot be added back.
     8  NOTE: Not all methods of container/list are (yet) implemented.
     9  NOTE: Removed elements need to DetachPrev or DetachNext consistently
    10  to ensure garbage collection of removed elements.
    11  
    12  */
    13  
    14  import (
    15  	"fmt"
    16  	"sync"
    17  
    18  	tmsync "github.com/vipernet-xyz/tm/libs/sync"
    19  )
    20  
    21  // MaxLength is the max allowed number of elements a linked list is
    22  // allowed to contain.
    23  // If more elements are pushed to the list it will panic.
    24  const MaxLength = int(^uint(0) >> 1)
    25  
    26  /*
    27  CElement is an element of a linked-list
    28  Traversal from a CElement is goroutine-safe.
    29  
    30  We can't avoid using WaitGroups or for-loops given the documentation
    31  spec without re-implementing the primitives that already exist in
    32  golang/sync. Notice that WaitGroup allows many go-routines to be
    33  simultaneously released, which is what we want. Mutex doesn't do
    34  this. RWMutex does this, but it's clumsy to use in the way that a
    35  WaitGroup would be used -- and we'd end up having two RWMutex's for
    36  prev/next each, which is doubly confusing.
    37  
    38  sync.Cond would be sort-of useful, but we don't need a write-lock in
    39  the for-loop. Use sync.Cond when you need serial access to the
    40  "condition". In our case our condition is if `next != nil || removed`,
    41  and there's no reason to serialize that condition for goroutines
    42  waiting on NextWait() (since it's just a read operation).
    43  */
    44  type CElement struct {
    45  	mtx        tmsync.RWMutex
    46  	prev       *CElement
    47  	prevWg     *sync.WaitGroup
    48  	prevWaitCh chan struct{}
    49  	next       *CElement
    50  	nextWg     *sync.WaitGroup
    51  	nextWaitCh chan struct{}
    52  	removed    bool
    53  
    54  	Value interface{} // immutable
    55  }
    56  
    57  // Blocking implementation of Next().
    58  // May return nil iff CElement was tail and got removed.
    59  func (e *CElement) NextWait() *CElement {
    60  	for {
    61  		e.mtx.RLock()
    62  		next := e.next
    63  		nextWg := e.nextWg
    64  		removed := e.removed
    65  		e.mtx.RUnlock()
    66  
    67  		if next != nil || removed {
    68  			return next
    69  		}
    70  
    71  		nextWg.Wait()
    72  		// e.next doesn't necessarily exist here.
    73  		// That's why we need to continue a for-loop.
    74  	}
    75  }
    76  
    77  // Blocking implementation of Prev().
    78  // May return nil iff CElement was head and got removed.
    79  func (e *CElement) PrevWait() *CElement {
    80  	for {
    81  		e.mtx.RLock()
    82  		prev := e.prev
    83  		prevWg := e.prevWg
    84  		removed := e.removed
    85  		e.mtx.RUnlock()
    86  
    87  		if prev != nil || removed {
    88  			return prev
    89  		}
    90  
    91  		prevWg.Wait()
    92  	}
    93  }
    94  
    95  // PrevWaitChan can be used to wait until Prev becomes not nil. Once it does,
    96  // channel will be closed.
    97  func (e *CElement) PrevWaitChan() <-chan struct{} {
    98  	e.mtx.RLock()
    99  	defer e.mtx.RUnlock()
   100  
   101  	return e.prevWaitCh
   102  }
   103  
   104  // NextWaitChan can be used to wait until Next becomes not nil. Once it does,
   105  // channel will be closed.
   106  func (e *CElement) NextWaitChan() <-chan struct{} {
   107  	e.mtx.RLock()
   108  	defer e.mtx.RUnlock()
   109  
   110  	return e.nextWaitCh
   111  }
   112  
   113  // Nonblocking, may return nil if at the end.
   114  func (e *CElement) Next() *CElement {
   115  	e.mtx.RLock()
   116  	val := e.next
   117  	e.mtx.RUnlock()
   118  	return val
   119  }
   120  
   121  // Nonblocking, may return nil if at the end.
   122  func (e *CElement) Prev() *CElement {
   123  	e.mtx.RLock()
   124  	prev := e.prev
   125  	e.mtx.RUnlock()
   126  	return prev
   127  }
   128  
   129  func (e *CElement) Removed() bool {
   130  	e.mtx.RLock()
   131  	isRemoved := e.removed
   132  	e.mtx.RUnlock()
   133  	return isRemoved
   134  }
   135  
   136  func (e *CElement) DetachNext() {
   137  	e.mtx.Lock()
   138  	if !e.removed {
   139  		e.mtx.Unlock()
   140  		panic("DetachNext() must be called after Remove(e)")
   141  	}
   142  	e.next = nil
   143  	e.mtx.Unlock()
   144  }
   145  
   146  func (e *CElement) DetachPrev() {
   147  	e.mtx.Lock()
   148  	if !e.removed {
   149  		e.mtx.Unlock()
   150  		panic("DetachPrev() must be called after Remove(e)")
   151  	}
   152  	e.prev = nil
   153  	e.mtx.Unlock()
   154  }
   155  
   156  // NOTE: This function needs to be safe for
   157  // concurrent goroutines waiting on nextWg.
   158  func (e *CElement) SetNext(newNext *CElement) {
   159  	e.mtx.Lock()
   160  
   161  	oldNext := e.next
   162  	e.next = newNext
   163  	if oldNext != nil && newNext == nil {
   164  		// See https://golang.org/pkg/sync/:
   165  		//
   166  		// If a WaitGroup is reused to wait for several independent sets of
   167  		// events, new Add calls must happen after all previous Wait calls have
   168  		// returned.
   169  		e.nextWg = waitGroup1() // WaitGroups are difficult to re-use.
   170  		e.nextWaitCh = make(chan struct{})
   171  	}
   172  	if oldNext == nil && newNext != nil {
   173  		e.nextWg.Done()
   174  		close(e.nextWaitCh)
   175  	}
   176  	e.mtx.Unlock()
   177  }
   178  
   179  // NOTE: This function needs to be safe for
   180  // concurrent goroutines waiting on prevWg
   181  func (e *CElement) SetPrev(newPrev *CElement) {
   182  	e.mtx.Lock()
   183  
   184  	oldPrev := e.prev
   185  	e.prev = newPrev
   186  	if oldPrev != nil && newPrev == nil {
   187  		e.prevWg = waitGroup1() // WaitGroups are difficult to re-use.
   188  		e.prevWaitCh = make(chan struct{})
   189  	}
   190  	if oldPrev == nil && newPrev != nil {
   191  		e.prevWg.Done()
   192  		close(e.prevWaitCh)
   193  	}
   194  	e.mtx.Unlock()
   195  }
   196  
   197  func (e *CElement) SetRemoved() {
   198  	e.mtx.Lock()
   199  
   200  	e.removed = true
   201  
   202  	// This wakes up anyone waiting in either direction.
   203  	if e.prev == nil {
   204  		e.prevWg.Done()
   205  		close(e.prevWaitCh)
   206  	}
   207  	if e.next == nil {
   208  		e.nextWg.Done()
   209  		close(e.nextWaitCh)
   210  	}
   211  	e.mtx.Unlock()
   212  }
   213  
   214  //--------------------------------------------------------------------------------
   215  
   216  // CList represents a linked list.
   217  // The zero value for CList is an empty list ready to use.
   218  // Operations are goroutine-safe.
   219  // Panics if length grows beyond the max.
   220  type CList struct {
   221  	mtx    tmsync.RWMutex
   222  	wg     *sync.WaitGroup
   223  	waitCh chan struct{}
   224  	head   *CElement // first element
   225  	tail   *CElement // last element
   226  	len    int       // list length
   227  	maxLen int       // max list length
   228  }
   229  
   230  func (l *CList) Init() *CList {
   231  	l.mtx.Lock()
   232  
   233  	l.wg = waitGroup1()
   234  	l.waitCh = make(chan struct{})
   235  	l.head = nil
   236  	l.tail = nil
   237  	l.len = 0
   238  	l.mtx.Unlock()
   239  	return l
   240  }
   241  
   242  // Return CList with MaxLength. CList will panic if it goes beyond MaxLength.
   243  func New() *CList { return newWithMax(MaxLength) }
   244  
   245  // Return CList with given maxLength.
   246  // Will panic if list exceeds given maxLength.
   247  func newWithMax(maxLength int) *CList {
   248  	l := new(CList)
   249  	l.maxLen = maxLength
   250  	return l.Init()
   251  }
   252  
   253  func (l *CList) Len() int {
   254  	l.mtx.RLock()
   255  	len := l.len
   256  	l.mtx.RUnlock()
   257  	return len
   258  }
   259  
   260  func (l *CList) Front() *CElement {
   261  	l.mtx.RLock()
   262  	head := l.head
   263  	l.mtx.RUnlock()
   264  	return head
   265  }
   266  
   267  func (l *CList) FrontWait() *CElement {
   268  	// Loop until the head is non-nil else wait and try again
   269  	for {
   270  		l.mtx.RLock()
   271  		head := l.head
   272  		wg := l.wg
   273  		l.mtx.RUnlock()
   274  
   275  		if head != nil {
   276  			return head
   277  		}
   278  		wg.Wait()
   279  		// NOTE: If you think l.head exists here, think harder.
   280  	}
   281  }
   282  
   283  func (l *CList) Back() *CElement {
   284  	l.mtx.RLock()
   285  	back := l.tail
   286  	l.mtx.RUnlock()
   287  	return back
   288  }
   289  
   290  func (l *CList) BackWait() *CElement {
   291  	for {
   292  		l.mtx.RLock()
   293  		tail := l.tail
   294  		wg := l.wg
   295  		l.mtx.RUnlock()
   296  
   297  		if tail != nil {
   298  			return tail
   299  		}
   300  		wg.Wait()
   301  		// l.tail doesn't necessarily exist here.
   302  		// That's why we need to continue a for-loop.
   303  	}
   304  }
   305  
   306  // WaitChan can be used to wait until Front or Back becomes not nil. Once it
   307  // does, channel will be closed.
   308  func (l *CList) WaitChan() <-chan struct{} {
   309  	l.mtx.Lock()
   310  	defer l.mtx.Unlock()
   311  
   312  	return l.waitCh
   313  }
   314  
   315  // Panics if list grows beyond its max length.
   316  func (l *CList) PushBack(v interface{}) *CElement {
   317  	l.mtx.Lock()
   318  
   319  	// Construct a new element
   320  	e := &CElement{
   321  		prev:       nil,
   322  		prevWg:     waitGroup1(),
   323  		prevWaitCh: make(chan struct{}),
   324  		next:       nil,
   325  		nextWg:     waitGroup1(),
   326  		nextWaitCh: make(chan struct{}),
   327  		removed:    false,
   328  		Value:      v,
   329  	}
   330  
   331  	// Release waiters on FrontWait/BackWait maybe
   332  	if l.len == 0 {
   333  		l.wg.Done()
   334  		close(l.waitCh)
   335  	}
   336  	if l.len >= l.maxLen {
   337  		panic(fmt.Sprintf("clist: maximum length list reached %d", l.maxLen))
   338  	}
   339  	l.len++
   340  
   341  	// Modify the tail
   342  	if l.tail == nil {
   343  		l.head = e
   344  		l.tail = e
   345  	} else {
   346  		e.SetPrev(l.tail) // We must init e first.
   347  		l.tail.SetNext(e) // This will make e accessible.
   348  		l.tail = e        // Update the list.
   349  	}
   350  	l.mtx.Unlock()
   351  	return e
   352  }
   353  
   354  // CONTRACT: Caller must call e.DetachPrev() and/or e.DetachNext() to avoid memory leaks.
   355  // NOTE: As per the contract of CList, removed elements cannot be added back.
   356  func (l *CList) Remove(e *CElement) interface{} {
   357  	l.mtx.Lock()
   358  
   359  	prev := e.Prev()
   360  	next := e.Next()
   361  
   362  	if l.head == nil || l.tail == nil {
   363  		l.mtx.Unlock()
   364  		panic("Remove(e) on empty CList")
   365  	}
   366  	if prev == nil && l.head != e {
   367  		l.mtx.Unlock()
   368  		panic("Remove(e) with false head")
   369  	}
   370  	if next == nil && l.tail != e {
   371  		l.mtx.Unlock()
   372  		panic("Remove(e) with false tail")
   373  	}
   374  
   375  	// If we're removing the only item, make CList FrontWait/BackWait wait.
   376  	if l.len == 1 {
   377  		l.wg = waitGroup1() // WaitGroups are difficult to re-use.
   378  		l.waitCh = make(chan struct{})
   379  	}
   380  
   381  	// Update l.len
   382  	l.len--
   383  
   384  	// Connect next/prev and set head/tail
   385  	if prev == nil {
   386  		l.head = next
   387  	} else {
   388  		prev.SetNext(next)
   389  	}
   390  	if next == nil {
   391  		l.tail = prev
   392  	} else {
   393  		next.SetPrev(prev)
   394  	}
   395  
   396  	// Set .Done() on e, otherwise waiters will wait forever.
   397  	e.SetRemoved()
   398  
   399  	l.mtx.Unlock()
   400  	return e.Value
   401  }
   402  
   403  func waitGroup1() (wg *sync.WaitGroup) {
   404  	wg = &sync.WaitGroup{}
   405  	wg.Add(1)
   406  	return
   407  }