concurrentify shitlist

This commit is contained in:
zzz
2009-02-03 15:15:09 +00:00
parent d236b9b44a
commit ececf5407d
2 changed files with 57 additions and 74 deletions

View File

@ -50,4 +50,11 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E> {
public Iterator<E> iterator() { public Iterator<E> iterator() {
return _map.keySet().iterator(); return _map.keySet().iterator();
} }
public boolean addAll(Collection<? extends E> c) {
boolean rv = false;
for (E e : c)
rv |= _map.put(e, DUMMY) == null;
return rv;
}
} }

View File

@ -10,6 +10,7 @@ package net.i2p.router;
import java.io.IOException; import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -23,6 +24,7 @@ import java.util.TreeMap;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.router.peermanager.PeerProfile; import net.i2p.router.peermanager.PeerProfile;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.Log; import net.i2p.util.Log;
/** /**
@ -34,7 +36,7 @@ import net.i2p.util.Log;
public class Shitlist { public class Shitlist {
private Log _log; private Log _log;
private RouterContext _context; private RouterContext _context;
private Map _entries; private Map<Hash, Entry> _entries;
private static class Entry { private static class Entry {
/** when it should expire, per the i2p clock */ /** when it should expire, per the i2p clock */
@ -42,7 +44,7 @@ public class Shitlist {
/** why they were shitlisted */ /** why they were shitlisted */
String cause; String cause;
/** what transports they were shitlisted for (String), or null for all transports */ /** what transports they were shitlisted for (String), or null for all transports */
Set transports; Set<String> transports;
} }
public final static long SHITLIST_DURATION_MS = 20*60*1000; public final static long SHITLIST_DURATION_MS = 20*60*1000;
@ -54,33 +56,23 @@ public class Shitlist {
public Shitlist(RouterContext context) { public Shitlist(RouterContext context) {
_context = context; _context = context;
_log = context.logManager().getLog(Shitlist.class); _log = context.logManager().getLog(Shitlist.class);
_entries = new HashMap(32); _entries = new ConcurrentHashMap(8);
_context.jobQueue().addJob(new Cleanup(_context)); _context.jobQueue().addJob(new Cleanup(_context));
} }
private class Cleanup extends JobImpl { private class Cleanup extends JobImpl {
private List _toUnshitlist;
public Cleanup(RouterContext ctx) { public Cleanup(RouterContext ctx) {
super(ctx); super(ctx);
_toUnshitlist = new ArrayList(4); getTiming().setStartAfter(ctx.clock().now() + SHITLIST_CLEANER_START_DELAY);
getTiming().setStartAfter(_context.clock().now() + SHITLIST_CLEANER_START_DELAY);
} }
public String getName() { return "Cleanup shitlist"; } public String getName() { return "Cleanup shitlist"; }
public void runJob() { public void runJob() {
_toUnshitlist.clear();
long now = getContext().clock().now(); long now = getContext().clock().now();
synchronized (_entries) { for (Iterator iter = _entries.entrySet().iterator(); iter.hasNext(); ) {
for (Iterator iter = _entries.keySet().iterator(); iter.hasNext(); ) { Map.Entry<Hash, Entry> e = (Map.Entry) iter.next();
Hash peer = (Hash)iter.next(); if (e.getValue().expireOn <= now) {
Entry entry = (Entry)_entries.get(peer);
if (entry.expireOn <= now) {
iter.remove(); iter.remove();
_toUnshitlist.add(peer); Hash peer = e.getKey();
}
}
}
for (int i = 0; i < _toUnshitlist.size(); i++) {
Hash peer = (Hash)_toUnshitlist.get(i);
PeerProfile prof = _context.profileOrganizer().getProfile(peer); PeerProfile prof = _context.profileOrganizer().getProfile(peer);
if (prof != null) if (prof != null)
prof.unshitlist(); prof.unshitlist();
@ -88,16 +80,15 @@ public class Shitlist {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Unshitlisting router (expired) " + peer.toBase64()); _log.info("Unshitlisting router (expired) " + peer.toBase64());
} }
}
requeue(30*1000); requeue(30*1000);
} }
} }
public int getRouterCount() { public int getRouterCount() {
synchronized (_entries) {
return _entries.size(); return _entries.size();
} }
}
public boolean shitlistRouter(Hash peer) { public boolean shitlistRouter(Hash peer) {
return shitlistRouter(peer, null); return shitlistRouter(peer, null);
@ -143,12 +134,11 @@ public class Shitlist {
e.cause = reason; e.cause = reason;
e.transports = null; e.transports = null;
if (transport != null) { if (transport != null) {
e.transports = new HashSet(1); e.transports = new ConcurrentHashSet(1);
e.transports.add(transport); e.transports.add(transport);
} }
synchronized (_entries) { Entry old = _entries.get(peer);
Entry old = (Entry)_entries.get(peer);
if (old != null) { if (old != null) {
wasAlready = true; wasAlready = true;
// take the oldest expiration and cause, combine transports // take the oldest expiration and cause, combine transports
@ -166,7 +156,6 @@ public class Shitlist {
} }
} }
_entries.put(peer, e); _entries.put(peer, e);
}
if (transport == null) { if (transport == null) {
// we hate the peer on *any* transport // we hate the peer on *any* transport
@ -190,9 +179,8 @@ public class Shitlist {
_log.debug("Calling unshitlistRouter " + peer.toBase64() _log.debug("Calling unshitlistRouter " + peer.toBase64()
+ (transport != null ? "/" + transport : "")); + (transport != null ? "/" + transport : ""));
boolean fully = false; boolean fully = false;
Entry e;
synchronized (_entries) { Entry e = _entries.remove(peer);
e = (Entry)_entries.remove(peer);
if ( (e == null) || (e.transports == null) || (transport == null) || (e.transports.size() <= 1) ) { if ( (e == null) || (e.transports == null) || (transport == null) || (e.transports.size() <= 1) ) {
// fully unshitlisted // fully unshitlisted
fully = true; fully = true;
@ -203,7 +191,7 @@ public class Shitlist {
else else
_entries.put(peer, e); _entries.put(peer, e);
} }
}
if (fully) { if (fully) {
if (realUnshitlist) { if (realUnshitlist) {
PeerProfile prof = _context.profileOrganizer().getProfile(peer); PeerProfile prof = _context.profileOrganizer().getProfile(peer);
@ -221,25 +209,18 @@ public class Shitlist {
public boolean isShitlisted(Hash peer, String transport) { public boolean isShitlisted(Hash peer, String transport) {
boolean rv = false; boolean rv = false;
boolean unshitlist = false; boolean unshitlist = false;
synchronized (_entries) {
Entry entry = (Entry)_entries.get(peer); Entry entry = _entries.get(peer);
if (entry == null) { if (entry == null) {
rv = false; rv = false;
} else { } else if (entry.expireOn <= _context.clock().now()) {
if (entry.expireOn <= _context.clock().now()) {
_entries.remove(peer); _entries.remove(peer);
unshitlist = true; unshitlist = true;
rv = false; rv = false;
} else { } else if (entry.transports == null) {
if (entry.transports == null) {
rv = true;
} else if (entry.transports.contains(transport)) {
rv = true; rv = true;
} else { } else {
rv = false; rv = entry.transports.contains(transport);
}
}
}
} }
if (unshitlist) { if (unshitlist) {
@ -255,10 +236,7 @@ public class Shitlist {
} }
public boolean isShitlistedForever(Hash peer) { public boolean isShitlistedForever(Hash peer) {
Entry entry; Entry entry = _entries.get(peer);
synchronized (_entries) {
entry = (Entry)_entries.get(peer);
}
return entry != null && entry.expireOn > _context.clock().now() + SHITLIST_DURATION_MAX; return entry != null && entry.expireOn > _context.clock().now() + SHITLIST_DURATION_MAX;
} }
@ -271,17 +249,15 @@ public class Shitlist {
public void renderStatusHTML(Writer out) throws IOException { public void renderStatusHTML(Writer out) throws IOException {
StringBuffer buf = new StringBuffer(1024); StringBuffer buf = new StringBuffer(1024);
buf.append("<h2>Shitlist</h2>"); buf.append("<h2>Shitlist</h2>");
Map entries = new TreeMap(new HashComparator()); Map<Hash, Entry> entries = new TreeMap(new HashComparator());
synchronized (_entries) {
entries.putAll(_entries); entries.putAll(_entries);
}
buf.append("<ul>"); buf.append("<ul>");
for (Iterator iter = entries.entrySet().iterator(); iter.hasNext(); ) { for (Map.Entry<Hash, Entry> e : entries.entrySet()) {
Map.Entry mentry = (Map.Entry)iter.next(); Hash key = e.getKey();
Hash key = (Hash)mentry.getKey(); Entry entry = e.getValue();
Entry entry = (Entry)mentry.getValue();
buf.append("<li><b>").append(key.toBase64()).append("</b>"); buf.append("<li><b>").append(key.toBase64()).append("</b>");
buf.append(" (<a href=\"netdb.jsp?r=").append(key.toBase64().substring(0, 6)).append("\">netdb</a>)"); buf.append(" (<a href=\"netdb.jsp?r=").append(key.toBase64().substring(0, 6)).append("\">netdb</a>)");
buf.append(" expiring in "); buf.append(" expiring in ");