1a6d63b89Sgerardnico<?php 2a6d63b89Sgerardnico 3a6d63b89Sgerardniconamespace ComboStrap; 4a6d63b89Sgerardnico 5a6d63b89Sgerardnico 6a6d63b89Sgerardnicouse dokuwiki\Search\Indexer; 7a6d63b89Sgerardnico 8a6d63b89Sgerardnico/** 9a6d63b89Sgerardnico * Adapted from the {@link Indexer::lock()} 10a6d63b89Sgerardnico * because the TaskRunner does not run serially 11a6d63b89Sgerardnico * Only the indexer does 12a6d63b89Sgerardnico * https://forum.dokuwiki.org/d/21044-taskrunner-running-multiple-times-eating-the-memory-lock 13a6d63b89Sgerardnico */ 14a6d63b89Sgerardnicoclass Lock 15a6d63b89Sgerardnico{ 16a6d63b89Sgerardnico private string $lockName; 17a6d63b89Sgerardnico private string $lockFile; 18a6d63b89Sgerardnico /** 19a6d63b89Sgerardnico * @var mixed|null 20a6d63b89Sgerardnico */ 21a6d63b89Sgerardnico private $perm; 229fc13f00Sgerardnico private int $timeOut = 5; 23*2becc3acSgerardnico /** 24*2becc3acSgerardnico * @var false|resource 25*2becc3acSgerardnico */ 26*2becc3acSgerardnico private $filePointer = null; 27a6d63b89Sgerardnico 28a6d63b89Sgerardnico 29a6d63b89Sgerardnico /** 30a6d63b89Sgerardnico * @param string $name 31a6d63b89Sgerardnico */ 32a6d63b89Sgerardnico public function __construct(string $name) 33a6d63b89Sgerardnico { 34a6d63b89Sgerardnico $this->lockName = $name; 35a6d63b89Sgerardnico global $conf; 36a6d63b89Sgerardnico $this->lockFile = $conf['lockdir'] . "/_{$this->lockName}.lock"; 37a6d63b89Sgerardnico $this->perm = $conf['dperm'] ?? null; 38a6d63b89Sgerardnico } 39a6d63b89Sgerardnico 40a6d63b89Sgerardnico public static function create(string $name): Lock 41a6d63b89Sgerardnico { 42a6d63b89Sgerardnico return new Lock($name); 43a6d63b89Sgerardnico } 44a6d63b89Sgerardnico 45a6d63b89Sgerardnico /** 46a6d63b89Sgerardnico * @throws ExceptionTimeOut - with the timeout 47a6d63b89Sgerardnico */ 489fc13f00Sgerardnico function acquire(): Lock 49a6d63b89Sgerardnico { 50a6d63b89Sgerardnico $run = 0; 51*2becc3acSgerardnico /** 52*2becc3acSgerardnico * The flock function follows the semantics of the Unix system call bearing the same name. 53*2becc3acSgerardnico * Flock utilizes ADVISORY locking only; that is: 54*2becc3acSgerardnico * * other processes may ignore the lock completely it only affects those that call the flock call. 55*2becc3acSgerardnico * 56*2becc3acSgerardnico * * LOCK_SH means SHARED LOCK. Any number of processes MAY HAVE A SHARED LOCK simultaneously. It is commonly called a reader lock. 57*2becc3acSgerardnico * * LOCK_EX means EXCLUSIVE LOCK. Only a single process may possess an exclusive lock to a given file at a time. 58*2becc3acSgerardnico * 59*2becc3acSgerardnico * ie if the file has been LOCKED with LOCK_SH in another process, 60*2becc3acSgerardnico * * flock with LOCK_SH will SUCCEED. 61*2becc3acSgerardnico * * flock with LOCK_EX will BLOCK UNTIL ALL READER LOCKS HAVE BEEN RELEASED. 62*2becc3acSgerardnico * 63*2becc3acSgerardnico * When the file is closed, the lock is released by the system anyway. 64*2becc3acSgerardnico */ 65*2becc3acSgerardnico // LOCK_NB to not block the process 66*2becc3acSgerardnico while (!$this->getLock()) { 67a6d63b89Sgerardnico usleep(1000); 68a6d63b89Sgerardnico /** 69*2becc3acSgerardnico * Old lock ? More than 10 minutes run 70a6d63b89Sgerardnico */ 71*2becc3acSgerardnico if (is_file($this->lockFile) && (time() - @filemtime($this->lockFile)) > 60 * 10) { 72*2becc3acSgerardnico if (!@unlink($this->lockFile)) { 73a6d63b89Sgerardnico throw new ExceptionRuntimeInternal("Removing the lock failed ($this->lockFile)"); 74a6d63b89Sgerardnico } 75a6d63b89Sgerardnico } 769fc13f00Sgerardnico $run++; 779fc13f00Sgerardnico if ($run >= $this->timeOut) { 789fc13f00Sgerardnico throw new ExceptionTimeOut("Unable to get the lock ($this->lockFile) for ($this->timeOut) seconds"); 79a6d63b89Sgerardnico } 80a6d63b89Sgerardnico } 81a6d63b89Sgerardnico if ($this->perm) { 82a6d63b89Sgerardnico chmod($this->lockFile, $this->perm); 83a6d63b89Sgerardnico } 849fc13f00Sgerardnico return $this; 85a6d63b89Sgerardnico 86a6d63b89Sgerardnico } 87a6d63b89Sgerardnico 88a6d63b89Sgerardnico /** 89*2becc3acSgerardnico * Release the lock 90*2becc3acSgerardnico * and the resources 91*2becc3acSgerardnico * (Need to be called in all cases) 92a6d63b89Sgerardnico */ 93a6d63b89Sgerardnico function release() 94a6d63b89Sgerardnico { 95*2becc3acSgerardnico if ($this->filePointer !== null) { 96*2becc3acSgerardnico fclose($this->filePointer); 97*2becc3acSgerardnico $this->filePointer = null; 98*2becc3acSgerardnico } 99*2becc3acSgerardnico if (file_exists($this->lockFile)) { 100*2becc3acSgerardnico unlink($this->lockFile); 101*2becc3acSgerardnico } 102a6d63b89Sgerardnico } 103a6d63b89Sgerardnico 1040360a848Sgerardnico public function isReleased(): bool 1050360a848Sgerardnico { 106*2becc3acSgerardnico return !file_exists($this->lockFile); 1070360a848Sgerardnico } 1080360a848Sgerardnico 109*2becc3acSgerardnico public function isLocked(): bool 110*2becc3acSgerardnico { 111*2becc3acSgerardnico return file_exists($this->lockFile); 112*2becc3acSgerardnico } 113*2becc3acSgerardnico 114*2becc3acSgerardnico public function setTimeout(int $int): Lock 1159fc13f00Sgerardnico { 1169fc13f00Sgerardnico $this->timeOut = $int; 1179fc13f00Sgerardnico return $this; 1189fc13f00Sgerardnico } 1199fc13f00Sgerardnico 120*2becc3acSgerardnico private function getLock(): bool 121*2becc3acSgerardnico { 122*2becc3acSgerardnico /** 123*2becc3acSgerardnico * We test also on the file because 124*2becc3acSgerardnico * on some operating systems, flock() is implemented at the process level. 125*2becc3acSgerardnico * 126*2becc3acSgerardnico * ie when using a multithreaded server API you may not be able to rely on flock() 127*2becc3acSgerardnico * to protect files against other PHP scripts running in parallel threads of the same server instance 128*2becc3acSgerardnico */ 129*2becc3acSgerardnico if (file_exists($this->lockFile)) { 130*2becc3acSgerardnico return false; 131*2becc3acSgerardnico } 132*2becc3acSgerardnico 133*2becc3acSgerardnico if ($this->filePointer === null) { 134*2becc3acSgerardnico $mode = "c"; // as specified in the doc 135*2becc3acSgerardnico $this->filePointer = fopen($this->lockFile, $mode); 136*2becc3acSgerardnico } 137*2becc3acSgerardnico /** 138*2becc3acSgerardnico * LOCK_EX: exclusive lock 139*2becc3acSgerardnico * LOCK_NB: to not wait 140*2becc3acSgerardnico */ 141*2becc3acSgerardnico return flock($this->filePointer, LOCK_EX | LOCK_NB); 142*2becc3acSgerardnico } 143*2becc3acSgerardnico 144a6d63b89Sgerardnico} 145