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();
    }
}

Reply via email to