1596d5287SAndreas Gohr<?php 2596d5287SAndreas Gohr 3596d5287SAndreas Gohrnamespace dokuwiki\Search\Index; 4596d5287SAndreas Gohr 5*c66b5ec6SAndreas Gohruse dokuwiki\Search\Exception\IndexLockException; 6*c66b5ec6SAndreas Gohr 7596d5287SAndreas Gohr/** 8*c66b5ec6SAndreas Gohr * Static lock registry for index writing 9596d5287SAndreas Gohr * 10*c66b5ec6SAndreas Gohr * Manages filesystem locks (directories in the lock dir) with in-process 11*c66b5ec6SAndreas Gohr * reference counting. Multiple callers can acquire the same lock name — 12*c66b5ec6SAndreas Gohr * the filesystem lock is only created on the first acquire and removed 13*c66b5ec6SAndreas Gohr * on the last release. 14596d5287SAndreas Gohr */ 15596d5287SAndreas Gohrclass Lock 16596d5287SAndreas Gohr{ 17*c66b5ec6SAndreas Gohr /** @var array<string, int> Lock names held by this process with reference counts */ 18*c66b5ec6SAndreas Gohr protected static array $held = []; 19596d5287SAndreas Gohr 20596d5287SAndreas Gohr /** 21*c66b5ec6SAndreas Gohr * Acquire a filesystem lock and register it 22*c66b5ec6SAndreas Gohr * 23*c66b5ec6SAndreas Gohr * Idempotent within a process — if already held, increments 24*c66b5ec6SAndreas Gohr * reference count without touching the filesystem. 25*c66b5ec6SAndreas Gohr * 26*c66b5ec6SAndreas Gohr * @param string $name The index base name to lock 27*c66b5ec6SAndreas Gohr * @throws IndexLockException 28596d5287SAndreas Gohr */ 29*c66b5ec6SAndreas Gohr public static function acquire(string $name): void 30*c66b5ec6SAndreas Gohr { 31*c66b5ec6SAndreas Gohr if (isset(self::$held[$name])) { 32*c66b5ec6SAndreas Gohr self::$held[$name]++; 33*c66b5ec6SAndreas Gohr return; 34*c66b5ec6SAndreas Gohr } 35*c66b5ec6SAndreas Gohr 36*c66b5ec6SAndreas Gohr $dir = self::lockDir($name); 37*c66b5ec6SAndreas Gohr if (!@mkdir($dir)) { 38*c66b5ec6SAndreas Gohr // check for stale lock 39*c66b5ec6SAndreas Gohr if (time() - @filemtime($dir) > 60 * 5) { 40*c66b5ec6SAndreas Gohr @rmdir($dir); 41*c66b5ec6SAndreas Gohr if (!@mkdir($dir)) { 42*c66b5ec6SAndreas Gohr throw new IndexLockException('Could not lock ' . $name); 43*c66b5ec6SAndreas Gohr } 44*c66b5ec6SAndreas Gohr } else { 45*c66b5ec6SAndreas Gohr throw new IndexLockException('Could not lock ' . $name); 46*c66b5ec6SAndreas Gohr } 47*c66b5ec6SAndreas Gohr } 48*c66b5ec6SAndreas Gohr 49*c66b5ec6SAndreas Gohr self::$held[$name] = 1; 50*c66b5ec6SAndreas Gohr } 51*c66b5ec6SAndreas Gohr 52*c66b5ec6SAndreas Gohr /** 53*c66b5ec6SAndreas Gohr * Release a filesystem lock 54*c66b5ec6SAndreas Gohr * 55*c66b5ec6SAndreas Gohr * Decrements reference count. Only removes the filesystem lock 56*c66b5ec6SAndreas Gohr * when the count reaches zero. 57*c66b5ec6SAndreas Gohr * 58*c66b5ec6SAndreas Gohr * @param string $name The index base name to unlock 59*c66b5ec6SAndreas Gohr */ 60*c66b5ec6SAndreas Gohr public static function release(string $name): void 61*c66b5ec6SAndreas Gohr { 62*c66b5ec6SAndreas Gohr if (!isset(self::$held[$name])) return; 63*c66b5ec6SAndreas Gohr 64*c66b5ec6SAndreas Gohr self::$held[$name]--; 65*c66b5ec6SAndreas Gohr if (self::$held[$name] <= 0) { 66*c66b5ec6SAndreas Gohr unset(self::$held[$name]); 67*c66b5ec6SAndreas Gohr @rmdir(self::lockDir($name)); 68*c66b5ec6SAndreas Gohr } 69*c66b5ec6SAndreas Gohr } 70*c66b5ec6SAndreas Gohr 71*c66b5ec6SAndreas Gohr /** 72*c66b5ec6SAndreas Gohr * Release all held locks 73*c66b5ec6SAndreas Gohr * 74*c66b5ec6SAndreas Gohr * Intended for test teardown to ensure a clean state. 75*c66b5ec6SAndreas Gohr */ 76*c66b5ec6SAndreas Gohr public static function releaseAll(): void 77*c66b5ec6SAndreas Gohr { 78*c66b5ec6SAndreas Gohr foreach (array_keys(self::$held) as $name) { 79*c66b5ec6SAndreas Gohr @rmdir(self::lockDir($name)); 80*c66b5ec6SAndreas Gohr } 81*c66b5ec6SAndreas Gohr self::$held = []; 82*c66b5ec6SAndreas Gohr } 83*c66b5ec6SAndreas Gohr 84*c66b5ec6SAndreas Gohr /** 85*c66b5ec6SAndreas Gohr * Get the lock directory path for a given index name 86*c66b5ec6SAndreas Gohr * 87*c66b5ec6SAndreas Gohr * @param string $name The index base name 88*c66b5ec6SAndreas Gohr * @return string 89*c66b5ec6SAndreas Gohr */ 90*c66b5ec6SAndreas Gohr protected static function lockDir(string $name): string 91596d5287SAndreas Gohr { 92596d5287SAndreas Gohr global $conf; 93*c66b5ec6SAndreas Gohr return $conf['lockdir'] . $name . '.index'; 94596d5287SAndreas Gohr } 95596d5287SAndreas Gohr} 96