blob: b87a74a11488f5d4965bed76c134ff6f7a09f414 [file] [log] [blame]
/*
* 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
*/
@Override 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
*/
@Override 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();
}