All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.cp.ISemaphore Maven / Gradle / Ivy

There is a newer version: 5.0-BETA-1
Show newest version
/*
 * Copyright (c) 2008-2021, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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 com.hazelcast.cp;

import com.hazelcast.config.cp.CPSubsystemConfig;
import com.hazelcast.config.cp.SemaphoreConfig;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.cp.lock.FencedLock;
import com.hazelcast.cp.session.CPSession;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * ISemaphore is a fault-tolerant distributed alternative to the
 * {@link Semaphore}. Semaphores are often used to restrict the number of
 * threads than can access some physical or logical resource.
 * 

* ISemaphore is a cluster-wide counting semaphore. Conceptually, it maintains * a set of permits. Each {@link #acquire()} blocks if necessary until a permit * is available, and then takes it. Dually, each {@link #release()} adds a * permit, potentially releasing a blocking acquirer. However, no actual permit * objects are used; the semaphore just keeps a count of the number available * and acts accordingly. *

* Hazelcast's distributed semaphore implementation guarantees that threads * invoking any of the {@link #acquire() acquire} methods are selected to * obtain permits in the order of their invocations (first-in-first-out; FIFO). * Note that FIFO ordering implies the order which the primary replica of an * {@link ISemaphore} receives these acquire requests. Therefore, it is * possible for one member to invoke {@code acquire} before another member, * but its request hits the primary replica after the other member. *

* This class also provides convenience methods, such as * {@link #acquire(int) acquire} and {@link #release(int) release}, to work * with multiple permits at once. Beware of the increased risk of * indefinite postponement when using the multiple-permit acquire. If permits * are released one by one, a thread waiting for one permit will acquire * it before a thread waiting for multiple permits regardless of the call order. *

* Correct usage of a semaphore is established by programming convention * in the application. *

* {@link ISemaphore} is accessed via {@link CPSubsystem#getSemaphore(String)}. * It works on top of the Raft consensus algorithm. It offers linearizability during crash * failures and network partitions. It is CP with respect to the CAP principle. * If a network partition occurs, it remains available on at most one side of * the partition. *

* It has 2 variations: *

    *
  • * The default impl accessed via {@link CPSubsystem} is session-aware. In this * one, when a caller makes its very first {@link #acquire()} call, it starts * a new CP session with the underlying CP group. Then, liveliness of the * caller is tracked via this CP session. When the caller fails, permits * acquired by this HazelcastInstance are automatically and safely released. * However, the session-aware version comes with a limitation, that is, * a HazelcastInstance cannot release permits before acquiring them * first. In other words, a HazelcastInstance can release only * the permits it has acquired earlier. It means, you can acquire a permit * from one thread and release it from another thread using the same * HazelcastInstance, but not different instances of HazelcastInstance. This * behaviour is not compatible with {@link Semaphore#release()}. You can use * the session-aware CP {@link ISemaphore} impl by disabling JDK compatibility * via {@link SemaphoreConfig#setJDKCompatible(boolean)}. Although * the session-aware impl has a minor difference to the JDK Semaphore, we think * it is a better fit for distributed environments because of its safe * auto-cleanup mechanism for acquired permits. Please see * {@link CPSession} for the rationale behind the session mechanism. *
  • *
  • * The second impl offered by {@link CPSubsystem} is sessionless. This impl * does not perform auto-cleanup of acquired permits on failures. Acquired * permits are not bound to HazelcastInstance and permits can be released without * acquiring first. This one is compatible with {@link Semaphore#release()}. * However, you need to handle failed permit owners on your own. If a Hazelcast * server or a client fails while holding some permits, they will not be * automatically released. You can use the sessionless CP {@link ISemaphore} * impl by enabling JDK compatibility via * {@link SemaphoreConfig#setJDKCompatible(boolean)}. *
  • *
*

* There is a subtle difference between the lock and semaphore abstractions. * A lock can be assigned to at most one endpoint at a time, so we have a total * order among its holders. However, permits of a semaphore can be assigned to * multiple endpoints at a time, which implies that we may not have a total * order among permit holders. In fact, permit holders are partially ordered. * For this reason, the fencing token approach, which is explained in * {@link FencedLock}, does not work for the semaphore abstraction. Moreover, * each permit is an independent entity. Multiple permit acquires and reentrant * lock acquires of a single endpoint are not equivalent. The only case where * a semaphore behaves like a lock is the binary case, where the semaphore has * only 1 permit. In this case, the semaphore works like a non-reentrant lock. *

* All of the API methods in the new CP {@link ISemaphore} impl offer * the exactly-once execution semantics for the session-aware version. * For instance, even if a {@link #release()} call is internally retried * because of a crashed Hazelcast member, the permit is released only once. * However, this guarantee is not given for the sessionless, a.k.a, * JDK-compatible CP {@link ISemaphore}. For this version, you can tune * execution semantics via * {@link CPSubsystemConfig#setFailOnIndeterminateOperationState(boolean)}. */ public interface ISemaphore extends DistributedObject { /** * Returns the name of this ISemaphore instance. * * @return name of this instance */ String getName(); /** * Tries to initialize this ISemaphore instance with the given permit count * * @param permits the given permit count * @return true if initialization success. false if already initialized * @throws IllegalArgumentException if {@code permits} is negative */ boolean init(int permits); /** * Acquires a permit if one is available, and returns immediately, * reducing the number of available permits by one. *

* If no permit is available, then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of three things happens: *

    *
  • some other thread invokes one of the {@link #release} methods for this * semaphore and the current thread is next to be assigned a permit,
  • *
  • this ISemaphore instance is destroyed, or
  • *
  • some other thread {@linkplain Thread#interrupt interrupts} * the current thread.
  • *
*

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or
  • *
  • is {@linkplain Thread#interrupt interrupted} while waiting for a permit,
  • *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted * @throws IllegalStateException if hazelcast instance is shutdown while waiting */ void acquire() throws InterruptedException; /** * Acquires the given number of permits if they are available, * and returns immediately, reducing the number of available permits * by the given amount. *

* If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of three things happens: *

    *
  • some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request,
  • *
  • or this ISemaphore instance is destroyed,
  • *
  • or some other thread {@linkplain Thread#interrupt interrupts} * the current thread.
  • *
* If the current thread: *
    *
  • has its interrupted status set on entry to this method, or
  • *
  • is {@linkplain Thread#interrupt interrupted} while waiting for a permit,
  • *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @param permits the number of permits to acquire * @throws InterruptedException if the current thread is interrupted * @throws IllegalArgumentException if {@code permits} is negative or zero * @throws IllegalStateException if hazelcast instance is shutdown while waiting */ void acquire(int permits) throws InterruptedException; /** * Returns the current number of permits currently available in this semaphore. *

* This method is typically used for debugging and testing purposes. * * @return the number of permits available in this semaphore */ int availablePermits(); /** * Acquires and returns all permits that are available at invocation time. * * @return the number of permits drained */ int drainPermits(); /** * Reduces the number of available permits by the indicated amount. This * method differs from {@code acquire} as it does not block until permits * become available. Similarly, if the caller has acquired some permits, * they are not released with this call. * * @param reduction the number of permits to reduce * @throws IllegalArgumentException if {@code reduction} is negative */ void reducePermits(int reduction); /** * Increases the number of available permits by the indicated amount. If * there are some threads waiting for permits to become available, they * will be notified. Moreover, if the caller has acquired some permits, * they are not released with this call. * * @param increase the number of permits to increase * @throws IllegalArgumentException if {@code increase} is negative */ void increasePermits(int increase); /** * Releases a permit and increases the number of available permits by one. * If some threads in the cluster are blocked for acquiring a permit, one * of them will unblock by acquiring the permit released by this call. *

* If the underlying {@link ISemaphore} is configured as non-JDK compatible * via {@link SemaphoreConfig} then a HazelcastInstance can only release a permit which * it has acquired before. In other words, a HazelcastInstance cannot release a permit * without acquiring it first. *

* Otherwise, which means the underlying impl is the JDK compatible * Semaphore is configured via {@link SemaphoreConfig}, there is no requirement * that a HazelcastInstance that releases a permit must have acquired that permit by * calling one of the {@link #acquire()} methods. A HazelcastInstance can freely * release a permit without acquiring it first. In this case, correct usage * of a semaphore is established by programming convention in the application. * * @throws IllegalStateException if the Semaphore is non-JDK-compatible * and the caller does not have a permit */ void release(); /** * Releases the given number of permits and increases the number of * available permits by that amount. If some threads in the cluster are * blocked for acquiring permits, they will be notified. *

* If the underlying {@link ISemaphore} impl is the non-JDK compatible * CP impl that is configured via {@link SemaphoreConfig} and fetched * via {@link CPSubsystem}, then a HazelcastInstance can only release a permit which * it has acquired before. In other words, a HazelcastInstance cannot release a permit * without acquiring it first. *

* Otherwise, which means the underlying impl is the JDK compatible * Semaphore is configured via {@link SemaphoreConfig}, there is no requirement * that a HazelcastInstance that releases a permit must have acquired that permit by * calling one of the {@link #acquire()} methods. A HazelcastInstance can freely * release a permit without acquiring it first. In this case, correct usage * of a semaphore is established by programming convention in the application. * * @param permits the number of permits to release * @throws IllegalArgumentException if {@code permits} is negative or zero * @throws IllegalStateException if the Semaphore is non-JDK-compatible * and the caller does not have a permit */ void release(int permits); /** * Acquires a permit if one is available, and returns {@code true} * immediately. If no permit is available, returns {@code false} * immediately. * * @return {@code true} if a permit was acquired, {@code false} otherwise */ boolean tryAcquire(); /** * Acquires the given number of permits if they are available, and * returns {@code true} immediately. If the requested number of permits are * not available, returns {@code false} immediately. * * @param permits the number of permits to acquire * @return {@code true} if the permits were acquired, {@code false} otherwise * @throws IllegalArgumentException if {@code permits} is negative */ boolean tryAcquire(int permits); /** * Acquires a permit and returns {@code true}, if one becomes available * during the given waiting time and the current thread has not been * {@linkplain Thread#interrupt interrupted}. If a permit is acquired, * the number of available permits in the {@link ISemaphore} instance is * also reduced by one. *

* If no permit is available, then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of three things happens: *

    *
  • some other thread releases a permit and the current thread is next * to be assigned a permit,
  • *
  • or this ISemaphore instance is destroyed,
  • *
  • or some other thread {@linkplain Thread#interrupt interrupts} * the current thread,
  • *
  • or the specified waiting time elapses.
  • *
*

* Returns {@code true} if a permit is successfully acquired. *

* Returns {@code false} if the specified waiting time elapses without * acquiring a permit. If the time is less than or equal to zero, * the method will not wait at all. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method,
  • *
  • or is {@linkplain Thread#interrupt interrupted} while waiting for * a permit,
  • *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired * @throws InterruptedException if the current thread is interrupted * @throws IllegalStateException if hazelcast instance is shutdown while waiting */ boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException; /** * Acquires the given number of permits and returns {@code true}, if they * become available during the given waiting time. If permits are acquired, * the number of available permits in the {@link ISemaphore} instance is * also reduced by the given amount. *

* If no sufficient permits are available, then the current thread becomes * disabled for thread scheduling purposes and lies dormant until one of * three things happens: *

    *
  • permits are released by other threads, the current thread is next to * be assigned permits and the number of available permits satisfies this * request,
  • *
  • or some other thread {@linkplain Thread#interrupt interrupts} * the current thread,
  • *
  • or the specified waiting time elapses.
  • *
* Returns {@code true} if requested permits are successfully acquired. *

* Returns {@code false} if the specified waiting time elapses without * acquiring permits. If the time is less than or equal to zero, * the method will not wait at all. *

* If the current thread: *

    *
  • has its interrupted status set on entry to this method,
  • *
  • or is {@linkplain Thread#interrupt interrupted} while waiting for * a permit,
  • *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @param permits the number of permits to acquire * @param timeout the maximum time to wait for the permits * @param unit the time unit of the {@code timeout} argument * @return {@code true} if all permits were acquired, * {@code false} if the waiting time elapsed before all permits could be acquired * @throws InterruptedException if the current thread is interrupted * @throws IllegalArgumentException if {@code permits} is negative or zero * @throws IllegalStateException if hazelcast instance is shutdown while waiting */ boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException; }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy