Matthew Toseland schrieb:
> The problem with this is that AFAIK, the id is never modified once it
> has been set in construction...
hmm, looked at the source a bit:
a) why does an exception in the NodeMessage#logHeisenbug function set
s="" and not details="";
b) I grepped through the source a bit *where* those IDs come from. I
could not see if they were send over the wire (nearly every class
containing the word id gets it over the constructor from its
instantiating class), but when it *is* generated, it is done by e.g.
long id = n.randSource.nextLong();
Hmm, why is that not
i) encapsulated in a method of Node here, instead of a public Random
object?
ii) checked at that point whether it is a heisenbugged ID or not (that
kind of IDs *can* occur without bugs at this point. I'd try to generate
another random number at that point then)
iii) synchronized? I don't know if Random (or the subclass you use) is
really thread-safe.
I wrote a servlet that tries to test thread-safeness of your Random
implementation (i attached it) - I hope there is no bug (other than the
heisenbug) in it that causes these results. Results from a few runs:
-----
BugHunter was invoked with threads=200&ids=3000 and found:
Clean IDs: 599908
Bugged IDs: 92
-----
BugHunter was invoked with threads=10&ids=100000 and found:
Clean IDs: 999871
Bugged IDs: 129
-----
BugHunter was invoked with threads=1&ids=1000000 and found:
Clean IDs: 1000000
Bugged IDs: 0
-----
I think these numbers show quite well where the problem is.
(and now all my randomness is wasted, I know ;-))
but it seems to be a bit of work to replace all that
n.randSource.nextLong() refs by a synchronized method of Node/Core...
(okay, full text search finds 13 files, but what if it is not n or if
the call is separated into two?
HTH,
mihi
package mihi;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import freenet.node.*;
import freenet.support.*;
import freenet.client.*;
/* use the following code in your freenet.ini/conf: *
mainport.params.servlet.hunt.uri=/hunt/
mainport.params.servlet.hunt.method=GET
mainport.params.servlet.hunt.class=mihi.HeisenHunterServlet
mainport.params.servlet.hunt.name=Hunt
*/
public class HeisenHunterServlet extends HttpServlet {
private static final String LOCATION ="/try/";
private Node node;
private Object syncher=new Object();
private int cleanIDs, buggedIDs, numThreads, numIDs;
public void init () throws ServletException
{
super.init();
ServletContext context = getServletContext();
node = (Node)context.getAttribute("freenet.node.Node");
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
try {
numThreads =Integer.parseInt(req.getParameter("threads"));
numIDs=Integer.parseInt(req.getParameter("ids"));
} catch (NumberFormatException ex) {
numThreads=200;
numIDs=3000;
}
PrintWriter pw = resp.getWriter();
cleanIDs=buggedIDs=0;
// sorry for bypassing your thread factory...
Thread[] ts = new Thread[numThreads];
for (int i=0;i<numThreads;i++) {
ts[i]=new Thread(new Runnable() {
public void run() {
int clean=0, bugged=0;
for (int j=0;j<numIDs;j++) {
long id = node.randSource.nextLong();
if (((id>>>32) & 0xffffffffL) ==
(id & 0xffffffffL)) {
bugged++;
} else {
clean++;
}
}
synchronized(syncher) {
cleanIDs+=clean;
buggedIDs+=bugged;
}
}
});
}
for (int i=0;i<numThreads;i++) {
ts[i].start();
}
try {
for (int i=0;i<numThreads;i++) {
ts[i].join();
}
pw.println("BugHunter was invoked with threads="+numThreads+
"&ids="+numIDs+" and found: \n\nClean IDs: "+
cleanIDs+"\n"+"Bugged IDs: "+buggedIDs+"\n");
} catch(InterruptedException e) {
pw.println("Interrupted!");
}
pw.close();
}
}