Initial commit

This commit is contained in:
2018-04-15 16:41:46 +02:00
commit 86ff40274b
29 changed files with 1368 additions and 0 deletions

133
src/Shm/SharedData.php Normal file
View File

@ -0,0 +1,133 @@
<?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->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);
}
}

94
src/Shm/SharedMemory.php Normal file
View File

@ -0,0 +1,94 @@
<?php
namespace NoccyLabs\Ipc\Shm;
use NoccyLabs\Ipc\Key\KeyInterface;
/**
* Shared memory segment
*/
class SharedMemory implements \ArrayAccess
{
protected $resource;
/**
* Constructor
*
* @param KeyInterface $key
* @param integer $memsize
* @param integer $perm
*/
public function __construct(KeyInterface $key, int $memsize = 64000, int $perm = 0666)
{
if (!($shm_resource = shm_attach($key->getKey(), $memsize, $perm))) {
throw new \RuntimeException("Unable to attach shm resource {$key}");
}
$this->resource = $shm_resource;
}
/**
* Destructor
*/
public function __destruct()
{
shm_detach($this->resource);
}
/**
* Destroy the shm segment
*
* @return void
*/
public function destroy()
{
shm_remove($this->resource);
}
/**
* {@inheritDoc}
*
* @param int $offset
* @return void
*/
public function offsetExists($offset)
{
return shm_has_var($this->resource, $offset);
}
/**
* {@inheritDoc}
*
* @param int $offset
* @return mixed
*/
public function offsetGet($offset)
{
return shm_has_var($this->resource, $offset)?shm_get_var($this->resource, $offset):null;
}
/**
* {@inheritDoc}
*
* @param int $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value)
{
shm_put_var($this->resource, $offset, $value);
}
/**
* {@inheritDoc}
*
* @param int $offset
* @return void
*/
public function offsetUnset($offset)
{
shm_remove_var($this->resource, $offset);
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace NoccyLabs\Ipc\Shm;
use NoccyLabs\Ipc\Key\KeyInterface;
/**
* A block of shared memory that can be read and write like a string buffer
*
*
*/
class SharedMemoryBlock
{
const FLAG_ACCESS = "a";
const FLAG_CREATE = "c";
const FLAG_WRITE = "w";
const FLAG_NEW = "n";
protected $resource;
/**
* Constructor
*
* @param KeyInterface $key
* @param string $flags
* @param integer $memsize
* @param integer $perm
*/
public function __construct(KeyInterface $key, string $flags, int $memsize, int $perm = 0666)
{
if (!($shm_resource = shmop_open($key->getKey(), $memsize, $perm))) {
throw new \RuntimeException("Unable to attach shm resource {$key}");
}
$this->resource = $shm_resource;
}
/**
* Destructor
*/
public function __destruct()
{
if (!$this->resource) {
return;
}
shmop_close($this->resource);
}
/**
* Destroy the memory block
*
* @return void
*/
public function destroy()
{
shmop_delete($this->resource);
shmop_close($this->resource);
$this->resource = null;
}
/**
* Read bytes from the shared block
*
* @param integer $length
* @param integer $offset
* @return mixed
*/
public function read($length=0, $offset=0)
{
if ($length == 0) {
$length = shmop_size($this->resource) - $offset;
}
return shmop_read($this->resource, $offset, $length);
}
/**
* Write bytes to the shared block
*
* @param mixed $data
* @param integer $offset
* @return integer
*/
public function write($data, $offset=0):int
{
return shmop_write($this->resource, $data, $offset);
}
/**
* Get the size of the memory block
*
* @return integer
*/
public function getSize():int
{
return shmop_size($this->resource);
}
}