Giter VIP home page Giter VIP logo

lock's Introduction

Build Status

This library helps executing critical code in concurrent situations.

Installation

Use Composer:

composer require malkusch/lock

Usage

The package is in the namespace malkusch\lock.

Mutex

The Mutex provides the API for this library.

Mutex::synchronized()

Mutex::synchronized() executes code exclusively. This method guarantees that the code is only executed by one process at once. Other processes have to wait until the mutex is available. The critical code may throw an exception, which would release the lock as well.

Example:

$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

Mutex::check()

Mutex::check() performs a double-checked locking pattern. I.e. if the check fails, no lock will be acquired. Else if the check was true, a lock will be acquired and the check will be perfomed as well together with the critical code.

Example:

$mutex->check(function () use ($bankAccount, $amount) {
    return $bankAccount->getBalance() >= $amount;

})->then(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    $bankAccount->setBalance($balance);
});

Implementations

The Mutex is an abstract class. You will have to choose an implementation:

CASMutex

The CASMutex has to be used with a Compare-and-swap operation. This mutex is lock free. It will repeat executing the code until the CAS operation was successful. The code should therefore notify the mutex by calling CASMutex::notify().

As the mutex keeps executing the critical code, it must not have any side effects as long as the CAS operation was not successful.

Example:

$mutex = new CASMutex();
$mutex->synchronized(function () use ($memcached, $mutex, $amount) {
    $balance = $memcached->get("balance", null, $casToken);
    $balance -= $amount;
    if (!$memcached->cas($casToken, "balance", $balance)) {
        return;

    }
    $mutex->notify();
});

FlockMutex

The FlockMutex is a lock implementation based on flock().

Example:

$mutex = new FlockMutex(fopen(__FILE__, "r"));
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

Timeouts are supported as an optional second argument. This uses the pcntl extension if possible or busy waiting if not.

MemcachedMutex

The MemcachedMutex is a spinlock implementation which uses the Memcached API.

Example:

$memcache = new \Memcached();
$memcache->addServer("localhost", 11211);

$mutex = new MemcachedMutex("balance", $memcache);
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

PHPRedisMutex

The PHPRedisMutex is the distributed lock implementation of RedLock which uses the phpredis extension.

This implementation requires at least phpredis-2.2.4.

If used with a cluster of Redis servers, acquiring and releasing locks will continue to function as long as a majority of the servers still works.

Example:

$redis = new Redis();
$redis->connect("localhost");

$mutex = new PHPRedisMutex([$redis], "balance");
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

PredisMutex

The PredisMutex is the distributed lock implementation of RedLock which uses the Predis API.

Example:

$redis = new Client("redis://localhost");

$mutex = new PredisMutex([$redis], "balance");
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

SemaphoreMutex

The SemaphoreMutex is a lock implementation based on Semaphore.

Example:

$semaphore = sem_get(ftok(__FILE__, "a"));
$mutex     = new SemaphoreMutex($semaphore);
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

TransactionalMutex

The TransactionalMutex delegates the serialization to the DBS. The exclusive code is executed within a transaction. It's up to you to set the correct transaction isolation level. However if the transaction fails (i.e. a PDOException was thrown), the code will be executed again in a new transaction. Therefore the code must not have any side effects besides SQL statements. Also the isolation level should be conserved for the repeated transaction. If the code throws an exception, the transaction is rolled back and not replayed again.

Example:

$mutex = new TransactionalMutex($pdo);
$mutex->synchronized(function () use ($pdo, $accountId, $amount) {
    $select = $pdo->prepare("SELECT balance FROM account WHERE id = ? FOR UPDATE");
    $select->execute([$accountId]);
    $balance = $select->fetchColumn();

    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $pdo->prepare("UPDATE account SET balance = ? WHERE id = ?")
        ->execute([$balance, $accountId]);
});

MySQLMutex

The MySQLMutex uses MySQL's GET_LOCK function.

It supports time outs. If the connection to the database server is lost or interrupted, the lock is automatically released.

Note that before MySQL 5.7.5 you cannot use nested locks, any new lock will silently release already held locks. You should probably refrain from using this mutex on MySQL versions < 5.7.5.

$pdo = new PDO("mysql:host=localhost;dbname=test", "username");

$mutex = new MySQLMutex($pdo, "balance", 15);
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

PgAdvisoryLockMutex

The PgAdvisoryLockMutex uses PostgreSQL's advisory locking functions.

Named locks are offered. PostgreSQL locking functions require integers but the conversion is handled automatically.

No time outs are supported. If the connection to the database server is lost or interrupted, the lock is automatically released.

$pdo = new PDO("pgsql:host=localhost;dbname=test;", "username");

$mutex = new PgAdvisoryLockMutex($pdo, "balance");
$mutex->synchronized(function () use ($bankAccount, $amount) {
    $balance = $bankAccount->getBalance();
    $balance -= $amount;
    if ($balance < 0) {
        throw new \DomainException("You have no credit.");

    }
    $bankAccount->setBalance($balance);
});

License and authors

This project is free and under the WTFPL. Responsible for this project is Willem Stuursma-Ruwen [email protected].

Donations

If you like this project and feel generous donate a few Bitcoins here: 1P5FAZ4QhXCuwYPnLZdk3PJsqePbu1UDDA

lock's People

Contributors

malkusch avatar willemstuursma avatar thelevti avatar glensc avatar

Watchers

Nikolya avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.