Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F90529
D7774.diff
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D7774.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -15,7 +15,6 @@
'AASTToken' => 'parser/aast/api/AASTToken.php',
'AASTTree' => 'parser/aast/api/AASTTree.php',
'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php',
- 'PhutilRope' => 'utils/PhutilRope.php',
'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php',
'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php',
'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php',
@@ -118,6 +117,7 @@
'PhutilCallbackFilterIterator' => 'utils/PhutilCallbackFilterIterator.php',
'PhutilChannel' => 'channel/PhutilChannel.php',
'PhutilChannelChannel' => 'channel/PhutilChannelChannel.php',
+ 'PhutilChannelTestCase' => 'channel/__tests__/PhutilChannelTestCase.php',
'PhutilChunkedIterator' => 'utils/PhutilChunkedIterator.php',
'PhutilChunkedIteratorTestCase' => 'utils/__tests__/PhutilChunkedIteratorTestCase.php',
'PhutilCodeSnippetContextFreeGrammar' => 'grammar/code/PhutilCodeSnippetContextFreeGrammar.php',
@@ -264,6 +264,8 @@
'PhutilRemarkupRuleItalic' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleItalic.php',
'PhutilRemarkupRuleLinebreaks' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleLinebreaks.php',
'PhutilRemarkupRuleMonospace' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleMonospace.php',
+ 'PhutilRope' => 'utils/PhutilRope.php',
+ 'PhutilRopeTestCase' => 'utils/__tests__/PhutilRopeTestCase.php',
'PhutilSafeHTML' => 'markup/PhutilSafeHTML.php',
'PhutilSafeHTMLProducerInterface' => 'markup/PhutilSafeHTMLProducerInterface.php',
'PhutilSafeHTMLTestCase' => 'markup/__tests__/PhutilSafeHTMLTestCase.php',
@@ -363,6 +365,7 @@
'phutil_escape_uri' => 'markup/render.php',
'phutil_escape_uri_path_component' => 'markup/render.php',
'phutil_exit' => 'utils/utils.php',
+ 'phutil_fwrite_nonblocking_stream' => 'utils/utils.php',
'phutil_get_library_name_for_root' => 'moduleutils/moduleutils.php',
'phutil_get_library_root' => 'moduleutils/moduleutils.php',
'phutil_get_library_root_for_path' => 'moduleutils/moduleutils.php',
@@ -516,6 +519,7 @@
'PhutilCLikeCodeSnippetContextFreeGrammar' => 'PhutilCodeSnippetContextFreeGrammar',
'PhutilCallbackFilterIterator' => 'FilterIterator',
'PhutilChannelChannel' => 'PhutilChannel',
+ 'PhutilChannelTestCase' => 'PhutilTestCase',
'PhutilChunkedIterator' => 'Iterator',
'PhutilChunkedIteratorTestCase' => 'PhutilTestCase',
'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar',
@@ -617,6 +621,8 @@
'PhutilRemarkupRuleItalic' => 'PhutilRemarkupRule',
'PhutilRemarkupRuleLinebreaks' => 'PhutilRemarkupRule',
'PhutilRemarkupRuleMonospace' => 'PhutilRemarkupRule',
+ 'PhutilRope' => 'Phobject',
+ 'PhutilRopeTestCase' => 'PhutilTestCase',
'PhutilSafeHTMLTestCase' => 'PhutilTestCase',
'PhutilSaturateStdoutDaemon' => 'PhutilTortureTestDaemon',
'PhutilShellLexer' => 'PhutilLexer',
diff --git a/src/channel/PhutilChannel.php b/src/channel/PhutilChannel.php
--- a/src/channel/PhutilChannel.php
+++ b/src/channel/PhutilChannel.php
@@ -34,6 +34,7 @@
private $ibuf = '';
private $obuf;
private $name;
+ private $readBufferSize;
public function __construct() {
$this->obuf = new PhutilRope();
@@ -178,13 +179,19 @@
* @task update
*/
public function update() {
- while (true) {
- $in = $this->readBytes();
+ $maximum_read = PHP_INT_MAX;
+ if ($this->readBufferSize !== null) {
+ $maximum_read = ($this->readBufferSize - strlen($this->ibuf));
+ }
+
+ while ($maximum_read > 0) {
+ $in = $this->readBytes($maximum_read);
if (!strlen($in)) {
// Reading is blocked for now.
break;
}
$this->ibuf .= $in;
+ $maximum_read -= strlen($in);
}
while ($this->obuf->getByteLength()) {
@@ -275,11 +282,12 @@
/**
* Read from the channel's underlying I/O.
*
+ * @param int Maximum number of bytes to read.
* @return string Bytes, if available.
*
* @task impl
*/
- abstract protected function readBytes();
+ abstract protected function readBytes($length);
/**
@@ -318,6 +326,21 @@
/**
+ * Set the maximum size of the channel's read buffer. Reads will artificially
+ * block once the buffer reaches this size until the in-process buffer is
+ * consumed.
+ *
+ * @param int|null Maximum read buffer size, or `null` for a limitless buffer.
+ * @return this
+ * @task impl
+ */
+ public function setReadBufferSize($size) {
+ $this->readBufferSize = $size;
+ return $this;
+ }
+
+
+ /**
* Test state of the read buffer.
*
* @return bool True if the read buffer is empty.
diff --git a/src/channel/PhutilChannelChannel.php b/src/channel/PhutilChannelChannel.php
--- a/src/channel/PhutilChannelChannel.php
+++ b/src/channel/PhutilChannelChannel.php
@@ -53,7 +53,7 @@
return $this->channel->isOpenForWriting();
}
- protected function readBytes() {
+ protected function readBytes($length) {
$this->throwOnRawByteOperations();
}
@@ -69,6 +69,11 @@
return $this->channel->getWriteSockets();
}
+ public function setReadBufferSize($size) {
+ $this->channel->setReadBufferSize($size);
+ return $this;
+ }
+
public function isReadBufferEmpty() {
return $this->channel->isReadBufferEmpty();
}
diff --git a/src/channel/PhutilExecChannel.php b/src/channel/PhutilExecChannel.php
--- a/src/channel/PhutilExecChannel.php
+++ b/src/channel/PhutilExecChannel.php
@@ -90,7 +90,7 @@
return !$this->future->isReady();
}
- protected function readBytes() {
+ protected function readBytes($length) {
list($stdout, $stderr) = $this->future->read();
$this->future->discardBuffers();
@@ -126,6 +126,15 @@
return $this->future->getWriteSockets();
}
+ public function setReadBufferSize($size) {
+ // NOTE: We may end up using 2x the buffer size here, one inside
+ // ExecFuture and one inside the Channel. We could tune this eventually, but
+ // it should be fine for now.
+ parent::setReadBufferSize($size);
+ $this->future->setReadBufferSize($size);
+ return $this;
+ }
+
public function isWriteBufferEmpty() {
return $this->future->isWriteBufferEmpty();
}
diff --git a/src/channel/PhutilSocketChannel.php b/src/channel/PhutilSocketChannel.php
--- a/src/channel/PhutilSocketChannel.php
+++ b/src/channel/PhutilSocketChannel.php
@@ -103,13 +103,13 @@
return (bool)$this->writeSocket;
}
- protected function readBytes() {
+ protected function readBytes($length) {
$socket = $this->readSocket;
if (!$socket) {
return '';
}
- $data = @fread($socket, 4096);
+ $data = @fread($socket, min($length, 64 * 1024));
if ($data === false) {
$this->closeReadSocket();
diff --git a/src/channel/__tests__/PhutilChannelTestCase.php b/src/channel/__tests__/PhutilChannelTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/channel/__tests__/PhutilChannelTestCase.php
@@ -0,0 +1,45 @@
+<?php
+
+final class PhutilChannelTestCase extends PhutilTestCase {
+
+ public function testChannelBasics() {
+ list($x, $y) = PhutilSocketChannel::newChannelPair();
+
+ $str_len_8 = 'abcdefgh';
+ $str_len_4 = 'abcd';
+
+ // Do a write with no buffer limit.
+
+ $x->write($str_len_8);
+ while (true) {
+ $x->update();
+ $y->update();
+ $read = $y->read();
+ if (strlen($read)) {
+ break;
+ }
+ }
+
+ // We expect to read the entire message.
+ $this->assertEqual($str_len_8, $read);
+
+
+ // Again, with a read buffer limit.
+
+ $y->setReadBufferSize(4);
+ $x->write($str_len_8);
+
+ while (true) {
+ $x->update();
+ $y->update();
+ $read = $y->read();
+ if (strlen($read)) {
+ break;
+ }
+ }
+
+ // We expect to see only the first 4 bytes of the message.
+ $this->assertEqual($str_len_4, $read);
+ }
+
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/nj/dd/ffyathk7qljvu66x
Default Alt Text
D7774.diff (8 KB)
Attached To
Mode
D7774: Add a read buffer size to PhutilChannel
Attached
Detach File
Event Timeline
Log In to Comment