You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

402 lines
8.6 KiB

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