/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite; import java.io.Closeable; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * This interface provides a rich API for working with distributed reentrant locks. * <p> * <h1 class="header">Functionality</h1> * Distributed reentrant lock provides functionality similar to {@code java.util.concurrent.ReentrantLock}. * <h1 class="header">Creating Distributed ReentrantLock</h1> * Instance of cache reentrant lock can be created by calling the following method: * {@link Ignite#reentrantLock(String, boolean, boolean, boolean)}. * <h1 class="header">Protection from failover</h1> * Ignite lock can automatically recover from node failure. * <ul> * <li>If failoverSafe flag is set to true upon creation, * in case a node owning the lock fails, lock will be automatically released and become available for threads on other * nodes to acquire. No exception will be thrown. * <li>If failoverSafe flag is set to false upon creation, * in case a node owning the lock fails, {@code IgniteException} will be thrown on every other node attempting to * perform any operation on this lock. No automatic recovery will be attempted, * and lock will be marked as broken (i.e. unusable), which can be checked using the method #isBroken(). * Broken lock cannot be reused again. * </ul> * * <h1 class="header">Implementation issues</h1> * Ignite lock comes in two flavours: fair and non-fair. Non-fair lock assumes no ordering should be imposed * on acquiring threads; in case of contention, threads from all nodes compete for the lock once the lock is released. * In most cases this is the desired behaviour. However, in some cases, using the non-fair lock can lead to uneven load * distribution among nodes. * Fair lock solves this issue by imposing strict FIFO ordering policy at a cost of an additional transaction. * This ordering does not guarantee fairness of thread scheduling (similar to {@code java.util.concurrent.ReentrantLock}). * Thus, one of many threads on any node using a fair lock may obtain it multiple times in succession while other * active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock method * does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting. * * </p> * As a rule of thumb, whenever there is a reasonable time window between successive calls to release and acquire * the lock, non-fair lock should be preferred: * * <pre> {@code * while(someCondition){ * // do anything * lock.lock(); * try{ * // ... * } * finally { * lock.unlock(); * } * } * }</pre> * * If successive calls to release/acquire are following immediately, * e.g. * * <pre> {@code * while(someCondition){ * lock.lock(); * try { * // do something * } * finally { * lock.unlock(); * } * } * }</pre> * * using the fair lock is reasonable in order to allow even distribution of load among nodes * (although overall throughput may be lower due to increased overhead). * */ public interface IgniteLock extends Lock, Closeable { /** * Name of atomic reentrant lock. * * @return Name of atomic reentrant lock. */ public String name(); /** * Acquires the distributed reentrant lock. * * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * * <p>If the current thread already holds this lock then the hold count * is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of four things happens: * * <ul> * * <li>The lock is acquired by the current thread; or * * <li>Lock is broken (any node failed while owning this lock), and lock is created in * non-failoverSafe mode. * * <li>Local node is stopped. * * @throws IgniteException if the node is stopped or broken in non-failoverSafe mode */ void lock() throws IgniteException; /** * Acquires the lock unless the current thread is * {@linkplain Thread#interrupt interrupted}. * * <p>Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * * <p>If the current thread already holds this lock then the hold count * is incremented by one and the method returns immediately. * * <p>If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of four things happens: * * <ul> * * <li>The lock is acquired by the current thread; or * * <li>Some other thread {@linkplain Thread#interrupt interrupts} the * current thread. * * <li>Lock is broken (any node failed while owning this lock), and lock is created in * non-failoverSafe mode. * * <li>Local node is stopped. * * </ul> * * <p>If the lock is acquired by the current thread then the lock hold * count is set to one. * * <p>If the current thread: * * <ul> * * <li>has its interrupted status set on entry to this method; or * * <li>is {@linkplain Thread#interrupt interrupted} while acquiring * the lock; or * * then {@link IgniteInterruptedException} is thrown and the current thread's * interrupted status is cleared. * * </ul> * * <p>{@link IgniteException} is thrown in case: * * <ul> * * <li>the lock is broken before or during the attempt to acquire this lock; or * * <li>local node is stopped, * * </ul> * * <p>In this implementation, as this method is an explicit * interruption point, preference is given to responding to the * interrupt over normal or reentrant acquisition of the lock. * * @throws IgniteInterruptedException if the current thread is interrupted * @throws IgniteException if the lock is broken in non-failoverSafe mode (any node failed while owning this lock), * or local node is stopped */ @Override public void lockInterruptibly() throws IgniteInterruptedException, IgniteException; /** * Acquires the lock only if it is free at the time of invocation. * * <p>Acquires the lock if it is available and returns immediately * with the value {@code true}. * If the lock is not available then this method will return * immediately with the value {@code false}. * * <p>A typical usage idiom for this method would be: * <pre> {@code * Lock lock = ...; * if (lock.tryLock()) { * try { * // manipulate protected state * } finally { * lock.unlock(); * } * } else { * // perform alternative actions * }}</pre> * * This usage ensures that the lock is unlocked if it was acquired, and * doesn't try to unlock if the lock was not acquired. * * If node is stopped, or any node failed while owning the lock in non-failoverSafe mode, * then {@link IgniteException} is thrown. * * @return {@code true} if the lock was acquired and * {@code false} otherwise * * @throws IgniteException if node is stopped, or lock is already broken in non-failover safe mode */ @Override public boolean tryLock() throws IgniteException; /** * Acquires the lock if it is not held by another thread within the given * waiting time and the current thread has not been * {@linkplain Thread#interrupt interrupted}. * * <p>Acquires the lock if it is not held by another thread and returns * immediately with the value {@code true}, setting the lock hold count * to one. * * <p>If the current thread * already holds this lock then the hold count is incremented by one and * the method returns {@code true}. * * <p>If the lock is held by another thread then the * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of five things happens: * * <ul> * * <li>The lock is acquired by the current thread; or * * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * * <li>Lock is broken (any node failed while owning this lock), and lock is created in * non-failoverSafe mode. * * <li>Local node is stopped. * * <li>The specified waiting time elapses * * </ul> * * <p>If the lock is acquired then the value {@code true} is returned and * the lock hold count is set to one. * * <p>If the current thread: * * <ul> * * <li>has its interrupted status set on entry to this method; or * * <li>is {@linkplain Thread#interrupt interrupted} while * acquiring the lock; or * * </ul> * then {@link IgniteInterruptedException} is thrown and the current thread's * interrupted status is cleared. * * <p>{@link IgniteException} is thrown in case: * * <ul> * * <li>the lock is broken before or during the attempt to acquire this lock; or * * <li>local node is stopped, * * </ul> * * <p>If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * <p>In this implementation, as this method is an explicit * interruption point, preference is given to responding to the * interrupt over normal or reentrant acquisition of the lock, and * over reporting the elapse of the waiting time. * * @param timeout the time to wait for the lock * @param unit the time unit of the timeout argument * @return {@code true} if the lock was free and was acquired by the * current thread, or the lock was already held by the current * thread; and {@code false} if the waiting time elapsed before * the lock could be acquired * @throws IgniteInterruptedException if the current thread is interrupted * @throws IgniteException if node is stopped, or lock is already broken in non-failover safe mode * @throws NullPointerException if the time unit is null */ @Override public boolean tryLock(long timeout, TimeUnit unit) throws IgniteInterruptedException, IgniteException; /** * Releases the lock. * * If lock is not owned by current thread, then an {@link * IllegalMonitorStateException} is thrown. * If lock is already broken prior to invocation of this method, and * lock is created in non-failover safe mode, then {@link IgniteException} is thrown. * * @throws IllegalMonitorStateException if not owned by current thread * @throws IgniteException if node is stopped, or lock is already broken in non-failover safe mode */ void unlock() throws IgniteInterruptedException; /** * Returns a {@link Condition} instance for use with this * {@link IgniteLock} instance. * * <ul> * * <li>If this lock is not held when any of the {@link Condition} * {@linkplain Condition#await() waiting} or {@linkplain * Condition#signal signalling} methods are called, then an {@link * IllegalMonitorStateException} is thrown. * * <li>When the condition {@linkplain Condition#await() waiting} * methods are called the lock is released and, before they * return, the lock is reacquired and the lock hold count restored * to what it was when the method was called. * * <li>If a thread is {@linkplain Thread#interrupt interrupted} * while waiting then the wait will terminate, an {@link * IgniteInterruptedException} will be thrown, and the thread's * interrupted status will be cleared. * * <li> Waiting threads are signalled in FIFO order. * * </ul> * * @param name Name of the distributed condition object * * @return the Condition object * @throws IgniteException if the lock is not initialized or already removed */ public IgniteCondition getOrCreateCondition(String name) throws IgniteException; /** * This method is not supported in IgniteLock, * Any invocation of this method will result in {@linkplain UnsupportedOperationException}. * Correct way to obtain Condition object is through method {@linkplain IgniteLock#getOrCreateCondition(String)} * */ @Override public Condition newCondition(); /** * Queries the number of holds on this lock by the current thread. * * @return the number of holds on this lock by the current thread, * or zero if this lock is not held by the current thread * @throws IgniteException if the lock is not initialized or already removed */ public int getHoldCount() throws IgniteException; /** * Queries if this lock is held by the current thread. * * @return {@code true} if current thread holds this lock and * {@code false} otherwise * @throws IgniteException if the lock is not initialized or already removed */ public boolean isHeldByCurrentThread() throws IgniteException; /** * Queries if this lock is held by any thread on any node. This method is * designed for use in monitoring of the system state, * not for synchronization control. * * @return {@code true} if any thread on this or any other node holds this lock and * {@code false} otherwise * @throws IgniteException if the lock is not initialized or already removed */ public boolean isLocked() throws IgniteException; /** * Queries whether any threads on this node are waiting to acquire this lock. Note that * because cancellations may occur at any time, a {@code true} * return does not guarantee that any other thread will ever * acquire this lock. This method is designed primarily for use in * monitoring of the system state. * * @return {@code true} if there may be other threads on this node waiting to * acquire the lock * @throws IgniteException if the lock is not initialized or already removed */ public boolean hasQueuedThreads() throws IgniteException; /** * Queries whether the given thread is waiting to acquire this * lock. Note that because cancellations may occur at any time, a * {@code true} return does not guarantee that this thread * will ever acquire this lock. This method is designed primarily for use * in monitoring of the system state. * * @param thread the thread * @return {@code true} if the given thread is queued waiting for this lock * @throws NullPointerException if the thread is null * @throws IgniteException if the lock is not initialized or already removed */ public boolean hasQueuedThread(Thread thread) throws IgniteException; /** * Queries whether any threads on this node are waiting on the given condition * associated with this lock. Note that because timeouts and * interrupts may occur at any time, a {@code true} return does * not guarantee that a future {@code signal} will awaken any * threads. This method is designed primarily for use in * monitoring of the system state. * * @param condition the condition * @return {@code true} if there are any waiting threads on this node * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is * not associated with this lock * @throws NullPointerException if the condition is null * @throws IgniteException if the lock is not initialized or already removed */ public boolean hasWaiters(IgniteCondition condition) throws IgniteException; /** * Returns an estimate of the number of threads on this node that are waiting on the * given condition associated with this lock. Note that because * timeouts and interrupts may occur at any time, the estimate * serves only as an upper bound on the actual number of waiters. * This method is designed for use in monitoring of the system * state, not for synchronization control. * * @param condition the condition * @return the estimated number of waiting threads on this node * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is * not associated with this lock * @throws NullPointerException if the condition is null * @throws IgniteException if the lock is not initialized or already removed */ public int getWaitQueueLength(IgniteCondition condition) throws IgniteException; /** * Returns {@code true} if this lock is safe to use after node failure. * If not, IgniteInterruptedException is thrown on every other node after node failure. * * @return {@code true} if this reentrant lock has failoverSafe set true * @throws IgniteException if the lock is not initialized or already removed */ public boolean isFailoverSafe(); /** * Returns {@code true} if this lock is fair. Fairness flag can only be set on lock creation. * * @return {@code true} if this reentrant lock has fairness flag set true. * @throws IgniteException if the lock is not initialized or already removed */ public boolean isFair(); /** * Returns true if any node that owned the locked failed before releasing the lock. * * @return true if any node failed while owning the lock since the lock on this node was initialized. * @throws IgniteException if the lock is not initialized or already removed */ public boolean isBroken() throws IgniteException; /** * Gets status of reentrant lock. * * @return {@code true} if reentrant lock was removed from cache, {@code false} in other case. */ public boolean removed(); /** * Removes reentrant lock. * * @throws IgniteException If operation failed. */ @Override public void close(); }