/**
   * Spins/yields/blocks until node s is matched or caller gives up.
   *
   * @param s the waiting node
   * @param pred the predecessor of s, or s itself if it has no predecessor, or null if unknown (the
   *     null case does not occur in any current calls but may in possible future extensions)
   * @param e the comparison value for checking match
   * @param timed if true, wait only until timeout elapses
   * @param nanos timeout in nanosecs, used only if timed is true
   * @return matched item, or e if unmatched on interrupt or timeout
   */
  private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    Thread w = Thread.currentThread();
    int spins = -1; // initialized after first item and cancel checks
    ThreadLocalRandom randomYields = null; // bound if needed

    for (; ; ) {
      Object item = s.item;
      if (item != e) { // matched
        // assert item != s;
        s.forgetContents(); // avoid garbage
        return LinkedTransferQueue.<E>cast(item);
      }
      if ((w.isInterrupted() || (timed && nanos <= 0)) && s.casItem(e, FORGOTTEN)) { // cancel
        unsplice(pred, s);
        return e;
      }

      if (spins < 0) { // establish spins at/near front
        if ((spins = spinsFor(pred, s.isData)) > 0) randomYields = ThreadLocalRandom.current();
      } else if (spins > 0) { // spin
        --spins;
        if (randomYields.nextInt(CHAINED_SPINS) == 0) Thread.yield(); // occasionally yield
      } else if (s.waiter == null) {
        s.waiter = w; // request unpark then recheck
      } else if (timed) {
        nanos = deadline - System.nanoTime();
        if (nanos > 0L) LockSupport.parkNanos(this, nanos);
      } else {
        LockSupport.park(this);
      }
    }
  }
 /** Main implementation of remove(Object) */
 private boolean findAndRemove(Object e) {
   if (e != null) {
     for (Node pred = null, p = head; p != null; ) {
       Object item = p.item;
       if (p.isData) {
         if (item != null && item != FORGOTTEN && e.equals(item) && p.tryMatchData()) {
           unsplice(pred, p);
           return true;
         }
       } else if (item == null) break;
       pred = p;
       if ((p = p.next) == UNLINKED) { // stale
         pred = null;
         p = head;
       }
     }
   }
   return false;
 }