Initial commit
This commit is contained in:
133
src/Shm/SharedData.php
Normal file
133
src/Shm/SharedData.php
Normal 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
94
src/Shm/SharedMemory.php
Normal 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);
|
||||
}
|
||||
}
|
99
src/Shm/SharedMemoryBlock.php
Normal file
99
src/Shm/SharedMemoryBlock.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user