github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/lib/list/linked_list.go (about)

     1  package list
     2  
     3  // References:
     4  // Valois, John D. "Lock-free linked lists using compare-and-swap."
     5  //  https://dl.acm.org/doi/pdf/10.1145/224964.224988
     6  // Tim Harris, "A pragmatic implementation of non-blocking linked lists"
     7  //  http://www.cl.cam.ac.uk/~tlh20/publications.html
     8  // Maged Michael "High Performance Dynamic Lock-Free Hash Tables and List-Based Sets"
     9  //  http://www.research.ibm.com/people/m/michael/pubs.htm
    10  // https://mirrors.edge.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html
    11  
    12  /* Pastes from JDK
    13  * The base lists use a variant of the HM linked ordered set
    14  * algorithm. See Tim Harris, "A pragmatic implementation of
    15  * non-blocking linked lists"
    16  * http://www.cl.cam.ac.uk/~tlh20/publications.html and Maged
    17  * Michael "High Performance Dynamic Lock-Free Hash Tables and
    18  * List-Based Sets"
    19  * http://www.research.ibm.com/people/m/michael/pubs.htm.  The
    20  * basic idea in these lists is to mark the "next" pointers of
    21  * deleted nodes when deleting to avoid conflicts with concurrent
    22  * insertions, and when traversing to keep track of triples
    23  * (predecessor, node, successor) in order to detect when and how
    24  * to unlink these deleted nodes.
    25  *
    26  * Rather than using mark-bits to mark list deletions (which can
    27  * be slow and space-intensive using AtomicMarkedReference), nodes
    28  * use direct CAS'able next pointers.  On deletion, instead of
    29  * marking a pointer, they splice in another node that can be
    30  * thought of as standing for a removingMarked pointer (see method
    31  * unlinkNode).  Using plain nodes acts roughly like "boxed"
    32  * implementations of removingMarked pointers, but uses new nodes only
    33  * when nodes are deleted, not for every link.  This requires less
    34  * space and supports faster traversal. Even if removingMarked references
    35  * were better supported by JVMs, traversal using this technique
    36  * might still be faster because any search need only read ahead
    37  * one more node than otherwise required (to check for trailing
    38  * marker) rather than unmasking mark bits or whatever on each
    39  * read.
    40  *
    41  * This approach maintains the essential property needed in the HM
    42  * algorithm of changing the next-pointer of a deleted node so
    43  * that any other CAS of it will fail, but implements the idea by
    44  * changing the pointer to point to a different node (with
    45  * otherwise illegal null fields), not by marking it.  While it
    46  * would be possible to further squeeze space by defining marker
    47  * nodes not to have key/value fields, it isn't worth the extra
    48  * type-testing overhead.  The deletion markers are rarely
    49  * encountered during traversal, are easily detected via null
    50  * checks that are needed anyway, and are normally quickly garbage
    51  * collected. (Note that this technique would not work well in
    52  * systems without garbage collection.)
    53  *
    54  * In addition to using deletion markers, the lists also use
    55  * nullness of value fields to indicate deletion, in a style
    56  * similar to typical lazy-deletion schemes.  If a node's value is
    57  * null, then it is considered logically deleted and ignored even
    58  * though it is still reachable.
    59  *
    60  * Here's the sequence of events for a deletion of node n with
    61  * predecessor b and successor f, initially:
    62  *
    63  *        +------+       +------+      +------+
    64  *   ...  |   b  |------>|   n  |----->|   f  | ...
    65  *        +------+       +------+      +------+
    66  *
    67  * 1. CAS n's value field from non-null to null.
    68  *    Traversals encountering a node with null value ignore it.
    69  *    However, ongoing insertions and deletions might still modify
    70  *    n's next pointer.
    71  *
    72  * 2. CAS n's next pointer to point to a new marker node.
    73  *    From this point on, no other nodes can be appended to n.
    74  *    which avoids deletion errors in CAS-based linked lists.
    75  *
    76  *        +------+       +------+      +------+       +------+
    77  *   ...  |   b  |------>|   n  |----->|marker|------>|   f  | ...
    78  *        +------+       +------+      +------+       +------+
    79  *
    80  * 3. CAS b's next pointer over both n and its marker.
    81  *    From this point on, no new traversals will encounter n,
    82  *    and it can eventually be GCed.
    83  *        +------+                                    +------+
    84  *   ...  |   b  |----------------------------------->|   f  | ...
    85  *        +------+                                    +------+
    86  *
    87  * A failure at step 1 leads to simple retry due to a lost race
    88  * with another operation. Steps 2-3 can fail because some other
    89  * thread noticed during a traversal a node with null value and
    90  * helped out by marking and/or unlinking.  This helping-out
    91  * ensures that no thread can become stuck waiting for progress of
    92  * the deleting thread.
    93  *
    94  * Skip lists add indexing to this scheme, so that the base-level
    95  * traversals start close to the locations being found, inserted
    96  * or deleted -- usually base level traversals only traverse a few
    97  * nodes. This doesn't change the basic algorithm except for the
    98  * need to make sure base traversals start at predecessors (here,
    99  * b) that are not (structurally) deleted, otherwise retrying
   100  * after processing the deletion.
   101  *
   102  * Index levels are maintained using CAS to link and unlink
   103  * successors ("right" fields).  Races are allowed in index-list
   104  * operations that can (rarely) fail to link in a new index node.
   105  * (We can't do this of course for bits nodes.)  However, even
   106  * when this happens, the index lists correctly guide search.
   107  * This can impact performance, but since skip lists are
   108  * probabilistic anyway, the net result is that under contention,
   109  * the effective "p" value may be lower than its nominal value.
   110  *
   111  * Index insertion and deletion sometimes require a separate
   112  * traversal pass occurring after the base-level action, to add or
   113  * remove index nodes.  This adds to single-threaded overhead, but
   114  * improves contended multithreaded performance by narrowing
   115  * interference windows, and allows deletion to ensure that all
   116  * index nodes will be made unreachable upon return from a public
   117  * remove operation, thus avoiding unwanted garbage retention.
   118  *
   119  * Indexing uses skip list parameters that maintain good search
   120  * performance while using sparser-than-usual indexCount: The
   121  * hardwired parameters k=1, p=0.5 (see method doPut) mean that
   122  * about one-quarter of the nodes have indexCount. Of those that do,
   123  * half have one level, a quarter have two, and so on (see Pugh's
   124  * Skip List Cookbook, sec 3.4), up to a maximum of 62 levels
   125  * (appropriate for up to 2^63 elements).  The expected total
   126  * space requirement for a map is slightly less than for the
   127  * current implementation of java.util.TreeMap.
   128  *
   129  * Changing the level of the index (i.e, the height of the
   130  * tree-like structure) also uses CAS.  Creation of an index with
   131  * height greater than the current level adds a level to the head
   132  * index by CAS'ing on a new top-most head. To maintain good
   133  * performance after a lot of removals, deletion methods
   134  * heuristically try to reduce the height if the topmost levels
   135  * appear to be empty.  This may encounter races in which it is
   136  * possible (but rare) to reduce and "lose" a level just as it is
   137  * about to contain an index (that will then never be
   138  * encountered). This does no structural harm, and in practice
   139  * appears to be a better option than allowing unrestrained growth
   140  * of levels.
   141  *
   142  * This class provides concurrent-reader-style memory consistency,
   143  * ensuring that read-only methods report status and/or values no
   144  * staler than those holding at method entry. This is done by
   145  * performing all publication and structural updates using
   146  * (volatile) CAS, placing an acquireFence in a few access
   147  * methods, and ensuring that linked objects are transitively
   148  * acquired via dependent reads (normally once) unless performing
   149  * a volatile-mode CAS operation (that also acts as an acquire and
   150  * release).  This form of fence-hoisting is similar to RCU and
   151  * related techniques (see McKenney's online book
   152  * https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html)
   153  * It minimizes overhead that may otherwise occur when using so
   154  * many volatile-mode reads. Using explicit acquireFences is
   155  * logistically easier than targeting particular fields to be read
   156  * in acquire mode: fences are just hoisted up as far as possible,
   157  * to the entry points or loop headers of a few methods. A
   158  * potential disadvantage is that these few remaining fences are
   159  * not easily optimized away by compilers under exclusively
   160  * single-thread use.  It requires some care to avoid volatile
   161  * mode reads of other fields. (Note that the memory semantics of
   162  * a reference dependently read in plain mode exactly once are
   163  * equivalent to those for atomic opaque mode.)  Iterators and
   164  * other traversals encounter each node and value exactly once.
   165  * Other operations locate an element (or position to insert an
   166  * element) via a sequence of dereferences. This search is broken
   167  * into two parts. Method findPredecessor (and its specialized
   168  * embeddings) searches index nodes only, returning a base-level
   169  * predecessor of the key. Callers carry out the base-level
   170  * search, restarting if encountering a marker preventing link
   171  * modification.  In some cases, it is possible to encounter a
   172  * node multiple times while descending levels. For mutative
   173  * operations, the reported value is validated using CAS (else
   174  * retrying), preserving linearizability with respect to each
   175  * other. Others may return any (non-null) value holding in the
   176  * course of the method call.  (Search-based methods also include
   177  * some useless-looking explicit null checks designed to allow
   178  * more fields to be nulled out upon removal, to reduce floating
   179  * garbage, but which is not currently done, pending discovery of
   180  * a way to do this with less impact on other operations.)
   181  *
   182  * To produce random values without interference across threads,
   183  * we use within-JDK thread local random support (via the
   184  * "secondary seed", to avoid interference with user-level
   185  * ThreadLocalRandom.)
   186  *
   187  * For explanation of algorithms sharing at least a couple of
   188  * features with this one, see Mikhail Fomitchev's thesis
   189  * (http://www.cs.yorku.ca/~mikhail/), Keir Fraser's thesis
   190  * (http://www.cl.cam.ac.uk/users/kaf24/), and Hakan Sundell's
   191  * thesis (http://www.cs.chalmers.se/~phs/).
   192  *
   193  * Notation guide for local variables
   194  * Node:         b, n, f, p for  predecessor, node, successor, aux
   195  * Index:        q, r, d    for index node, right, down.
   196  * Head:         h
   197  * Keys:         k, key
   198  * Values:       v, value
   199  * Comparisons:  c
   200  *
   201   */
   202  
   203  import (
   204  	"sync/atomic"
   205  )
   206  
   207  var _ LinkedList[struct{}] = (*doublyLinkedList[struct{}])(nil) // Type check assertion
   208  
   209  type nodeElementInListStatus uint8
   210  
   211  const (
   212  	notInList nodeElementInListStatus = iota
   213  	emtpyList
   214  	theOnlyOne
   215  	theFirstButNotTheLast
   216  	theLastButNotTheFirst
   217  	inMiddle
   218  )
   219  
   220  // TODO Lock-free linked list
   221  type doublyLinkedList[T comparable] struct {
   222  	root *NodeElement[T]
   223  	len  atomic.Int64
   224  }
   225  
   226  func NewLinkedList[T comparable]() LinkedList[T] {
   227  	return new(doublyLinkedList[T]).init()
   228  }
   229  
   230  func (l *doublyLinkedList[T]) getRoot() *NodeElement[T] {
   231  	return l.root
   232  }
   233  
   234  func (l *doublyLinkedList[T]) getRootHead() *NodeElement[T] {
   235  	return l.root.next
   236  }
   237  
   238  func (l *doublyLinkedList[T]) setRootHead(targetE *NodeElement[T]) {
   239  	l.root.next = targetE
   240  	targetE.prev = l.root
   241  }
   242  
   243  func (l *doublyLinkedList[T]) getRootTail() *NodeElement[T] {
   244  	return l.root.prev
   245  }
   246  
   247  func (l *doublyLinkedList[T]) setRootTail(targetE *NodeElement[T]) {
   248  	l.root.prev = targetE
   249  	targetE.next = l.root
   250  }
   251  
   252  func (l *doublyLinkedList[T]) init() *doublyLinkedList[T] {
   253  	l.root = &NodeElement[T]{
   254  		listRef: l,
   255  	}
   256  	l.setRootHead(l.root)
   257  	l.setRootTail(l.root)
   258  	l.len.Store(0)
   259  	return l
   260  }
   261  
   262  func (l *doublyLinkedList[T]) Len() int64 {
   263  	return l.len.Load()
   264  }
   265  
   266  func (l *doublyLinkedList[T]) checkElement(targetE *NodeElement[T]) (*NodeElement[T], nodeElementInListStatus) {
   267  	if l.len.Load() == 0 {
   268  		return l.getRoot(), emtpyList
   269  	}
   270  
   271  	if targetE == nil || targetE.listRef != l || targetE.prev == nil || targetE.next == nil {
   272  		return nil, notInList
   273  	}
   274  
   275  	// mem address compare
   276  	switch {
   277  	case targetE.Prev() == l.getRoot() && targetE.Next() == l.getRoot():
   278  		// targetE is the first one and the last one
   279  		if l.getRootHead() != targetE || l.getRootTail() != targetE {
   280  			return nil, notInList
   281  		}
   282  		return targetE, theOnlyOne
   283  	case targetE.Prev() == l.getRoot() && targetE.Next() != l.getRoot():
   284  		// targetE is the first one but not the last one
   285  		if targetE.Next().Prev() != targetE {
   286  			return nil, notInList
   287  		}
   288  		// Ignore l.setRootTail (tail)
   289  		return targetE, theFirstButNotTheLast
   290  	case targetE.Prev() != l.getRoot() && targetE.Next() == l.getRoot():
   291  		// targetE is the last one but not the first one
   292  		if targetE.Prev().Next() != targetE {
   293  			return nil, notInList
   294  		}
   295  		// Ignore l.setRootHead (head)
   296  		return targetE, theLastButNotTheFirst
   297  	case targetE.Prev() != l.getRoot() && targetE.Next() != l.getRoot():
   298  		// targetE is neither the first one nor the last one
   299  		if targetE.Prev().Next() != targetE && targetE.Next().Prev() != targetE {
   300  			return nil, notInList
   301  		}
   302  		return targetE, inMiddle
   303  	}
   304  	return nil, notInList
   305  }
   306  
   307  func (l *doublyLinkedList[T]) append(e *NodeElement[T]) *NodeElement[T] {
   308  	e.listRef = l
   309  	e.next, e.prev = nil, nil
   310  
   311  	if l.len.Load() == 0 {
   312  		// empty list, new append element is the first one
   313  		l.setRootHead(e)
   314  		l.setRootTail(e)
   315  		l.len.Add(1)
   316  		return e
   317  	}
   318  
   319  	lastOne := l.getRootTail()
   320  	lastOne.next = e
   321  	e.prev, e.next = lastOne, nil
   322  	l.setRootTail(e)
   323  
   324  	l.len.Add(1)
   325  	return e
   326  }
   327  
   328  func (l *doublyLinkedList[T]) Append(elements ...*NodeElement[T]) []*NodeElement[T] {
   329  	if l.getRootTail() == nil {
   330  		return nil
   331  	}
   332  
   333  	for i := 0; i < len(elements); i++ {
   334  		if elements[i] == nil {
   335  			continue
   336  		}
   337  		e := elements[i]
   338  		if e.listRef != l {
   339  			continue
   340  		}
   341  		elements[i] = l.append(e)
   342  	}
   343  	return elements
   344  }
   345  
   346  func (l *doublyLinkedList[T]) AppendValue(values ...T) []*NodeElement[T] {
   347  	// FIXME How to decrease the memory allocation for each operation?
   348  	//  sync.Pool reused the released objects?
   349  	if len(values) <= 0 {
   350  		return nil
   351  	} else if len(values) == 1 {
   352  		return l.Append(newNodeElement(values[0], l))
   353  	}
   354  
   355  	newElements := make([]*NodeElement[T], 0, len(values))
   356  	for _, v := range values {
   357  		newElements = append(newElements, newNodeElement(v, l))
   358  	}
   359  	return l.Append(newElements...)
   360  }
   361  
   362  func (l *doublyLinkedList[T]) insertAfter(newE, at *NodeElement[T]) *NodeElement[T] {
   363  	newE.prev = at
   364  
   365  	if l.len.Load() == 0 {
   366  		l.setRootHead(newE)
   367  		l.setRootTail(newE)
   368  	} else {
   369  		newE.next = at.Next()
   370  		at.next = newE
   371  		if newE.Next() != nil {
   372  			newE.Next().prev = newE
   373  		}
   374  	}
   375  	l.len.Add(1)
   376  	return newE
   377  }
   378  
   379  func (l *doublyLinkedList[T]) InsertAfter(v T, dstE *NodeElement[T]) *NodeElement[T] {
   380  	at, status := l.checkElement(dstE)
   381  	if status == notInList {
   382  		return nil
   383  	}
   384  
   385  	newE := newNodeElement(v, l)
   386  	newE = l.insertAfter(newE, at)
   387  	switch status {
   388  	case theOnlyOne, theLastButNotTheFirst:
   389  		l.setRootTail(newE)
   390  	default:
   391  
   392  	}
   393  	return newE
   394  }
   395  
   396  func (l *doublyLinkedList[T]) insertBefore(newE, at *NodeElement[T]) *NodeElement[T] {
   397  	newE.next = at
   398  	if l.len.Load() == 0 {
   399  		l.setRootHead(newE)
   400  		l.setRootTail(newE)
   401  	} else {
   402  		newE.prev = at.prev
   403  		at.prev = newE
   404  		if newE.Prev() != nil {
   405  			newE.Prev().next = newE
   406  		}
   407  	}
   408  	l.len.Add(1)
   409  	return newE
   410  }
   411  
   412  func (l *doublyLinkedList[T]) InsertBefore(v T, dstE *NodeElement[T]) *NodeElement[T] {
   413  	at, status := l.checkElement(dstE)
   414  	if status == notInList {
   415  		return nil
   416  	}
   417  	newE := newNodeElement(v, l)
   418  	newE = l.insertBefore(newE, at)
   419  	switch status {
   420  	case theOnlyOne, theFirstButNotTheLast:
   421  		l.setRootHead(newE)
   422  	default:
   423  
   424  	}
   425  	return newE
   426  }
   427  
   428  func (l *doublyLinkedList[T]) Remove(targetE *NodeElement[T]) *NodeElement[T] {
   429  	if l == nil || l.root == nil || l.len.Load() == 0 {
   430  		return nil
   431  	}
   432  
   433  	var (
   434  		at     *NodeElement[T]
   435  		status nodeElementInListStatus
   436  	)
   437  
   438  	// doubly linked list removes element is free to do iteration, but we have to check the value if equals.
   439  	// avoid memory leaks
   440  	switch at, status = l.checkElement(targetE); status {
   441  	case theOnlyOne:
   442  		l.setRootHead(l.getRoot())
   443  		l.setRootTail(l.getRoot())
   444  	case theFirstButNotTheLast:
   445  		l.setRootHead(at.Next())
   446  		at.Next().prev = l.getRoot()
   447  	case theLastButNotTheFirst:
   448  		l.setRootTail(at.Prev())
   449  		at.Prev().next = l.getRoot()
   450  	case inMiddle:
   451  		at.Prev().next = at.next
   452  		at.Next().prev = at.prev
   453  	default:
   454  		return nil
   455  	}
   456  
   457  	// avoid memory leaks
   458  	at.listRef = nil
   459  	at.next = nil
   460  	at.prev = nil
   461  
   462  	l.len.Add(-1)
   463  	return at
   464  }
   465  
   466  // Foreach, allows remove linked list elements while iterating.
   467  func (l *doublyLinkedList[T]) Foreach(fn func(idx int64, e *NodeElement[T]) error) error {
   468  	if l == nil || l.root == nil || fn == nil || l.len.Load() == 0 ||
   469  		l.getRoot() == l.getRootHead() && l.getRoot() == l.getRootTail() {
   470  		return nil
   471  	}
   472  
   473  	var (
   474  		iterator       = l.getRoot().Next()
   475  		idx      int64 = 0
   476  	)
   477  	// Avoid remove in an iteration, result in memory leak
   478  	for iterator != l.getRoot() {
   479  		n := iterator.Next()
   480  		if err := fn(idx, iterator); err != nil {
   481  			return err
   482  		}
   483  		iterator = n
   484  		idx++
   485  	}
   486  	return nil
   487  }
   488  
   489  // ReverseForeach, allows remove linked list elements while iterating.
   490  func (l *doublyLinkedList[T]) ReverseForeach(fn func(idx int64, e *NodeElement[T])) {
   491  	if l == nil || l.root == nil || fn == nil || l.len.Load() == 0 ||
   492  		l.getRoot() == l.getRootHead() && l.getRoot() == l.getRootTail() {
   493  		return
   494  	}
   495  
   496  	var (
   497  		iterator       = l.getRoot().Prev()
   498  		idx      int64 = 0
   499  	)
   500  	// Avoid remove in an iteration, result in memory leak
   501  	for iterator != l.getRoot() {
   502  		p := iterator.Prev()
   503  		fn(idx, iterator)
   504  		iterator = p
   505  		idx++
   506  	}
   507  }
   508  
   509  func (l *doublyLinkedList[T]) FindFirst(targetV T, compareFn ...func(e *NodeElement[T]) bool) (*NodeElement[T], bool) {
   510  	if l == nil || l.root == nil || l.len.Load() == 0 {
   511  		return nil, false
   512  	}
   513  
   514  	if len(compareFn) <= 0 {
   515  		compareFn = []func(e *NodeElement[T]) bool{
   516  			func(e *NodeElement[T]) bool {
   517  				return e.Value == targetV
   518  			},
   519  		}
   520  	}
   521  
   522  	iterator := l.getRoot()
   523  	for iterator.HasNext() {
   524  		if compareFn[0](iterator.Next()) {
   525  			return iterator.Next(), true
   526  		}
   527  		iterator = iterator.Next()
   528  	}
   529  	return nil, false
   530  }
   531  
   532  func (l *doublyLinkedList[T]) Front() *NodeElement[T] {
   533  	if l == nil || l.root == nil || l.len.Load() == 0 {
   534  		return nil
   535  	}
   536  
   537  	return l.root.Next()
   538  }
   539  
   540  func (l *doublyLinkedList[T]) Back() *NodeElement[T] {
   541  	if l == nil || l.root == nil || l.len.Load() == 0 {
   542  		return nil
   543  	}
   544  
   545  	return l.root.Prev()
   546  }
   547  
   548  func (l *doublyLinkedList[T]) PushFront(v T) *NodeElement[T] {
   549  	if l == nil || l.root == nil {
   550  		return nil
   551  	}
   552  
   553  	return l.InsertBefore(v, l.getRootHead())
   554  }
   555  
   556  func (l *doublyLinkedList[T]) PushBack(v T) *NodeElement[T] {
   557  	if l == nil || l.root == nil {
   558  		return nil
   559  	}
   560  
   561  	return l.InsertAfter(v, l.getRootTail())
   562  }
   563  
   564  func (l *doublyLinkedList[T]) move(src, dst *NodeElement[T]) bool {
   565  	if src == dst {
   566  		return false
   567  	}
   568  	// Ordinarily, it is move src next to dst
   569  	src.Prev().next = src.next
   570  	src.Next().prev = src.prev
   571  
   572  	src.prev = dst
   573  	src.next = dst.Next()
   574  	src.Next().prev = src
   575  	dst.next = src
   576  	return true
   577  }
   578  
   579  func (l *doublyLinkedList[T]) MoveToFront(targetE *NodeElement[T]) bool {
   580  	if l == nil || l.root == nil || l.len.Load() == 0 {
   581  		return false
   582  	}
   583  
   584  	src, status := l.checkElement(targetE)
   585  	switch status {
   586  	case notInList, theOnlyOne, theFirstButNotTheLast:
   587  		return false
   588  	default:
   589  	}
   590  	return l.move(src, l.root)
   591  }
   592  
   593  func (l *doublyLinkedList[T]) MoveToBack(targetE *NodeElement[T]) bool {
   594  	if l == nil || l.root == nil || l.len.Load() == 0 {
   595  		return false
   596  	}
   597  
   598  	src, status := l.checkElement(targetE)
   599  	switch status {
   600  	case notInList, theLastButNotTheFirst, theOnlyOne:
   601  		return false
   602  	default:
   603  	}
   604  	return l.move(src, l.getRootTail())
   605  }
   606  
   607  // MoveBefore.
   608  // Ordinarily, it is move srcE just prev to dstE.
   609  func (l *doublyLinkedList[T]) MoveBefore(srcE, dstE *NodeElement[T]) bool {
   610  	if l == nil || l.root == nil || l.len.Load() == 0 || srcE == dstE {
   611  		return false
   612  	}
   613  
   614  	var (
   615  		dst, src             *NodeElement[T]
   616  		dstStatus, srcStatus nodeElementInListStatus
   617  	)
   618  	switch dst, dstStatus = l.checkElement(dstE); dstStatus {
   619  	case notInList, emtpyList, theOnlyOne:
   620  		return false
   621  	default:
   622  	}
   623  
   624  	switch src, srcStatus = l.checkElement(srcE); srcStatus {
   625  	case notInList, emtpyList, theOnlyOne:
   626  		return false
   627  	default:
   628  	}
   629  
   630  	dstPrev := dst.Prev()
   631  	return l.move(src, dstPrev)
   632  }
   633  
   634  func (l *doublyLinkedList[T]) MoveAfter(srcE, dstE *NodeElement[T]) bool {
   635  	if l == nil || l.root == nil || l.len.Load() == 0 || srcE == dstE {
   636  		return false
   637  	}
   638  
   639  	var (
   640  		dst, src             *NodeElement[T]
   641  		dstStatus, srcStatus nodeElementInListStatus
   642  	)
   643  	switch dst, dstStatus = l.checkElement(dstE); dstStatus {
   644  	case notInList, emtpyList, theOnlyOne:
   645  		return false
   646  	default:
   647  	}
   648  
   649  	switch src, srcStatus = l.checkElement(srcE); srcStatus {
   650  	case notInList, emtpyList, theOnlyOne:
   651  		return false
   652  	default:
   653  	}
   654  
   655  	return l.move(src, dst)
   656  }
   657  
   658  func (l *doublyLinkedList[T]) PushFrontList(src LinkedList[T]) {
   659  	if l == nil || l.root == nil || l.len.Load() == 0 {
   660  		return
   661  	}
   662  
   663  	if dl, ok := src.(*doublyLinkedList[T]); !ok || dl != nil && dl.getRoot() == l.getRoot() {
   664  		// avoid type mismatch and self copy
   665  		return
   666  	}
   667  
   668  	for i, e := src.Len(), src.Back(); i > 0; i-- {
   669  		prev := e.Prev()
   670  		// Clean the node element reference, not a required operation
   671  		e.listRef = l
   672  		e.prev = nil
   673  		e.next = nil
   674  		l.insertAfter(e, l.root)
   675  		e = prev
   676  	}
   677  }
   678  
   679  func (l *doublyLinkedList[T]) PushBackList(src LinkedList[T]) {
   680  	if l == nil || l.root == nil || l.len.Load() == 0 {
   681  		return
   682  	}
   683  
   684  	if dl, ok := src.(*doublyLinkedList[T]); !ok || dl != nil && dl.getRoot() == l.getRoot() {
   685  		// avoid type mismatch and self copy
   686  		return
   687  	}
   688  	for i, e := src.Len(), src.Front(); i > 0; i-- {
   689  		next := e.Next()
   690  		// Clean the node element reference, not a required operation
   691  		e.listRef = l
   692  		e.prev = nil
   693  		e.next = nil
   694  		l.Append(e)
   695  		e = next
   696  	}
   697  }