package edu.brown.api; import java.io.IOException; import org.apache.log4j.Logger; import org.voltdb.client.Client; import edu.brown.logging.LoggerUtil; import edu.brown.logging.LoggerUtil.LoggerBoolean; import edu.brown.profilers.ProfileMeasurement; import edu.brown.utils.ThreadUtil; /** * Thread that executes the derives classes run loop which invokes stored * procedures indefinitely */ class ControlWorker extends Thread { private static final Logger LOG = Logger.getLogger(ControlWorker.class); private static final LoggerBoolean debug = new LoggerBoolean(); static { LoggerUtil.setupLogging(); LoggerUtil.attachObserver(LOG, debug); } /** * */ private final BenchmarkComponent cmp; /** * Time in milliseconds since requests were last sent. */ private long m_lastRequestTime; private boolean profiling = false; private ProfileMeasurement execute_time = new ProfileMeasurement("EXECUTE"); private ProfileMeasurement block_time = new ProfileMeasurement("BLOCK"); /** * Constructor * @param benchmarkComponent */ public ControlWorker(BenchmarkComponent benchmarkComponent) { cmp = benchmarkComponent; } @Override public void run() { Thread self = Thread.currentThread(); self.setName(String.format("worker-%03d", cmp.getClientId())); cmp.invokeStartCallback(); try { if (cmp.m_txnRate == -1) { if (cmp.m_sampler != null) { cmp.m_sampler.start(); } cmp.runLoop(); } else { if (debug.val) LOG.debug(String.format("Running rate controlled [m_txnRate=%d, m_txnsPerMillisecond=%f]", cmp.m_txnRate, cmp.m_txnsPerMillisecond)); this.rateControlledRunLoop(); } } catch (Throwable ex) { ex.printStackTrace(); throw new RuntimeException(ex); } finally { if (cmp.m_exitOnCompletion) { if (debug.val) LOG.debug(String.format("Stopping %s thread [id=%d]", this.getClass().getSimpleName(), cmp.getClientId())); return; } } } private void rateControlledRunLoop() throws InterruptedException { final Client client = cmp.getClientHandle(); m_lastRequestTime = System.currentTimeMillis(); boolean hadErrors = false; boolean bp = false; while (true) { // If there is back pressure don't send any requests. Update the // last request time so that a large number of requests won't // queue up to be sent when there is no longer any back // pressure. if (bp) { if (this.profiling) this.block_time.start(); try { client.backpressureBarrier(); } finally { if (this.profiling) this.block_time.stop(); } bp = false; } // Check whether we are currently being paused // We will block until we're allowed to go again if (cmp.m_controlState == ControlState.PAUSED) { if (debug.val) LOG.debug("Pausing until control lock is released"); cmp.m_pauseLock.acquire(); if (debug.val) LOG.debug("Control lock is released! Resuming execution! Tiger style!"); } assert(cmp.m_controlState != ControlState.PAUSED) : "Unexpected " + cmp.m_controlState; // Generate the correct number of transactions based on how much // time has passed since the last time transactions were sent. final long now = System.currentTimeMillis(); final long delta = now - m_lastRequestTime; if (delta > 0) { final int transactionsToCreate = (int) (delta * cmp.m_txnsPerMillisecond); if (transactionsToCreate < 1) { Thread.sleep(25); continue; } if (debug.val) LOG.debug(String.format("Submitting %d txn requests from client #%d", transactionsToCreate, cmp.getClientId())); if (this.profiling) execute_time.start(); try { for (int ii = 0; ii < transactionsToCreate; ii++) { bp = !cmp.runOnce(); if (bp || cmp.m_controlState != ControlState.RUNNING) { break; } } // FOR } catch (final IOException e) { if (hadErrors) return; hadErrors = true; // HACK: Sleep for a little bit to give time for the site logs to flush // if (debug.val) LOG.error("Failed to execute transaction: " + e.getMessage(), e); ThreadUtil.sleep(5000); } finally { if (this.profiling) execute_time.stop(); } } else { Thread.sleep(25); } m_lastRequestTime = now; } // WHILE } public void enableProfiling(boolean val) { this.profiling = val; } public ProfileMeasurement getExecuteTime() { return (execute_time); } public ProfileMeasurement getBlockedTime() { return (this.block_time); } }