protected void sendStableMessage(Address dest, short conn_id, long low, long high) { Message stable_msg = new Message(dest, null, null); Unicast2Header hdr = Unicast2Header.createStableHeader(conn_id, low, high); stable_msg.putHeader(this.id, hdr); stable_msg.setFlag(Message.OOB); if (log.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); sb.append(local_addr) .append(" --> STABLE(") .append(dest) .append(": ") .append(low) .append("-") .append(high) .append(", conn_id=") .append(conn_id) .append(")"); log.trace(sb.toString()); } down_prot.down(new Event(Event.MSG, stable_msg)); ReceiverEntry entry = recv_table.get(dest); NakReceiverWindow win = entry != null ? entry.received_msgs : null; if (win != null) win.stable(win.getHighestDelivered()); }
private ReceiverEntry getOrCreateReceiverEntry(Address sender, long seqno, short conn_id) { NakReceiverWindow win = new NakReceiverWindow( sender, this, seqno - 1, timer, use_range_based_retransmitter, xmit_table_num_rows, xmit_table_msgs_per_row, xmit_table_resize_factor, xmit_table_max_compaction_time, xmit_table_automatic_purging); if (exponential_backoff > 0) win.setRetransmitTimeouts(new ExponentialInterval(exponential_backoff)); else win.setRetransmitTimeouts(new StaticInterval(timeout)); ReceiverEntry entry = new ReceiverEntry(win, conn_id, max_stable_msgs); ReceiverEntry entry2 = recv_table.putIfAbsent(sender, entry); if (entry2 != null) return entry2; if (log.isTraceEnabled()) log.trace( local_addr + ": created receiver window for " + sender + " at seqno=#" + seqno + " for conn-id=" + conn_id); return entry; }
@ManagedOperation( description = "Sends a STABLE message to all senders. This causes message purging and potential" + " retransmissions from senders") public void sendStableMessages() { for (Map.Entry<Address, ReceiverEntry> entry : recv_table.entrySet()) { Address dest = entry.getKey(); ReceiverEntry val = entry.getValue(); NakReceiverWindow win = val != null ? val.received_msgs : null; if (win != null) { long[] tmp = win.getDigest(); long low = tmp[0], high = tmp[1]; if (val.last_highest == high) { if (val.num_stable_msgs >= val.max_stable_msgs) { continue; } else val.num_stable_msgs++; } else { val.last_highest = high; val.num_stable_msgs = 1; } sendStableMessage(dest, val.recv_conn_id, low, high); } } }
@ManagedOperation(description = "Compacts the retransmission tables") public void compact() { for (Map.Entry<Address, ReceiverEntry> entry : recv_table.entrySet()) { NakReceiverWindow win = entry.getValue().received_msgs; win.compact(); } }
@ManagedOperation( description = "Purges highes delivered messages and compacts the retransmission tables") public void purgeAndCompact() { for (Map.Entry<Address, ReceiverEntry> entry : recv_table.entrySet()) { NakReceiverWindow win = entry.getValue().received_msgs; win.stable(win.getHighestDelivered()); win.compact(); } }
public void removeReceiveConnection(Address mbr) { ReceiverEntry entry2 = recv_table.remove(mbr); if (entry2 != null) { NakReceiverWindow win = entry2.received_msgs; if (win != null) sendStableMessage( mbr, entry2.recv_conn_id, win.getHighestDelivered(), win.getHighestReceived()); entry2.reset(); } }
@ManagedOperation(description = "Returns the sizes of all NakReceiverWindow.RetransmitTables") public String printRetransmitTableSizes() { StringBuilder sb = new StringBuilder(); for (Map.Entry<Address, ReceiverEntry> entry : recv_table.entrySet()) { NakReceiverWindow win = entry.getValue().received_msgs; sb.append(entry.getKey() + ": ") .append(win.getRetransmitTableSize()) .append(" (capacity=" + win.getRetransmitTableCapacity()) .append(", fill factor=" + win.getRetransmitTableFillFactor() + "%)\n"); } return sb.toString(); }
/** * Check whether the hashtable contains an entry e for <code>sender</code> (create if not). If * e.received_msgs is null and <code>first</code> is true: create a new AckReceiverWindow(seqno) * and add message. Set e.received_msgs to the new window. Else just add the message. */ protected void handleDataReceived( Address sender, long seqno, short conn_id, boolean first, Message msg, Event evt) { if (log.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); sb.append(local_addr).append(" <-- DATA(").append(sender).append(": #").append(seqno); if (conn_id != 0) sb.append(", conn_id=").append(conn_id); if (first) sb.append(", first"); sb.append(')'); log.trace(sb); } ReceiverEntry entry; NakReceiverWindow win; recv_table_lock.lock(); try { entry = recv_table.get(sender); win = entry != null ? entry.received_msgs : null; if (first) { if (entry == null) { entry = getOrCreateReceiverEntry(sender, seqno, conn_id); win = entry.received_msgs; } else { // entry != null && win != null if (conn_id != entry.recv_conn_id) { if (log.isTraceEnabled()) log.trace( local_addr + ": conn_id=" + conn_id + " != " + entry.recv_conn_id + "; resetting receiver window"); ReceiverEntry entry2 = recv_table.remove(sender); if (entry2 != null) entry2.received_msgs.destroy(); entry = getOrCreateReceiverEntry(sender, seqno, conn_id); win = entry.received_msgs; } else {; } } } else { // entry == null && win == null OR entry != null && win == null OR entry != null && // win != null if (win == null || entry.recv_conn_id != conn_id) { recv_table_lock.unlock(); sendRequestForFirstSeqno(sender, seqno); // drops the message and returns (see below) return; } } } finally { if (recv_table_lock.isHeldByCurrentThread()) recv_table_lock.unlock(); } entry.update(); boolean added = win.add(seqno, msg); // win is guaranteed to be non-null if we get here num_msgs_received++; num_bytes_received += msg.getLength(); if (added) { int len = msg.getLength(); if (len > 0) { boolean send_stable_msg = false; entry.lock(); try { entry.received_bytes += len; if (entry.received_bytes >= max_bytes) { entry.received_bytes = 0; send_stable_msg = true; } } finally { entry.unlock(); } if (send_stable_msg) sendStableMessage( sender, entry.recv_conn_id, win.getHighestDelivered(), win.getHighestReceived()); } } // An OOB message is passed up immediately. Later, when remove() is called, we discard it. This // affects ordering ! // http://jira.jboss.com/jira/browse/JGRP-377 if (msg.isFlagSet(Message.OOB) && added) { try { up_prot.up(evt); } catch (Throwable t) { log.error("couldn't deliver OOB message " + msg, t); } } final AtomicBoolean processing = win.getProcessing(); if (!processing.compareAndSet(false, true)) { return; } // try to remove (from the AckReceiverWindow) as many messages as possible and pass them up // Prevents concurrent passing up of messages by different threads // (http://jira.jboss.com/jira/browse/JGRP-198); // this is all the more important once we have a concurrent stack // (http://jira.jboss.com/jira/browse/JGRP-181), // where lots of threads can come up to this point concurrently, but only 1 is allowed to pass // at a time // We *can* deliver messages from *different* senders concurrently, e.g. reception of P1, Q1, // P2, Q2 can result in // delivery of P1, Q1, Q2, P2: FIFO (implemented by UNICAST) says messages need to be delivered // only in the // order in which they were sent by their senders boolean released_processing = false; try { while (true) { List<Message> msgs = win.removeMany(processing, true, max_msg_batch_size); // remove my own messages if (msgs == null || msgs.isEmpty()) { released_processing = true; return; } for (Message m : msgs) { // discard OOB msg: it has already been delivered // (http://jira.jboss.com/jira/browse/JGRP-377) if (m.isFlagSet(Message.OOB)) continue; try { up_prot.up(new Event(Event.MSG, m)); } catch (Throwable t) { log.error("couldn't deliver message " + m, t); } } } } finally { // processing is always set in win.remove(processing) above and never here ! This code is just // a // 2nd line of defense should there be an exception before win.remove(processing) sets // processing if (!released_processing) processing.set(false); } }
void reset() { if (received_msgs != null) received_msgs.destroy(); received_bytes = 0; last_highest = -1; num_stable_msgs = 0; }