/** * Add a new message to the beginning i.e. send it as soon as possible (e.g. if we tried to send * it and failed); it is assumed to already be urgent. */ public void addFirst(MessageItem item) { long id = item.getID(); Items list; if (itemsByID == null) { itemsByID = new HashMap<Long, Items>(); if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); list = new Items(id); nonEmptyItemsWithID.push(list); itemsByID.put(id, list); } else { list = itemsByID.get(id); if (list == null) { list = new Items(id); if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); nonEmptyItemsWithID.unshift(list); itemsByID.put(id, list); } else { if (list.items.isEmpty()) { assert (list.getParent() == emptyItemsWithID); // It already exists, so it has a valid time. // Which is probably in the past, so use Forward. moveFromEmptyToNonEmptyForward(list); } else assert (list.getParent() == nonEmptyItemsWithID); } } list.addFirst(item); }
/** * Add urgent messages, then non-urgent messages. Add a load message if need to. * * @param size * @param minSize * @param maxSize * @param now * @param messages * @return */ int addPriorityMessages( int size, int minSize, int maxSize, long now, ArrayList<MessageItem> messages, MutableBoolean incomplete) { synchronized (PeerMessageQueue.this) { // Urgent messages first. if (logMINOR) { int nonEmpty = nonEmptyItemsWithID == null ? 0 : nonEmptyItemsWithID.size(); int empty = emptyItemsWithID == null ? 0 : emptyItemsWithID.size(); int byID = itemsByID == null ? 0 : itemsByID.size(); if (nonEmpty + empty < byID) { Logger.error( this, "Leaking itemsByID? non empty = " + nonEmpty + " empty = " + empty + " by ID = " + byID + " on " + this); } else if (logDEBUG) Logger.debug( this, "Items: non empty " + nonEmpty + " empty " + empty + " by ID " + byID + " on " + this); } moveToUrgent(now); clearOldNonUrgent(now); size = addUrgentMessages(size, minSize, maxSize, now, messages); if (size < 0) { size = -size; incomplete.value = true; return size; } // If no more urgent messages, try to add some non-urgent messages too. size = addNonUrgentMessages(size, minSize, maxSize, now, messages); if (size < 0) { size = -size; incomplete.value = true; } return size; } }
private void addToEmptyBackward(Items list) { if (emptyItemsWithID == null) emptyItemsWithID = new DoublyLinkedListImpl<Items>(); Enumeration<Items> it = emptyItemsWithID.reverseElements(); while (it.hasMoreElements()) { Items compare = it.nextElement(); if (compare.timeLastSent <= list.timeLastSent) { emptyItemsWithID.insertNext(compare, list); return; } } emptyItemsWithID.unshift(list); }
private void addToNonEmptyForward(Items list) { if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); Enumeration<Items> it = nonEmptyItemsWithID.elements(); while (it.hasMoreElements()) { Items compare = it.nextElement(); if (compare.timeLastSent >= list.timeLastSent) { nonEmptyItemsWithID.insertPrev(compare, list); return; } } nonEmptyItemsWithID.unshift(list); }
private void moveToUrgent(long now) { if (itemsNonUrgent == null) return; ListIterator<MessageItem> it = itemsNonUrgent.listIterator(); int moved = 0; while (it.hasNext()) { MessageItem item = it.next(); if (item.submitted + PacketSender.MAX_COALESCING_DELAY <= now) { // Move to urgent list long id = item.getID(); Items list; if (itemsByID == null) { itemsByID = new HashMap<Long, Items>(); if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); list = new Items(id); nonEmptyItemsWithID.push(list); itemsByID.put(id, list); } else { list = itemsByID.get(id); if (list == null) { list = new Items(id); if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); // In order to ensure fairness, we add it at the beginning. // addLast() is typically called by sendAsync(). // If there are later items they are probably block transfers that are // already in progress; it is fairer to send the new item first. nonEmptyItemsWithID.unshift(list); itemsByID.put(id, list); } else { if (list.items.isEmpty()) { assert (list.getParent() == emptyItemsWithID); // It already exists, so it has a valid time. // Which is probably in the past, so use Forward. moveFromEmptyToNonEmptyForward(list); } else { assert (list.getParent() == nonEmptyItemsWithID); } } } list.addLast(item); it.remove(); moved++; } else { if (logDEBUG && moved > 0) Logger.debug(this, "Moved " + moved + " items to urgent round-robin"); return; } } }
private void moveFromEmptyToNonEmptyForward(Items list) { // Presumably is in emptyItemsWithID assert (list.items.isEmpty()); if (logMINOR) { if (list.getParent() == nonEmptyItemsWithID) { Logger.error(this, "Already in non-empty yet empty?!"); return; } } if (emptyItemsWithID != null) emptyItemsWithID.remove(list); addToNonEmptyForward(list); }
public void removeUIDs(Long[] list) { if (itemsByID == null) return; for (Long l : list) { Items items = itemsByID.get(l); if (items == null) continue; if (items.items.isEmpty()) { itemsByID.remove(l); assert (emptyItemsWithID != null); assert (items.getParent() == emptyItemsWithID); emptyItemsWithID.remove(items); } } }
private void clearOldNonUrgent(long now) { int removed = 0; if (emptyItemsWithID == null) return; while (true) { if (emptyItemsWithID.isEmpty()) return; Items list = emptyItemsWithID.head(); if (!list.items.isEmpty()) { // FIXME remove paranoia Logger.error(this, "List with items in emptyItemsWithID!!"); emptyItemsWithID.remove(list); addToNonEmptyBackward(list); return; } if (list.timeLastSent == -1 || now - list.timeLastSent > FORGET_AFTER) { // FIXME: Urgh, what a braindead API! remove(Object) on a Map<Long, Items> !?!?!?! // Anyway we'd better check the return value! Items old = itemsByID.remove(list.id); if (old == null) Logger.error(this, "List was not in the items by ID tracker: " + list.id); else if (old != list) Logger.error( this, "Different list in the items by ID tracker: " + old + " not " + list + " for " + list.id); emptyItemsWithID.remove(list); removed++; } else { if (logDEBUG && removed > 0) Logger.debug(this, "Removed " + removed + " old empty UID trackers"); break; } } }
public boolean removeMessage(MessageItem item) { long id = item.getID(); Items list; if (itemsByID != null) { list = itemsByID.get(id); if (list != null) { if (list.remove(item)) { if (list.items.isEmpty()) { nonEmptyItemsWithID.remove(list); addToEmptyBackward(list); } return true; } } } if (itemsNonUrgent != null) return itemsNonUrgent.remove(item); else return false; }
/** * Add messages to <code>messages</code> until there are no more messages to add or <code>size * </code> would exceed <code>maxSize</code>. If <code>size == maxSize</code>, a message in the * queue will be added even if it makes <code>size</code> exceed <code>maxSize</code>. If <code> * isUrgent</code> is set, only messages that are considered urgent are added. * * @param size the current size of <code>messages</code> * @param minSize the size when <code>messages</code> is empty * @param maxSize the maximum size of <code>messages</code> * @param now the current time * @param messages the list that messages will be added to * @param isUrgent <code>true</code> if only urgent messages should be added * @return the size of <code>messages</code>, multiplied by -1 if there were messages that * didn't fit */ private int addUrgentMessages( int size, int minSize, int maxSize, long now, ArrayList<MessageItem> messages) { assert (size >= 0); assert (minSize >= 0); assert (maxSize >= minSize); if (size < 0) size = -size; // FIXME remove extra paranoia int added = 0; while (true) { boolean addedNone = true; int lists = 0; if (nonEmptyItemsWithID == null) return size; lists += nonEmptyItemsWithID.size(); Items list = nonEmptyItemsWithID.head(); for (int i = 0; i < lists && list != null; i++) { if (list.items.isEmpty()) { // Should not happen, but check for it anyway since it keeps happening. :( Logger.error(this, "List is in nonEmptyItemsWithID yet it is empty?!: " + list); nonEmptyItemsWithID.remove(list); addToEmptyBackward(list); if (nonEmptyItemsWithID.isEmpty()) return size; list = nonEmptyItemsWithID.head(); continue; } MessageItem item = list.items.getFirst(); int thisSize = item.getLength(); boolean oversize = false; if (size + 2 + thisSize > maxSize) { if (size == minSize) { // Won't fit regardless, send it on its own. oversize = true; } else { // Send what we have so far. if (logDEBUG && added != 0) Logger.debug( this, "Added " + added + " urgent messages, could add more but out of space at " + size); return -size; } } size += 2 + thisSize; list.items.removeFirst(); // Move to end of list. Items prev = list.getPrev(); nonEmptyItemsWithID.remove(list); list.timeLastSent = now; if (!list.items.isEmpty()) { addToNonEmptyBackward(list); } else { addToEmptyBackward(list); } if (prev == null) list = nonEmptyItemsWithID.head(); else list = prev.getNext(); messages.add(item); added++; addedNone = false; if (oversize) { if (logDEBUG) Logger.debug(this, "Returning with oversize urgent message"); return size; } } if (addedNone) { if (logDEBUG && added != 0) Logger.debug( this, "Added " + added + " urgent messages, size now " + size + " no more queued at this priority"); return size; } } }
private int addNonUrgentMessages( int size, int minSize, int maxSize, long now, ArrayList<MessageItem> messages) { assert (size >= 0); assert (minSize >= 0); assert (maxSize >= minSize); if (size < 0) size = -size; // FIXME remove extra paranoia if (itemsNonUrgent == null) return size; int added = 0; for (ListIterator<MessageItem> items = itemsNonUrgent.listIterator(); items.hasNext(); ) { MessageItem item = items.next(); int thisSize = item.getLength(); boolean oversize = false; if (size + 2 + thisSize > maxSize) { if (size == minSize) { // Won't fit regardless, send it on its own. oversize = true; } else { // Send what we have so far. if (logDEBUG && added != 0) Logger.debug( this, "Returning with " + added + " non-urgent messages (have more but they don't fit)"); return -size; } } size += 2 + thisSize; items.remove(); messages.add(item); if (itemsByID != null) { long id = item.getID(); Items tracker = itemsByID.get(id); if (tracker != null) { tracker.timeLastSent = now; DoublyLinkedList<? super Items> parent = tracker.getParent(); // Demote the corresponding tracker to maintain round-robin. if (tracker.items.isEmpty()) { if (emptyItemsWithID == null) emptyItemsWithID = new DoublyLinkedListImpl<Items>(); if (parent == null) { Logger.error(this, "Tracker is in itemsByID but not in either list! (empty)"); } else if (parent == emptyItemsWithID) { // Normal. Remove it so we can re-add it in the right place. emptyItemsWithID.remove(tracker); } else if (parent == nonEmptyItemsWithID) { Logger.error(this, "Tracker is in non empty items list when is empty"); nonEmptyItemsWithID.remove(tracker); } else assert (false); addToEmptyBackward(tracker); } else { if (nonEmptyItemsWithID == null) nonEmptyItemsWithID = new DoublyLinkedListImpl<Items>(); if (parent == null) { Logger.error(this, "Tracker is in itemsByID but not in either list! (non-empty)"); } else if (parent == nonEmptyItemsWithID) { // Normal. Remove it so we can re-add it in the right place. nonEmptyItemsWithID.remove(tracker); } else if (parent == emptyItemsWithID) { Logger.error(this, "Tracker is in empty items list when is non-empty"); emptyItemsWithID.remove(tracker); } else assert (false); addToNonEmptyBackward(tracker); } } } added++; if (oversize) { if (logDEBUG) Logger.debug(this, "Returning with non-urgent oversize message"); return size; } } if (logDEBUG && added != 0) Logger.debug(this, "Returning with " + added + " non-urgent messages (all gone)"); return size; }
private void moveFromEmptyToNonEmptyBackward(Items list) { // Presumably is in emptyItemsWithID emptyItemsWithID.remove(list); addToNonEmptyBackward(list); }