changeset 331:1c029a6b4a20

Runners: reconsider threading in BinaryLink and TestExecutor, improve test error matching.
author shade
date Fri, 21 Oct 2016 19:56:25 +0200
parents 07d859cba98d
children acec859bce0e
files jcstress-core/src/main/java/org/openjdk/jcstress/ForkedMain.java jcstress-core/src/main/java/org/openjdk/jcstress/Options.java jcstress-core/src/main/java/org/openjdk/jcstress/TestExecutor.java jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkClient.java jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkServer.java jcstress-core/src/main/java/org/openjdk/jcstress/link/OkResponseFrame.java jcstress-core/src/main/java/org/openjdk/jcstress/link/ServerListener.java jcstress-core/src/main/java/org/openjdk/jcstress/link/WTFWasThatFrame.java
diffstat 8 files changed, 272 insertions(+), 228 deletions(-) [+]
line wrap: on
line diff
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/ForkedMain.java	Fri Oct 21 16:56:01 2016 +0200
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/ForkedMain.java	Fri Oct 21 19:56:25 2016 +0200
@@ -53,8 +53,6 @@
         String token = args[2];
 
         BinaryLinkClient link = new BinaryLinkClient(host, port);
-        Runtime.getRuntime().addShutdownHook(new CloseBinaryLinkHook(link));
-
         EmbeddedExecutor executor = new EmbeddedExecutor(result -> link.addResult(token, result));
 
         TestConfig config;
@@ -63,27 +61,6 @@
         }
     }
 
-    /**
-     * Shutdown hook dedicated to properly closing the binary link.
-     * Can be used for regular or exceptional shutdown.
-     */
-    private static class CloseBinaryLinkHook extends Thread {
-        private final BinaryLinkClient link;
 
-        CloseBinaryLinkHook(BinaryLinkClient link) {
-            this.link = link;
-        }
-
-        @Override
-        public void run() {
-            try {
-                link.close();
-            } catch (IOException e) {
-                // IOException on closing the link means we can only communicate the
-                // failure via the exit code.
-                Runtime.getRuntime().halt(1);
-            }
-        }
-    }
 
 }
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/Options.java	Fri Oct 21 16:56:01 2016 +0200
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/Options.java	Fri Oct 21 19:56:25 2016 +0200
@@ -198,7 +198,7 @@
             this.time = 300;
             this.iters = 5;
             this.forks = 1;
