com.hazelcast.core

## Interface ISemaphore

- All Superinterfaces:
    DistributedObject
  All Known Implementing Classes:
    ClientSemaphoreProxy, SemaphoreProxy

      public interface ISemaphore
      extends DistributedObject

  ISemaphore is a fault-tolerant distributed alternative to the `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 `acquire()` blocks if necessary until a permit is available, and then takes it. Dually, each `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 `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 `ISemaphore` receives these acquire requests. Therefore, it is possible for one member to invoke `acquire` before another member, but its request hits the primary replica after the other member.

  This class also provides convenience methods, such as `acquire` and `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.

  As of version 3.12, Hazelcast offers 2 different `ISemaphore` impls. Behaviour of `ISemaphore` under failure scenarios, including network partitions, depends on the impl. The first impl is the one accessed via `HazelcastInstance.getSemaphore(String)`. It works on top of Hazelcast's async replication algorithm and does not guarantee linearizability during failures. During a split-brain scenario, this impl loses its atomicity. It is possible for the same `ISemaphore` instance to exist in each side of the network partition and therefore the permit counters can diverge. When the cluster heals, Hazelcast will choose one of the `ISemaphore` instances. By default, it is selected from the largest partitioned cluster (by number of members). If the cluster sizes are all equal, then a random `ISemaphore` instance will be chosen.

  This `ISemaphore` impl also supports Quorum `QuorumConfig` in cluster versions 3.10 and higher. However, Hazelcast quorums do not guarantee strong consistency under failure scenarios.

  The second impl is a new one introduced with the `CPSubsystem` in version 3.12. It is accessed via `CPSubsystem.getSemaphore(String)`. It has a major difference to the old implementation, that is, 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.

  The new CP impl has 2 variations:

  - The default impl accessed via `CPSubsystem` is session-aware. In this one, when a caller makes its very first `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 caller 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 `Semaphore.release()`. You can use the session-aware CP `ISemaphore` impl by disabling JDK compatibility via `CPSemaphoreConfig.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 `CPSession` for the rationale behind the session mechanism.
  - The second impl offered by `CPSubsystem` is sessionless. This impl does not perform auto-cleanup of acquired permits on failures. Acquired permits are not bound to threads and permits can be released without acquiring first. This one is compatible with `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 `ISemaphore` impl by enabling JDK compatibility via `CPSemaphoreConfig.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 `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 `ISemaphore` impl offer the exactly-once execution semantics for the session-aware version. For instance, even if a `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 `ISemaphore`. For this version, you can tune execution semantics via `CPSubsystemConfig.setFailOnIndeterminateOperationState(boolean)`.

- - ### Method Summary

    All Methods Instance Methods Abstract Methods
    Modifier and Type | Method and Description
    --- | ---
    `void` | `acquire()` Acquires a permit if one is available, and returns immediately, reducing the number of available permits by one.
    `void` | `acquire(int permits)` Acquires the given number of permits if they are available, and returns immediately, reducing the number of available permits by the given amount.
    `int` | `availablePermits()` Returns the current number of permits currently available in this semaphore.
    `int` | `drainPermits()` Acquires and returns all permits that are available at invocation time.
    `String` | `getName()` Returns the name of this ISemaphore instance.
    `void` | `increasePermits(int increase)` Increases the number of available permits by the indicated amount.
    `boolean` | `init(int permits)` Tries to initialize this ISemaphore instance with the given permit count
    `void` | `reducePermits(int reduction)` Reduces the number of available permits by the indicated amount.
    `void` | `release()` Releases a permit and increases the number of available permits by one.
    `void` | `release(int permits)` Releases the given number of permits and increases the number of available permits by that amount.
    `boolean` | `tryAcquire()` Acquires a permit if one is available, and returns `true` immediately.
    `boolean` | `tryAcquire(int permits)` Acquires the given number of permits if they are available, and returns `true` immediately.
    `boolean` | `tryAcquire(int permits, long timeout, TimeUnit unit)` Acquires the given number of permits and returns `true`, if they become available during the given waiting time.
    `boolean` | `tryAcquire(long timeout, TimeUnit unit)` Acquires a permit and returns `true`, if one becomes available during the given waiting time and the current thread has not been interrupted.

    - ### Methods inherited from interface com.hazelcast.core.DistributedObject

      `destroy, getPartitionKey, getServiceName`

