-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Asynchronous invocation #25
Comments
This is how it would work with xp-framework/networking#22 extended by the following patch: diff --git a/src/main/php/peer/server/AsyncServer.class.php b/src/main/php/peer/server/AsyncServer.class.php
index 13a55f5..af25f68 100755
--- a/src/main/php/peer/server/AsyncServer.class.php
+++ b/src/main/php/peer/server/AsyncServer.class.php
@@ -3,7 +3,7 @@
use Throwable;
use lang\IllegalStateException;
use peer\server\protocol\SocketAcceptHandler;
-use peer\{ServerSocket, SocketException, SocketTimeoutException};
+use peer\{Socket, ServerSocket, SocketException, SocketTimeoutException};
/**
* Asynchronous TCP/IP Server
@@ -140,7 +140,26 @@ class AsyncServer extends Server {
}
});
return $i;
- }
+ }
+
+ /**
+ * Returns a slot to watch for a given awaitable
+ *
+ * @param var $awaitable
+ * @param int $slot the slot to signal
+ * @return int
+ */
+ private function watch($awaitable, $slot) {
+ if ($awaitable instanceof Socket) {
+ $new= $this->select ? array_key_last($this->select) + 1 : 1;
+ $this->select[$new]= $awaitable;
+ $this->continuation[$new]= new Continuation(function() use($slot) { yield 'signal' => $slot; });
+ return $new;
+ }
+
+ // No awaitable given, watch the current slot
+ return $slot;
+ }
/**
* Runs service until shutdown() is called.
@@ -193,11 +212,15 @@ class AsyncServer extends Server {
continue;
}
- // `yield 'accept' => $socket`: Check for being able to read from socket
- // `yield 'read' => $_`: Continue as soon as the socket becomes readable
- // `yield 'write' => $_`: Continue as soon as the socket becomes writeable
- // `yield 'delay' => $millis`: Wait a specified number of milliseconds
- // `yield`: Continue at the next possible execution slot (`delay => 0`)
+ // Internal use:
+ // * `yield 'accept' => $socket`: Check for being able to read from socket
+ // * `yield 'signal' => $n`: Finish signalling task, continue slot #n immediately
+ //
+ // Public use:
+ // * `yield 'read' => $awaitable`: Continue as soon as the awaitable becomes readable
+ // * `yield 'write' => $awaitable`: Continue as soon as the awaitable becomes writeable
+ // * `yield 'delay' => $millis`: Wait a specified number of milliseconds
+ // * `yield`: Continue at the next possible execution slot (`delay => 0`)
switch ($execute->key()) {
case 'accept':
$socket= $execute->current();
@@ -206,13 +229,21 @@ class AsyncServer extends Server {
$wait[]= $socket->getTimeout();
break;
+ case 'signal':
+ unset($this->tasks[$i], $this->select[$i], $this->continuation[$i], $write[$i]);
+ $waitable[$execute->current()]= true;
+ $wait[]= 0;
+ break;
+
case 'write':
+ $i= $this->watch($execute->current(), $i);
$write[$i]= true;
$writeable[$i]= $this->select[$i];
$wait[]= $this->select[$i]->getTimeout();
break;
case 'read':
+ $i= $this->watch($execute->current(), $i);
unset($write[$i]);
$readable[$i]= $this->select[$i];
$wait[]= $this->select[$i]->getTimeout(); Now, code could also pass sockets to select on via $s= new Socket('thekid.de', 80);
$s->connect();
yield 'write' => $s;
$s->write("GET / HTTP/1.1\r\nHost: thekid.de\r\nConnection: close\r\n\r\n");
do {
yield 'read' => $s;
$buffer= $s->read();
yield 'write' => $response;
$response->write($buffer);
} while (!$s->eof());
$s->close(); This could be extracted into a library as follows: class HttpConnection {
private $target;
public function __construct($target) {
$this->target= $target;
}
public function get($uri= '/') {
$s= new Socket($this->target, 80);
$s->connect();
yield 'write' => $s;
$s->write("GET {$uri} HTTP/1.1\r\nHost: {$this->target}\r\nConnection: close\r\n\r\n");
return $s;
}
}
// Usage:
$c= new HttpConnection('thekid.de');
$s= yield from $c->get('/');
do {
// Same as above
} while (!$s->eof());
$s->close(); However, $r= (function() use($argc, $argv) {
// Put all top-level statements here
})();
if ($r instanceof Generator) {
while ($r->valid()) $r->next();
return $r->getReturn();
} else {
return $r;
} The last problem is forgetting the $c= new HttpConnection('thekid.de');
$s= $c->get('/');
// Call to undefined method Generator::read()
$s->read(); There are a couple of options here:
|
XP compiler could throw errors for async method invocations without |
If used inside a single-threaded
xp-forge/web
web application, waiting will block the entire server. We should also have an asynchronous method to execute requests and wait for them.Blocks server until API call returns
Current functionality
Asynchronous handler function
Idea: Defer reading from request until any of RestResponse's methods are called, add await():
The text was updated successfully, but these errors were encountered: