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.

285 lines
5.4 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. "sync"
  12. "sync/atomic"
  13. "unsafe"
  14. )
  15. // CElement is an element of a linked-list
  16. // Traversal from a CElement are goroutine-safe.
  17. type CElement struct {
  18. prev unsafe.Pointer
  19. prevWg *sync.WaitGroup
  20. next unsafe.Pointer
  21. nextWg *sync.WaitGroup
  22. removed uint32
  23. Value interface{}
  24. }
  25. // Blocking implementation of Next().
  26. // May return nil iff CElement was tail and got removed.
  27. func (e *CElement) NextWait() *CElement {
  28. for {
  29. e.nextWg.Wait()
  30. next := e.Next()
  31. if next == nil {
  32. if e.Removed() {
  33. return nil
  34. } else {
  35. continue
  36. }
  37. } else {
  38. return next
  39. }
  40. }
  41. }
  42. // Blocking implementation of Prev().
  43. // May return nil iff CElement was head and got removed.
  44. func (e *CElement) PrevWait() *CElement {
  45. for {
  46. e.prevWg.Wait()
  47. prev := e.Prev()
  48. if prev == nil {
  49. if e.Removed() {
  50. return nil
  51. } else {
  52. continue
  53. }
  54. } else {
  55. return prev
  56. }
  57. }
  58. }
  59. // Nonblocking, may return nil if at the end.
  60. func (e *CElement) Next() *CElement {
  61. return (*CElement)(atomic.LoadPointer(&e.next))
  62. }
  63. // Nonblocking, may return nil if at the end.
  64. func (e *CElement) Prev() *CElement {
  65. return (*CElement)(atomic.LoadPointer(&e.prev))
  66. }
  67. func (e *CElement) Removed() bool {
  68. return atomic.LoadUint32(&(e.removed)) > 0
  69. }
  70. func (e *CElement) DetachNext() {
  71. if !e.Removed() {
  72. panic("DetachNext() must be called after Remove(e)")
  73. }
  74. atomic.StorePointer(&e.next, nil)
  75. }
  76. func (e *CElement) DetachPrev() {
  77. if !e.Removed() {
  78. panic("DetachPrev() must be called after Remove(e)")
  79. }
  80. atomic.StorePointer(&e.prev, nil)
  81. }
  82. func (e *CElement) setNextAtomic(next *CElement) {
  83. for {
  84. oldNext := atomic.LoadPointer(&e.next)
  85. if !atomic.CompareAndSwapPointer(&(e.next), oldNext, unsafe.Pointer(next)) {
  86. continue
  87. }
  88. if next == nil && oldNext != nil { // We for-loop in NextWait() so race is ok
  89. e.nextWg.Add(1)
  90. }
  91. if next != nil && oldNext == nil {
  92. e.nextWg.Done()
  93. }
  94. return
  95. }
  96. }
  97. func (e *CElement) setPrevAtomic(prev *CElement) {
  98. for {
  99. oldPrev := atomic.LoadPointer(&e.prev)
  100. if !atomic.CompareAndSwapPointer(&(e.prev), oldPrev, unsafe.Pointer(prev)) {
  101. continue
  102. }
  103. if prev == nil && oldPrev != nil { // We for-loop in PrevWait() so race is ok
  104. e.prevWg.Add(1)
  105. }
  106. if prev != nil && oldPrev == nil {
  107. e.prevWg.Done()
  108. }
  109. return
  110. }
  111. }
  112. func (e *CElement) setRemovedAtomic() {
  113. atomic.StoreUint32(&(e.removed), 1)
  114. }
  115. //--------------------------------------------------------------------------------
  116. // CList represents a linked list.
  117. // The zero value for CList is an empty list ready to use.
  118. // Operations are goroutine-safe.
  119. type CList struct {
  120. mtx sync.Mutex
  121. wg *sync.WaitGroup
  122. head *CElement // first element
  123. tail *CElement // last element
  124. len int // list length
  125. }
  126. func (l *CList) Init() *CList {
  127. l.mtx.Lock()
  128. defer l.mtx.Unlock()
  129. l.wg = waitGroup1()
  130. l.head = nil
  131. l.tail = nil
  132. l.len = 0
  133. return l
  134. }
  135. func New() *CList { return new(CList).Init() }
  136. func (l *CList) Len() int {
  137. l.mtx.Lock()
  138. defer l.mtx.Unlock()
  139. return l.len
  140. }
  141. func (l *CList) Front() *CElement {
  142. l.mtx.Lock()
  143. defer l.mtx.Unlock()
  144. return l.head
  145. }
  146. func (l *CList) FrontWait() *CElement {
  147. for {
  148. l.mtx.Lock()
  149. head := l.head
  150. wg := l.wg
  151. l.mtx.Unlock()
  152. if head == nil {
  153. wg.Wait()
  154. } else {
  155. return head
  156. }
  157. }
  158. }
  159. func (l *CList) Back() *CElement {
  160. l.mtx.Lock()
  161. defer l.mtx.Unlock()
  162. return l.tail
  163. }
  164. func (l *CList) BackWait() *CElement {
  165. for {
  166. l.mtx.Lock()
  167. tail := l.tail
  168. wg := l.wg
  169. l.mtx.Unlock()
  170. if tail == nil {
  171. wg.Wait()
  172. } else {
  173. return tail
  174. }
  175. }
  176. }
  177. func (l *CList) PushBack(v interface{}) *CElement {
  178. l.mtx.Lock()
  179. defer l.mtx.Unlock()
  180. // Construct a new element
  181. e := &CElement{
  182. prev: nil,
  183. prevWg: waitGroup1(),
  184. next: nil,
  185. nextWg: waitGroup1(),
  186. Value: v,
  187. }
  188. // Release waiters on FrontWait/BackWait maybe
  189. if l.len == 0 {
  190. l.wg.Done()
  191. }
  192. l.len += 1
  193. // Modify the tail
  194. if l.tail == nil {
  195. l.head = e
  196. l.tail = e
  197. } else {
  198. l.tail.setNextAtomic(e)
  199. e.setPrevAtomic(l.tail)
  200. l.tail = e
  201. }
  202. return e
  203. }
  204. // CONTRACT: Caller must call e.DetachPrev() and/or e.DetachNext() to avoid memory leaks.
  205. // NOTE: As per the contract of CList, removed elements cannot be added back.
  206. func (l *CList) Remove(e *CElement) interface{} {
  207. l.mtx.Lock()
  208. defer l.mtx.Unlock()
  209. prev := e.Prev()
  210. next := e.Next()
  211. if l.head == nil || l.tail == nil {
  212. panic("Remove(e) on empty CList")
  213. }
  214. if prev == nil && l.head != e {
  215. panic("Remove(e) with false head")
  216. }
  217. if next == nil && l.tail != e {
  218. panic("Remove(e) with false tail")
  219. }
  220. // If we're removing the only item, make CList FrontWait/BackWait wait.
  221. if l.len == 1 {
  222. l.wg.Add(1)
  223. }
  224. l.len -= 1
  225. // Connect next/prev and set head/tail
  226. if prev == nil {
  227. l.head = next
  228. } else {
  229. prev.setNextAtomic(next)
  230. }
  231. if next == nil {
  232. l.tail = prev
  233. } else {
  234. next.setPrevAtomic(prev)
  235. }
  236. // Set .Done() on e, otherwise waiters will wait forever.
  237. e.setRemovedAtomic()
  238. if prev == nil {
  239. e.prevWg.Done()
  240. }
  241. if next == nil {
  242. e.nextWg.Done()
  243. }
  244. return e.Value
  245. }
  246. func waitGroup1() (wg *sync.WaitGroup) {
  247. wg = &sync.WaitGroup{}
  248. wg.Add(1)
  249. return
  250. }