commit a296fd61aac25297e0c17d9d2ec2adb96fd3aa32 Author: Joop Schilder Date: Wed Jan 16 01:10:54 2019 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16b9744 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/vendor/ +/composer.lock +/.idea/ +/.phpstorm/ +/.vscode/ \ No newline at end of file diff --git a/bin/app.php b/bin/app.php new file mode 100755 index 0000000..6f1a672 --- /dev/null +++ b/bin/app.php @@ -0,0 +1,35 @@ +#!/usr/bin/php + 0) { + foreach ($promises as $index => $promise) { + if ($promise->isResolved() && !$promise->isEmpty()) { + print("Response retrieved: " . $promise->getValue() . PHP_EOL); + unset($promises[$index]); + } + } +} + + +exit(0); + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..cfcb8f7 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "joop/php-async", + "authors": [ + { + "name": "Joop Schilder", + "email": "j.n.m.schilder@st.hanze.nl" + } + ], + "autoload": { + "psr-0": { + "": ["src"] + } + }, + "require": { + "ext-pcntl": "*", + "ext-curl": "*", + "ext-sysvshm": "*" + } +} diff --git a/src/Asynchronous.php b/src/Asynchronous.php new file mode 100644 index 0000000..3798762 --- /dev/null +++ b/src/Asynchronous.php @@ -0,0 +1,127 @@ +tempFile = tempnam(__DIR__ . '/../temp', 'PHP'); + $this->shmKey = ftok($this->tempFile, 'a'); + Promise::_setShmKey($this->shmKey); + $this->attach(); + } + + /** + * @return $this + */ + private function attach() + { + $this->shm = shm_attach($this->shmKey); + + return $this; + } + + /** + * @return Asynchronous + */ + private static function getInstance() + { + if (is_null(self::$instance)) + self::$instance = new static(); + + return self::$instance; + } + + + /** + * @param callable $function + * @param mixed ...$parameters + * @return Promise|null; + */ + public static function run(callable $function, ...$parameters) + { + $instance = self::getInstance(); + $pid = pcntl_fork(); + + if ($pid === false) + return null; + + $key = self::generatePromiseKey(); + + if ($pid > 0) { + $instance->children[] = $pid; + + return new Promise($key); + } + + $instance->isChild = true; + $instance->attach(); + try { + $response = call_user_func($function, ...$parameters); + shm_put_var($instance->shm, $key, $response ?? Promise::RESPONSE_NONE); + exit(0); + } catch (Throwable $throwable) { + exit(1); + } + + + } + + /** + * @return int + */ + private static function generatePromiseKey() + { + $promiseKey = self::$key; + self::$key++; + if (self::$key > 9999999) + self::$key = 0; + + return $promiseKey; + } + + + /** + * + */ + public function __destruct() + { + if ($this->isChild) + return; + + while (count($this->children) > 0) { + pcntl_wait($status); + array_shift($this->children); + } + shm_remove($this->shm); + shm_detach($this->shm); + unlink($this->tempFile); + } + +} \ No newline at end of file diff --git a/src/Promise.php b/src/Promise.php new file mode 100644 index 0000000..ba4be81 --- /dev/null +++ b/src/Promise.php @@ -0,0 +1,72 @@ +key = $key; + $this->value = null; + $this->shm = shm_attach(self::$shmKey); + } + + /** + * @return bool + */ + public function isResolved() + { + return shm_has_var($this->shm, $this->key); + } + + /** + * @return bool + */ + public function isEmpty() + { + return $this->getValue() === self::RESPONSE_NONE; + } + + /** + * @return mixed|null + */ + public function getValue() + { + return $this->isResolved() ? $this->resolve()->value : null; + } + + /** + * @return $this + */ + public function resolve() + { + while (!$this->isResolved()) + usleep(1000); // 1ms + + $this->value = shm_get_var($this->shm, $this->key); + return $this; + } + + public function __destruct() + { + if (is_resource($this->shm)) + shm_detach($this->shm); + } +} \ No newline at end of file