1
0
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:
coderstephen 2015-08-10 13:21:22 -05:00
parent b0ebadedf6
commit 4de9909c00
2 changed files with 46 additions and 21 deletions

View File

@ -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.');
}
}
} }

View File

@ -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();
} }
} }