Skip to content

Commit

Permalink
Swapped out tick-logic closures in Strand for a state-machine approac…
Browse files Browse the repository at this point in the history
…h. Un-abstracted some shared code in GenerateCoroutine.
  • Loading branch information
jmalloc committed Feb 5, 2014
1 parent 4e73687 commit 3f3e019
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 70 deletions.
38 changes: 20 additions & 18 deletions benchmark/loop-benchmark
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<?php
require dirname(__DIR__) . '/vendor/autoload.php';

define('ITERATIONS', 10000);
define('ITERATIONS', 5000);

use React\EventLoop\Factory;
use Recoil\Recoil;
Expand All @@ -22,32 +22,34 @@ function runBenchmark($name, callable $work)
printf(PHP_EOL);
}

runBenchmark(
'react - futureTick',
function () {
$loop = Factory::create();

$remaining = ITERATIONS;
$go = null;
$go = function () use ($loop, &$remaining, &$go) {
if ($remaining--) {
$loop->futureTick($go);
}
};
if (!ini_get('xdebug.profiler_enable')) {
runBenchmark(
'react - futureTick',
function () {
$loop = Factory::create();

$remaining = ITERATIONS;
$go = null;
$go = function () use ($loop, &$remaining, &$go) {
if ($remaining--) {
$loop->nextTick($go);
}
};

$go();
$go();

$loop->run();
}
);
$loop->run();
}
);
}

runBenchmark(
'recoil - futureTick via cooperative yield',
function () {
Recoil::run(
function () {
for ($i = 0; $i < ITERATIONS; ++$i) {
yield;
yield Recoil::noop();
}
}
);
Expand Down
45 changes: 24 additions & 21 deletions src/Coroutine/GeneratorCoroutine.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,18 @@ public function call(StrandInterface $strand)
$e = null;
$valid = $this->generator->valid();
} catch (Exception $e) {
$valid = false;
$strand->throwException($e);

return;
}

$this->dispatch($strand, $valid, $e);
if ($valid) {
$strand->call(
$this->generator->current()
);
} else {
$strand->returnValue(null);
}
}

/**
Expand All @@ -51,10 +59,18 @@ public function resumeWithValue(StrandInterface $strand, $value)
$this->generator->send($value);
$valid = $this->generator->valid();
} catch (Exception $e) {
$valid = false;
$strand->throwException($e);

return;
}

$this->dispatch($strand, $valid, $e);
if ($valid) {
$strand->call(
$this->generator->current()
);
} else {
$strand->returnValue(null);
}
}

/**
Expand All @@ -70,25 +86,12 @@ public function resumeWithException(StrandInterface $strand, Exception $exceptio
$this->generator->throw($exception);
$valid = $this->generator->valid();
} catch (Exception $e) {
$valid = false;
}
$strand->throwException($e);

$this->dispatch($strand, $valid, $e);
}
return;
}

/**
* Dispatch the value or exception produced by the latest tick of the
* generator.
*
* @param StrandInterface $strand The strand that is executing the coroutine.
* @param boolean $valid Whether or not the generator is valid.
* @param Exception|null $exception The exception thrown during the latest tick, if any.
*/
protected function dispatch(StrandInterface $strand, $valid, Exception $exception = null)
{
if ($exception) {
$strand->throwException($exception);
} elseif ($valid) {
if ($valid) {
$strand->call(
$this->generator->current()
);
Expand Down
65 changes: 34 additions & 31 deletions src/Kernel/Strand/Strand.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,8 @@ public function call($coroutine)
return null;
}

$this->tickLogic = function () {
$this->tickLogic = null;
$this->current->call($this);
};
$this->state = self::STATE_CALL;
$this->resumeData = null;

return $coroutine;
}
Expand Down Expand Up @@ -180,10 +178,8 @@ public function resumeWithValue($value)
{
$this->resume();

$this->tickLogic = function () use ($value) {
$this->tickLogic = null;
$this->current->resumeWithValue($this, $value);
};
$this->state = self::STATE_RESUME;
$this->resumeData = $value;
}

/**
Expand All @@ -195,10 +191,8 @@ public function resumeWithException(Exception $exception)
{
$this->resume();

$this->tickLogic = function () use ($exception) {
$this->tickLogic = null;
$this->current->resumeWithException($this, $exception);
};
$this->state = self::STATE_EXCEPTION;
$this->resumeData = $exception;
}

/**
Expand All @@ -208,19 +202,8 @@ public function terminate()
{
$this->resume();

$tickLogic = null;
$tickLogic = function () use (&$tickLogic) {
$this->current->terminate($this);

// Check if the tick logic has been changed, if not continue with
// termination of the strand.
if ($this->tickLogic === $tickLogic) {
$this->pop();
// Note that tickLogic is not reset to null.
}
};

$this->tickLogic = $tickLogic;
$this->state = self::STATE_TERMINATE;
$this->resumeData = null;
}

/**
Expand All @@ -239,19 +222,39 @@ public function hasExited()
public function tick()
{
while (!$this->suspended) {
$tickLogic = $this->tickLogic;

if (!$tickLogic) {
if ($this->state === self::STATE_CALL) {
$this->state = null;
$this->current->call($this);
} elseif ($this->state === self::STATE_RESUME) {
$this->state = null;
$this->current->resumeWithValue($this, $this->resumeData);
} elseif ($this->state === self::STATE_EXCEPTION) {
$this->state = null;
$this->current->resumeWithException($this, $this->resumeData);
} elseif (self::STATE_TERMINATE === $this->state) {
$this->current->terminate($this);

// Check if the tick logic has been changed, if not continue
// with termination of the strand.
if ($this->state === self::STATE_TERMINATE) {
$this->pop();
// Note that tickLogic is not reset to null.
}
} else {
throw new LogicException('No action has been requested.');
}

$tickLogic();
}
}

const STATE_CALL = 1;
const STATE_RESUME = 2;
const STATE_EXCEPTION = 3;
const STATE_TERMINATE = 4;

private $kernel;
private $suspended;
private $stack;
private $current;
private $tickLogic;
private $state;
private $resumeData;
}

0 comments on commit 3f3e019

Please sign in to comment.