- - ### Method Detail

    - #### getName

          String getName()

      Returns the name of this ISemaphore instance.
      Specified by:
        `getName` in interface `DistributedObject`
      Returns:
        name of this instance

    - #### init

          boolean init(int permits)

      Tries to initialize this ISemaphore instance with the given permit count
      Parameters:
        `permits` - the given permit count
      Returns:
        true if initialization success. false if already initialized

    - #### acquire

          void acquire()
                throws InterruptedException

      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 `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 interrupts the current thread.

      If the current thread:

      - has its interrupted status set on entry to this method; or
      - is interrupted while waiting for a permit,
      then `InterruptedException` is thrown and the current thread's interrupted status is cleared.
      Throws:
        `InterruptedException` - if the current thread is interrupted
        `IllegalStateException` - if hazelcast instance is shutdown while waiting

    - #### acquire

          void acquire(int permits)
                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 `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 interrupts the current thread.
      If the current thread:
      - has its interrupted status set on entry to this method, or
      - is interrupted while waiting for a permit,
      then `InterruptedException` is thrown and the current thread's interrupted status is cleared.
      Parameters:
        `permits` - the number of permits to acquire
      Throws:
        `InterruptedException` - if the current thread is interrupted
        `IllegalArgumentException` - if `permits` is negative
        `IllegalStateException` - if hazelcast instance is shutdown while waiting

    - #### availablePermits

          int availablePermits()

      Returns the current number of permits currently available in this semaphore.

      This method is typically used for debugging and testing purposes.

      Returns:
        the number of permits available in this semaphore

    - #### drainPermits

          int drainPermits()

      Acquires and returns all permits that are available at invocation time.
      Returns:
        the number of permits drained

    - #### reducePermits

          void reducePermits(int reduction)

      Reduces the number of available permits by the indicated amount. This method differs from `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.
      Parameters:
        `reduction` - the number of permits to reduce
      Throws:
        `IllegalArgumentException` - if `reduction` is negative

    - #### increasePermits

          void increasePermits(int increase)

      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.
      Parameters:
        `increase` - the number of permits to increase
      Throws:
        `IllegalArgumentException` - if `increase` is negative

    - #### release

          void release()

      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 `ISemaphore` impl is the non-JDK compatible CP impl that is configured via `CPSemaphoreConfig` and fetched via `CPSubsystem`, then a thread can only release a permit which it has acquired before. In other words, a thread cannot release a permit without acquiring it first.

      Otherwise, which means the underlying impl is either the JDK compatible CP impl configured via `CPSemaphoreConfig` and fetched via `CPSubsystem`, or it is the old impl fetched via `HazelcastInstance.getSemaphore(String)`, there is no requirement that a thread that releases a permit must have acquired that permit by calling one of the `acquire()` methods. A thread 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.

    - #### release

          void release(int permits)

      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 `ISemaphore` impl is the non-JDK compatible CP impl that is configured via `CPSemaphoreConfig` and fetched via `CPSubsystem`, then a thread can only release a permit which it has acquired before. In other words, a thread cannot release a permit without acquiring it first.

      Otherwise, which means the underlying impl is either the JDK compatible CP impl configured via `CPSemaphoreConfig` and fetched via `CPSubsystem`, or it is the old impl fetched via `HazelcastInstance.getSemaphore(String)`, there is no requirement that a thread that releases a permit must have acquired that permit by calling one of the `acquire()` methods. A thread 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.

      Parameters:
        `permits` - the number of permits to release
      Throws:
        `IllegalArgumentException` - if `permits` is negative

    - #### tryAcquire

          boolean tryAcquire()

      Acquires a permit if one is available, and returns `true` immediately. If no permit is available, returns `false` immediately.
      Returns:
        `true` if a permit was acquired, `false` otherwise

    - #### tryAcquire

          boolean tryAcquire(int permits)

      Acquires the given number of permits if they are available, and returns `true` immediately. If the requested number of permits are not available, returns `false` immediately.
      Parameters:
        `permits` - the number of permits to acquire
      Returns:
        `true` if the permits were acquired, `false` otherwise
      Throws:
        `IllegalArgumentException` - if `permits` is negative

    - #### tryAcquire

          boolean tryAcquire(long timeout,
                             TimeUnit unit)
                      throws InterruptedException

      Acquires a permit and returns `true`, if one becomes available during the given waiting time and the current thread has not been interrupted. If a permit is acquired, the number of available permits in the `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 interrupts the current thread,
      - or the specified waiting time elapses.

      Returns `true` if a permit is successfully acquired.

      Returns `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 interrupted while waiting for a permit,
      then `InterruptedException` is thrown and the current thread's interrupted status is cleared.
      Parameters:
        `timeout` - the maximum time to wait for a permit
        `unit` - the time unit of the `timeout` argument
      Returns:
        `true` if a permit was acquired and `false` if the waiting time elapsed before a permit was acquired
      Throws:
        `InterruptedException` - if the current thread is interrupted
        `IllegalStateException` - if hazelcast instance is shutdown while waiting

    - #### tryAcquire

          boolean tryAcquire(int permits,
                             long timeout,
                             TimeUnit unit)
                      throws InterruptedException

      Acquires the given number of permits and returns `true`, if they become available during the given waiting time. If permits are acquired, the number of available permits in the `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 interrupts the current thread,
      - or the specified waiting time elapses.
      Returns `true` if requested permits are successfully acquired.

      Returns `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 interrupted while waiting for a permit,
      then `InterruptedException` is thrown and the current thread's interrupted status is cleared.
      Parameters:
        `permits` - the number of permits to acquire
        `timeout` - the maximum time to wait for the permits
        `unit` - the time unit of the `timeout` argument
      Returns:
        `true` if all permits were acquired, `false` if the waiting time elapsed before all permits could be acquired
      Throws:
        `InterruptedException` - if the current thread is interrupted
        `IllegalArgumentException` - if `permits` is negative
        `IllegalStateException` - if hazelcast instance is shutdown while waiting

Copyright © 2020. All rights reserved.