package io.cattle.platform.lock.impl; import io.cattle.platform.async.utils.AsyncUtils; import io.cattle.platform.lock.Lock; import io.cattle.platform.lock.LockDelegator; import io.cattle.platform.lock.LockManager; import io.cattle.platform.lock.definition.BlockingLockDefinition; import io.cattle.platform.lock.definition.LockDefinition; import io.cattle.platform.lock.definition.MultiLockDefinition; import io.cattle.platform.lock.provider.LockProvider; import io.cattle.platform.util.type.InitializationTask; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import javax.inject.Inject; import org.apache.cloudstack.managed.context.NoExceptionRunnable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.SettableFuture; public class LockDelegatorImpl implements LockDelegator, InitializationTask { private static final Logger log = LoggerFactory.getLogger(LockDelegatorImpl.class); LockManager lockManager; Map<String, Lock> holding = new ConcurrentHashMap<String, Lock>(); BlockingQueue<LockOp> ops = new LinkedBlockingQueue<LockOp>(); ExecutorService executorService; boolean shutdown = false; @Override public boolean isLocked(LockDefinition lockDef) { if (!acceptableLock(lockDef)) { return false; } return holding.containsKey(lockDef.getLockId()); } @Override public boolean tryLock(LockDefinition lockDef) { return doOp(lockDef, true); } @Override public boolean unlock(LockDefinition lockDef) { return doOp(lockDef, false); } protected boolean doOp(LockDefinition lockDef, boolean lock) { SettableFuture<Boolean> future = SettableFuture.create(); ops.add(new LockOp(lockDef, lock, future)); return AsyncUtils.get(future); } @Override public void start() { executorService.execute(new NoExceptionRunnable() { @Override protected void doRun() throws Exception { runLoop(); } }); } public void stop() { shutdown = true; } protected void runLoop() { /* This loop should never end, unless it has been shutdown */ LockOp op = null; while (true) { try { op = ops.take(); try { if (op.lock) { lock(op); } else { unlock(op); } } catch (Throwable t) { op.future.set(false); log.error("Exception in lock delegator, lockdef [{}]", op.lock, t); } } catch (Throwable t) { if (shutdown) { return; } try { Thread.sleep(2000); } catch (InterruptedException e) { log.info("Interrupted", e); } } if (shutdown) { return; } } } protected boolean acceptableLock(LockDefinition lockDef) { if (lockDef instanceof MultiLockDefinition) { log.error("Can not lock a multilock with a lock delegator"); return false; } if (lockDef instanceof BlockingLockDefinition) { log.error("Can not lock a blocking lock with a lock delegator"); return false; } return true; } protected void lock(LockOp op) { if (!acceptableLock(op.def)) { op.future.set(false); return; } if (holding.containsKey(op.def.getLockId())) { op.future.set(true); return; } LockProvider lockProvider = lockManager.getLockProvider(); boolean success = false; Lock lock = lockProvider.getLock(op.def); try { success = lock.tryLock(); if (success) { log.trace("Acquired lock [{}]", op.def.getLockId()); } } finally { if (success) { holding.put(op.def.getLockId(), lock); op.future.set(true); } else { op.future.set(false); lockProvider.releaseLock(lock); } } } protected void unlock(LockOp op) { Lock lock = holding.get(op.def.getLockId()); if (lock != null) { LockProvider lockProvider = lockManager.getLockProvider(); lock.unlock(); log.trace("Released lock [{}]", op.def.getLockId()); holding.remove(op.def.getLockId()); lockProvider.releaseLock(lock); } op.future.set(true); } public LockManager getLockManager() { return lockManager; } @Inject public void setLockManager(LockManager lockManager) { this.lockManager = lockManager; } public ExecutorService getExecutorService() { return executorService; } public void setExecutorService(ExecutorService executorService) { this.executorService = executorService; } private static final class LockOp { LockDefinition def; boolean lock; SettableFuture<Boolean> future; public LockOp(LockDefinition def, boolean lock, SettableFuture<Boolean> future) { super(); this.def = def; this.lock = lock; this.future = future; } } }