// Got a response UDP packet, or completed a large TCP answer-receive. // Install it as The Answer packet and wake up anybody waiting on an answer. protected void response(AutoBuffer ab) { assert _tasknum == ab.getTask(); if (_done) { ab.close(); return; } // Ignore duplicate response packet int flag = ab.getFlag(); // Must read flag also, to advance ab if (flag == SERVER_TCP_SEND) { ab.close(); return; } // Ignore UDP packet for a TCP reply assert flag == SERVER_UDP_SEND; synchronized (this) { // Install the answer under lock if (_done) { ab.close(); return; } // Ignore duplicate response packet _dt.read(ab); // Read the answer (under lock?) ab.close(); // Also finish the read (under lock?) _dt.onAck(); // One time only execute (before sending ACKACK) _done = true; UDPTimeOutThread.PENDING.remove(this); TASKS.remove(_tasknum); // Flag as task-completed, even if the result is null notifyAll(); // And notify in any case } }
// Attempt to cancel job public final boolean cancel(boolean mayInterruptIfRunning) { boolean did = false; synchronized (this) { // Install the answer under lock if (!isCancelled()) { did = true; // Did cancel (was not canceled already) _target = null; // Flag as canceled UDPTimeOutThread.PENDING.remove(this); TASKS.remove(_tasknum); } notifyAll(); // notify in any case } return did; }
// Make an initial RPC, or re-send a packet. Always called on 1st send; also // called on a timeout. public synchronized RPC<V> call() { // Keep a global record, for awhile TASKS.put(_tasknum, this); // We could be racing timeouts-vs-replies. Blow off timeout if we have an answer. if (isDone()) { TASKS.remove(_tasknum); return this; } // Default strategy: (re)fire the packet and (re)start the timeout. We // "count" exactly 1 failure: just whether or not we shipped via TCP ever // once. After that we fearlessly (re)send UDP-sized packets until the // server replies. // Pack classloader/class & the instance data into the outgoing // AutoBuffer. If it fits in a single UDP packet, ship it. If not, // finish off the current AutoBuffer (which is now going TCP style), and // make a new UDP-sized packet. On a re-send of a TCP-sized hunk, just // send the basic UDP control packet. if (!_sentTcp) { // Ship the UDP packet with clazz name to execute // totally replace me with Michal's enums!!! UDP.udp fjq = _dt.isHighPriority() ? UDP.udp.exechi : UDP.udp.execlo; AutoBuffer ab = new AutoBuffer(_target).putTask(fjq, _tasknum); ab.put1(CLIENT_UDP_SEND).put(_dt).close(); if (ab.hasTCP()) _sentTcp = true; } // Double retry until we exceed existing age. This is the time to delay // until we try again. Note that we come here immediately on creation, // so the first doubling happens before anybody does any waiting. Also // note the generous 5sec cap: ping at least every 5 sec. _retry += (_retry < 5000) ? _retry : 5000; // Put self on the "TBD" list of tasks awaiting Timeout. // So: dont really 'forget' but remember me in a little bit. assert !UDPTimeOutThread.PENDING.contains(this); UDPTimeOutThread.PENDING.add(this); return this; }