-            this.batchSize = 20;
+            this.batchSize = orDefault(set.valueOf(batchSize), 20);
         } else
         if (this.mode.equalsIgnoreCase("default")) {
             this.time = orDefault(set.valueOf(time), 1000);
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/TestExecutor.java	Fri Oct 21 16:56:01 2016 +0200
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/TestExecutor.java	Fri Oct 21 19:56:25 2016 +0200
@@ -29,6 +29,7 @@
 import org.openjdk.jcstress.infra.collectors.TestResultCollector;
 import org.openjdk.jcstress.infra.runners.TestConfig;
 import org.openjdk.jcstress.link.BinaryLinkServer;
+import org.openjdk.jcstress.link.ServerListener;
 import org.openjdk.jcstress.util.HashMultimap;
 import org.openjdk.jcstress.util.Multimap;
 import org.openjdk.jcstress.vm.VMSupport;
@@ -42,11 +43,11 @@
 
 /**
  * Manages test execution for the entire run.
- *
+ * <p>
  * This executor is deliberately single-threaded for two reasons:
- *   a) Tests are heavily multithreaded and spawning new threads here may
- *      deplete the thread budget sooner rather than later;
- *   b) Dead-locks in scheduling logic are more visible without threads;
+ * a) Tests are heavily multithreaded and spawning new threads here may
+ * deplete the thread budget sooner rather than later;
+ * b) Dead-locks in scheduling logic are more visible without threads;
  */
 public class TestExecutor {
 
@@ -60,19 +61,31 @@
     private final int batchSize;
     private final TestResultCollector sink;
     private final Multimap<BatchKey, TestConfig> tasks;
-    private final Set<VM> vms;
     private final EmbeddedExecutor embeddedExecutor;
 
+    private final Map<String, VM> vmByToken;
+
     public TestExecutor(int maxThreads, int batchSize, TestResultCollector sink, boolean possiblyForked) throws IOException {
         this.maxThreads = maxThreads;
         this.batchSize = batchSize;
         this.sink = sink;
 
         this.tasks = new HashMultimap<>();
-        this.vms = Collections.newSetFromMap(new ConcurrentHashMap<>());
+        this.vmByToken = new ConcurrentHashMap<>();
 
         semaphore = new Semaphore(maxThreads);
-        server = possiblyForked ? new BinaryLinkServer(maxThreads, sink) : null;
+        server = possiblyForked ? new BinaryLinkServer(new ServerListener() {
+            @Override
+            public TestConfig onJobRequest(String token) {
+                return vmByToken.get(token).jobRequest();
+            }
+
+            @Override
+            public void onResult(String token, TestResult result) {
+                vmByToken.get(token).processResult(result);
+                sink.add(result);
+            }
+        }) : null;
         embeddedExecutor = new EmbeddedExecutor(sink, (cfg) -> semaphore.release(cfg.threads));
     }
 
@@ -112,11 +125,15 @@
         server.terminate();
     }
 
-    private void doSchedule(BatchKey batchKey, Collection<TestConfig> configs)  {
+    private void doSchedule(BatchKey batchKey, Collection<TestConfig> configs) {
         // Make fat tasks bypass in exclusive mode:
         final int threads = Math.min(batchKey.threads, maxThreads);
         waitForMoreThreads(threads);
-        startVM(batchKey, configs);
+
+        String token = "fork-token-" + ID.incrementAndGet();
+        VM vm = new VM(server.getHost(), server.getPort(), batchKey, token, configs);
+        vmByToken.put(token, vm);
+        vm.start();
     }
 
     private void waitForMoreThreads(int threads) {
@@ -130,42 +147,27 @@
         }
     }
 
-    private void startVM(BatchKey batchKey, Collection<TestConfig> configs) {
-        String token = "fork-token-" + ID.incrementAndGet();
-        server.addTask(token, configs);
-
-        VM vm = new VM(server.getHost(), server.getPort(), batchKey, token);
-        vms.add(vm);
-        vm.start();
-    }
-
-    private void stopVM(VM vm) {
-        vms.remove(vm);
-        semaphore.release(vm.key.threads);
-    }
-
     private void processReadyVMs() {
-        for (VM vm : vms) {
+        for (VM vm : vmByToken.values()) {
             try {
-                if (vm.checkTermination()) {
-                    stopVM(vm);
-                }
+                if (!vm.checkTermination()) continue;
             } catch (ForkFailedException e) {
                 // Record the failure for the actual test
-                TestConfig failed = server.getCurrentTask(vm.token);
-                if (failed != null) {
-                    // TODO: Handle the VM bootup failure better, when failed == null
-                    TestResult result = new TestResult(failed, Status.VM_ERROR, -1);
-                    for (String i : e.getInfo()) {
-                        result.addAuxData(i);
-                    }
-                    sink.add(result);
+                TestConfig failed = vm.getVictimTask();
+                TestResult result = new TestResult(failed, Status.VM_ERROR, -1);
+                for (String i : e.getInfo()) {
+                    result.addAuxData(i);
                 }
+                sink.add(result);
+            }
 
-                stopVM(vm);
+            vmByToken.remove(vm.token, vm);
+            semaphore.release(vm.key.threads);
 
-                // Remaining tasks from the fork need to get back on queue
-                doSchedule(vm.key, server.removePendingTasks(vm.token));
+            // Remaining tasks from the fork need to get back on queue
+            List<TestConfig> pending = vm.getPendingTasks();
+            if (!pending.isEmpty()) {
+                doSchedule(vm.key, pending);
             }
         }
     }
@@ -177,13 +179,20 @@
         private final String token;
         private final File stdout;
         private final File stderr;
+        private final TestConfig firstTask;
         private Process process;
+        private final List<TestConfig> pendingTasks;
+        private TestConfig currentTask;
+        private TestConfig lastTask;
+        private IOException pendingException;
 
-        public VM(String host, int port, BatchKey key, String token) {
+        public VM(String host, int port, BatchKey key, String token, Collection<TestConfig> configs) {
             this.host = host;
             this.port = port;
             this.key = key;
             this.token = token;
+            this.pendingTasks = new ArrayList<>(configs);
+            this.firstTask = pendingTasks.get(0);
             try {
                 this.stdout = File.createTempFile("jcstress", "stdout");
                 this.stderr = File.createTempFile("jcstress", "stderr");
@@ -192,7 +201,7 @@
             }
         }
 
-        void start() throws ForkFailedException {
+        void start() {
             try {
                 List<String> command = new ArrayList<>();
 
@@ -215,11 +224,15 @@
                 pb.redirectError(stderr);
                 process = pb.start();
             } catch (IOException ex) {
-                throw new ForkFailedException(ex.getMessage());
+                pendingException = ex;
             }
         }
 
         boolean checkTermination() {
+            if (pendingException != null) {
+                throw new ForkFailedException(pendingException.getMessage());
+            }
+
             if (process.isAlive()) {
                 return false;
             } else {
@@ -238,6 +251,14 @@
                         } catch (IOException e) {
                             output.add("Failed to read stderr: " + e.getMessage());
                         }
+
+                        if (stdout.delete()) {
+                            output.add("Failed to delete stdout log: " + stdout);
+                        }
+                        if (stderr.delete()) {
+                            output.add("Failed to delete stderr log: " + stderr);
+                        }
+
                         throw new ForkFailedException(output);
                     }
                 } catch (InterruptedException ex) {
@@ -246,6 +267,40 @@
                 return true;
             }
         }
+
+        public synchronized TestConfig jobRequest() {
+            if (pendingTasks.isEmpty()) {
+                return null;
+            } else {
+                TestConfig task = pendingTasks.remove(0);
+                currentTask = task;
+                return task;
+            }
+        }
+
+        public synchronized void processResult(TestResult result) {
+            lastTask = currentTask;
+            currentTask = null;
+        }
+
+        public synchronized TestConfig getVictimTask() {
+            if (currentTask != null) {
+                // Current task had failed
+                return currentTask;
+            }
+
+            if (lastTask != null) {
+                // Already replied the results for last task, blame it too
+                return lastTask;
+            }
+
+            // We have not executed anything yet, blame the first task
+            return firstTask;
+        }
+
+        public List<TestConfig> getPendingTasks() {
+            return new ArrayList<>(pendingTasks);
+        }
     }
 
     static class BatchKey {
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkClient.java	Fri Oct 21 16:56:01 2016 +0200
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkClient.java	Fri Oct 21 19:56:25 2016 +0200
@@ -26,103 +26,66 @@
 
 import org.openjdk.jcstress.infra.collectors.TestResult;
 import org.openjdk.jcstress.infra.runners.TestConfig;
-import org.openjdk.jcstress.util.FileUtils;
 
 import java.io.*;
 import java.net.Socket;
 
 public final class BinaryLinkClient {
 
-    private static final int RESET_EACH = Integer.getInteger("jcstress.link.resetEach", 100);
-    private static final int BUFFER_SIZE = Integer.getInteger("jcstress.link.bufferSize", 64*1024);
-    private static final int LINK_TIMEOUT_MS = Integer.getInteger("jcstress.link.timeoutMs", 30*1000);
+    private static final int LINK_TIMEOUT_MS = Integer.getInteger("jcstress.link.timeoutMs", 30 * 1000);
 
     private final Object lock;
-
-    private final Socket clientSocket;
-    private final ObjectOutputStream oos;
-    private final ObjectInputStream ois;
-    private volatile boolean failed;
-    private int resetToGo;
+    private final String hostName;
+    private final int hostPort;
 
     public BinaryLinkClient(String hostName, int hostPort) throws IOException {
+        this.hostName = hostName;
+        this.hostPort = hostPort;
         this.lock = new Object();
-        this.clientSocket = new Socket(hostName, hostPort);
-        clientSocket.setSoTimeout(LINK_TIMEOUT_MS);
-
-        // Initialize the OOS first, and flush, letting the other party read the stream header.
-        this.oos = new ObjectOutputStream(new BufferedOutputStream(clientSocket.getOutputStream(), BUFFER_SIZE));
-        this.oos.flush();
-
-        this.ois = new ObjectInputStream(new BufferedInputStream(clientSocket.getInputStream(), BUFFER_SIZE));
     }
 
-    private void pushFrame(Serializable frame) throws IOException {
-        if (failed) {
-            throw new IOException("Link had failed already");
-        }
+    private Object requestResponse(Object frame) throws IOException {
+        synchronized (lock) {
+            Socket socket = null;
+            try {
+                socket = new Socket(hostName, hostPort);
+                socket.setKeepAlive(true);
+                socket.setSoTimeout(LINK_TIMEOUT_MS);
 
-        // It is important to reset the OOS to avoid garbage buildup in internal identity
-        // tables. However, we cannot do that after each frame since the huge referenced
-        // objects like benchmark and iteration parameters will be duplicated on the receiver
-        // side. This is why we reset only each RESET_EACH frames.
-        //
-        // It is as much as important to flush the stream to let the other party know we
-        // pushed something out.
-
-        synchronized (lock) {
-            try {
-                if (resetToGo-- < 0) {
-                    oos.reset();
-                    resetToGo = RESET_EACH;
-                }
-
+                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                 oos.writeObject(frame);
                 oos.flush();
+
+                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
+                Object o = ois.readObject();
+
+                oos.close();
+                ois.close();
+                return o;
             } catch (IOException e) {
-                failed = true;
                 throw e;
+            } catch (ClassNotFoundException e) {
+                throw new IOException(e);
+            } finally {
+                if (socket != null) {
+                    socket.close();
+                }
             }
         }
     }
 
-    private Object readFrame() throws IOException, ClassNotFoundException {
-        try {
-            return ois.readObject();
-        } catch (ClassNotFoundException ex) {
-            failed = true;
-            throw ex;
-        } catch (IOException ex) {
-            failed = true;
-            throw ex;
-        }
-    }
-
-    public void close() throws IOException {
-        synchronized (lock) {
-            oos.writeObject(new FinishingFrame());
-            FileUtils.safelyClose(ois);
-            FileUtils.safelyClose(oos);
-            clientSocket.close();
-        }
-    }
-
     public TestConfig nextJob(String token) throws IOException, ClassNotFoundException {
-        synchronized (lock) {
-            pushFrame(new JobRequestFrame(token));
-
-            Object reply = readFrame();
-            if (reply instanceof JobResponseFrame) {
-                return ((JobResponseFrame) reply).getConfig();
-            } else {
-                throw new IllegalStateException("Got the erroneous reply: " + reply);
-            }
+        Object reply = requestResponse(new JobRequestFrame(token));
+        if (reply instanceof JobResponseFrame) {
+            return ((JobResponseFrame) reply).getConfig();
+        } else {
+            throw new IllegalStateException("Got the erroneous reply: " + reply);
         }
     }
 
     public void addResult(String token, TestResult result) {
         try {
-            pushFrame(new ResultsFrame(token, result));
+            requestResponse(new ResultsFrame(token, result));
         } catch (IOException e) {
             throw new IllegalStateException(e);
         }
--- a/jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkServer.java	Fri Oct 21 16:56:01 2016 +0200
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/link/BinaryLinkServer.java	Fri Oct 21 19:56:25 2016 +0200
@@ -24,18 +24,10 @@
  */
 package org.openjdk.jcstress.link;
 
-import org.openjdk.jcstress.infra.Status;
-import org.openjdk.jcstress.infra.collectors.TestResult;
-import org.openjdk.jcstress.infra.collectors.TestResultCollector;
 import org.openjdk.jcstress.infra.runners.TestConfig;
 
 import java.io.*;
 import java.net.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.*;
 
 /**
  * Accepts the binary data from the forked VM and pushes it to parent VM
@@ -44,29 +36,24 @@
  */
 public final class BinaryLinkServer {
 
-    private static final int BUFFER_SIZE = Integer.getInteger("jcstress.link.bufferSize", 64*1024);
     private static final String LINK_ADDRESS = System.getProperty("jcstress.link.address");
     private static final int LINK_PORT = Integer.getInteger("jcstress.link.port", 0);
-    private static final int LINK_TIMEOUT_MS = Integer.getInteger("jcstress.link.timeoutMs", 30*1000);
+    private static final int LINK_TIMEOUT_MS = Integer.getInteger("jcstress.link.timeoutMs", 30 * 1000);
 
     private final ServerSocket server;
     private final InetAddress listenAddress;
-    private final TestResultCollector out;
-    private final ConcurrentMap<String, List<TestConfig>> configs;
-    private final ConcurrentMap<String, TestConfig> currentTask;
-    private final ExecutorService executor;
-    private final Collection<Handler> outstandingHandlers;
+    private final Handler handler;
+    private final ServerListener listener;
 
-    public BinaryLinkServer(int workers, TestResultCollector out) throws IOException {
-        this.out = out;
-        this.configs = new ConcurrentHashMap<>();
-        this.currentTask = new ConcurrentHashMap<>();
+    public BinaryLinkServer(ServerListener listener) throws IOException {
+        this.listener = listener;
 
         listenAddress = getListenAddress();
         server = new ServerSocket(LINK_PORT, 50, listenAddress);
         server.setSoTimeout(LINK_TIMEOUT_MS);
-        executor = Executors.newFixedThreadPool(workers);
-        outstandingHandlers = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+        handler = new Handler(server);
+        handler.start();
     }
 
     private InetAddress getListenAddress() {
@@ -84,9 +71,8 @@
     }
 
     public void terminate() {
-        // no more Handlers to schedule; the Handlers in queue had not
-        // opened the socket yet.
-        executor.shutdownNow();
+        // set interrupt flag
+        handler.interrupt();
 
         // all existing Handlers blocked on accept() should exit now
         try {
@@ -95,24 +81,13 @@
             // do nothing
         }
 
-        // all existing Handlers blocked on socket read should exit now:
-        for (Handler h : outstandingHandlers) {
-            h.close();
+        // wait for handler to join
+        try {
+            handler.join();
+        } catch (InterruptedException e) {
+            // do nothing
         }
-    }
 
-    public void addTask(String token, Collection<TestConfig> cfgs) {
-        List<TestConfig> exist = configs.put(token, new ArrayList<>(cfgs));
-        if (exist != null) {
-            throw new IllegalStateException("Trying to overwrite the same token");
-        }
-        executor.submit(new Handler(server));
-    }
-
-    public List<TestConfig> removePendingTasks(String token) {
-        List<TestConfig> conf = configs.get(token);
-        configs.remove(token);
-        return conf;
     }
 
     public String getHost() {
@@ -124,13 +99,8 @@
         return server.getLocalPort();
     }
 
-    public TestConfig getCurrentTask(String token) {
-        return currentTask.get(token);
-    }
-
-    private final class Handler implements Runnable {
+    private final class Handler extends Thread {
         private final ServerSocket server;
-        private Socket socket;
 
         public Handler(ServerSocket server) {
             this.server = server;
@@ -138,65 +108,48 @@
 
         @Override
         public void run() {
-            outstandingHandlers.add(this);
+            while (!Thread.interrupted()) {
+                acceptAndProcess();
+            }
+        }
 
-            TestConfig config = null;
+        private void acceptAndProcess() {
+            Socket socket = null;
             try {
                 socket = server.accept();
 
-                InputStream is = socket.getInputStream();
-                OutputStream os = socket.getOutputStream();
+                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
+                Object obj = ois.readObject();
 
-                // eager OOS initialization, let the other party read the stream header
-                ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(os, BUFFER_SIZE));
-                oos.flush();
+                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
 
-                // late OIS initialization, otherwise we'll block reading the header
-                ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(is, BUFFER_SIZE));
+                if (obj instanceof JobRequestFrame) {
+                    String tkn = ((JobRequestFrame) obj).getToken();
+                    TestConfig cfg = listener.onJobRequest(tkn);
+                    oos.writeObject(new JobResponseFrame(cfg));
+                } else if (obj instanceof ResultsFrame) {
+                    ResultsFrame rf = (ResultsFrame) obj;
+                    listener.onResult(rf.getToken(), rf.getRes());
+                    oos.writeObject(new OkResponseFrame());
+                } else {
+                    // should always reply something
+                    oos.writeObject(new WTFWasThatFrame());
+                }
 
-                Object obj;
-                while ((obj = ois.readObject()) != null) {
-                    if (obj instanceof JobRequestFrame) {
-                        String tkn = ((JobRequestFrame) obj).getToken();
-                        List<TestConfig> cfgs = configs.get(tkn);
-                        if (cfgs.isEmpty()) {
-                            oos.writeObject(new JobResponseFrame(null));
-                        } else {
-                            config = cfgs.remove(0);
-                            currentTask.put(tkn, config);
-                            oos.writeObject(new JobResponseFrame(config));
-                        }
-                        oos.flush();
-                    }
-                    if (obj instanceof ResultsFrame) {
-                        ResultsFrame rf = (ResultsFrame) obj;
-                        out.add(rf.getRes());
-                        currentTask.remove(rf.getToken());
-                    }
-                    if (obj instanceof FinishingFrame) {
-                        // close the streams
-                        break;
-                    }
-                }
+                oos.close();
+                ois.close();
             } catch (EOFException e) {
                 // ignore
             } catch (Exception e) {
-                TestResult tr = new TestResult(config, Status.VM_ERROR, -1);
-                tr.addAuxData("<binary link had failed, forked VM corrupted the stream?");
-                tr.addAuxData(e.getMessage());
-                out.add(tr);
+                e.printStackTrace();
+                // ignore, the exit code would be non-zero, and TestExecutor would handle it.
             } finally {
-                outstandingHandlers.remove(this);
-                close();
-            }
-        }
-
-        public void close() {
-            if (socket != null) {
-                try {
-                    socket.close();
-                } catch (IOException e) {
-                    // ignore
+                if (socket != null) {
+                    try {
+                        socket.close();
+                    } catch (IOException e) {
+                        // do nothing
+                    }
                 }
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/link/OkResponseFrame.java	Fri Oct 21 19:56:25 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jcstress.link;
+
+import java.io.Serializable;
+
+public class OkResponseFrame implements Serializable {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/link/ServerListener.java	Fri Oct 21 19:56:25 2016 +0200
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jcstress.link;
+
+import org.openjdk.jcstress.infra.collectors.TestResult;
+import org.openjdk.jcstress.infra.runners.TestConfig;
+
+public interface ServerListener {
+
+    TestConfig onJobRequest(String token);
+
+    void onResult(String token, TestResult result);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jcstress-core/src/main/java/org/openjdk/jcstress/link/WTFWasThatFrame.java	Fri Oct 21 19:56:25 2016 +0200
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jcstress.link;
+
+import java.io.Serializable;
+
+public class WTFWasThatFrame implements Serializable {
+}