github.com/devwanda/aphelion-staking@v0.33.9/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 19 // MaxLength is the max allowed number of elements a linked list is 20 // allowed to contain. 21 // If more elements are pushed to the list it will panic. 22 const MaxLength = int(^uint(0) >> 1) 23 24 /* 25 26 CElement is an element of a linked-list 27 Traversal from a CElement is goroutine-safe. 28 29 We can't avoid using WaitGroups or for-loops given the documentation 30 spec without re-implementing the primitives that already exist in 31 golang/sync. Notice that WaitGroup allows many go-routines to be 32 simultaneously released, which is what we want. Mutex doesn't do 33 this. RWMutex does this, but it's clumsy to use in the way that a 34 WaitGroup would be used -- and we'd end up having two RWMutex's for 35 prev/next each, which is doubly confusing. 36 37 sync.Cond would be sort-of useful, but we don't need a write-lock in 38 the for-loop. Use sync.Cond when you need serial access to the 39 "condition". In our case our condition is if `next != nil || removed`, 40 and there's no reason to serialize that condition for goroutines 41 waiting on NextWait() (since it's just a read operation). 42 43 */ 44 type CElement struct { 45 mtx sync.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 sync.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 }