diff --git a/inc/lock.php b/inc/lock.php index 23c0bce4..5a83562a 100644 --- a/inc/lock.php +++ b/inc/lock.php @@ -1,43 +1,84 @@ f = fopen("tmp/locks/$key", "w"); +class Locks { + private static function filesystem(string $key): Lock|false { + $key = str_replace('/', '::', $key); + $key = str_replace("\0", '', $key); + + $fd = fopen("tmp/locks/$key", "w"); + if ($fd === false) { + return false; } + + return new class($fd) implements Lock { + // Resources have no type in php. + private mixed $f; + + + function __construct($fd) { + $this->f = $fd; + } + + public function get(bool $nonblock = false): Lock|false { + $wouldblock = false; + flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock); + if ($nonblock && $wouldblock) { + return false; + } + return $this; + } + + public function get_ex(bool $nonblock = false): Lock|false { + $wouldblock = false; + flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock); + if ($nonblock && $wouldblock) { + return false; + } + return $this; + } + + public function free(): Lock { + flock($this->f, LOCK_UN); + return $this; + } + }; } - // Get a shared lock - function get($nonblock = false) { - global $config; - if ($config['lock']['enabled'] == 'fs') { - $wouldblock = false; - flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock); - if ($nonblock && $wouldblock) return false; - } - return $this; + /** + * No-op. Can be used for mocking. + */ + public static function none(): Lock|false { + return new class() implements Lock { + public function get(bool $nonblock = false): Lock|false { + return $this; + } + + public function get_ex(bool $nonblock = false): Lock|false { + return $this; + } + + public function free(): Lock { + return $this; + } + }; } - // Get an exclusive lock - function get_ex($nonblock = false) { - global $config; + public static function get_lock(array $config, string $key): Lock|false { if ($config['lock']['enabled'] == 'fs') { - $wouldblock = false; - flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock); - if ($nonblock && $wouldblock) return false; + return self::filesystem($key); + } else { + return self::none(); } - return $this; - } - - // Free a lock - function free() { - global $config; - if ($config['lock']['enabled'] == 'fs') { - flock($this->f, LOCK_UN); - } - return $this; } } + +interface Lock { + // Get a shared lock + public function get(bool $nonblock = false): Lock|false; + + // Get an exclusive lock + public function get_ex(bool $nonblock = false): Lock|false; + + // Free a lock + public function free(): Lock; +} diff --git a/inc/queue.php b/inc/queue.php index 66305b3b..a3873491 100644 --- a/inc/queue.php +++ b/inc/queue.php @@ -3,7 +3,7 @@ class Queue { function __construct($key) { global $config; if ($config['queue']['enabled'] == 'fs') { - $this->lock = new Lock($key); + $this->lock = Locks::get_lock($config, $key); $key = str_replace('/', '::', $key); $key = str_replace("\0", '', $key); $this->key = "tmp/queue/$key/";