* Signal dispatch() method now uses own pid as default value * SharedData now updates checksum cache on set()
134 lines
3.1 KiB
PHP
134 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace NoccyLabs\Ipc\Shm;
|
|
|
|
/**
|
|
* Shared data storage with a key-value interface
|
|
*
|
|
*/
|
|
class SharedData extends SharedMemory
|
|
{
|
|
private const IDX_LOCK = 0;
|
|
private const IDX_MAP = 1;
|
|
private const IDX_DATA = 2;
|
|
|
|
private $checks = [];
|
|
|
|
/**
|
|
* Acquire a lock
|
|
*
|
|
* @return void
|
|
*/
|
|
private function lock()
|
|
{
|
|
$expire = microtime(true) + 1;
|
|
while ($this[self::IDX_LOCK] !== null) {
|
|
usleep(1000);
|
|
// Forcefully continue after 1 second
|
|
if (microtime(true)>$expire) {
|
|
break;
|
|
}
|
|
}
|
|
$this[self::IDX_LOCK] = 1;
|
|
}
|
|
|
|
/**
|
|
* Release the lock
|
|
*
|
|
* @return void
|
|
*/
|
|
private function unlock()
|
|
{
|
|
$this[self::IDX_LOCK] = null;
|
|
}
|
|
|
|
/**
|
|
* Get the value of a key
|
|
*
|
|
* @param string $key
|
|
* @return mixed
|
|
*/
|
|
public function get(string $key)
|
|
{
|
|
$map = (array)$this[self::IDX_MAP];
|
|
|
|
// If the key doesn't exist, return null
|
|
if (!array_key_exists($key, $map)) {
|
|
return null;
|
|
}
|
|
|
|
$index = $map[$key];
|
|
$keyvar = $this[self::IDX_DATA + $index];
|
|
|
|
// If for some reason we don't get anything back, return null
|
|
if (!$keyvar) {
|
|
return null;
|
|
}
|
|
|
|
// Save the checksum for later and return the value
|
|
$this->checks[$key] = $keyvar[0];
|
|
return $keyvar[1];
|
|
|
|
}
|
|
|
|
/**
|
|
* Set a key to a specific value
|
|
*
|
|
* @param string $key
|
|
* @param mixed $value
|
|
* @param boolean $check
|
|
* @return boolean
|
|
*/
|
|
public function set(string $key, $value, $check=false):bool
|
|
{
|
|
// Acquire a lock and fetch the map
|
|
$this->lock();
|
|
$map = (array)$this[self::IDX_MAP];
|
|
|
|
// If the key doesn't exist in the map, we need to set it up
|
|
if (!array_key_exists($key, $map)) {
|
|
$free = 0;
|
|
// Find first free index
|
|
foreach ($map as $map_key=>$index) {
|
|
if ($index != $free) { break; }
|
|
$free++;
|
|
}
|
|
// Store the index in the map, and sort it by index.
|
|
$map[$key] = $free;
|
|
asort($map);
|
|
$this[self::IDX_MAP] = $map;
|
|
}
|
|
|
|
// Look up the key and fetch the data
|
|
$index = $map[$key];
|
|
$keyvar = $this[self::IDX_DATA + $index];
|
|
|
|
// Check the data to make sure the data hasn't changed since last read
|
|
if ($check && $keyvar && array_key_exists($key, $this->checks)) {
|
|
if ($keyvar[0] != $this->checks[$key]) {
|
|
$this->unlock();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Update the data
|
|
$this[self::IDX_DATA + $index] = [ md5($value), $value ];
|
|
$this->checks[$key] = md5($value);
|
|
$this->unlock();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if the map contains the key. This method doesn't acquire a lock
|
|
*
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public function contains(string $key):bool
|
|
{
|
|
$map = (array)$this[self::IDX_MAP];
|
|
|
|
return array_key_exists($key, $map);
|
|
}
|
|
|
|
} |