github.com/andy2046/gopie@v0.7.0/pkg/dll/dll.go (about)

     1  // Package dll provides a lock-free implementation of doubly linked list.
     2  package dll
     3  
     4  type (
     5  	// List represents a doubly linked list.
     6  	List struct {
     7  		head *Element
     8  		tail *Element
     9  	}
    10  
    11  	// Element is an element of a linked list.
    12  	Element struct {
    13  		// Next and previous pointers in the doubly-linked list of elements.
    14  		next, prev *atomicMarkableReference
    15  
    16  		// The value stored with this element.
    17  		Value interface{}
    18  	}
    19  )
    20  
    21  // New returns an initialized list.
    22  func New() *List { return new(List).Init() }
    23  
    24  // Init initializes or clears list l.
    25  func (l *List) Init() *List {
    26  	l.head = &Element{
    27  		prev: newAtomicMarkableReference(nil, false),
    28  		next: newAtomicMarkableReference(nil, false),
    29  	}
    30  	l.tail = &Element{
    31  		prev: newAtomicMarkableReference(nil, false),
    32  		next: newAtomicMarkableReference(nil, false),
    33  	}
    34  
    35  	l.head.next.compareAndSet(nil, l.tail, false, false)
    36  	l.tail.prev.compareAndSet(nil, l.head, false, false)
    37  
    38  	return l
    39  }
    40  
    41  // Empty returns true if list l is empty, false otherwise
    42  func (l *List) Empty() bool {
    43  	t := l.head.next.getReference()
    44  	h := l.tail.prev.getReference()
    45  
    46  	if t == l.tail && h == l.head {
    47  		return true
    48  	}
    49  
    50  	return false
    51  }
    52  
    53  // Remove removes e from l if e is an element of list l.
    54  // It returns the element value e.Value if succeed, nil otherwise
    55  func (l *List) Remove(e *Element) interface{} {
    56  	node := e
    57  	if node == nil {
    58  		return nil
    59  	}
    60  
    61  	if node == l.tail || node == l.head {
    62  		return nil
    63  	}
    64  
    65  	for {
    66  		removed, nodeNext := node.next.get()
    67  		if removed {
    68  			return nil
    69  		}
    70  		if node.next.compareAndSet(nodeNext, nodeNext, false, true) {
    71  			for {
    72  				removed2, nodePrev := node.prev.get()
    73  				if removed2 || node.prev.compareAndSet(nodePrev, nodePrev, false, true) {
    74  					break
    75  				}
    76  			}
    77  
    78  			l.correctPrev(node.prev.getReference(), nodeNext)
    79  			return node.Value
    80  		}
    81  	}
    82  }
    83  
    84  func (l *List) correctPrev(prev, node *Element) *Element {
    85  	var lastLink *Element
    86  
    87  	for {
    88  		removed, link1 := node.prev.get()
    89  		if removed {
    90  			break
    91  		}
    92  		removed2, prevNext := prev.next.get()
    93  		if removed2 {
    94  			if lastLink != nil {
    95  				setMark(prev.prev)
    96  				lastLink.next.compareAndSet(prev, prevNext, false, false)
    97  				prev = lastLink
    98  				lastLink = nil
    99  				continue
   100  			}
   101  			prevNext = prev.prev.getReference()
   102  			prev = prevNext
   103  			continue
   104  		}
   105  
   106  		if prevNext != node {
   107  			lastLink = prev
   108  			prev = prevNext
   109  			continue
   110  		}
   111  
   112  		if node.prev.compareAndSet(link1, prev, removed, false) {
   113  			if prev.prev.isMarked() {
   114  				continue
   115  			}
   116  			break
   117  		}
   118  	}
   119  
   120  	return prev
   121  }
   122  
   123  func setMark(node *atomicMarkableReference) {
   124  	for {
   125  		removed, link := node.get()
   126  		if removed || node.compareAndSet(link, link, false, true) {
   127  			break
   128  		}
   129  	}
   130  }
   131  
   132  // PopLeft returns the first element of list l or nil if the list is empty.
   133  func (l *List) PopLeft() interface{} {
   134  	prev := l.head
   135  
   136  	for {
   137  		node := prev.next.getReference()
   138  		// deque is empty
   139  		if node == l.tail {
   140  			return nil
   141  		}
   142  
   143  		removed, nodeNext := node.next.get()
   144  		// concurrent pop started to delete this node, help it, then continue
   145  		if removed {
   146  			helpDelete(node, "help concurrent")
   147  			continue
   148  		}
   149  
   150  		// 1 pop step
   151  		if node.next.compareAndSet(nodeNext, nodeNext, false, true) {
   152  			// 2, 3 step
   153  			helpDelete(node, "1st step")
   154  			next := node.next.getReference()
   155  			// 4 step
   156  			helpInsert(prev, next, "popLeft New")
   157  
   158  			return node.Value
   159  		}
   160  	}
   161  }
   162  
   163  // PopRight returns the last element of list l or nil if the list is empty.
   164  func (l *List) PopRight() interface{} {
   165  	next := l.tail
   166  	node := next.prev.getReference()
   167  
   168  	for {
   169  		if !node.next.compareAndSet(next, next, false, false) {
   170  			node = helpInsert(node, next, "popRight")
   171  			continue
   172  		}
   173  
   174  		if node == l.head {
   175  			return nil
   176  		}
   177  
   178  		if node.next.compareAndSet(next, next, false, true) {
   179  			helpDelete(node, "")
   180  			prev := node.prev.getReference()
   181  			helpInsert(prev, next, "popRight")
   182  
   183  			return node.Value
   184  		}
   185  	}
   186  }
   187  
   188  // PushLeft inserts a new element e with value v at the front of list l and returns e.
   189  func (l *List) PushLeft(v interface{}) *Element {
   190  	node := &Element{Value: v}
   191  	prev := l.head
   192  	next := prev.next.getReference()
   193  
   194  	for {
   195  		if !prev.next.compareAndSet(next, next, false, false) {
   196  			next = prev.next.getReference()
   197  			continue
   198  		}
   199  
   200  		node.prev = newAtomicMarkableReference(prev, false)
   201  		node.next = newAtomicMarkableReference(next, false)
   202  
   203  		if prev.next.compareAndSet(next, node, false, false) {
   204  			break
   205  		}
   206  	}
   207  
   208  	pushCommon(node, next)
   209  
   210  	return node
   211  }
   212  
   213  // PushRight inserts a new element e with value v at the back of list l and returns e.
   214  func (l *List) PushRight(v interface{}) *Element {
   215  	node := &Element{Value: v}
   216  	next := l.tail
   217  	prev := next.prev.getReference()
   218  
   219  	for {
   220  		if !prev.next.compareAndSet(next, next, false, false) {
   221  			// concurrent push inserted -> get new prev
   222  			prev = helpInsert(prev, next, "concurrentPushRight")
   223  			continue
   224  		}
   225  
   226  		// 0 push step
   227  		node.prev = newAtomicMarkableReference(prev, false)
   228  		node.next = newAtomicMarkableReference(next, false)
   229  		// 1 push step
   230  		if prev.next.compareAndSet(next, node, false, false) {
   231  			break
   232  		}
   233  	}
   234  
   235  	// 2 push step
   236  	pushCommon(node, next)
   237  
   238  	return node
   239  }
   240  
   241  func pushCommon(node, next *Element) {
   242  	for {
   243  		link1 := next.prev
   244  		if link1.isMarked() || !node.next.compareAndSet(next, next, false, false) {
   245  			break
   246  		}
   247  
   248  		if next.prev.compareAndSet(link1.getReference(), node, false, false) {
   249  			if node.prev.isMarked() {
   250  				helpInsert(node, next, "pushCommon")
   251  			}
   252  			break
   253  		}
   254  	}
   255  }
   256  
   257  // Next returns the next list element or nil.
   258  func (l *List) Next(node *Element) *Element {
   259  	for node != l.tail {
   260  		if node == nil {
   261  			break
   262  		}
   263  
   264  		next := node.next.getReference()
   265  		if next == nil {
   266  			break
   267  		}
   268  
   269  		removed, nextNext := next.next.get()
   270  		if removed {
   271  			// The next pointer of the node behind me has the deleted mark set
   272  			removed2, nodeNext := node.next.get()
   273  			if !removed2 || nodeNext != next {
   274  				setMark(next.prev)
   275  				node.next.compareAndSet(next, nextNext, false, false) // next removed == false?
   276  				continue
   277  			}
   278  		}
   279  
   280  		node = next
   281  
   282  		if !removed {
   283  			return next
   284  		}
   285  	}
   286  
   287  	return nil
   288  }
   289  
   290  // Prev returns the previous list element or nil.
   291  func (l *List) Prev(node *Element) *Element {
   292  	for node != l.head {
   293  		if node == nil {
   294  			break
   295  		}
   296  
   297  		prev := node.prev.getReference()
   298  		if prev == nil {
   299  			break
   300  		}
   301  
   302  		prevNext := prev.next.getReference()
   303  		removed := node.next.isMarked()
   304  
   305  		if prevNext == node && !removed {
   306  			return prev
   307  		} else if removed {
   308  			node = l.Next(node)
   309  		} else {
   310  			prev = l.correctPrev(prev, node)
   311  		}
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  /**
   318   * Correct node.prev to the closest previous node
   319   * helpInsert is very weak - does not reset node.prev to the actual prev.next
   320   * but just tries to set node.prev to the given suggestion of a prev node
   321   * (for 2 push step, 4 pop step)
   322   */
   323  func helpInsert(prev *Element, node *Element, method string) *Element {
   324  	// last = is the last node : last.next == prev and it is not marked as removed
   325  	var last, nodePrev *Element
   326  
   327  	for {
   328  		removed, prevNext := prev.next.get()
   329  
   330  		if removed {
   331  			if last != nil {
   332  				markPrev(prev)
   333  				next2 := prev.next.getReference()
   334  				last.next.compareAndSet(prev, next2, false, false)
   335  				prev = last
   336  				last = nil
   337  			} else {
   338  				prevNext = prev.prev.getReference()
   339  				prev = prevNext
   340  			}
   341  			continue
   342  		}
   343  
   344  		removed, nodePrev = node.prev.get()
   345  		if removed {
   346  			break
   347  		}
   348  
   349  		// prev is not the previous node of node
   350  		if prevNext != node {
   351  			last = prev
   352  			prev = prevNext
   353  			continue
   354  		}
   355  
   356  		if nodePrev == prev {
   357  			break
   358  		}
   359  
   360  		if prev.next.getReference() == node && node.prev.compareAndSet(nodePrev, prev, false, false) {
   361  			if prev.prev.isMarked() {
   362  				continue
   363  			}
   364  			break
   365  		}
   366  	}
   367  
   368  	return prev
   369  }
   370  
   371  // 2 and 3 pop steps
   372  func helpDelete(node *Element, place string) {
   373  	markPrev(node)
   374  
   375  	prev := node.prev.getReference()
   376  	next := node.next.getReference()
   377  	var last *Element
   378  
   379  	for {
   380  		if prev == next {
   381  			break
   382  		}
   383  
   384  		if next.next.isMarked() {
   385  			markPrev(next)
   386  			next = next.next.getReference()
   387  			continue
   388  		}
   389  
   390  		removed, prevNext := prev.next.get()
   391  		if removed {
   392  			if last != nil {
   393  				markPrev(prev)
   394  				next2 := prev.next.getReference()
   395  				last.next.compareAndSet(prev, next2, false, false)
   396  				prev = last
   397  				last = nil
   398  			} else {
   399  				prevNext = prev.prev.getReference()
   400  				prev = prevNext
   401  				// assert(prev != nil)
   402  			}
   403  			continue
   404  		}
   405  
   406  		if prevNext != node {
   407  			last = prev
   408  			prev = prevNext
   409  			continue
   410  		}
   411  
   412  		if prev.next.compareAndSet(node, next, false, false) {
   413  			break
   414  		}
   415  	}
   416  }
   417  
   418  func markPrev(node *Element) {
   419  	for {
   420  		link1 := node.prev
   421  		if link1.isMarked() ||
   422  			node.prev.compareAndSet(link1.getReference(), link1.getReference(), false, true) {
   423  			break
   424  		}
   425  	}
   426  }