/* * Copyright 2014 Red Hat, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.core.shareddata.impl; import io.vertx.core.*; import io.vertx.core.shareddata.Lock; import java.util.LinkedList; import java.util.Queue; /** * @author <a href="http://tfox.org">Tim Fox</a> */ public class AsynchronousLock implements Lock { private final Vertx vertx; private final Queue<LockWaiter> waiters = new LinkedList<>(); private boolean owned; public AsynchronousLock(Vertx vertx) { this.vertx = vertx; } public void acquire(long timeout, Handler<AsyncResult<Lock>> resultHandler) { Context context = vertx.getOrCreateContext(); doAcquire(context, timeout, resultHandler); } @Override public synchronized void release() { LockWaiter waiter = pollWaiters(); if (waiter != null) { waiter.acquire(this); } else { owned = false; } } public void doAcquire(Context context, long timeout, Handler<AsyncResult<Lock>> resultHandler) { synchronized (this) { if (!owned) { // We now have the lock owned = true; lockAcquired(context, resultHandler); } else { waiters.add(new LockWaiter(this, context, timeout, resultHandler)); } } } private void lockAcquired(Context context, Handler<AsyncResult<Lock>> resultHandler) { context.runOnContext(v -> resultHandler.handle(Future.succeededFuture(this))); } private LockWaiter pollWaiters() { while (true) { LockWaiter waiter = waiters.poll(); if (waiter == null) { return null; } else if (!waiter.timedOut) { return waiter; } } } private static class LockWaiter { final AsynchronousLock lock; final Context context; final Handler<AsyncResult<Lock>> resultHandler; volatile boolean timedOut; volatile boolean acquired; LockWaiter(AsynchronousLock lock, Context context, long timeout, Handler<AsyncResult<Lock>> resultHandler) { this.lock = lock; this.context = context; this.resultHandler = resultHandler; if (timeout != Long.MAX_VALUE) { context.owner().setTimer(timeout, tid -> timedOut()); } } void timedOut() { synchronized (lock) { if (!acquired) { timedOut = true; context.runOnContext(v -> resultHandler.handle(Future.failedFuture(new VertxException("Timed out waiting to get lock")))); } } } void acquire(AsynchronousLock lock) { acquired = true; lock.lockAcquired(context, resultHandler); } } }