gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r21370 - in gnunet-java: . src/org/gnunet/statistics src/or


From: gnunet
Subject: [GNUnet-SVN] r21370 - in gnunet-java: . src/org/gnunet/statistics src/org/gnunet/util src/org/grothoff test/org test/org/grothoff
Date: Wed, 9 May 2012 12:07:53 +0200

Author: dold
Date: 2012-05-09 12:07:53 +0200 (Wed, 09 May 2012)
New Revision: 21370

Added:
   gnunet-java/src/org/gnunet/statistics/GetMessage.java
   gnunet-java/src/org/gnunet/statistics/GetResponseEndMessage.java
   gnunet-java/src/org/gnunet/statistics/GetResponseMessage.java
   gnunet-java/src/org/gnunet/statistics/WatchMessage.java
   gnunet-java/src/org/gnunet/statistics/WatchResponseMessage.java
   gnunet-java/src/org/gnunet/util/RequestQueue.java
   gnunet-java/test/org/grothoff/
   gnunet-java/test/org/grothoff/RunaboutBenchmark.java
Removed:
   gnunet-java/src/org/gnunet/statistics/Request.java
   gnunet-java/src/org/gnunet/statistics/RequestMessage.java
   gnunet-java/src/org/gnunet/statistics/RequestQueue.java
   gnunet-java/src/org/gnunet/statistics/ResponseEndMessage.java
   gnunet-java/src/org/gnunet/statistics/ResponseValueMessage.java
Modified:
   gnunet-java/ISSUES
   gnunet-java/src/org/gnunet/statistics/SetMessage.java
   gnunet-java/src/org/gnunet/statistics/Statistics.java
   gnunet-java/src/org/gnunet/util/AbsoluteTime.java
   gnunet-java/src/org/gnunet/util/Connection.java
   gnunet-java/src/org/gnunet/util/Scheduler.java
   gnunet-java/src/org/grothoff/Runabout.java
Log:
implemented the RequestQueue, which is now used in statistics. statistics is 
now more robust and supports watching values. implemented support for anonymous 
inner classes in the runabout.

Modified: gnunet-java/ISSUES
===================================================================
--- gnunet-java/ISSUES  2012-05-09 09:59:06 UTC (rev 21369)
+++ gnunet-java/ISSUES  2012-05-09 10:07:53 UTC (rev 21370)
@@ -3,4 +3,20 @@
 * gnunet vs GNUnet, gnunet-java project name
 * I'm currently trying to increase the robustness of the service APIs,
   discuss behavior in some cases
