Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- class SystemCall {
- protected $callback;
- public function __construct(callable $callback) {
- $this->callback = $callback;
- }
- public function __invoke(Task $task, Scheduler $scheduler) {
- $callback = $this->callback;
- return $callback($task, $scheduler);
- }
- }
- class Task {
- protected $taskId;
- protected $coroutine;
- protected $sendValue = null;
- protected $beforeFirstYield = true; // The first yield is implicitly called to
- // determine that the value of the first
- // yield can be returned correctly.
- public function __construct($taskId, Generator $coroutine) {
- $this->taskId = $taskId;
- $this->coroutine = $coroutine;
- }
- public function getTaskId() {
- return $this->taskId;
- }
- public function setSendValue($sendValue) {
- $this->sendValue = $sendValue;
- }
- public function run() {
- if ($this->beforeFirstYield) {
- $this->beforeFirstYield = false;
- return $this->coroutine->current(); //the first yield, see comment above.
- } else {
- $retval = $this->coroutine->send($this->sendValue); // the returned yield point
- $this->sendValue = null;
- return $retval;
- }
- }
- public function isFinished() {
- return !$this->coroutine->valid();
- }
- }
- class Scheduler {
- protected $maxTaskId = 0;
- protected $taskMap = []; // taskId => task
- protected $taskQueue;
- public function __construct() {
- $this->taskQueue = new SplQueue();
- }
- public function newTask(Generator $coroutine) {
- $tid = ++$this->maxTaskId;
- $task = new Task($tid, $coroutine);
- $this->taskMap[$tid] = $task;
- $this->schedule($task);
- return $tid;
- }
- public function schedule(Task $task) {
- $this->taskQueue->enqueue($task);
- }
- public function run() {
- while (!$this->taskQueue->isEmpty()) {
- $task = $this->taskQueue->dequeue();
- $retval = $task->run();
- if ($retval instanceof SystemCall) {
- $retval($task, $this);
- continue;
- }
- if ($task->isFinished()) {
- unset($this->taskMap[$task->getTaskId()]);
- } else {
- $this->schedule($task);
- }
- }
- }
- public function killTask($tid) {
- if (!isset($this->taskMap[$tid])) {
- return false;
- }
- unset($this->taskMap[$tid]);
- // This is a bit ugly and could be optimized so it does not have to walk the queue,
- // but assuming that killing tasks is rather rare I won't bother with it now
- foreach ($this->taskQueue as $i => $task) {
- if ($task->getTaskId() === $tid) {
- unset($this->taskQueue[$i]);
- break;
- }
- }
- return true;
- }
- }
- // generate a system call for a new task
- function newTask(Generator $coroutine) {
- return new SystemCall(
- function(Task $task, Scheduler $scheduler) use ($coroutine) {
- $task->setSendValue($scheduler->newTask($coroutine));
- $scheduler->schedule($task);
- }
- );
- }
- // kill the current coroutine
- function killTask($tid) {
- return new SystemCall(
- function(Task $task, Scheduler $scheduler) use ($tid) {
- $task->setSendValue($scheduler->killTask($tid));
- $scheduler->schedule($task);
- }
- );
- }
- // get the current task id
- function getTaskId() {
- return new SystemCall(function(Task $task, Scheduler $scheduler) {
- $task->setSendValue($task->getTaskId());
- $scheduler->schedule($task);
- });
- }
- /*
- *
- * fix engine here
- *
- */
- function childTask() {
- $myid = (yield getTaskId());
- while (true) {
- echo "Child task $myid still alive!\n";
- yield;
- }
- }
- function task() {
- $myid = (yield getTaskId());
- echo "Parent task $myid boot.\n";
- $child1 = (yield newTask(childTask()));
- $child2 = (yield newTask(childTask()));
- $child3 = (yield newTask(childTask()));
- $child4 = (yield newTask(childTask()));
- for ($i = 0; $i < 20; $i++) {
- echo "Parent task $myid iteration $i.\n";
- yield;
- if ($i == 4) yield killTask($child1);
- if ($i == 8) yield killTask($child2);
- if ($i == 12) yield killTask($child3);
- if ($i == 16) yield killTask($child4);
- }
- }
- $scheduler = new Scheduler;
- $scheduler->newTask(task());
- $scheduler->run();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement