private Token receive0() { Node s = new Node(null, false); // the node to append retry: for (; ; ) { // restart on append race Object item = tryMatch(null, null, false); if (item != NO_MATCH) { s.item = item; return new Token(s, null); } Node pred = tryAppend(s, false); if (pred == null) continue retry; // lost race vs opposite mode if (sendClosed) { s.item = CHANNEL_CLOSED; unsplice(pred, s); setReceiveClosed(); return new Token(s, null); } requestUnpark(s, Strand.currentStrand()); return new Token(s, pred); } }
@Override public boolean send(Message message, long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { if (message == null) throw new IllegalArgumentException("message is null"); if (isSendClosed()) return true; if (xfer1(message, true, TIMED, unit.toNanos(timeout)) == null) return true; if (!Strand.interrupted()) return false; throw new InterruptedException(); }
/** Tries to artificially match a data node -- used by remove. */ final boolean tryMatchData() { // assert isData; Object x = item; if (x != null && x != this && casItem(x, null)) { Strand.unpark(waiter, this); return true; } return false; }
protected Message receiveInternal(long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { if (receiveClosed) return null; Object m = xfer1(null, false, TIMED, unit.toNanos(timeout)); if (m != null || !Strand.interrupted()) { if (m == CHANNEL_CLOSED) return closeValue(); return (Message) m; } throw new InterruptedException(); }
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; }
@Suspendable public static void handleDelayWithStrand() { if (delay > 0) { try { Strand.sleep(delay); } catch (final InterruptedException ie) { ie.printStackTrace(System.err); throw new RuntimeException(ie); } catch (final SuspendExecution se) { throw new AssertionError(se); } } }
private void signalWaitersOnClose() { for (Node p = head; p != null; ) { if (!p.isMatched()) { if (!p.isData) { if (p.casItem(null, CHANNEL_CLOSED)) // match waiting requesters with CHANNEL_CLOSED Strand.unpark(p.waiter, this); // ... and wake 'em up } else p.tryMatchData(); } Node n = p.next; if (n != p) p = n; else p = head; } }
private static ActorRef getCurrentActor() { ActorRef actorRef = LocalActor.self(); if (actorRef == null) { // create a "dummy actor" on the current strand Actor actor = new Actor(Strand.currentStrand(), null, new MailboxConfig(5, OverflowPolicy.THROW)) { @Override protected Object doRun() throws InterruptedException, SuspendExecution { throw new AssertionError(); } }; actorRef = new TempActor(actor); } return actorRef; }
/** * 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 Message awaitMatch(Node s, Node pred, Message e, boolean timed, long nanos) throws SuspendExecution { long lastTime = timed ? System.nanoTime() : 0L; Strand w = Strand.currentStrand(); int spins = (w.isFiber() ? 0 : -1); // no spins in fiber; otherwise, initialized after first item and cancel checks ThreadLocalRandom randomYields = null; // bound if needed if (spins == 0) requestUnpark(s, w); for (; ; ) { Object item = s.item; if (item == CHANNEL_CLOSED) setReceiveClosed(); if (item != e) { // matched // assert item != s; s.forgetContents(); // avoid garbage return this.<Message>cast(item); } if ((w.isInterrupted() || (timed && nanos <= 0)) && s.casItem(e, s)) { // 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) Strand.yield(); // occasionally yield } else if (s.waiter == null) { requestUnpark(s, w); // request unpark then recheck } else if (timed) { long now = System.nanoTime(); if ((nanos -= now - lastTime) > 0) Strand.parkNanos(this, nanos); lastTime = now; } else { Strand.park(this); } } }