forked from GithubBackups/vichan
commit
98650ec2e7
@ -2918,7 +2918,20 @@ function generation_strategy($fun, $array=array()) { global $config;
|
|||||||
return 'rebuild';
|
return 'rebuild';
|
||||||
case 'defer':
|
case 'defer':
|
||||||
// Ok, it gets interesting here :)
|
// Ok, it gets interesting here :)
|
||||||
get_queue('generate')->push(serialize(array('build', $fun, $array, $action)));
|
$queue = Queues::get_queue($config, 'generate');
|
||||||
|
if ($queue === false) {
|
||||||
|
if ($config['syslog']) {
|
||||||
|
_syslog(LOG_ERR, "Could not initialize generate queue, falling back to immediate rebuild strategy");
|
||||||
|
}
|
||||||
|
return 'rebuild';
|
||||||
|
}
|
||||||
|
$ret = $queue->push(serialize(array('build', $fun, $array, $action)));
|
||||||
|
if ($ret === false) {
|
||||||
|
if ($config['syslog']) {
|
||||||
|
_syslog(LOG_ERR, "Could not push item in the queue, falling back to immediate rebuild strategy");
|
||||||
|
}
|
||||||
|
return 'rebuild';
|
||||||
|
}
|
||||||
return 'ignore';
|
return 'ignore';
|
||||||
case 'build_on_load':
|
case 'build_on_load':
|
||||||
return 'delete';
|
return 'delete';
|
||||||
|
79
inc/lock.php
79
inc/lock.php
@ -1,39 +1,84 @@
|
|||||||
<?php
|
<?php
|
||||||
class Lock {
|
|
||||||
function __construct($key) { global $config;
|
class Locks {
|
||||||
if ($config['lock']['enabled'] == 'fs') {
|
private static function filesystem(string $key): Lock|false {
|
||||||
$key = str_replace('/', '::', $key);
|
$key = str_replace('/', '::', $key);
|
||||||
$key = str_replace("\0", '', $key);
|
$key = str_replace("\0", '', $key);
|
||||||
|
|
||||||
$this->f = fopen("tmp/locks/$key", "w");
|
$fd = fopen("tmp/locks/$key", "w");
|
||||||
}
|
if ($fd === false) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a shared lock
|
return new class($fd) implements Lock {
|
||||||
function get($nonblock = false) { global $config;
|
// Resources have no type in php.
|
||||||
if ($config['lock']['enabled'] == 'fs') {
|
private mixed $f;
|
||||||
|
|
||||||
|
|
||||||
|
function __construct($fd) {
|
||||||
|
$this->f = $fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get(bool $nonblock = false): Lock|false {
|
||||||
$wouldblock = false;
|
$wouldblock = false;
|
||||||
flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
||||||
if ($nonblock && $wouldblock) return false;
|
if ($nonblock && $wouldblock) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an exclusive lock
|
public function get_ex(bool $nonblock = false): Lock|false {
|
||||||
function get_ex($nonblock = false) { global $config;
|
|
||||||
if ($config['lock']['enabled'] == 'fs') {
|
|
||||||
$wouldblock = false;
|
$wouldblock = false;
|
||||||
flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock);
|
||||||
if ($nonblock && $wouldblock) return false;
|
if ($nonblock && $wouldblock) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free a lock
|
public function free(): Lock {
|
||||||
function free() { global $config;
|
|
||||||
if ($config['lock']['enabled'] == 'fs') {
|
|
||||||
flock($this->f, LOCK_UN);
|
flock($this->f, LOCK_UN);
|
||||||
}
|
|
||||||
return $this;
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_lock(array $config, string $key): Lock|false {
|
||||||
|
if ($config['lock']['enabled'] == 'fs') {
|
||||||
|
return self::filesystem($key);
|
||||||
|
} else {
|
||||||
|
return self::none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -1,49 +1,98 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Queue {
|
class Queues {
|
||||||
function __construct($key) { global $config;
|
private static $queues = array();
|
||||||
if ($config['queue']['enabled'] == 'fs') {
|
|
||||||
$this->lock = new Lock($key);
|
|
||||||
|
/**
|
||||||
|
* This queue implementation isn't actually ordered, so it works more as a "bag".
|
||||||
|
*/
|
||||||
|
private static function filesystem(string $key, Lock $lock): Queue {
|
||||||
$key = str_replace('/', '::', $key);
|
$key = str_replace('/', '::', $key);
|
||||||
$key = str_replace("\0", '', $key);
|
$key = str_replace("\0", '', $key);
|
||||||
$this->key = "tmp/queue/$key/";
|
$key = "tmp/queue/$key/";
|
||||||
}
|
|
||||||
|
return new class($key, $lock) implements Queue {
|
||||||
|
private Lock $lock;
|
||||||
|
private string $key;
|
||||||
|
|
||||||
|
|
||||||
|
function __construct(string $key, Lock $lock) {
|
||||||
|
$this->lock = $lock;
|
||||||
|
$this->key = $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
function push($str) { global $config;
|
public function push(string $str): bool {
|
||||||
if ($config['queue']['enabled'] == 'fs') {
|
|
||||||
$this->lock->get_ex();
|
$this->lock->get_ex();
|
||||||
file_put_contents($this->key.microtime(true), $str);
|
$ret = file_put_contents($this->key . microtime(true), $str);
|
||||||
$this->lock->free();
|
$this->lock->free();
|
||||||
}
|
return $ret !== false;
|
||||||
return $this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pop($n = 1) { global $config;
|
public function pop(int $n = 1): array {
|
||||||
if ($config['queue']['enabled'] == 'fs') {
|
|
||||||
$this->lock->get_ex();
|
$this->lock->get_ex();
|
||||||
$dir = opendir($this->key);
|
$dir = opendir($this->key);
|
||||||
$paths = array();
|
$paths = array();
|
||||||
|
|
||||||
while ($n > 0) {
|
while ($n > 0) {
|
||||||
$path = readdir($dir);
|
$path = readdir($dir);
|
||||||
if ($path === FALSE) break;
|
if ($path === false) {
|
||||||
elseif ($path == '.' || $path == '..') continue;
|
break;
|
||||||
else { $paths[] = $path; $n--; }
|
} elseif ($path == '.' || $path == '..') {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$paths[] = $path;
|
||||||
|
$n--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$out = array();
|
$out = array();
|
||||||
foreach ($paths as $v) {
|
foreach ($paths as $v) {
|
||||||
$out[] = file_get_contents($this->key . $v);
|
$out[] = file_get_contents($this->key . $v);
|
||||||
unlink($this->key . $v);
|
unlink($this->key . $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->lock->free();
|
$this->lock->free();
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No-op. Can be used for mocking.
|
||||||
|
*/
|
||||||
|
public static function none(): Queue {
|
||||||
|
return new class() implements Queue {
|
||||||
|
public function push(string $str): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pop(int $n = 1): array {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_queue(array $config, string $name): Queue|false {
|
||||||
|
if (!isset(self::$queues[$name])) {
|
||||||
|
if ($config['queue']['enabled'] == 'fs') {
|
||||||
|
$lock = Locks::get_lock($config, $name);
|
||||||
|
if ($lock === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self::$queues[$name] = self::filesystem($name, $lock);
|
||||||
|
} else {
|
||||||
|
self::$queues[$name] = self::none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::$queues[$name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't use the constructor. Use the get_queue function.
|
interface Queue {
|
||||||
$queues = array();
|
// Push a string in the queue.
|
||||||
|
public function push(string $str): bool;
|
||||||
|
|
||||||
function get_queue($name) { global $queues;
|
// Get a string from the queue.
|
||||||
return $queues[$name] = isset ($queues[$name]) ? $queues[$name] : new Queue($name);
|
public function pop(int $n = 1): array;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user