-* packaging requirements
\ No newline at end of file
+* packaging requirements
+
+
+
+* the Runabout can now be an anonymous inner class
+ * implementation overhead: for public subclasses only constant overhead in 
the Runabout constructor
+ * for private/anonymous inner classes: 10-20% overhead, measured over 100M 
calls
+ * only issue left: visit methods have to be public, but this is a non issue.
+
+
+* Statistics:
+ * currently watches can't be canceled on the service level, only on the api 
level, is this intentional?
+
+* with the changes in ARM there is no way to restart gnunet with arm if some 
client is misbehaving
+
+* I'm often getting
+  May 09 09:21:49-194786 util-11121 WARNING `socket' failed at 
connection.c:892 with error: Too many open files

Copied: gnunet-java/src/org/gnunet/statistics/GetMessage.java (from rev 21245, 
gnunet-java/src/org/gnunet/statistics/RequestMessage.java)
===================================================================
--- gnunet-java/src/org/gnunet/statistics/GetMessage.java                       
        (rev 0)
+++ gnunet-java/src/org/gnunet/statistics/GetMessage.java       2012-05-09 
10:07:53 UTC (rev 21370)
@@ -0,0 +1,23 @@
+package org.gnunet.statistics;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Client --> Service
+ *
+ */
address@hidden(169)
+public class GetMessage implements GnunetMessage.Body {
+    /**
+     * Subsystem of interest, empty string for all subsystems.
+     */
+    @ZeroTerminatedString
+    public String subsystemName;
+    /**
+     * Statistics value name of interest, empty string for all values.
+     */
+    @ZeroTerminatedString
+    public String statisticsName;
+}

Copied: gnunet-java/src/org/gnunet/statistics/GetResponseEndMessage.java (from 
rev 21245, gnunet-java/src/org/gnunet/statistics/ResponseEndMessage.java)
===================================================================
--- gnunet-java/src/org/gnunet/statistics/GetResponseEndMessage.java            
                (rev 0)
+++ gnunet-java/src/org/gnunet/statistics/GetResponseEndMessage.java    
2012-05-09 10:07:53 UTC (rev 21370)
@@ -0,0 +1,16 @@
+package org.gnunet.statistics;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+* Created with IntelliJ IDEA.
+* User: dold
+* Date: 5/2/12
+* Time: 7:09 PM
+* To change this template use File | Settings | File Templates.
+*/
address@hidden(171)
+public class GetResponseEndMessage implements GnunetMessage.Body {
+    // empty
+}

Copied: gnunet-java/src/org/gnunet/statistics/GetResponseMessage.java (from rev 
21245, gnunet-java/src/org/gnunet/statistics/ResponseValueMessage.java)
===================================================================
--- gnunet-java/src/org/gnunet/statistics/GetResponseMessage.java               
                (rev 0)
+++ gnunet-java/src/org/gnunet/statistics/GetResponseMessage.java       
2012-05-09 10:07:53 UTC (rev 21370)
@@ -0,0 +1,29 @@
+package org.gnunet.statistics;
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * service --> client
+ *
+ *
+ */
address@hidden(170)
+public class GetResponseMessage implements GnunetMessage.Body {
+    /**
+     * Unique numerical identifier for the value (will
+     * not change during the same client-session).  Highest
+     * bit will be set for persistent values.
+     */
+    @UInt32
+    public long uid;
+    @UInt64
+    public long value;
+    @ZeroTerminatedString
+    public String subsystemName;
+    @ZeroTerminatedString
+    public String statisticName;
+}

Deleted: gnunet-java/src/org/gnunet/statistics/Request.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/Request.java  2012-05-09 09:59:06 UTC 
(rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/Request.java  2012-05-09 10:07:53 UTC 
(rev 21370)
@@ -1,29 +0,0 @@
-package org.gnunet.statistics;
-
-import org.gnunet.util.AbsoluteTime;
-import org.gnunet.util.MessageTransmitter;
-
-public interface Request extends MessageTransmitter {
-    /**
-     * cancel action specific to a certain type of request
-     */
-    void cancel();
-
-    /**
-     * Deadline for transmitting the request.
-     *
-     */
-    AbsoluteTime getDeadline();
-
-    /**
-     *
-     * @return true if the request should be kept after the destroy request
-     */
-    boolean onDestroy();
-
-    /**
-     *
-     * @return true if the request should be kept after the reconnect
-     */
-    boolean onReconnect();
-}

Deleted: gnunet-java/src/org/gnunet/statistics/RequestMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/RequestMessage.java   2012-05-09 
09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/RequestMessage.java   2012-05-09 
10:07:53 UTC (rev 21370)
@@ -1,13 +0,0 @@
-package org.gnunet.statistics;
-
-import org.gnunet.construct.UnionCase;
-import org.gnunet.construct.ZeroTerminatedString;
-import org.gnunet.util.GnunetMessage;
-
address@hidden(169)
-public class RequestMessage implements GnunetMessage.Body {
-    @ZeroTerminatedString
-    public String subsystemName;
-    @ZeroTerminatedString
-    public String statisticsName;
-}

Deleted: gnunet-java/src/org/gnunet/statistics/RequestQueue.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/RequestQueue.java     2012-05-09 
09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/RequestQueue.java     2012-05-09 
10:07:53 UTC (rev 21370)
@@ -1,129 +0,0 @@
-package org.gnunet.statistics;
-
-import org.gnunet.util.*;
-
-import java.util.LinkedList;
-
-/**
-* Created with IntelliJ IDEA.
-* User: dold
-* Date: 5/2/12
-* Time: 8:03 PM
-* To change this template use File | Settings | File Templates.
-*/
-public class RequestQueue {
-
-    /**
-     * Requests to be transmitted to the service.
-     */
-    private final LinkedList<Request> requests = new LinkedList<Request>();
-    /**
-     * The designated receiver for all messages.
-     */
-    private MessageReceiver receiver;
-
-    /**
-     * The active transmit request handle, if any.
-     */
-    private Cancelable currentTransmit;
-
-    /**
-     * Current receive handler.
-     */
-    private Cancelable currentReceive;
-
-    private boolean destroyRequested = false;
-    private final Client client;
-
-    public RequestQueue(Client client, MessageReceiver receiver) {
-        this.client = client;
-        this.receiver = receiver;
-    }
-
-    /**
-     * Handle next request.
-     */
-    private void handleNextRequest() {
-        if (currentTransmit != null) {
-            return;
-        }
-
-        final Request request = requests.poll();
-        if (request == null) {
-            if (destroyRequested) {
-                client.disconnect();
-            }
-            return;
-        }
-
-        currentTransmit = 
client.notifyTransmitReady(request.getDeadline().getRemaining(), true, 0, new 
MessageTransmitter() {
-            @Override
-            public void transmit(Connection.MessageSink sink) {
-                currentTransmit = null;
-
-                request.transmit(sink);
-
-                handleReceive();
-                handleNextRequest();
-            }
-
-            @Override
-            public void handleError() {
-                request.handleError();
-            }
-        });
-    }
-
-    private void handleReceive() {
-        if (currentReceive != null || destroyRequested) {
-            return;
-        }
-        currentReceive = client.receive(RelativeTime.FOREVER, new 
MessageReceiver() {
-            @Override
-            public void process(GnunetMessage.Body msg) {
-                currentReceive = null;
-
-                receiver.process(msg);
-
-                handleNextRequest();
-                handleReceive();
-            }
-
-            @Override
-            public void handleError() {
-                receiver.handleError();
-            }
-        });
-    }
-
-    public Cancelable add(final Request request) {
-        requests.add(request);
-        handleNextRequest();
-
-        return new Cancelable() {
-            @Override
-            public void cancel() {
-                RequestQueue.this.requests.remove(request);
-                request.cancel();
-            }
-        };
-    }
-
-    public void destroy() {
-        destroyRequested = true;
-
-        final LinkedList<Request> remove = new LinkedList<Request>();
-
-        for (Request r : requests) {
-            boolean keep = r.onDestroy();
-            if (!keep) {
-                remove.add(r);
-            }
-        }
-        requests.removeAll(remove);
-
-        if (requests.isEmpty()) {
-            client.disconnect();
-        }
-    }
-}

Deleted: gnunet-java/src/org/gnunet/statistics/ResponseEndMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/ResponseEndMessage.java       
2012-05-09 09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/ResponseEndMessage.java       
2012-05-09 10:07:53 UTC (rev 21370)
@@ -1,16 +0,0 @@
-package org.gnunet.statistics;
-
-import org.gnunet.construct.UnionCase;
-import org.gnunet.util.GnunetMessage;
-
-/**
-* Created with IntelliJ IDEA.
-* User: dold
-* Date: 5/2/12
-* Time: 7:09 PM
-* To change this template use File | Settings | File Templates.
-*/
address@hidden(171)
-public class ResponseEndMessage implements GnunetMessage.Body {
-    // empty
-}

Deleted: gnunet-java/src/org/gnunet/statistics/ResponseValueMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/ResponseValueMessage.java     
2012-05-09 09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/ResponseValueMessage.java     
2012-05-09 10:07:53 UTC (rev 21370)
@@ -1,31 +0,0 @@
-package org.gnunet.statistics;
-
-import org.gnunet.construct.UInt32;
-import org.gnunet.construct.UInt64;
-import org.gnunet.construct.UnionCase;
-import org.gnunet.construct.ZeroTerminatedString;
-import org.gnunet.util.GnunetMessage;
-
-/**
-* Created with IntelliJ IDEA.
-* User: dold
-* Date: 5/2/12
-* Time: 7:09 PM
-* To change this template use File | Settings | File Templates.
-*/
address@hidden(170)
-public class ResponseValueMessage implements GnunetMessage.Body {
-    /**
-     * Unique numerical identifier for the value (will
-     * not change during the same client-session).  Highest
-     * bit will be set for persistent values.
-     */
-    @UInt32
-    public long uid;
-    @UInt64
-    public long value;
-    @ZeroTerminatedString
-    public String subsystemName;
-    @ZeroTerminatedString
-    public String statisticName;
-}

Modified: gnunet-java/src/org/gnunet/statistics/SetMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/SetMessage.java       2012-05-09 
09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/SetMessage.java       2012-05-09 
10:07:53 UTC (rev 21370)
@@ -6,13 +6,10 @@
 import org.gnunet.construct.ZeroTerminatedString;
 import org.gnunet.util.GnunetMessage;
 
+
 /**
-* Created with IntelliJ IDEA.
-* User: dold
-* Date: 5/2/12
-* Time: 7:10 PM
-* To change this template use File | Settings | File Templates.
-*/
+ * Sent to the service by the client to set a statistics value.
+ */
 @UnionCase(168)
 public class SetMessage implements GnunetMessage.Body {
     @UInt32

Modified: gnunet-java/src/org/gnunet/statistics/Statistics.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/Statistics.java       2012-05-09 
09:59:06 UTC (rev 21369)
+++ gnunet-java/src/org/gnunet/statistics/Statistics.java       2012-05-09 
10:07:53 UTC (rev 21370)
@@ -6,12 +6,15 @@
 
 package org.gnunet.statistics;
 
+import org.gnunet.construct.UnionCase;
 import org.gnunet.util.*;
 import org.gnunet.util.getopt.Option;
 import org.gnunet.util.getopt.OptionAction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+
 /**
  * API for the gnunet statistics service.
  * <p/>
@@ -28,21 +31,36 @@
      */
     private static final RelativeTime SET_TIMEOUT = 
RelativeTime.SECOND.multiply(5);
 
-    private final static int SETFLAG_ABSOLUTE = 0;
     private final static int SETFLAG_RELATIVE = 1;
     private final static int SETFLAG_PERSIST = 2;
 
-    private Client client;
+    private final Client client;
 
-    private RequestQueue requestQueue;
+    private final RequestQueue requestQueue;
 
+    /**
+     * Callback for the current get request. Only one get request ist allowed 
at a time.
+     */
     private StatisticsReceiver currentGetReceiver;
+    /**
+     * Success/Error continuation for the current get request.
+     */
     private Continuation currentGetContinuation;
 
+    ArrayList<StatisticsWatchRequest> watchRequests = new 
ArrayList<StatisticsWatchRequest>();
+
+    public interface StatisticsReceiver {
+        public void onReceive(String subsystem, String name, long value);
+    }
+
+    public interface Continuation {
+        public void cont(boolean success);
+    }
+
     /**
      * A request to the statistics service.
      */
-    private abstract class StatisticsRequest implements Request {
+    private abstract class StatisticsRequest extends RequestQueue.Request {
         public String name;
         public String subsystem;
         public AbsoluteTime deadline;
@@ -50,53 +68,37 @@
 
     private class StatisticsGetRequest extends StatisticsRequest {
         public StatisticsReceiver receiver;
-        public Cancelable receiveHandle;
 
-        @Override
-        public void cancel() {
+        public void onCancel(boolean alreadyTransmitted) {
             currentGetReceiver = null;
         }
 
-        @Override
+        public AbsoluteTime getDeadline() {
+            return deadline;
+        }
+
+
         public void transmit(Connection.MessageSink sink) {
-            RequestMessage rm = new RequestMessage();
+            GetMessage rm = new GetMessage();
             rm.statisticsName = name;
             rm.subsystemName = subsystem;
 
             sink.send(rm);
         }
 
-        @Override
-        public void handleError() {
-            throw new RuntimeException("unexpected transmit error");
-        }
-
-        @Override
-        public AbsoluteTime getDeadline() {
-            return deadline;
-        }
-
-        @Override
-        public boolean onDestroy() {
-            return false;
-        }
-
-        @Override
         public boolean onReconnect() {
             return true;
         }
     }
 
-    private class StatisticsPutRequest extends StatisticsRequest implements 
Cancelable {
+    private class StatisticsSetRequest extends StatisticsRequest {
         public long value;
         public int flags;
 
-        @Override
-        public void cancel() {
-            // do nothing
+        public AbsoluteTime getDeadline() {
+            return SET_TIMEOUT.toAbsolute();
         }
 
-        @Override
         public void transmit(Connection.MessageSink sink) {
             SetMessage sm = new SetMessage();
             sm.statisticName = name;
@@ -106,52 +108,91 @@
             sink.send(sm);
         }
 
-        @Override
-        public void handleError() {
-            throw new RuntimeException("unexpected transmit error");
+        public boolean onDestroy() {
+            // keep the request
+            return true;
         }
 
-        @Override
+        public boolean onReconnect() {
+            // just keep the request on reconnect
+            return true;
+        }
+    }
+
+
+    private class StatisticsWatchRequest extends StatisticsRequest {
+        public StatisticsReceiver receiver;
+
         public AbsoluteTime getDeadline() {
-            return deadline;
+            return AbsoluteTime.FOREVER;
         }
 
-        @Override
-        public boolean onDestroy() {
-            return true;
+        public void onCancel(boolean alreadyTransmitted) {
+            System.out.println("already transmitted: " + alreadyTransmitted);
+            if (alreadyTransmitted) {
+                watchRequests.set(watchRequests.indexOf(this), null);
+            }
+
         }
 
-        @Override
         public boolean onReconnect() {
+            System.out.println("onReconnect()!");
+
+            // do this because we'll probably get new WatchIDs for every watch 
request on reconnect
+            if (watchRequests.contains(this)) {
+                watchRequests.clear();
+            }
+
+            watchRequests.add(this);
+
             return true;
         }
+
+        public void transmit(Connection.MessageSink sink) {
+            WatchMessage wm = new WatchMessage();
+            wm.statisticsName = name;
+            wm.subsystemName = subsystem;
+            sink.send(wm);
+
+
+            watchRequests.add(this);
+        }
     }
 
 
     public class StatisticsMessageReceiver extends RunaboutMessageReceiver {
-        public void visit(ResponseValueMessage m) {
+        public void visit(GetResponseMessage m) {
             currentGetReceiver.onReceive(m.subsystemName, m.statisticName, 
m.value);
         }
 
-        public void visit(ResponseEndMessage m) {
+        public void visit(GetResponseEndMessage m) {
             if (currentGetContinuation != null) {
-                currentGetContinuation.onDone();
+                currentGetContinuation.cont(true);
             }
         }
 
+        public void visit(TESTMessage m) {
+            client.disconnect();
+        }
+
+        public void visit(WatchResponseMessage wrm) {
+            if (watchRequests.size() <= wrm.wid) {
+                logger.warn("statistics service got confused with watch 
request");
+                return;
+            }
+            StatisticsWatchRequest wr = watchRequests.get(wrm.wid);
+            // request may have been canceled by the api (but not by the 
server)
+            if (wr != null) {
+                wr.receiver.onReceive(wr.subsystem, wr.name, wrm.value);
+            }
+        }
+
         @Override
         public void handleError() {
+            requestQueue.reconnect();
         }
     }
 
-    public interface StatisticsReceiver {
-        public void onReceive(String subsystem, String name, long value);
-    }
-
-    public interface Continuation {
-        public void onDone();
-    }
-
     public Statistics(Configuration cfg) {
         client = new Client("statistics", cfg);
         requestQueue = new RequestQueue(this.client, new 
StatisticsMessageReceiver());
@@ -160,11 +201,10 @@
     /**
      * Retrieve values from statistics.
      *
-     *
-     * @param timeout   time after we give up and call receiver.onTimeout
-     * @param subsystem the subsystem of interest
-     * @param name      name of the statistics value belongs to
-     * @param receiver  callback
+     * @param timeout      time after we give up and call receiver.onTimeout
+     * @param subsystem    the subsystem of interest
+     * @param name         name of the statistics value belongs to
+     * @param receiver     callback
      * @param continuation
      * @return handle to cancel the request
      */
@@ -179,8 +219,8 @@
 
         final StatisticsGetRequest getRequest = new StatisticsGetRequest();
         getRequest.deadline = timeout.toAbsolute();
-        getRequest.name = name == null ? "" : name;
-        getRequest.subsystem = subsystem == null ? "" : name;
+        getRequest.name = (name == null) ? "" : name;
+        getRequest.subsystem = (subsystem == null) ? "" : subsystem;
         getRequest.receiver = receiver;
 
         return requestQueue.add(getRequest);
@@ -196,30 +236,59 @@
      * @return a handle to cancel the request
      */
     public Cancelable set(final String subsystem, final String name, final 
long value, boolean persist) {
-        StatisticsPutRequest putRequest = new StatisticsPutRequest();
-        putRequest.deadline = SET_TIMEOUT.toAbsolute();
-        putRequest.subsystem = subsystem;
-        putRequest.name = name;
-        putRequest.value = value;
-        putRequest.flags = persist ? SETFLAG_PERSIST : 0;
+        StatisticsSetRequest setRequest = new StatisticsSetRequest();
+        setRequest.deadline = SET_TIMEOUT.toAbsolute();
+        setRequest.subsystem = subsystem;
+        setRequest.name = name;
+        setRequest.value = value;
+        setRequest.flags = persist ? SETFLAG_PERSIST : 0;
 
-        return requestQueue.add(putRequest);
+        return requestQueue.add(setRequest);
     }
 
+    /**
+     * Changes a statistics value asynchronously.
+     *
+     * @param name    name of the entry
+     * @param delta   relative difference to the old value
+     * @param persist keep value even if the statistics service restarts
+     * @return a handle to cancel the request
+     */
+    public Cancelable update(final String subsystem, final String name, final 
long delta, boolean persist) {
+        StatisticsSetRequest setRequest = new StatisticsSetRequest();
+        setRequest.deadline = SET_TIMEOUT.toAbsolute();
+        setRequest.subsystem = subsystem;
+        setRequest.name = name;
+        setRequest.value = delta;
+        setRequest.flags = (persist ? SETFLAG_PERSIST : 0) | SETFLAG_RELATIVE;
+
+        return requestQueue.add(setRequest);
+    }
+
     public Cancelable watch(final String subsystem, final String name, 
StatisticsReceiver receiver) {
-        return null;
+        StatisticsWatchRequest watchRequest = new StatisticsWatchRequest();
+        watchRequest.deadline = AbsoluteTime.FOREVER;
+        watchRequest.name = name;
+        watchRequest.subsystem = subsystem;
+        watchRequest.receiver = receiver;
+
+        // even after the request has been sent, we want to keep it
+        // (e.g. for retransmission on reconnect)
+        return requestQueue.addPersistent(watchRequest);
     }
 
     /**
      * Destroy handle to the statistics service. Always finishes writing 
pending values.
      */
     public void destroy() {
+        // the request queue handles the destruction, maybe we still have 
important messages pending etc.
+        requestQueue.add(new TESTRequest());
         requestQueue.destroy();
     }
 
 
     /**
-     * Statistics utility entry point
+     * Statistics command line utility entry point
      *
      * @param args command line arguments
      */
@@ -232,6 +301,12 @@
                     description = "set a value")
             boolean test;
             @Option(
+                    shortname = "w",
+                    longname = "watch",
+                    action = OptionAction.SET,
+                    description = "set a value")
+            boolean watch;
+            @Option(
                     shortname = "n",
                     longname = "name",
                     action = OptionAction.STORE_STRING,
@@ -266,19 +341,31 @@
                     statistics.destroy();
                 } else {
                     if (unprocessedArgs.length == 0) {
-                        statistics.get(RelativeTime.SECOND, subsystemName, 
statisticsName,
-                                new StatisticsReceiver() {
-                                    @Override
-                                    public void onReceive(String subsystem, 
String name, long value) {
-                                        System.out.println(subsystem + "(" + 
name + ") = " + value);
+                        if (watch) {
+                            final Cancelable c = 
statistics.watch(subsystemName, statisticsName,
+                                    new StatisticsReceiver() {
+                                        @Override
+                                        public void onReceive(String 
subsystem, String name, long value) {
+                                            System.out.println(subsystem + "(" 
+ name + ") = " + value);
+                                        }
                                     }
-                                },
-                                new Continuation() {
-                                    @Override
-                                    public void onDone() {
-                                        statistics.destroy();
+                            );
+                        } else {
+                            statistics.get(RelativeTime.SECOND, subsystemName, 
statisticsName,
+                                    new StatisticsReceiver() {
+                                        @Override
+                                        public void onReceive(String 
subsystem, String name, long value) {
+                                            System.out.println(subsystem + "(" 
+ name + ") = " + value);
+                                        }
+                                    },
+                                    new Continuation() {
+                                        @Override
+                                        public void cont(boolean success) {
+                                            statistics.destroy();
+                                        }
                                     }
-                                });
+                            );
+                        }
                     } else {
                         System.err.println("dumping statistics does not take 
any positional parameters");
                     }
@@ -286,4 +373,26 @@
             }
         }.start();
     }
+
+
+    @UnionCase(1)
+    public static class TESTMessage implements GnunetMessage.Body {
+        // empty
+    }
+
+    private class TESTRequest extends RequestQueue.Request {
+        public boolean onDestroy() {
+            // keep on destroy
+            return true;
+        }
+
+        public AbsoluteTime getDeadline() {
+            return SET_TIMEOUT.toAbsolute();
+        }
+
+        public void transmit(Connection.MessageSink sink) {
+            sink.send(new TESTMessage());
+            // todo: disconnect when not receiving the TEST message back after 
timeout
+        }
+    }
 }

Added: gnunet-java/src/org/gnunet/statistics/WatchMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/WatchMessage.java                     
        (rev 0)
+++ gnunet-java/src/org/gnunet/statistics/WatchMessage.java     2012-05-09 
10:07:53 UTC (rev 21370)
@@ -0,0 +1,20 @@
+package org.gnunet.statistics;
+
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(172)
+public class WatchMessage implements GnunetMessage.Body {
+    /**
+     * Subsystem of interest, may not be empty.
+     */
+    @ZeroTerminatedString
+    public String subsystemName;
+    /**
+     * Statistics value name of interest, may not be empty.
+     */
+    @ZeroTerminatedString
+    public String statisticsName;
+}

Added: gnunet-java/src/org/gnunet/statistics/WatchResponseMessage.java
===================================================================
--- gnunet-java/src/org/gnunet/statistics/WatchResponseMessage.java             
                (rev 0)
+++ gnunet-java/src/org/gnunet/statistics/WatchResponseMessage.java     
2012-05-09 10:07:53 UTC (rev 21370)
@@ -0,0 +1,19 @@
+package org.gnunet.statistics;
+
+
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UInt64;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(173)
+public class WatchResponseMessage implements GnunetMessage.Body {
+    @UInt32
+    public int flags;
+    @UInt32
+    public int wid;
+    @UInt32
+    public int reserved ;
+    @UInt64
+    public long value;
+}

Modified: gnunet-java/src/org/gnunet/util/AbsoluteTime.java
===================================================================
--- gnunet-java/src/org/gnunet/util/AbsoluteTime.java   2012-05-09 09:59:06 UTC 
(rev 21369)
+++ gnunet-java/src/org/gnunet/util/AbsoluteTime.java   2012-05-09 10:07:53 UTC 
(rev 21370)
@@ -62,7 +62,7 @@
             return AbsoluteTime.FOREVER;
         }
         if (abs_value + duration.getMilliseconds() < abs_value) {
-            logger.warn("time overflow");
+            Thread.dumpStack();
             return AbsoluteTime.FOREVER;
         }
         return new AbsoluteTime(abs_value + duration.getMilliseconds());
@@ -177,6 +177,9 @@
      * @return this - now
      */
     public RelativeTime getRemaining() {
+        if (abs_value == Long.MAX_VALUE) {
+            return RelativeTime.FOREVER;
+        }
         return getDifference(AbsoluteTime.now());
     }
 

Modified: gnunet-java/src/org/gnunet/util/Connection.java
===================================================================
--- gnunet-java/src/org/gnunet/util/Connection.java     2012-05-09 09:59:06 UTC 
(rev 21369)
+++ gnunet-java/src/org/gnunet/util/Connection.java     2012-05-09 10:07:53 UTC 
(rev 21370)
@@ -73,6 +73,7 @@
      * transmitting larger messages.
      */
     private ByteBuffer transmitBuffer = 
ByteBuffer.allocate(GnunetMessage.Header.SIZE);
+    private boolean disconnected=false;
 
 
     private class AddressProbe {
@@ -148,6 +149,8 @@
                         receiver.handleError();
                         return;
                     }
+                    // todo: this was most probably wrong, revise it!
+                    /*
                     if (n == 0) {
                         logger.info("service gracefully disconnected us");
                         connectionChannel.close();
@@ -155,6 +158,7 @@
                         receiver.handleError();
                         return;
                     }
+                    */
                     logger.debug(String.format("connectionChannel read %s 
bytes", n));
                 } catch (IOException e) {
                     logger.error("read failed with exception:", e);
@@ -502,6 +506,11 @@
      * Disconnect. Cancel pending receive/transmit requests.
      */
     public void disconnect() {
+        if (disconnected) {
+            logger.warn("disconnect called twice");
+        }
+        disconnected = true;
+
         logger.debug(""+this+".disconnect()");
         if (nextTransmitHelper != null) {
             nextTransmitHelper.cancel();

Copied: gnunet-java/src/org/gnunet/util/RequestQueue.java (from rev 21245, 
gnunet-java/src/org/gnunet/statistics/RequestQueue.java)
===================================================================
--- gnunet-java/src/org/gnunet/util/RequestQueue.java                           
(rev 0)
+++ gnunet-java/src/org/gnunet/util/RequestQueue.java   2012-05-09 10:07:53 UTC 
(rev 21370)
@@ -0,0 +1,218 @@
+package org.gnunet.util;
+
+import java.util.LinkedList;
+
+/**
+ * Generic queues for Requests to be sent to the service.
+ */
+public class RequestQueue {
+
+    /**
+     * Requests to be transmitted to the service.
+     */
+    private final LinkedList<Request> requestsAwaitingTransmit = new 
LinkedList<Request>();
+
+
+    /**
+     * Persistent requests. Will be informed about reconnect / destroy events 
even
+     * if already transmitted. Have to be canceled manually.
+     */
+    private final LinkedList<Request> persistentRequests = new 
LinkedList<Request>();
+
+
+    /**
+     * List of all requests from requestAwaitingTransmit and 
persistentRequest, containing no duplicates.
+     */
+    private final LinkedList<Request> allRequests = new LinkedList<Request>();
+
+    /**
+     * The designated receiver for all messages.
+     */
+    private MessageReceiver receiver;
+
+    /**
+     * The active transmit request handle, if any.
+     */
+    private Cancelable currentTransmit;
+
+    /**
+     * Current receive handler.
+     */
+    private Cancelable currentReceive;
+
+    private boolean destroyRequested = false;
+    private final Client client;
+
+
+    public static abstract class Request {
+
+        public boolean onTransmitTimeout() {
+            // per default, just drop the message on timeout!
+            return false;
+        }
+
+        /**
+         * @return true if the request should be kept after the destroy request
+         */
+        public boolean onDestroy() {
+            // per default, do not keep on destroy
+            return false;
+        }
+
+        /**
+         * @return true if the request should be kept after the reconnect
+         */
+        public boolean onReconnect() {
+            // per default, do not keep on reconnect
+            return false;
+        }
+
+        /**
+         * @param alreadyTransmitted true if message has already been sent 
over the network
+         */
+        public void onCancel(boolean alreadyTransmitted) {
+            // do nothing
+        }
+
+        public abstract AbsoluteTime getDeadline();
+
+        public abstract void transmit(Connection.MessageSink sink);
+    }
+
+    public RequestQueue(Client client, MessageReceiver receiver) {
+        this.client = client;
+        this.receiver = receiver;
+    }
+
+    /**
+     * Handle next request.
+     */
+    private void handleNextTransmit() {
+        if (currentTransmit != null) {
+            return;
+        }
+
+        final Request request = requestsAwaitingTransmit.poll();
+        if (request == null) {
+            handleReceive();
+            return;
+        }
+
+        AbsoluteTime deadline = request.getDeadline();
+        if (deadline == null) {
+            throw new AssertionError("getDeadline() must return a non-null 
AbsoluteTime");
+        }
+
+        currentTransmit = client.notifyTransmitReady(deadline.getRemaining(), 
true, 0, new MessageTransmitter() {
+            @Override
+            public void transmit(Connection.MessageSink sink) {
+                currentTransmit = null;
+
+                request.transmit(sink);
+
+                handleReceive();
+                handleNextTransmit();
+            }
+
+            @Override
+            public void handleError() {
+                throw new AssertionError("not implemented");
+            }
+        });
+    }
+
+    private void handleReceive() {
+        if (currentReceive != null || destroyRequested) {
+            return;
+        }
+        currentReceive = client.receive(RelativeTime.FOREVER, new 
MessageReceiver() {
+            @Override
+            public void process(GnunetMessage.Body msg) {
+                currentReceive = null;
+
+                receiver.process(msg);
+
+                handleNextTransmit();
+                handleReceive();
+            }
+
+            @Override
+            public void handleError() {
+                receiver.handleError();
+            }
+        });
+    }
+
+    public Cancelable add(final Request request) {
+        allRequests.add(request);
+        requestsAwaitingTransmit.add(request);
+        handleNextTransmit();
+
+        return new Cancelable() {
+            @Override
+            public void cancel() {
+                RequestQueue.this.requestsAwaitingTransmit.remove(request);
+                RequestQueue.this.persistentRequests.remove(request);
+                RequestQueue.this.allRequests.remove(request);
+                request.onCancel(!requestsAwaitingTransmit.contains(request));
+            }
+        };
+    }
+
+
+    public Cancelable addPersistent(final Request request) {
+        persistentRequests.add(request);
+        return add(request);
+    }
+
+    public void reconnect() {
+        client.reconnect();
+        currentReceive = null;
+        currentTransmit = null;
+
+        final LinkedList<Request> remove = new LinkedList<Request>();
+
+
+        for (Request r : allRequests) {
+            boolean keep = r.onReconnect();
+            if (!keep) {
+                remove.add(r);
+            } else {
+                // retransmit an apparently persistent request.
+                if (!requestsAwaitingTransmit.contains(r)) {
+                    requestsAwaitingTransmit.add(r);
+                }
+            }
+        }
+        requestsAwaitingTransmit.removeAll(remove);
+        persistentRequests.removeAll(remove);
+        allRequests.removeAll(remove);
+
+        // only transmit, receive should only be called after the first 
transmit
+        handleNextTransmit();
+    }
+
+    public void destroy() {
+        destroyRequested = true;
+
+        final LinkedList<Request> remove = new LinkedList<Request>();
+
+        for (Request r : allRequests) {
+            boolean keep = r.onDestroy();
+            if (!keep) {
+                remove.add(r);
+            } else {
+                // retransmit an apparently persistent request.
+                if (!requestsAwaitingTransmit.contains(r)) {
+                    requestsAwaitingTransmit.add(r);
+                }
+            }
+        }
+        requestsAwaitingTransmit.removeAll(remove);
+        persistentRequests.removeAll(remove);
+        allRequests.removeAll(remove);
+
+        handleNextTransmit();
+        handleReceive();
+    }
+}

Modified: gnunet-java/src/org/gnunet/util/Scheduler.java
===================================================================
--- gnunet-java/src/org/gnunet/util/Scheduler.java      2012-05-09 09:59:06 UTC 
(rev 21369)
+++ gnunet-java/src/org/gnunet/util/Scheduler.java      2012-05-09 10:07:53 UTC 
(rev 21370)
@@ -149,6 +149,9 @@
         }
 
         private void addChannelEvent(SelectableChannel channel, int eventType) 
{
+            if (channel == null) {
+                throw new AssertionError("channel must be non-null");
+            }
             if (eventChannels == null) {
                 eventChannels = new ArrayList<SelectableChannel>();
                 eventTypes = new ArrayList<Integer>();
@@ -215,6 +218,7 @@
         }
 
         private void deregister() {
+            logger.debug("deregistering");
             if (eventChannels == null) {
                 return;
             }
@@ -359,7 +363,7 @@
     private static RelativeTime handleTimeouts() {
         RelativeTime timeout = RelativeTime.FOREVER;
 
-        // check if any timeouts occured
+        // check if any timeouts occurred
         while (true) {
             TaskConfiguration t = pending.peek();
             if (t == null) {
@@ -370,7 +374,6 @@
                 t.deregister();
                 t.ctx.reasons = EnumSet.of(Reason.TIMEOUT);
                 queueReady(t);
-                pending.remove(t);
             } else {
                 timeout = remaining;
                 break;
@@ -406,10 +409,18 @@
         for (SelectionKey sk : selector.selectedKeys()) {
             TaskConfiguration[] subscribers = (TaskConfiguration[]) 
sk.attachment();
 
-            if (sk.isReadable()) addSubscriberTask(executableTasks, 
subscribers, EVENT_READ);
-            if (sk.isWritable()) addSubscriberTask(executableTasks, 
subscribers, EVENT_WRITE);
-            if (sk.isAcceptable()) addSubscriberTask(executableTasks, 
subscribers, EVENT_ACCEPT);
-            if (sk.isConnectable()) addSubscriberTask(executableTasks, 
subscribers, EVENT_CONNECT);
+            if (sk.isReadable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_READ);
+            }
+            if (sk.isWritable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_WRITE);
+            }
+            if (sk.isAcceptable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_ACCEPT);
+            }
+            if (sk.isConnectable()) {
+                addSubscriberTask(executableTasks, subscribers, EVENT_CONNECT);
+            }
 
         }
         for (TaskConfiguration tt : executableTasks) {
@@ -432,6 +443,9 @@
         // the gnunet main loop
         while (checkLiveness()) {
             RelativeTime nextTimeout = handleTimeouts();
+            if (nextTimeout.getMilliseconds() < 0) {
+                logger.warn("negative timeout for select");
+            }
 
             // don't select if there are no tasks; we are done!
             if (readyCount == 0 && pending.isEmpty()) {

Modified: gnunet-java/src/org/grothoff/Runabout.java
===================================================================
--- gnunet-java/src/org/grothoff/Runabout.java  2012-05-09 09:59:06 UTC (rev 
21369)
+++ gnunet-java/src/org/grothoff/Runabout.java  2012-05-09 10:07:53 UTC (rev 
21370)
@@ -20,6 +20,7 @@
 
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.HashMap;
@@ -28,29 +29,29 @@
  * Runabout is a fast implementation of the Walkabout which is a variant of the
  * Visitor Pattern that does not require an accept method and uses reflection
  * instead.
- * <p>
+ * <p/>
  * An instance of Runabout is able to walk over an arbitrary object graph using
  * visit methods which take arguments of the specific type of the object to
  * visit. For each node in the object graph the Runabout invokes the most
  * appropriate visit method.
- * <p>
+ * <p/>
  * Using the Runabout typically involves subclassing Runabout and adding a
  * couple of visit methods. The Runabout provides a 'visitAppropriate' method
  * which will invoke the most appropriate visit method of the current Runabout
  * instance. If no visit method is applicable, visitAppropriate calls
- * visitDefault() which, if not overriden, throws an exception.
- * <p>
+ * visitDefault() which, if not overridden, throws an exception.
+ * <p/>
  * The elements of the object graph typically extend the Element class, which
  * provides a generic way to quickly invoke the Runabout on all the fields of
  * the Element.
- * <p>
+ * <p/>
  * Note that the Runabout uses dynamic code generation and dynamic loading in
  * order to be quickly able to invoke the appropriate visit methods. To make 
the
  * dynamic code generation fast, the code inlines parts of Java class-files in
  * binary form (ugly!).<br>
  * A per-thread Cache is used to speed-up the creation of the Runabout by
  * caching reflection, code creation and dynamic loading operations.
- * <p>
+ * <p/>
  * <bf>Restrictions:</bf> Java semantics require:
  * <ul>
  * <li>all subclasses must be public, sadly this also means that <strong>you
@@ -59,10 +60,10 @@
  * <li>all visit methods must be public (!)</li>
  * </ul>
  * Otherwise the visitor will die with an IllegalAccessError during execution.
- * <p>
+ * <p/>
  *
+ * @author Christian Grothoff
  * @version 5.0
- * @author Christian Grothoff
  */
 public class Runabout {
 
@@ -89,9 +90,15 @@
     private final Code noCode = new NoCode();
 
     /**
+     * Set when the subclass of the runabout is not public.
+     */
+    private final boolean isPublic;
+
+    /**
      * Create a Runabout.
      */
     public Runabout() {
+        this.isPublic = Modifier.isPublic(getClass().getModifiers());
         map_ = cache_.get(this);
     }
 
@@ -177,7 +184,7 @@
      * Find the appropriate Code to call in the map. If no code is found, 
return
      * null.
      *
-     * @param c the class for which to find the code
+     * @param c  the class for which to find the code
      * @param cl the class where to start looking from
      * @return the code to run, or null if no code was found
      */
@@ -226,19 +233,42 @@
         me = this.getClass();
         while (me != null) {
             Method[] methods = me.getDeclaredMethods();
-            for (Method m : methods) {
+            for (final Method m : methods) {
                 if ((m.getName().equals("visit"))
                         && (!Modifier.isStatic(m.getModifiers()))) {
                     Class[] args = m.getParameterTypes();
                     if (args.length != 1)
                         throw new RunaboutException("Invalid number of 
arguments for Runabout in method "
                                 + m);
-                    Class viC = args[0];
+                    final Class viC = args[0];
                     if (result.get(viC) != null)
                         continue;
-                    Code co = makeCode(viC);
-                    if (co == null)
-                        throw new RunaboutException("Could not create/load 
dynamic code!");
+                    Code co;
+                    if (isPublic) {
+                        // invoke the visitor with generated code
+                        co = makeCode(viC);
+                        if (co == null) {
+                            throw new RunaboutException("Could not create/load 
dynamic code!");
+                        }
+                    } else {
+                        // invoke the visitor with an anonymous inner class,
+                        // allows for the runabout to be public as the method 
made accessible
+                        // by the Method instance.
+                        // For Java 7+ the performance of this could be 
improved by using a MethodHandle
+                        co = new Code() {
+                            @Override
+                            public void visit(Runabout r, Object o) {
+                                try {
+                                    m.invoke(r, o);
+                                } catch (IllegalAccessException e) {
+                                    e.printStackTrace();
+                                } catch (InvocationTargetException e) {
+                                    e.printStackTrace();
+                                }
+                            }
+                        };
+                    }
+
                     result.put(viC, co);
 
                 }
@@ -255,11 +285,11 @@
      */
     private Code makeCode(Class c) {
         byte[] myName // Lovm/util/RunaboutExample; substitute
-        = canonicalName(getClass(), false);
+                = canonicalName(getClass(), false);
         final int myNameLen = myName.length;
         final int myNameLenM2 = myNameLen - 2;
         byte[] cName // Ljava/lang/String; substitute
-        = canonicalName(c, false);
+                = canonicalName(c, false);
         byte[] cNameCast = canonicalName(c, true);
         final int cNameLen = cName.length;
         final int cNameLenCast = cNameCast.length - 2;
@@ -290,11 +320,11 @@
         code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 1] = 
(byte) ')';
         code[120 + myNameLenM2 + cNameLenCast + 250 - 164 + cNameLen + 2] = 
(byte) 'V';
         System.arraycopy(genCodeTemplate,
-                         271,
-                         code,
-                         120 + myNameLenM2 + cNameLenCast + 250 - 164
-                                 + cNameLen + 3,
-                         genCodeTemplate.length - 271);
+                271,
+                code,
+                120 + myNameLenM2 + cNameLenCast + 250 - 164
+                        + cNameLen + 3,
+                genCodeTemplate.length - 271);
         return cache_.loadCode(code, 120 + myNameLenM2 + cNameLenCast + 192
                 - 164);
     }
@@ -367,7 +397,7 @@
         Cache() {
             loader_ = new ClassLoader() {
                 public Class<?> loadClass(String name)
-                    throws ClassNotFoundException {
+                        throws ClassNotFoundException {
                     //noinspection StringEquality
                     if (name == "Code") // == works here, as both strings are 
guaranteed to be interned
                         return defineClass(null, code, 0, code.length);
@@ -387,14 +417,14 @@
          * classname.
          *
          * @param byteCode the bytecode of the class which must describe a 
class
-         *        of type 'Code'. The class must contain a sequence XXXXXXXX at
-         *        offset xIdx where the classname is to be patched
-         * @param xIdx the index of the XXXXXXXX sequence
+         *                 of type 'Code'. The class must contain a sequence 
XXXXXXXX at
+         *                 offset xIdx where the classname is to be patched
+         * @param xIdx     the index of the XXXXXXXX sequence
          * @return an instance of the loaded class, null on error
          * @throws ArrayIndexOutOfBoundsException if more than 62<sup>8</sup>
-         *         classes are loaded :-)
-         * @throws RunaboutException if there are problems with dynamic loading
-         *         of the byteCode
+         *                                        classes are loaded :-)
+         * @throws RunaboutException              if there are problems with 
dynamic loading
+         *                                        of the byteCode
          */
         Code loadCode(byte[] byteCode, int xIdx) {
             boolean overflow = true;
@@ -415,7 +445,7 @@
             System.arraycopy(lastName_, 0, byteCode, xIdx, 8);
             code = byteCode;
 
-            Code co = null;
+            Code co;
             try {
                 co = (Code) loader_.loadClass("Code").newInstance();
             } catch (InstantiationException ie) {
@@ -427,6 +457,7 @@
             } catch (ClassFormatError cfe) {
                 throw new RunaboutException(cfe.toString());
             } catch (IllegalAccessException iae) {
+                throw new RunaboutException(iae.toString());
             }
             code = null; // help GC
             return co;
@@ -501,7 +532,7 @@
      * not to include any debugging information. This is the generated class
      * file.
      */
-    private final static byte genCodeTemplate[] = { -54, -2, -70, -66, 0, 0, 0,
+    private final static byte genCodeTemplate[] = {-54, -2, -70, -66, 0, 0, 0,
             49, 0, 22, 10, 0, 6, 0, 12, 7, 0, 13, 7, 0, 14, 10, 0, 2, 0, 15, 7,
             0, 16, 7, 0, 18, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40,
             41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 5, 118, 105, 115, 105,
@@ -525,6 +556,15 @@
             1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 0, 0, 1, 0, 10, 0, 11,
             0, 1, 0, 9, 0, 0, 0, 24, 0, 2, 0, 3, 0, 0, 0, 12, 43, -64, 0, 2,
             44, -64, 0, 3, -74, 0, 4, -79, 0, 0, 0, 0, 0, 1, 0, 19, 0, 0, 0,
-            10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9 }; // GenCodeXXXXXXXX.class
+            10, 0, 1, 0, 6, 0, 17, 0, 9, 4, 9}; // GenCodeXXXXXXXX.class
 
+    public static void main(String[] args) {
+        Runabout r = new Runabout() {
+            public void visit(String s) {
+                System.out.println("hi!!");
+            }
+        };
+        r.visitAppropriate("foo");
+    }
+
 } // end of Runabout

Added: gnunet-java/test/org/grothoff/RunaboutBenchmark.java
===================================================================
--- gnunet-java/test/org/grothoff/RunaboutBenchmark.java                        
        (rev 0)
+++ gnunet-java/test/org/grothoff/RunaboutBenchmark.java        2012-05-09 
10:07:53 UTC (rev 21370)
@@ -0,0 +1,63 @@
+package org.grothoff;
+
+
+public class RunaboutBenchmark {
+
+    public static class MyRunabout extends Runabout {
+        public void visit(String s) {
+
+        }
+        public void visit(Integer i) {
+
+        }
+    }
+
+
+    public static void main(String[] args) {
+        final int runs = 50000000;
+
+        Runabout r = new MyRunabout();
+
+        long start = System.currentTimeMillis();
+
+        Integer integer = 42;
+        for (int i = 0; i < runs; ++i) {
+            r.visitAppropriate("foo");
+            r.visitAppropriate(integer);
+        }
+
+        long end = System.currentTimeMillis();
+
+        long duration1 = end - start;
+
+
+
+        Runabout r2 = new Runabout() {
+            public void visit(String s) {
+
+            }
+            public void visit(Integer i) {
+
+            }
+        };
+
+
+        start = System.currentTimeMillis();
+
+        for (int i = 0; i < runs; ++i) {
+            r2.visitAppropriate("foo");
+            r2.visitAppropriate(integer);
+        }
+
+        end = System.currentTimeMillis();
+
+        long duration2 = end - start;
+
+
+        System.out.println("Runs: " + runs);
+        System.out.println("public: " + duration1);
+        System.out.println("AIC: " + duration2);
+        System.out.println("Overhead: " + duration2 / (double) duration1);
+
+    }
+}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]