Package org.multiverse.api

## Interface Lock

- All Known Implementing Classes:
    `AbstractGammaObject`, `BaseGammaTxnRef`, `GammaTxnBoolean`, `GammaTxnDouble`, `GammaTxnInteger`, `GammaTxnLong`, `GammaTxnRef`

      public interface Lock

  The Lock provides access to pessimistic behavior of a `TxnObject`. STM normally is very optimistic, but in some cases a more pessimistic approach (one with less retries) could be a better fitting solution.

  There are 4 different types of lockmodes:
  - LockMode.None: it doesn't do any locking
  - LockMode.Read: it allows multiple transactions to acquire the read lock, but transaction acquiring the write-lock or exclusive lock (needed when a transaction wants to commit) is prohibited. If the read lock is acquired by a different transaction, a transaction still is able to read/write, but it isn't allowed to commit the changes (since and exclusive lock is required for that).
  - LockMode.Write: it allows only one transaction to acquire the write lock, but unlike a traditional write-lock, reads still are allowed. Normally this would not be acceptable because once the write-lock is acquired, the internals could be modified. But in case of STM, the STM can still provide a consistent view even though the locking transaction has made changes. This essentially is the same behavior you get with the 'select for update' from Oracle. Once the write lock is acquired, other transactions can't acquire the Lock.
  - LockMode.Exclusive: it allows only one transaction to acquire the commit lock, and readers are not allowed to read anymore. From an isolation perspective, the exclusive lock looks a lot like the synchronized statement (or a `ReentrantLock`} where only mutually exclusive access is possible. The exclusive lock normally is used by the STM when it commits.

  ### Lock duration and release

  Locks atm are acquired for the remaining duration of the transaction and only will always be automatically released once the transaction commits/aborts. This is essentially the same behavior you get with Oracle once a update/delete/insert is done, or when the record is locked manually by executing the 'select for update'. For this to work it is very important that the `ControlFlowError` is not caught by the logic executed in an transactional closure, but is caught by the TxnExecutor itself.

  ### Blocking

  Atm it isn't possible to block on a lock. What happens is that some spinning is done `TxnFactoryBuilder.setSpinCount(int)` and then some retries `TxnFactoryBuilder.setMaxRetries(int)` in combination with a backoff `TxnFactoryBuilder.setBackoffPolicy(BackoffPolicy)`. In the 0.8 release blocking will probably be added.

  ### Fairness

  Atm there is no support for fairness. The big problem with fairness and STM is that the locks are released and the transaction needs to begin again. It could be that a lower priority transaction is faster and acquires the lock again. This is a topic that needs more research and probably will be integrated in the contention management.

  ### Lock upgrade

  It is possible to upgrade a lock to more strict version, e.g. to upgrade a read-lock to a write-lock. The following upgrades are possible:

  1. LockMode.Read to LockMode.Write: as long as no other transaction has acquired the Lock in LockMode.Read
  2. LockMode.Read to LockMode.Exclusive: as long as no other transaction has acquired the Lock in LockMode.Read
  3. LockMode.Write to LockMode.Exclusive: will always succeed

  The Txn is allowed to apply a more strict LockMode than the one specified.

  ### Lock downgrade

  Downgrading locks currently is not possible and downgrade calls are ignored.

  ### Locking scope

  Locking can be done on the Txn level (see the `TxnFactoryBuilder.setReadLockMode(LockMode)` and `TxnFactoryBuilder.setWriteLockMode(LockMode)` where all reads or all writes (to do a write also a read is needed) are locked automatically. It can also be done on the reference level using getAndLock/setAndLock/getAndSetAndLock methods or by accessing the `TxnObject.getLock()`.

  ### Lock escalation

  In traditional lock based databases, managing locks in memory can be quite expensive. That is one of the reason why different Lock granularities are used (record level, page level, table level for example). To prevent managing too many locks, some databases apply lock escalation so that multiple low granularity locks are upgraded to a single higher granular lock. The problem with lock escalations is that the system could be subject to lock contention and to deadlocks.

  The GammaStm (the main STM implementation) doesn't use lock escalation, but keeps on managing locks on the transactional object (ref) level.

  ### Deadlocks

  2 Ingredients are needed for a deadlock:

  1. Transactions acquiring locks in a different order
  2. Transactions that do an unbound waiting for a lock to come available
  The problem with applying locks in the same order is that it places an extra borden on the developer. That is why atm the second ingredient is always missing if the GammaStm (the default STM implementation) is used. Therefor a developer doesn't need to worry about deadlocks (although it shifts the problem to an increased chance of starvation and livelocks).
  See Also:
    `TxnFactoryBuilder.setReadLockMode(LockMode)`, `TxnFactoryBuilder.setWriteLockMode(LockMode)`

- - ### Method Summary

    All Methods Instance Methods Abstract Methods
    Modifier and Type | Method | Description
    --- | --- | ---
    `void` | `acquire​(LockMode desiredLockMode)` | Acquires a Lock with the provided LockMode.
    `void` | `acquire​(Txn txn, LockMode desiredLockMode)` | Acquires a Lock with the provided LockMode using the provided transaction.
    `LockMode` | `atomicGetLockMode()` | Returns the current LockMode.
    `LockMode` | `getLockMode()` | Gets the LockMode the transaction stored in the the `TxnThreadLocal` has on this Lock.
    `LockMode` | `getLockMode​(Txn txn)` | Gets the LockMode the transaction has on the Lock.

- - ### Method Detail

    - #### atomicGetLockMode

          LockMode atomicGetLockMode()

      Returns the current LockMode. This call doesn't look at any running transaction, it shows the actual state of the Lock. The value could be stale as soon as it is received. To retrieve the LockMode a a Txn has on a Lock, the `getLockMode()` or `getLockMode(Txn)` need to be used.
      Returns:
        the current LockMode.

    - #### getLockMode

          LockMode getLockMode()

      Gets the LockMode the transaction stored in the the `TxnThreadLocal` has on this Lock. To retrieve the actual LockMode of the Lock, you need to use the `atomicGetLockMode()`.
      Returns:
        the LockMode.
      Throws:
        `TxnExecutionException` - if something failed while using the transaction. The transaction is guaranteed to have been aborted.
        `ControlFlowError` - if the Stm needs to control the flow in a different way than normal returns of exceptions. The transaction is guaranteed to have been aborted.
      See Also:
        `atomicGetLockMode()`, `getLockMode(Txn)`

    - #### getLockMode

          LockMode getLockMode​(Txn txn)

      Gets the LockMode the transaction has on the Lock. This call makes use of the tx. To retrieve the actual LockMode of the Lock, you need to use the `atomicGetLockMode()`
      Parameters:
        `txn` - the Lock
      Returns:
        the LockMode the transaction has on the Lock.
      Throws:
        `TxnExecutionException` - if something failed while using the transaction. The transaction is guaranteed to have been aborted.
        `ControlFlowError` - if the Stm needs to control the flow in a different way than normal returns of exceptions. The transaction is guaranteed to have been aborted.
      See Also:
        `atomicGetLockMode()`, `getLockMode(Txn)`

    - #### acquire

          void acquire​(LockMode desiredLockMode)

      Acquires a Lock with the provided LockMode. This call doesn't block if the Lock can't be upgraded, but throws a `ReadWriteConflict`. It could also be that the Lock is acquired, but the Txn sees that it isn't consistent anymore. In that case also a `ReadWriteConflict` is thrown.

      This call makes use of the Txn stored in the `TxnThreadLocal`.

      If the lockMode is lower than the LockMode the transaction already has on this Lock, the call is ignored.

      Parameters:
        `desiredLockMode` - the desired lockMode.
      Throws:
        `TxnExecutionException` - if something failed while using the transaction. The transaction is guaranteed to have been aborted.
        `ControlFlowError` - if the Stm needs to control the flow in a different way than normal returns of exceptions. The transaction is guaranteed to have been aborted.
        `NullPointerException` - if desiredLockMode is null. If an alive transaction is available, it will be aborted.

    - #### acquire

          void acquire​(Txn txn,
                       LockMode desiredLockMode)

      Acquires a Lock with the provided LockMode using the provided transaction. This call doesn't block if the Lock can't be upgraded but throws a `ReadWriteConflict`. It could also be that the Lock is acquired, but the Txn sees that it isn't consistent anymore. In that case also a `ReadWriteConflict` is thrown.

      If the lockMode is lower than the LockMode the transaction already has on this Lock, the call is ignored.

      Parameters:
        `txn` - the Txn used for this operation.
        `desiredLockMode` - the desired lockMode.
      Throws:
        `TxnExecutionException` - if something failed while using the transaction. The transaction is guaranteed to have been aborted.
        `ControlFlowError` - if the Stm needs to control the flow in a different way than normal returns of exceptions. The transaction is guaranteed to have been aborted.
        `NullPointerException` - if tx or desiredLockMode is null. If an alive transaction is available, it will be aborted.