Mercurial > hg > blitz_condensed
diff src/EDU/oswego/cs/dl/util/concurrent/misc/SynchronizationTimer.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/EDU/oswego/cs/dl/util/concurrent/misc/SynchronizationTimer.java Sat Mar 21 11:00:06 2009 +0000 @@ -0,0 +1,2302 @@ +/* + File: SynchronizationTimer.java + + Originally written by Doug Lea and released into the public domain. + This may be used for any purposes whatsoever without acknowledgment. + Thanks for the assistance and support of Sun Microsystems Labs, + and everyone contributing, testing, and using this code. + + History: + Date Who What + 7Jul1998 dl Create public version + 16Jul1998 dl fix intialization error for compute loops + combined into one frame + misc layout and defaults changes + increase printed precision + overlap get/set in Executor tests + Swap defaults for swing import + Active thread counts reflect executors + 30Aug1998 dl Misc revisions to mesh with 1.1.0 + 27jan1999 dl Eliminate GC calls + 24Nov2001 dl Increase some default values +*/ + +package EDU.oswego.cs.dl.util.concurrent.misc; + +// Swap the following sets of imports if necessary. + +import javax.swing.*; +import javax.swing.border.*; + +//import com.sun.java.swing.*; +//import com.sun.java.swing.border.*; + +import EDU.oswego.cs.dl.util.concurrent.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.lang.reflect.*; + +/** + * + * This program records times for various fine-grained synchronization + * schemes, and provides some ways of measuring them over different + * context parameters. + * + * <p> + * Quick start: + * <ol> + * <li>javac -d <em>base of some CLASSPATH</em> *.java <br> + * You'll need Swing (JFC). (This + * program currently imports the javax.swing versions. + * You can edit imports to instead use other versions.) + * <li>java EDU.oswego.cs.dl.util.concurrent.misc.SynchronizationTimer <br> + * <li> Click<em> start</em>. + * Clicking <em>stop</em> cancels the run. Cancellation can take + * a while when there are a lot of threads. + * <li>For more explanation about tested classes, see + * <a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html">Documentation for util.concurrent</a> + * </ol> + * + * <p> + * Synchronization schemes are tested around implementations and + * subclasses of <code>RNG</code>, which is just a hacked random + * number generator class. Objects of this class have just enough + * state and require just enough computation to be reasonable minimal + * targets. (Additionally, random numbers are needed a lot in these + * kinds of time tests, so basing them on objects that produce random + * numbers is convenient.) Computation of each random number is + * padded a bit with an adjustable compute loop running a random + * number of times to avoid getting schedulers locked into + * uninteresting patterns. + * + * <p> + * Each iteration of each test ultimately somehow calls + * the random number generation + * method of an RNG. The time listed is the average time it took to do + * one iteration, in microseconds. These are just based on wallclock + * time (System.currentTimeMillis()). Thread + * construction time is <em>NOT</em> included in these times. + * In tests with many threads, construction and other bookkeeping + * can take longer than the tests themselves. + * <p> + * Results are listed in a table, and optionally printed on standard output. + * You can redirect standard output to save to a file. + * <p> + * The total amount of ``real'' computation reported in each cell is + * the same. Thus, the unobtainably ideal pattern of results would be + * for every cell of the table to be the same (and very small). + * <p> + * A thread pool (PooledExecutor) is used to manage the threads used in + * test runs. The current number of active threads is indicated in + * the panel. It should normally be at most three plus the number of threads used in the + * indicated test column (there are at most three overhead threads per run), although + * it may transiently climb, and is larger in those tests that + * generate their own internal threads (for example ThreadedExceutor). If the + * indicated size fails to return to zero within about 10 seconds of + * either hitting stop + * or the end of a run, you may have a + * problem with interruption handling on your Java VM. + * + * <p> + * This program cannot + * tell you how busy your computer is while running tests. + * You can run a utility program (for + * example <code>perfmeter</code> or <code>top</code> on unix) + * alongside this program + * to find out. + * <p> + * A number of control parameters can be changed at any time. + * Most combinations of parameter settings create contexts + * that are completely unrepresentative of those seen in practical + * applications. However, they can be set to provide rough analogs of + * real applications, and the results used as rough guesses about + * performance impact. Also, do not be too upset about slow + * performance on tests representing situations + * that would never occur in practice. + * <p> + * + * You can control parameters by clicking any of the following, + * at any time. (You can even change parameters + * while tests are running, in which case they will take + * effect as soon as possible. Most controls momentarily stall + * while test objects and threads are being constructed, to avoid + * inconsistencies during test runs.) + * <dl> + * + * <dt> Number of threads + * + * <dd> Controls concurrency. The indicated number of threads are + * started simultaneously and then waited out. + * + * <dt>Contention. + * + * <dd>Percent sharing among threads. Zero percent means that each + * thread has its own RNG object, so there is no + * interference among threads. The zero + * percent case thus shows the cost of synchronization mechanics that + * happen to never be needed. + * 100 percent sharing means that all + * threads call methods on the same object, so each thread will have to + * wait until the RNG objects are not being used by others. + * In between is in between: Only the given percentage of calls are + * made to shared RNG objects; others are to unshared. + * Contention in classes that use Channels works slightly differently: + * The Channels are shared, not the base RNG objects. (Another way + * of looking at it is that tests demonstrate effects of multiple + * producers and consumers on the same channel.) + * + * <dt>Classes + * <dd>You can choose to only test the indicated classes. You can + * probably figure out how to add more classes to run yourself. + * + * <dt>Calls per thread per test + * + * <dd>Specifies number of iterations per thread per test. The listed + * times are averages over these iterations. The default seems to + * provide precise enough values for informal testing purposes. + * You should expect to see a fair amount of variation across + * repeated runs. + * If you get zeroes printed in any cell, this means that the + * test ran too fast to measure in milleconds, so you should increase the + * iterations value. + * + * <dt> Computations per call + * + * <dd>Specifies length of each call by setting an internal looping + * parameter inside each RNG object. Shorter calls lead to shorter + * times between synchronization measures. Longer calls, along with + * high contention can be used to force timeouts to occur. + * + * <dt> Iterations per barrier. + * + * <dd> Specifies the number of iterations performed by each thread until + * a synchronization barrier is forced with other threads, forcing + * it to wait for other threads to reach the same number of iterations. This + * controls the amount of interaction (versus contention) among threads. + * Setting to a value greater than the number of iterations per test + * effectively disables barriers. + * + * <dt> Threads per barrier + * + * <dd> Specifies how many threads are forced to synchronize at each + * barrier point. Greater numbers cause more threads to wait for each + * other at barriers. Setting to 1 means that a thread only has to + * wait for itself, which means not to wait at all. + * + * <dt>Lock mode + * + * <dd>For classes that support it, this controls whether mutual + * exclusion waits are done via standard blocking synchronization, or + * a loop repeatedly calling a timed wait. + * + * <dt>Producer mode + * + * <dd>For classes that support it, this controls whether producers + * perform blocking puts versus loops repeatedly calling offer. + * + * <dt>Consumer mode + * + * <dd>For classes that support it, this controls whether consumers + * perform blocking takes versus loops repeatedly calling poll. + * + * <dt> Timeouts + * + * <dd> Specifies the duration of timeouts used in timeout mode. A + * value of zero results in pure spin-loops. + * + * <dt>Producer/consumer rates. + * + * <dd>For tests involving producer/consumer pairs, this controls + * whether the producer is much faster, about the same speed, or much + * slower than the consumer. This is implemented by having the + * producer do all, half, or none of the actual calls to update, in + * addition to adding elements to channel. + * + * <dt>Buffer capacity + * + * <dd>For tests involving finite capacity + * buffers, this controls maximum buffer size. + * + * </dl> + * + * <p> + * To scaffold all this, the RNG class is defined in + * layers. Each RNG has internal non-public methods that do the actual + * computation, and public methods that call the internal ones. The + * particular classes run in tests might change over time, but + * currently includes the following general kinds: + * + * <dl> + * + * + * <dt> Using built-in synchronization + * <dd> Versions of RNG classes that use (or don't use) + * synchronized methods and/or blocks. Also some tests of + * simple SynchronizedVariables. Tests that would not + * be thread-safe are not run when there is more than one + * thread and non-zero contention. + * + * + * <dt> Using <code>Sync</code> classes as locks + * <dd> Classes protecting public methods via Semaphores, mutexes, etc. + * In each case, the outer public methods delegate actions to + * another RNG object, surrounded by acquire/release/etc. The + * class called SDelegated does this using builtin + * synchronization rather than Sync locks + * so might be a useful comparison. + * + * <dt> Using Channels + * <dd> These classes work a little bit differently than the others. + * Each test arranges that half of the threads behave as producers, + * and half as consumers. Each test iteration puts/takes an RNG object + * through a channel before or after executing its update + * method. When the number of threads is one, each producer + * simply consumers its own object. Some Channels (notably + * SynchronousChannels) cannot be used with only one thread, + * in which case the test is skipped. + * + * <dt> Using Executors + * <dd> These classes arrange for each RNG update to occur + * as an executable command. Each test iteration passes a + * command to an Executor, which eventually executes it. + * Execution is overlapped: Each iteration starts a new + * command, and then waits for the previous command to complete. + * + * </dl> + * + * <p> + * + * The test code is ugly; it has just evolved over the years. Sorry. +**/ + +public class SynchronizationTimer { + + /** Start up this application **/ + public static void main(String[] args) { + + JFrame frame = new JFrame("Times per call in microseconds"); + + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + frame.getContentPane().add(new SynchronizationTimer().mainPanel()); + frame.pack(); + frame.setVisible(true); + } + + /** + * Information about classes to be tested + **/ + static class TestedClass { + final String name; + final Class cls; + final boolean multipleOK; + final boolean singleOK; + final Class buffCls; + Boolean enabled_ = new Boolean(true); + + synchronized void setEnabled(Boolean b) { enabled_ = b; } + synchronized Boolean getEnabled() { return enabled_; } + + synchronized void toggleEnabled() { + boolean enabled = enabled_.booleanValue(); + enabled_ = new Boolean(!enabled); + } + + synchronized boolean isEnabled(int nthreads, Fraction shared) { + boolean enabled = enabled_.booleanValue(); + if (!enabled) return false; + if (!singleOK && nthreads <= 1) return false; + if (!multipleOK && nthreads > 1 && shared.compareTo(0) > 0) return false; + return true; + } + + + TestedClass(String n, Class c, boolean m, boolean sok) { + name = n; cls = c; multipleOK = m; singleOK = sok; + buffCls = null; + } + + TestedClass(String n, Class c, boolean m, boolean sok, Class bc) { + name = n; cls = c; multipleOK = m; singleOK = sok; + buffCls = bc; + } + + static final TestedClass dummy = + new TestedClass("", null, false, false); + + static final TestedClass[] classes = { + + new TestedClass("NoSynchronization", NoSynchRNG.class, false, true), + new TestedClass("PublicSynchronization", PublicSynchRNG.class, true, true), + new TestedClass("NestedSynchronization", AllSynchRNG.class, true, true), + + new TestedClass("SDelegated", SDelegatedRNG.class, true, true), + + new TestedClass("SynchLongUsingSet", SynchLongRNG.class, true, true), + new TestedClass("SynchLongUsingCommit", AClongRNG.class, true, true), + + new TestedClass("Semaphore", SemRNG.class, true, true), + new TestedClass("WaiterPrefSemaphore", WpSemRNG.class, true, true), + new TestedClass("FIFOSemaphore", FifoRNG.class, true, true), + new TestedClass("PrioritySemaphore", PrioritySemRNG.class, true, true), + new TestedClass("Mutex", MutexRNG.class, true, true), + new TestedClass("ReentrantLock", RlockRNG.class, true, true), + + new TestedClass("WriterPrefRWLock", WpRWlockRNG.class, true, true), + new TestedClass("ReaderPrefRWLock", ReaderPrefRWlockRNG.class, true, true), + new TestedClass("FIFORWLock", FIFORWlockRNG.class, true, true), + new TestedClass("ReentrantRWL", ReentrantRWlockRNG.class, true, true), + + + + new TestedClass("LinkedQueue", ChanRNG.class, true, true, + LinkedQueue.class), + + new TestedClass("WaitFreeQueue", ChanRNG.class, true, true, + WaitFreeQueue.class), + + new TestedClass("BoundedLinkedQueue", ChanRNG.class, true, true, + BoundedLinkedQueue.class), + new TestedClass("BoundedBuffer", ChanRNG.class, true, true, + BoundedBuffer.class), + new TestedClass("CondVarBoundedBuffer", ChanRNG.class, true, true, + CVBuffer.class), + new TestedClass("BoundedPriorityQueue", ChanRNG.class, true, true, + BoundedPriorityQueue.class), + new TestedClass("Slot", ChanRNG.class, true, true, + Slot.class), + // new TestedClass("FIFOSlot", ChanRNG.class, true, true, FIFOSlot.class), + new TestedClass("SynchronousChannel", ChanRNG.class, true, false, + SynchronousChannel.class), + + + new TestedClass("DirectExecutor", DirectExecutorRNG.class, true, true), + new TestedClass("SemaphoreLckExecutor", LockedSemRNG.class, true, true), + new TestedClass("QueuedExecutor", QueuedExecutorRNG.class, true, true), + new TestedClass("ThreadedExecutor", ThreadedExecutorRNG.class, true, true), + new TestedClass("PooledExecutor", PooledExecutorRNG.class, true, true), + + // new TestedClass("Pipe", ChanRNG.class, true, true, PipedChannel.class), + }; + } + + + + // test parameters + + static final int[] nthreadsChoices = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024 + }; + + static final int BLOCK_MODE = 0; + static final int TIMEOUT_MODE = 1; + + static final int[] syncModes = { BLOCK_MODE, TIMEOUT_MODE, }; + + // misc formatting utilities + + static String modeToString(int m) { + String sms; + if (m == BLOCK_MODE) sms = "block"; + else if (m == TIMEOUT_MODE) sms = "timeout"; + else sms = "No such mode"; + return sms; + } + + static String biasToString(int b) { + String sms; + if (b < 0) sms = "slower producer"; + else if (b == 0) sms = "balanced prod/cons rate"; + else if (b > 0) sms = "slower consumer"; + else sms = "No such bias"; + return sms; + } + + + static String p2ToString(int n) { // print power of two + String suf = ""; + if (n >= 1024) { + n = n / 1024; + suf = "K"; + if (n >= 1024) { + n = n / 1024; + suf = "M"; + } + } + return n + suf; + } + + static final int PRECISION = 10; // microseconds + + static String formatTime(long ns, boolean showDecimal) { + long intpart = ns / PRECISION; + long decpart = ns % PRECISION; + if (!showDecimal) { + if (decpart >= PRECISION/2) + ++intpart; + return Long.toString(intpart); + } + else { + String sint = Long.toString(intpart); + String sdec = Long.toString(decpart); + if (decpart == 0) { + int z = PRECISION; + while (z > 10) { + sdec = "0" + sdec; + z /= 10; + } + } + String ts = sint + "." + sdec; + return ts; + } + } + + static class ThreadInfo { + final String name; + final int number; + Boolean enabled; + ThreadInfo(int nthr) { + number = nthr; + name = p2ToString(nthr); + enabled = new Boolean(true); + } + + synchronized Boolean getEnabled() { return enabled; } + synchronized void setEnabled(Boolean v) { enabled = v; } + synchronized void toggleEnabled() { + enabled = new Boolean(!enabled.booleanValue()); + } + } + + final ThreadInfo[] threadInfo = new ThreadInfo[nthreadsChoices.length]; + + boolean threadEnabled(int nthreads) { + return threadInfo[nthreads].getEnabled().booleanValue(); + } + + // This used to be a JTable datamodel, but now just part of this class ... + + final static int headerRows = 1; + final static int classColumn = 0; + final static int headerColumns = 1; + final int tableRows = TestedClass.classes.length + headerRows; + final int tableColumns = nthreadsChoices.length + headerColumns; + + final JComponent[][] resultTable_ = new JComponent[tableRows][tableColumns]; + + JPanel resultPanel() { + + JPanel[] colPanel = new JPanel[tableColumns]; + for (int col = 0; col < tableColumns; ++col) { + colPanel[col] = new JPanel(); + colPanel[col].setLayout(new GridLayout(tableRows, 1)); + if (col != 0) + colPanel[col].setBackground(Color.white); + } + + Color hdrbg = colPanel[0].getBackground(); + Border border = new LineBorder(hdrbg); + + Font font = new Font("Dialog", Font.PLAIN, 12); + Dimension labDim = new Dimension(40, 16); + Dimension cbDim = new Dimension(154, 16); + + JLabel cornerLab = new JLabel(" Classes \\ Threads"); + cornerLab.setMinimumSize(cbDim); + cornerLab.setPreferredSize(cbDim); + cornerLab.setFont(font); + resultTable_[0][0] = cornerLab; + colPanel[0].add(cornerLab); + + for (int col = 1; col < tableColumns; ++col) { + final int nthreads = col - headerColumns; + JCheckBox tcb = new JCheckBox(threadInfo[nthreads].name, true); + tcb.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + threadInfo[nthreads].toggleEnabled(); + }}); + + + tcb.setMinimumSize(labDim); + tcb.setPreferredSize(labDim); + tcb.setFont(font); + tcb.setBackground(hdrbg); + resultTable_[0][col] = tcb; + colPanel[col].add(tcb); + } + + + for (int row = 1; row < tableRows; ++row) { + final int cls = row - headerRows; + + JCheckBox cb = new JCheckBox(TestedClass.classes[cls].name, true); + cb.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + TestedClass.classes[cls].toggleEnabled(); + }}); + + resultTable_[row][0] = cb; + cb.setMinimumSize(cbDim); + cb.setPreferredSize(cbDim); + cb.setFont(font); + colPanel[0].add(cb); + + for (int col = 1; col < tableColumns; ++col) { + int nthreads = col - headerColumns; + JLabel lab = new JLabel(""); + resultTable_[row][col] = lab; + + lab.setMinimumSize(labDim); + lab.setPreferredSize(labDim); + lab.setBorder(border); + lab.setFont(font); + lab.setBackground(Color.white); + lab.setForeground(Color.black); + lab.setHorizontalAlignment(JLabel.RIGHT); + + colPanel[col].add(lab); + } + } + + JPanel tblPanel = new JPanel(); + tblPanel.setLayout(new BoxLayout(tblPanel, BoxLayout.X_AXIS)); + for (int col = 0; col < tableColumns; ++col) { + tblPanel.add(colPanel[col]); + } + + return tblPanel; + + } + + void setTime(final long ns, int clsIdx, int nthrIdx) { + int row = clsIdx+headerRows; + int col = nthrIdx+headerColumns; + final JLabel cell = (JLabel)(resultTable_[row][col]); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + cell.setText(formatTime(ns, true)); + } + }); + } + + + + void clearTable() { + for (int i = 1; i < tableRows; ++i) { + for (int j = 1; j < tableColumns; ++j) { + ((JLabel)(resultTable_[i][j])).setText(""); + } + } + } + + void setChecks(final boolean setting) { + for (int i = 0; i < TestedClass.classes.length; ++i) { + TestedClass.classes[i].setEnabled(new Boolean(setting)); + ((JCheckBox)resultTable_[i+1][0]).setSelected(setting); + } + } + + + public SynchronizationTimer() { + for (int i = 0; i < threadInfo.length; ++i) + threadInfo[i] = new ThreadInfo(nthreadsChoices[i]); + + } + + final SynchronizedInt nextClassIdx_ = new SynchronizedInt(0); + final SynchronizedInt nextThreadIdx_ = new SynchronizedInt(0); + + + JPanel mainPanel() { + new PrintStart(); // classloader bug workaround + JPanel paramPanel = new JPanel(); + paramPanel.setLayout(new GridLayout(5, 3)); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(1, 3)); + + startstop_.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (running_.get()) + cancel(); + else { + try { + startTestSeries(new TestSeries()); + } + catch (InterruptedException ex) { + endTestSeries(); + } + } + }}); + + paramPanel.add(startstop_); + + JPanel p1 = new JPanel(); + p1.setLayout(new GridLayout(1, 2)); + + JButton continueButton = new JButton("Continue"); + + continueButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + if (!running_.get()) { + try { + startTestSeries(new TestSeries(nextClassIdx_.get(), + nextThreadIdx_.get())); + } + catch (InterruptedException ex) { + endTestSeries(); + } + } + }}); + + p1.add(continueButton); + + JButton clearButton = new JButton("Clear cells"); + + clearButton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent evt) { + clearTable(); + } + }); + + p1.add(clearButton); + + paramPanel.add(p1); + + JPanel p3 = new JPanel(); + p3.setLayout(new GridLayout(1, 2)); + + JButton setButton = new JButton("All classes"); + + setButton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent evt) { + setChecks(true); + } + }); + + p3.add(setButton); + + + JButton unsetButton = new JButton("No classes"); + + unsetButton.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent evt) { + setChecks(false); + } + }); + + p3.add(unsetButton); + paramPanel.add(p3); + + JPanel p2 = new JPanel(); + // p2.setLayout(new GridLayout(1, 2)); + p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS)); + + + JCheckBox consoleBox = new JCheckBox("Console echo"); + consoleBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + echoToSystemOut.complement(); + } + }); + + + + JLabel poolinfo = new JLabel("Active threads: 0"); + + p2.add(poolinfo); + p2.add(consoleBox); + + paramPanel.add(p2); + + paramPanel.add(contentionBox()); + paramPanel.add(itersBox()); + paramPanel.add(cloopBox()); + paramPanel.add(barrierBox()); + paramPanel.add(exchangeBox()); + paramPanel.add(biasBox()); + paramPanel.add(capacityBox()); + paramPanel.add(timeoutBox()); + paramPanel.add(syncModePanel()); + paramPanel.add(producerSyncModePanel()); + paramPanel.add(consumerSyncModePanel()); + + startPoolStatus(poolinfo); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + + JPanel tblPanel = resultPanel(); + + mainPanel.add(tblPanel); + mainPanel.add(paramPanel); + return mainPanel; + } + + + + + JComboBox syncModePanel() { + JComboBox syncModeComboBox = new JComboBox(); + + for (int j = 0; j < syncModes.length; ++j) { + String lab = "Locks: " + modeToString(syncModes[j]); + syncModeComboBox.addItem(lab); + } + syncModeComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.syncMode.set(syncModes[idx]); + } + }); + + RNG.syncMode.set(syncModes[0]); + syncModeComboBox.setSelectedIndex(0); + return syncModeComboBox; + } + + JComboBox producerSyncModePanel() { + JComboBox producerSyncModeComboBox = new JComboBox(); + + for (int j = 0; j < syncModes.length; ++j) { + String lab = "Producers: " + modeToString(syncModes[j]); + producerSyncModeComboBox.addItem(lab); + } + producerSyncModeComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.producerMode.set(syncModes[idx]); + } + }); + + RNG.producerMode.set(syncModes[0]); + producerSyncModeComboBox.setSelectedIndex(0); + return producerSyncModeComboBox; + } + + JComboBox consumerSyncModePanel() { + JComboBox consumerSyncModeComboBox = new JComboBox(); + + for (int j = 0; j < syncModes.length; ++j) { + String lab = "Consumers: " + modeToString(syncModes[j]); + consumerSyncModeComboBox.addItem(lab); + } + consumerSyncModeComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.consumerMode.set(syncModes[idx]); + } + }); + + RNG.consumerMode.set(syncModes[0]); + consumerSyncModeComboBox.setSelectedIndex(0); + return consumerSyncModeComboBox; + } + + + + JComboBox contentionBox() { + final Fraction[] contentionChoices = { + new Fraction(0, 1), + new Fraction(1, 16), + new Fraction(1, 8), + new Fraction(1, 4), + new Fraction(1, 2), + new Fraction(1, 1) + }; + + JComboBox contentionComboBox = new JComboBox(); + + for (int j = 0; j < contentionChoices.length; ++j) { + String lab = contentionChoices[j].asDouble() * 100.0 + + "% contention/sharing"; + contentionComboBox.addItem(lab); + } + contentionComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + contention_.set(contentionChoices[idx]); + } + }); + + contention_.set(contentionChoices[3]); + contentionComboBox.setSelectedIndex(3); + return contentionComboBox; + } + + JComboBox itersBox() { + final int[] loopsPerTestChoices = { + 1, + 16, + 256, + 1024, + 2 * 1024, + 4 * 1024, + 8 * 1024, + 16 * 1024, + 32 * 1024, + 64 * 1024, + 128 * 1024, + 256 * 1024, + 512 * 1024, + 1024 * 1024, + }; + + JComboBox precComboBox = new JComboBox(); + + for (int j = 0; j < loopsPerTestChoices.length; ++j) { + String lab = p2ToString(loopsPerTestChoices[j]) + + " calls per thread per test"; + precComboBox.addItem(lab); + } + precComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + loopsPerTest_.set(loopsPerTestChoices[idx]); + } + }); + + loopsPerTest_.set(loopsPerTestChoices[8]); + precComboBox.setSelectedIndex(8); + + return precComboBox; + } + + JComboBox cloopBox() { + final int[] computationsPerCallChoices = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2 * 1024, + 4 * 1024, + 8 * 1024, + 16 * 1024, + 32 * 1024, + 64 * 1024, + }; + + JComboBox cloopComboBox = new JComboBox(); + + for (int j = 0; j < computationsPerCallChoices.length; ++j) { + String lab = p2ToString(computationsPerCallChoices[j]) + + " computations per call"; + cloopComboBox.addItem(lab); + } + cloopComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.computeLoops.set(computationsPerCallChoices[idx]); + } + }); + + RNG.computeLoops.set(computationsPerCallChoices[3]); + cloopComboBox.setSelectedIndex(3); + return cloopComboBox; + } + + JComboBox barrierBox() { + final int[] itersPerBarrierChoices = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2 * 1024, + 4 * 1024, + 8 * 1024, + 16 * 1024, + 32 * 1024, + 64 * 1024, + 128 * 1024, + 256 * 1024, + 512 * 1024, + 1024 * 1024, + }; + + JComboBox barrierComboBox = new JComboBox(); + + for (int j = 0; j < itersPerBarrierChoices.length; ++j) { + String lab = p2ToString(itersPerBarrierChoices[j]) + + " iterations per barrier"; + barrierComboBox.addItem(lab); + } + barrierComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.itersPerBarrier.set(itersPerBarrierChoices[idx]); + } + }); + + RNG.itersPerBarrier.set(itersPerBarrierChoices[13]); + barrierComboBox.setSelectedIndex(13); + + // RNG.itersPerBarrier.set(itersPerBarrierChoices[15]); + // barrierComboBox.setSelectedIndex(15); + + return barrierComboBox; + } + + JComboBox exchangeBox() { + final int[] exchangerChoices = { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + }; + + JComboBox exchComboBox = new JComboBox(); + + for (int j = 0; j < exchangerChoices.length; ++j) { + String lab = p2ToString(exchangerChoices[j]) + + " max threads per barrier"; + exchComboBox.addItem(lab); + } + exchComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.exchangeParties.set(exchangerChoices[idx]); + } + }); + + RNG.exchangeParties.set(exchangerChoices[1]); + exchComboBox.setSelectedIndex(1); + return exchComboBox; + } + + JComboBox biasBox() { + final int[] biasChoices = { + -1, + 0, + 1 + }; + + + JComboBox biasComboBox = new JComboBox(); + + for (int j = 0; j < biasChoices.length; ++j) { + String lab = biasToString(biasChoices[j]); + biasComboBox.addItem(lab); + } + biasComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.bias.set(biasChoices[idx]); + } + }); + + RNG.bias.set(biasChoices[1]); + biasComboBox.setSelectedIndex(1); + return biasComboBox; + } + + JComboBox capacityBox() { + + final int[] bufferCapacityChoices = { + 1, + 4, + 64, + 256, + 1024, + 4096, + 16 * 1024, + 64 * 1024, + 256 * 1024, + 1024 * 1024, + }; + + JComboBox bcapComboBox = new JComboBox(); + + for (int j = 0; j < bufferCapacityChoices.length; ++j) { + String lab = p2ToString(bufferCapacityChoices[j]) + + " element bounded buffers"; + bcapComboBox.addItem(lab); + } + bcapComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + DefaultChannelCapacity.set(bufferCapacityChoices[idx]); + } + }); + + + DefaultChannelCapacity.set(bufferCapacityChoices[3]); + bcapComboBox.setSelectedIndex(3); + return bcapComboBox; + } + + JComboBox timeoutBox() { + + + final long[] timeoutChoices = { + 0, + 1, + 10, + 100, + 1000, + 10000, + 100000, + }; + + + JComboBox timeoutComboBox = new JComboBox(); + + for (int j = 0; j < timeoutChoices.length; ++j) { + String lab = timeoutChoices[j] + " msec timeouts"; + timeoutComboBox.addItem(lab); + } + timeoutComboBox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent evt) { + JComboBox src = (JComboBox)(evt.getItemSelectable()); + int idx = src.getSelectedIndex(); + RNG.timeout.set(timeoutChoices[idx]); + } + }); + + RNG.timeout.set(timeoutChoices[3]); + timeoutComboBox.setSelectedIndex(3); + return timeoutComboBox; + } + + ClockDaemon timeDaemon = new ClockDaemon(); + + void startPoolStatus(final JLabel status) { + Runnable updater = new Runnable() { + int lastps = 0; + public void run() { + final int ps = Threads.activeThreads.get(); + if (lastps != ps) { + lastps = ps; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + status.setText("Active threads: " + ps); + } } ); + } + } + }; + timeDaemon.executePeriodically(250, updater, false); + } + + private final SynchronizedRef contention_ = new SynchronizedRef(null); + private final SynchronizedInt loopsPerTest_ = new SynchronizedInt(0); + + private final SynchronizedBoolean echoToSystemOut = + new SynchronizedBoolean(false); + + + private final JButton startstop_ = new JButton("Start"); + + private WaitableInt testNumber_ = new WaitableInt(1); + + private void runOneTest(Runnable tst) throws InterruptedException { + int nt = testNumber_.get(); + Threads.pool.execute(tst); + testNumber_.whenNotEqual(nt, null); + } + + private void endOneTest() { + testNumber_.increment(); + } + + private SynchronizedBoolean running_ = new SynchronizedBoolean(false); + + void cancel() { + // not stable enough to cancel during construction + synchronized (RNG.constructionLock) { + try { + Threads.pool.interruptAll(); + } + catch(Exception ex) { + System.out.println("\nException during cancel:\n" + ex); + return; + } + } + } + + + void startTestSeries(Runnable tst) throws InterruptedException { + running_.set(true); + startstop_.setText("Stop"); + Threads.pool.execute(tst); + } + + // prevent odd class-gc problems on some VMs? + class PrintStart implements Runnable { + public void run() { + startstop_.setText("Start"); + } + } + + + void endTestSeries() { + running_.set(false); + SwingUtilities.invokeLater(new PrintStart()); + } + + /* + void old_endTestSeries() { + running_.set(false); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + startstop_.setText("Start"); + } } ); + } + */ + + class TestSeries implements Runnable { + final int firstclass; + final int firstnthreads; + + TestSeries() { + firstclass = 0; + firstnthreads = 0; + } + + TestSeries(final int firstc, final int firstnt) { + firstclass = firstc; + firstnthreads = firstnt; + } + + public void run() { + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + + try { + int t = firstnthreads; + int c = firstclass; + + if (t < nthreadsChoices.length && + c < TestedClass.classes.length) { + + for (;;) { + + + // these checks are duplicated in OneTest, but added here + // to minimize unecessary thread construction, + // which can skew results + + if (threadEnabled(t)) { + + TestedClass entry = TestedClass.classes[c]; + + int nthreads = nthreadsChoices[t]; + int iters = loopsPerTest_.get(); + Fraction pshr = (Fraction)(contention_.get()); + + if (entry.isEnabled(nthreads, pshr)) { + + runOneTest(new OneTest(c, t)); + } + } + + if (++c >= TestedClass.classes.length) { + c = 0; + if (++t >= nthreadsChoices.length) + break; + } + + nextClassIdx_.set(c); + nextThreadIdx_.set(t); + + } + } + + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + finally { + endTestSeries(); + } + } + } + + static class BarrierTimer implements Runnable { + private long startTime_ = 0; + private long endTime_ = 0; + + public synchronized long getTime() { + return endTime_ - startTime_; + } + + public synchronized void run() { + long now = System.currentTimeMillis(); + if (startTime_ == 0) + startTime_ = now; + else + endTime_ = now; + } + } + + class OneTest implements Runnable { + final int clsIdx; + final int nthreadsIdx; + + OneTest(int idx, int t) { + clsIdx = idx; + nthreadsIdx = t; + } + + public void run() { + Thread.currentThread().setPriority(Thread.NORM_PRIORITY-3); + + boolean wasInterrupted = false; + + final TestedClass entry = TestedClass.classes[clsIdx]; + + final JLabel cell = (JLabel)(resultTable_[clsIdx+1][nthreadsIdx+1]); + final Color oldfg = cell.getForeground(); + + try { + + + if (Thread.interrupted()) return; + if (!threadEnabled(nthreadsIdx)) return; + + int nthreads = nthreadsChoices[nthreadsIdx]; + int iters = loopsPerTest_.get(); + Fraction pshr = (Fraction)(contention_.get()); + + if (!entry.isEnabled(nthreads, pshr)) return; + + BarrierTimer timer = new BarrierTimer(); + CyclicBarrier barrier = new CyclicBarrier(nthreads+1, timer); + + Class cls = entry.cls; + Class chanCls = entry.buffCls; + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + cell.setForeground(Color.blue); + cell.setText("RUN"); + cell.repaint(); + } + }); + } + catch (InvocationTargetException ex) { + ex.printStackTrace(); + System.exit(-1); + } + synchronized (RNG.constructionLock) { + RNG.reset(nthreads); + + if (chanCls == null) { + RNG shared = (RNG)(cls.newInstance()); + for (int k = 0; k < nthreads; ++k) { + RNG pri = (RNG)(cls.newInstance()); + TestLoop l = new TestLoop(shared, pri, pshr, iters, barrier); + Threads.pool.execute(l.testLoop()); + } + } + else { + Channel shared = (Channel)(chanCls.newInstance()); + if (nthreads == 1) { + ChanRNG single = (ChanRNG)(cls.newInstance()); + single.setSingle(true); + PCTestLoop l = new PCTestLoop(single.getDelegate(), single, pshr, + iters, barrier, + shared, shared); + Threads.pool.execute(l.testLoop(true)); + } + else if (nthreads % 2 != 0) + throw new Error("Must have even number of threads!"); + else { + int npairs = nthreads / 2; + + for (int k = 0; k < npairs; ++k) { + ChanRNG t = (ChanRNG)(cls.newInstance()); + t.setSingle(false); + Channel chan = (Channel)(chanCls.newInstance()); + + PCTestLoop l = new PCTestLoop(t.getDelegate(), t, pshr, + iters, barrier, + shared, chan); + + Threads.pool.execute(l.testLoop(false)); + Threads.pool.execute(l.testLoop(true)); + + } + } + } + + if (echoToSystemOut.get()) { + System.out.print( + entry.name + " " + + nthreads + "T " + + pshr + "S " + + RNG.computeLoops.get() + "I " + + RNG.syncMode.get() + "Lm " + + RNG.timeout.get() + "TO " + + RNG.producerMode.get() + "Pm " + + RNG.consumerMode.get() + "Cm " + + RNG.bias.get() + "B " + + DefaultChannelCapacity.get() + "C " + + RNG.exchangeParties.get() + "Xp " + + RNG.itersPerBarrier.get() + "Ib : " + ); + } + + } + + // Uncomment if AWT doesn't update right + // Thread.sleep(100); + + barrier.barrier(); // start + + barrier.barrier(); // stop + + long tm = timer.getTime(); + long totalIters = nthreads * iters; + double dns = tm * 1000.0 * PRECISION / totalIters; + long ns = Math.round(dns); + + setTime(ns, clsIdx, nthreadsIdx); + + if (echoToSystemOut.get()) { + System.out.println(formatTime(ns, true)); + } + + } + catch (BrokenBarrierException ex) { + wasInterrupted = true; + } + catch (InterruptedException ex) { + wasInterrupted = true; + Thread.currentThread().interrupt(); + } + catch (Exception ex) { + ex.printStackTrace(); + System.out.println("Construction Exception?"); + System.exit(-1); + } + finally { + final boolean clear = wasInterrupted; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (clear) cell.setText(""); + cell.setForeground(oldfg); + cell.repaint(); + } + }); + + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + endOneTest(); + } + } + } + +} + +class Threads implements ThreadFactory { + + static final SynchronizedInt activeThreads = new SynchronizedInt(0); + + static final Threads factory = new Threads(); + + static final PooledExecutor pool = new PooledExecutor(); + + static { + pool.setKeepAliveTime(10000); + pool.setThreadFactory(factory); + } + + static class MyThread extends Thread { + public MyThread(Runnable cmd) { + super(cmd); + } + + public void run() { + activeThreads.increment(); + + try { + super.run(); + } + finally { + activeThreads.decrement(); + } + } + } + + public Thread newThread(Runnable cmd) { + return new MyThread(cmd); + } +} + + + +class TestLoop { + + final RNG shared; + final RNG primary; + final int iters; + final Fraction pshared; + final CyclicBarrier barrier; + final boolean[] useShared; + final int firstidx; + + public TestLoop(RNG sh, RNG pri, Fraction pshr, int it, CyclicBarrier br) { + shared = sh; + primary = pri; + pshared = pshr; + iters = it; + barrier = br; + + firstidx = (int)(primary.get()); + + int num = (int)(pshared.numerator()); + int denom = (int)(pshared.denominator()); + + if (num == 0 || primary == shared) { + useShared = new boolean[1]; + useShared[0] = false; + } + else if (num >= denom) { + useShared = new boolean[1]; + useShared[0] = true; + } + else { + // create bool array and randomize it. + // This ensures that always same number of shared calls. + + // denom slots is too few. iters is too many. an arbitrary compromise is: + int xfactor = 1024 / denom; + if (xfactor < 1) xfactor = 1; + useShared = new boolean[denom * xfactor]; + for (int i = 0; i < num * xfactor; ++i) + useShared[i] = true; + for (int i = num * xfactor; i < denom * xfactor; ++i) + useShared[i] = false; + + for (int i = 1; i < useShared.length; ++i) { + int j = ((int) (shared.next() & 0x7FFFFFFF)) % (i + 1); + boolean tmp = useShared[i]; + useShared[i] = useShared[j]; + useShared[j] = tmp; + } + } + } + + public Runnable testLoop() { + return new Runnable() { + public void run() { + int itersPerBarrier = RNG.itersPerBarrier.get(); + try { + int delta = -1; + if (primary.getClass().equals(PrioritySemRNG.class)) { + delta = 2 - (int)((primary.get() % 5)); + } + Thread.currentThread().setPriority(Thread.NORM_PRIORITY+delta); + + int nshared = (int)(iters * pshared.asDouble()); + int nprimary = iters - nshared; + int idx = firstidx; + + barrier.barrier(); + + for (int i = iters; i > 0; --i) { + ++idx; + if (i % itersPerBarrier == 0) + primary.exchange(); + else { + + RNG r; + + if (nshared > 0 && useShared[idx % useShared.length]) { + --nshared; + r = shared; + } + else { + --nprimary; + r = primary; + } + long rnd = r.next(); + if (rnd % 2 == 0 && Thread.currentThread().isInterrupted()) + break; + } + } + } + catch (BrokenBarrierException ex) { + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + finally { + try { + barrier.barrier(); + } + catch (BrokenBarrierException ex) { + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + finally { + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + } + + } + } + }; + } +} + +class PCTestLoop extends TestLoop { + final Channel primaryChannel; + final Channel sharedChannel; + + public PCTestLoop(RNG sh, RNG pri, Fraction pshr, int it, + CyclicBarrier br, Channel shChan, Channel priChan) { + super(sh, pri, pshr, it, br); + sharedChannel = shChan; + primaryChannel = priChan; + } + + public Runnable testLoop(final boolean isProducer) { + return new Runnable() { + public void run() { + int delta = -1; + Thread.currentThread().setPriority(Thread.NORM_PRIORITY+delta); + int itersPerBarrier = RNG.itersPerBarrier.get(); + try { + + int nshared = (int)(iters * pshared.asDouble()); + int nprimary = iters - nshared; + int idx = firstidx; + + barrier.barrier(); + + ChanRNG target = (ChanRNG)(primary); + + for (int i = iters; i > 0; --i) { + ++idx; + if (i % itersPerBarrier == 0) + primary.exchange(); + else { + Channel c; + + if (nshared > 0 && useShared[idx % useShared.length]) { + --nshared; + c = sharedChannel; + } + else { + --nprimary; + c = primaryChannel; + } + + long rnd; + if (isProducer) + rnd = target.producerNext(c); + else + rnd = target.consumerNext(c); + + if (rnd % 2 == 0 && Thread.currentThread().isInterrupted()) + break; + } + } + } + catch (BrokenBarrierException ex) { + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + finally { + try { + barrier.barrier(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + catch (BrokenBarrierException ex) { + } + finally { + Thread.currentThread().setPriority(Thread.NORM_PRIORITY); + } + } + } + }; + } +} + +// ------------------------------------------------------------- + + +abstract class RNG implements Serializable, Comparable { + static final int firstSeed = 4321; + static final int rmod = 2147483647; + static final int rmul = 16807; + + static int lastSeed = firstSeed; + static final int smod = 32749; + static final int smul = 3125; + + static final Object constructionLock = RNG.class; + + // Use construction lock for all params to disable + // changes in midst of construction of test objects. + + static final SynchronizedInt computeLoops = + new SynchronizedInt(16, constructionLock); + static final SynchronizedInt syncMode = + new SynchronizedInt(0, constructionLock); + static final SynchronizedInt producerMode = + new SynchronizedInt(0, constructionLock); + static final SynchronizedInt consumerMode = + new SynchronizedInt(0, constructionLock); + static final SynchronizedInt bias = + new SynchronizedInt(0, constructionLock); + static final SynchronizedLong timeout = + new SynchronizedLong(100, constructionLock); + static final SynchronizedInt exchangeParties = + new SynchronizedInt(1, constructionLock); + static final SynchronizedInt sequenceNumber = + new SynchronizedInt(0, constructionLock); + static final SynchronizedInt itersPerBarrier = + new SynchronizedInt(0, constructionLock); + + static Rendezvous[] exchangers_; + + static void reset(int nthreads) { + synchronized(constructionLock) { + sequenceNumber.set(-1); + int parties = exchangeParties.get(); + if (nthreads < parties) parties = nthreads; + if (nthreads % parties != 0) + throw new Error("need even multiple of parties"); + exchangers_ = new Rendezvous[nthreads / parties]; + for (int i = 0; i < exchangers_.length; ++i) { + exchangers_[i] = new Rendezvous(parties); + } + } + } + + static long nextSeed() { + synchronized(constructionLock) { + long s = lastSeed; + lastSeed = (lastSeed * smul) % smod; + if (lastSeed == 0) + lastSeed = (int)(System.currentTimeMillis()); + return s; + } + } + + final int cloops = computeLoops.get(); + final int pcBias = bias.get(); + final int smode = syncMode.get(); + final int pmode = producerMode.get(); + final int cmode = consumerMode.get(); + final long waitTime = timeout.get(); + Rendezvous exchanger_ = null; + + synchronized Rendezvous getExchanger() { + if (exchanger_ == null) { + synchronized (constructionLock) { + int idx = sequenceNumber.increment(); + exchanger_ = exchangers_[idx % exchangers_.length]; + } + } + return exchanger_; + } + + public void exchange() throws InterruptedException { + Rendezvous ex = getExchanger(); + Runnable r = (Runnable)(ex.rendezvous(new UpdateCommand(this))); + if (r != null) r.run(); + } + + public int compareTo(Object other) { + int h1 = hashCode(); + int h2 = other.hashCode(); + if (h1 < h2) return -1; + else if (h1 > h2) return 1; + else return 0; + } + + protected final long compute(long l) { + int loops = (int)((l & 0x7FFFFFFF) % (cloops * 2)) + 1; + for (int i = 0; i < loops; ++i) l = (l * rmul) % rmod; + return (l == 0)? firstSeed : l; + } + + abstract protected void set(long l); + abstract protected long internalGet(); + abstract protected void internalUpdate(); + + public long get() { return internalGet(); } + public void update() { internalUpdate(); } + public long next() { internalUpdate(); return internalGet(); } +} + + +class UpdateCommand implements Runnable, Serializable, Comparable { + private final RNG obj_; + final long cmpVal; + public UpdateCommand(RNG o) { + obj_ = o; + cmpVal = o.get(); + } + + public void run() { obj_.update(); } + + public int compareTo(Object x) { + UpdateCommand u = (UpdateCommand)x; + if (cmpVal < u.cmpVal) return -1; + else if (cmpVal > u.cmpVal) return 1; + else return 0; + } +} + + +class GetFunction implements Callable { + private final RNG obj_; + public GetFunction(RNG o) { obj_ = o; } + public Object call() { return new Long(obj_.get()); } +} + +class NextFunction implements Callable { + private final RNG obj_; + public NextFunction(RNG o) { obj_ = o; } + public Object call() { return new Long(obj_.next()); } +} + + +class NoSynchRNG extends RNG { + protected long current_ = nextSeed(); + + protected void set(long l) { current_ = l; } + protected long internalGet() { return current_; } + protected void internalUpdate() { set(compute(internalGet())); } +} + +class PublicSynchRNG extends NoSynchRNG { + public synchronized long get() { return internalGet(); } + public synchronized void update() { internalUpdate(); } + public synchronized long next() { internalUpdate(); return internalGet(); } +} + +class AllSynchRNG extends PublicSynchRNG { + protected synchronized void set(long l) { current_ = l; } + protected synchronized long internalGet() { return current_; } + protected synchronized void internalUpdate() { set(compute(internalGet())); } +} + + +class AClongRNG extends RNG { + protected final SynchronizedLong acurrent_ = + new SynchronizedLong(nextSeed()); + + protected void set(long l) { throw new Error("No set allowed"); } + protected long internalGet() { return acurrent_.get(); } + + protected void internalUpdate() { + int retriesBeforeSleep = 100; + int maxSleepTime = 100; + int retries = 0; + for (;;) { + long v = internalGet(); + long n = compute(v); + if (acurrent_.commit(v, n)) + return; + else if (++retries >= retriesBeforeSleep) { + try { + Thread.sleep(n % maxSleepTime); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + retries = 0; + } + } + } + +} + +class SynchLongRNG extends RNG { + protected final SynchronizedLong acurrent_ = + new SynchronizedLong(nextSeed()); + + protected void set(long l) { acurrent_.set(l); } + protected long internalGet() { return acurrent_.get(); } + protected void internalUpdate() { set(compute(internalGet())); } + +} + +abstract class DelegatedRNG extends RNG { + protected RNG delegate_ = null; + public synchronized void setDelegate(RNG d) { delegate_ = d; } + protected synchronized RNG getDelegate() { return delegate_; } + + public long get() { return getDelegate().get(); } + public void update() { getDelegate().update(); } + public long next() { return getDelegate().next(); } + + protected void set(long l) { throw new Error(); } + protected long internalGet() { throw new Error(); } + protected void internalUpdate() { throw new Error(); } + +} + +class SDelegatedRNG extends DelegatedRNG { + public SDelegatedRNG() { setDelegate(new NoSynchRNG()); } + public synchronized long get() { return getDelegate().get(); } + public synchronized void update() { getDelegate().update(); } + public synchronized long next() { return getDelegate().next(); } +} + + +class SyncDelegatedRNG extends DelegatedRNG { + protected final Sync cond_; + public SyncDelegatedRNG(Sync c) { + cond_ = c; + setDelegate(new NoSynchRNG()); + } + + + protected final void acquire() throws InterruptedException { + if (smode == 0) { + cond_.acquire(); + } + else { + while (!cond_.attempt(waitTime)) {} + } + } + + public long next() { + try { + acquire(); + + getDelegate().update(); + long l = getDelegate().get(); + cond_.release(); + return l; + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return 0; + } + } + + public long get() { + try { + acquire(); + long l = getDelegate().get(); + cond_.release(); + return l; + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return 0; + } + } + + public void update() { + try { + acquire(); + getDelegate().update(); + cond_.release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + } + } + + +} + +class MutexRNG extends SyncDelegatedRNG { + public MutexRNG() { super(new Mutex()); } +} + + +class SemRNG extends SyncDelegatedRNG { + public SemRNG() { super(new Semaphore(1)); } +} + +class WpSemRNG extends SyncDelegatedRNG { + public WpSemRNG() { super(new WaiterPreferenceSemaphore(1)); } +} + +class FifoRNG extends SyncDelegatedRNG { + public FifoRNG() { super(new FIFOSemaphore(1)); } +} + +class PrioritySemRNG extends SyncDelegatedRNG { + public PrioritySemRNG() { super(new PrioritySemaphore(1)); } +} + +class RlockRNG extends SyncDelegatedRNG { + public RlockRNG() { super(new ReentrantLock()); } +} + + +class RWLockRNG extends NoSynchRNG { + protected final ReadWriteLock lock_; + public RWLockRNG(ReadWriteLock l) { + lock_ = l; + } + + protected final void acquireR() throws InterruptedException { + if (smode == 0) { + lock_.readLock().acquire(); + } + else { + while (!lock_.readLock().attempt(waitTime)) {} + } + } + + protected final void acquireW() throws InterruptedException { + if (smode == 0) { + lock_.writeLock().acquire(); + } + else { + while (!lock_.writeLock().attempt(waitTime)) {} + } + } + + + public long next() { + long l = 0; + try { + acquireR(); + l = current_; + lock_.readLock().release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return 0; + } + + l = compute(l); + + try { + acquireW(); + set(l); + lock_.writeLock().release(); + return l; + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return 0; + } + } + + + public long get() { + try { + acquireR(); + long l = current_; + lock_.readLock().release(); + return l; + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return 0; + } + } + + public void update() { + long l = 0; + + try { + acquireR(); + l = current_; + lock_.readLock().release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return; + } + + l = compute(l); + + try { + acquireW(); + set(l); + lock_.writeLock().release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + } + } + +} + +class WpRWlockRNG extends RWLockRNG { + public WpRWlockRNG() { super(new WriterPreferenceReadWriteLock()); } +} + +class ReaderPrefRWlockRNG extends RWLockRNG { + public ReaderPrefRWlockRNG() { + super(new ReaderPreferenceReadWriteLock()); + } + + +} + +class FIFORWlockRNG extends RWLockRNG { + public FIFORWlockRNG() { super(new FIFOReadWriteLock()); } +} + + +class ReentrantRWlockRNG extends RWLockRNG { + public ReentrantRWlockRNG() { + super(new ReentrantWriterPreferenceReadWriteLock()); + } + + public void update() { // use embedded acquires + long l = 0; + + try { + acquireW(); + + try { + acquireR(); + l = current_; + lock_.readLock().release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + return; + } + + l = compute(l); + + set(l); + lock_.writeLock().release(); + } + catch(InterruptedException x) { + Thread.currentThread().interrupt(); + } + } + +} + + +abstract class ExecutorRNG extends DelegatedRNG { + Executor executor_; + + + synchronized void setExecutor(Executor e) { executor_ = e; } + synchronized Executor getExecutor() { return executor_; } + + Runnable delegatedUpdate_ = null; + Callable delegatedNext_ = null; + + synchronized Runnable delegatedUpdateCommand() { + if (delegatedUpdate_ == null) + delegatedUpdate_ = new UpdateCommand(getDelegate()); + return delegatedUpdate_; + } + + synchronized Callable delegatedNextFunction() { + if (delegatedNext_ == null) + delegatedNext_ = new NextFunction(getDelegate()); + return delegatedNext_; + } + + public void update() { + try { + getExecutor().execute(delegatedUpdateCommand()); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + + // Each call to next gets result of previous future + FutureResult nextResult_ = null; + + public synchronized long next() { + long res = 0; + try { + if (nextResult_ == null) { // direct call first time through + nextResult_ = new FutureResult(); + nextResult_.set(new Long(getDelegate().next())); + } + FutureResult currentResult = nextResult_; + + nextResult_ = new FutureResult(); + Runnable r = nextResult_.setter(delegatedNextFunction()); + getExecutor().execute(r); + + res = ((Long)(currentResult.get())).longValue(); + + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + catch (InvocationTargetException ex) { + ex.printStackTrace(); + throw new Error("Bad Callable?"); + } + return res; + } +} + +class DirectExecutorRNG extends ExecutorRNG { + public DirectExecutorRNG() { + setDelegate(new PublicSynchRNG()); + setExecutor(new DirectExecutor()); + } +} + +class LockedSemRNG extends ExecutorRNG { + public LockedSemRNG() { + setDelegate(new NoSynchRNG()); + setExecutor(new LockedExecutor(new Semaphore(1))); + } +} + +class QueuedExecutorRNG extends ExecutorRNG { + static final QueuedExecutor exec = new QueuedExecutor(); + static { exec.setThreadFactory(Threads.factory); } + public QueuedExecutorRNG() { + setDelegate(new PublicSynchRNG()); + setExecutor(exec); + } +} + +class ForcedStartRunnable implements Runnable { + protected final Latch latch_ = new Latch(); + protected final Runnable command_; + + ForcedStartRunnable(Runnable command) { command_ = command; } + + public Latch started() { return latch_; } + + public void run() { + latch_.release(); + command_.run(); + } +} + + +class ForcedStartThreadedExecutor extends ThreadedExecutor { + public void execute(Runnable command) throws InterruptedException { + ForcedStartRunnable wrapped = new ForcedStartRunnable(command); + super.execute(wrapped); + wrapped.started().acquire(); + } +} + +class ThreadedExecutorRNG extends ExecutorRNG { + static final ThreadedExecutor exec = new ThreadedExecutor(); + static { exec.setThreadFactory(Threads.factory); } + + public ThreadedExecutorRNG() { + setDelegate(new PublicSynchRNG()); + setExecutor(exec); + } +} + + +class PooledExecutorRNG extends ExecutorRNG { + static final PooledExecutor exec = Threads.pool; + + public PooledExecutorRNG() { + setDelegate(new PublicSynchRNG()); + setExecutor(exec); + } +} + + +class ChanRNG extends DelegatedRNG { + + boolean single_; + + ChanRNG() { + setDelegate(new PublicSynchRNG()); + } + + public synchronized void setSingle(boolean s) { single_ = s; } + public synchronized boolean isSingle() { return single_; } + + public long producerNext(Channel c) throws InterruptedException { + RNG r = getDelegate(); + if (isSingle()) { + c.put(r); + r = (RNG)(c.take()); + r.update(); + } + else { + if (pcBias < 0) { + r.update(); + r.update(); // update consumer side too + } + else if (pcBias == 0) { + r.update(); + } + + if (pmode == 0) { + c.put(r); + } + else { + while (!(c.offer(r, waitTime))) {} + } + } + return r.get(); + } + + public long consumerNext(Channel c) throws InterruptedException { + RNG r = null; + if (cmode == 0) { + r = (RNG)(c.take()); + } + else { + while (r == null) r = (RNG)(c.poll(waitTime)); + } + + if (pcBias == 0) { + r.update(); + } + else if (pcBias > 0) { + r.update(); + r.update(); + } + return r.get(); + } +} +