private Token xfer0(SelectActionImpl<Message> e) { final boolean haveData = e.isData(); Node s = null; // the node to append, if needed retry: for (; ; ) { // restart on append race if (!e.lease()) return null; if (isClosed() || (isSendClosed() && e.isData())) { e.setItem(null); e.won(); return null; } Object item = tryMatch(e, e.message(), haveData); if (item == LOST) return null; if (item != NO_MATCH) { e.setItem(item == CHANNEL_CLOSED ? null : (Message) item); e.won(); return null; } e.returnLease(); if (s == null) { s = new Node(e); requestUnpark(s, e.selector().getWaiter()); } Node pred = tryAppend(s, haveData); if (pred == null) continue retry; // lost race vs opposite mode return new Token(s, pred); } }
void won() { if (sa != null) { Object x = item; sa.setItem(x == CHANNEL_CLOSED ? null : x); sa.won(); } }
private Object tryMatch(SelectActionImpl sa, Message e, boolean haveData) { boolean closed = isSendClosed(); // must be read before trying to match so as not to miss puts for (Node h = head, p = h; p != null; ) { // find & match first node boolean isData = p.isData; Object item = p.item; if (item != p && (item != null) == isData) { // unmatched if (isData == haveData) // can't match break; // avoid deadlock by orderdering lease acquisition: // if p requires a lease and is of lower hashCode than sa, we return sa's lease, acquire // p's, and then reacquire sa's. SelectActionImpl sa2 = p.sa; boolean leasedp; if (sa != null && sa2 != null && sa2.selector().id < sa.selector().id) { sa.returnLease(); leasedp = sa2.lease(); if (!sa.lease()) { if (leasedp) sa2.returnLease(); return LOST; } } else leasedp = p.lease(); if (leasedp) { if (p.casItem(item, e)) { // match p.won(); for (Node q = p; q != h; ) { Node n = q.next; // update by 2 unless singleton if (head == h && casHead(h, n == null ? q : n)) { h.forgetNext(); break; } // advance and retry if ((h = head) == null || (q = h.next) == null || !q.isMatched()) break; // unless slack < 2 } Strand.unpark(p.waiter, this); return item; } else p.returnLease(); } } Node n = p.next; p = (p != n) ? n : (h = head); // Use head if p offlist } if (closed) { assert !haveData; setReceiveClosed(); return CHANNEL_CLOSED; } return NO_MATCH; }
void returnLease() { if (sa != null) sa.returnLease(); }
boolean lease() { if (sa == null) return true; return sa.lease(); }
/** * Constructs a new node. Uses relaxed write because item can only be seen after publication via * casNext. */ Node(SelectActionImpl sa) { UNSAFE.putObject(this, itemOffset, sa.message()); // relaxed write UNSAFE.putObject(this, saOffset, sa); // relaxed write this.isData = sa.isData(); }