/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.engine.cache; import java.util.concurrent.BlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.OpenGammaRuntimeException; import com.sleepycat.je.Environment; import com.sleepycat.je.Transaction; /** * A worker thread that will perform all Berkeley I/O operations. Berkeley DB doesn't like its threads getting interrupted so we have to isolate it from the OpenGamma calculation and worker threads * that use interrupts to cancel running jobs. */ public class AbstractBerkeleyDBWorker implements Runnable { private static final Logger s_logger = LoggerFactory.getLogger(AbstractBerkeleyDBWorker.class); /** * Requests that this worker will satisfy. */ public abstract static class Request { private boolean _done; protected final synchronized void waitFor() { try { while (!_done) { wait(); } } catch (InterruptedException e) { throw new OpenGammaRuntimeException("Interrupted", e); } } protected final synchronized void signal() { _done = true; notify(); } protected abstract void runInTransaction(AbstractBerkeleyDBWorker worker); } /** * A request to stop this worker. */ public static final class PoisonRequest extends Request { @Override protected void runInTransaction(AbstractBerkeleyDBWorker worker) { worker.poison(this); } } private final Environment _environment; private final BlockingQueue<Request> _requests; private Transaction _transaction; private PoisonRequest _poisoned; public AbstractBerkeleyDBWorker(final Environment environment, final BlockingQueue<Request> requests) { _environment = environment; _requests = requests; } private void poison(final PoisonRequest poison) { _poisoned = poison; } protected Transaction getTransaction() { return _transaction; } @Override public void run() { s_logger.info("Worker started"); _poisoned = null; do { Request req = null; try { req = _requests.take(); if (req == null) { s_logger.info("Worker poisoned"); return; } boolean rollback = true; _transaction = (_environment != null) ? _environment.beginTransaction(null, null) : null; try { do { req.runInTransaction(this); req.signal(); req = _requests.poll(); } while (req != null); rollback = false; } finally { if (_transaction != null) { if (rollback) { s_logger.error("Rolling back transaction"); _transaction.abort(); } else { _transaction.commit(); } _transaction = null; } } } catch (Throwable t) { s_logger.error("Caught exception", t); } finally { if (req != null) { req.signal(); } } } while (_poisoned == null); // If there are other workers, put the poison back in the queue for them _requests.add(_poisoned); } }