| | |
| | | <?php |
| | | namespace JVBase\managers\queue; |
| | | use wpdb; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | | } |
| | | |
| | | class Locker |
| | | { |
| | | private string $lockKey; |
| | | private int $timeout; |
| | | protected wpdb $wpdb; |
| | | private ?string $token = null; |
| | | |
| | | public function __construct(string $key = 'queue', int $timeout = 0) |
| | | public function __construct(string $key = 'queue', int $timeout = 60) |
| | | { |
| | | $this->lockKey = BASE . $key . '_lock'; |
| | | $this->timeout = $timeout; |
| | | global $wpdb; |
| | | $this->wpdb = $wpdb; |
| | | } |
| | | |
| | | /** |
| | | * Execute callback with lock, auto-release after |
| | | */ |
| | | public function withLock(callable $callback): void |
| | | { |
| | | $acquired = $this->wpdb->get_var( |
| | | $this->wpdb->prepare( |
| | | 'SELECT GET_LOCK(%s, %d)', |
| | | $this->lockKey, |
| | | $this->timeout |
| | | ) |
| | | ); |
| | | |
| | | if ((int) $acquired !== 1) { |
| | | // Lock already held — just exit quietly |
| | | return; |
| | | } |
| | | if (!$this->acquire()) return; |
| | | |
| | | try { |
| | | $callback(); |
| | |
| | | } |
| | | } |
| | | |
| | | public function unlock():void |
| | | private function acquire(): bool |
| | | { |
| | | $this->wpdb->get_var( |
| | | $this->wpdb->prepare( |
| | | 'SELECT RELEASE_LOCK(%s)', |
| | | $this->lockKey |
| | | ) |
| | | ); |
| | | $this->token = bin2hex(random_bytes(8)); |
| | | return (bool) wp_cache_add($this->lockKey, $this->token, 'locks', $this->timeout); |
| | | } |
| | | |
| | | public function unlock(): void |
| | | { |
| | | $current = wp_cache_get($this->lockKey, 'locks'); |
| | | if ($current === $this->token) { |
| | | wp_cache_delete($this->lockKey, 'locks'); |
| | | } |
| | | $this->token = null; |
| | | } |
| | | } |