mirror of
https://github.com/danog/parallel.git
synced 2024-11-27 04:44:56 +01:00
Fix bugs with deref() and use set() instead of update()
This commit is contained in:
parent
b0ebadedf6
commit
4de9909c00
@ -59,7 +59,7 @@ class SharedObject implements \Serializable
|
|||||||
{
|
{
|
||||||
$this->key = abs(crc32(spl_object_hash($this)));
|
$this->key = abs(crc32(spl_object_hash($this)));
|
||||||
$this->memOpen($this->key, 'n', static::OBJECT_PERMISSIONS, static::SHM_DEFAULT_SIZE);
|
$this->memOpen($this->key, 'n', static::OBJECT_PERMISSIONS, static::SHM_DEFAULT_SIZE);
|
||||||
$this->update($value);
|
$this->set($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,22 +73,29 @@ class SharedObject implements \Serializable
|
|||||||
throw new SharedMemoryException('The object has already been freed.');
|
throw new SharedMemoryException('The object has already been freed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->memGet(0, static::SHM_DATA_OFFSET);
|
// Read from the memory block and handle moved blocks until we find the
|
||||||
$header = unpack('Cstate/Lsize', $data);
|
// correct block.
|
||||||
|
do {
|
||||||
|
$data = $this->memGet(0, static::SHM_DATA_OFFSET);
|
||||||
|
$header = unpack('Cstate/Lsize', $data);
|
||||||
|
|
||||||
|
// If the state is STATE_MOVED, the memory is stale and has been moved
|
||||||
|
// to a new location. Move handle and try to read again.
|
||||||
|
if ($header['state'] !== static::STATE_MOVED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// If the state is STATE_MOVED, the memory is stale and has been moved
|
|
||||||
// to a new location. Move handle and try to read again.
|
|
||||||
if ($header['state'] === static::STATE_MOVED) {
|
|
||||||
shmop_close($this->handle);
|
shmop_close($this->handle);
|
||||||
$this->key = $header['size'];
|
$this->key = $header['size'];
|
||||||
$this->memOpen($this->key, 'w', 0, 0);
|
$this->memOpen($this->key, 'w', 0, 0);
|
||||||
return $this->deref();
|
} while (true);
|
||||||
}
|
|
||||||
|
|
||||||
if ($header['size'] <= 0) {
|
// Make sure the header is in a valid state and format.
|
||||||
|
if ($header['state'] !== static::STATE_ALLOCATED || $header['size'] <= 0) {
|
||||||
throw new SharedMemoryException('Shared object memory is corrupt.');
|
throw new SharedMemoryException('Shared object memory is corrupt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the actual value data from memory and unserialize it.
|
||||||
$data = $this->memGet(static::SHM_DATA_OFFSET, $header['size']);
|
$data = $this->memGet(static::SHM_DATA_OFFSET, $header['size']);
|
||||||
return unserialize($data);
|
return unserialize($data);
|
||||||
}
|
}
|
||||||
@ -100,7 +107,7 @@ class SharedObject implements \Serializable
|
|||||||
*/
|
*/
|
||||||
public function set($value)
|
public function set($value)
|
||||||
{
|
{
|
||||||
$serialized = serialize($object);
|
$serialized = serialize($value);
|
||||||
$size = strlen($serialized);
|
$size = strlen($serialized);
|
||||||
|
|
||||||
// If we run out of space, we need to allocate a new shared memory
|
// If we run out of space, we need to allocate a new shared memory
|
||||||
@ -110,10 +117,10 @@ class SharedObject implements \Serializable
|
|||||||
// automatically after all other processes notice the change and close
|
// automatically after all other processes notice the change and close
|
||||||
// the old handle.
|
// the old handle.
|
||||||
if (shmop_size($this->handle) < $size + static::SHM_DATA_OFFSET) {
|
if (shmop_size($this->handle) < $size + static::SHM_DATA_OFFSET) {
|
||||||
$this->key = $this->key < 0xffffffff ? $this->__key + 1 : mt_rand(0x10, 0xfffffffe);
|
$this->key = $this->key < 0xffffffff ? $this->key + 1 : mt_rand(0x10, 0xfffffffe);
|
||||||
$header = pack('CL', static::STATE_MOVED, $this->key);
|
$header = pack('CL', static::STATE_MOVED, $this->key);
|
||||||
$this->memSet(0, $header);
|
$this->memSet(0, $header);
|
||||||
$this->free();
|
$this->memDelete();
|
||||||
shmop_close($this->handle);
|
shmop_close($this->handle);
|
||||||
|
|
||||||
$this->memOpen($this->key, 'n', static::OBJECT_PERMISSIONS, $size * 2);
|
$this->memOpen($this->key, 'n', static::OBJECT_PERMISSIONS, $size * 2);
|
||||||
@ -133,11 +140,12 @@ class SharedObject implements \Serializable
|
|||||||
*/
|
*/
|
||||||
public function free()
|
public function free()
|
||||||
{
|
{
|
||||||
// Invalidate the memory block first, then request it to be deleted.
|
// Invalidate the memory block by setting its state to FREED.
|
||||||
$this->memSet(0, pack('Cx4', static::STATE_FREED));
|
$this->memSet(0, pack('Cx4', static::STATE_FREED));
|
||||||
if (!shmop_delete($this->handle)) {
|
|
||||||
throw new SharedMemoryException('Failed to discard shared memory block.');
|
// Request the block to be deleted, then close our local handle.
|
||||||
}
|
$this->memDelete();
|
||||||
|
shmop_close($this->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -270,4 +278,16 @@ class SharedObject implements \Serializable
|
|||||||
throw new SharedMemoryException('Failed to write to shared memory block.');
|
throw new SharedMemoryException('Failed to write to shared memory block.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the shared memory segment to be deleted.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
private function memDelete()
|
||||||
|
{
|
||||||
|
if (!shmop_delete($this->handle)) {
|
||||||
|
throw new SharedMemoryException('Failed to discard shared memory block.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,14 @@ class SharedObjectTest extends TestCase
|
|||||||
$this->assertTrue($object->isFreed());
|
$this->assertTrue($object->isFreed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUpdate()
|
public function testSet()
|
||||||
{
|
{
|
||||||
$object = new \stdClass();
|
$shared = new SharedObject(3);
|
||||||
$object->foo = 3;
|
$this->assertEquals(3, $shared->deref());
|
||||||
$shared = new SharedObject($object);
|
|
||||||
$this->assertEquals(3, $shared->deref()->foo);
|
$shared->set(4);
|
||||||
|
$this->assertEquals(4, $shared->deref());
|
||||||
|
|
||||||
$shared->free();
|
$shared->free();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,9 +68,12 @@ class SharedObjectTest extends TestCase
|
|||||||
$object = new \stdClass();
|
$object = new \stdClass();
|
||||||
$shared = new SharedObject($object);
|
$shared = new SharedObject($object);
|
||||||
$clone = clone $shared;
|
$clone = clone $shared;
|
||||||
|
|
||||||
$this->assertNotSame($shared, $clone);
|
$this->assertNotSame($shared, $clone);
|
||||||
$this->assertNotSame($object, $clone->deref());
|
$this->assertNotSame($object, $clone->deref());
|
||||||
$this->assertNotEquals($shared->__debugInfo()['id'], $clone->__debugInfo()['id']);
|
$this->assertNotEquals($shared->__debugInfo()['id'], $clone->__debugInfo()['id']);
|
||||||
|
|
||||||
|
$clone->free();
|
||||||
$shared->free();
|
$shared->free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user