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