Page MenuHomePhabricator

D21666.id.diff
No OneTemporary

D21666.id.diff

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
@@ -378,6 +378,7 @@
'ArcanistNoParentScopeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistNoParentScopeXHPASTLinterRule.php',
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistNoParentScopeXHPASTLinterRuleTestCase.php',
'ArcanistNoURIConduitException' => 'conduit/ArcanistNoURIConduitException.php',
+ 'ArcanistNonblockingGuard' => 'utils/ArcanistNonblockingGuard.php',
'ArcanistNoneLintRenderer' => 'lint/renderer/ArcanistNoneLintRenderer.php',
'ArcanistObjectListHardpoint' => 'hardpoint/ArcanistObjectListHardpoint.php',
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistObjectOperatorSpacingXHPASTLinterRule.php',
@@ -1434,6 +1435,7 @@
'ArcanistNoParentScopeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistNoParentScopeXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistNoURIConduitException' => 'ArcanistConduitException',
+ 'ArcanistNonblockingGuard' => 'Phobject',
'ArcanistNoneLintRenderer' => 'ArcanistLintRenderer',
'ArcanistObjectListHardpoint' => 'ArcanistHardpoint',
'ArcanistObjectOperatorSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
diff --git a/src/toolset/ArcanistPrompt.php b/src/toolset/ArcanistPrompt.php
--- a/src/toolset/ArcanistPrompt.php
+++ b/src/toolset/ArcanistPrompt.php
@@ -93,16 +93,9 @@
// NOTE: We're making stdin nonblocking so that we can respond to signals
// immediately. If we don't, and you ^C during a prompt, the program does
- // not handle the signal until fgets() returns.
+ // not handle the signal until fgets() returns. See also T13649.
- // On Windows, we skip this because stdin can not be made nonblocking.
-
- if (!phutil_is_windows()) {
- $ok = stream_set_blocking($stdin, false);
- if (!$ok) {
- throw new Exception(pht('Unable to set stdin nonblocking.'));
- }
- }
+ $guard = ArcanistNonblockingGuard::newForStream($stdin);
echo "\n";
@@ -123,7 +116,7 @@
$is_saved = false;
- if (phutil_is_windows()) {
+ if (!$guard->getIsNonblocking()) {
$response = fgets($stdin);
} else {
while (true) {
diff --git a/src/utils/ArcanistNonblockingGuard.php b/src/utils/ArcanistNonblockingGuard.php
new file mode 100644
--- /dev/null
+++ b/src/utils/ArcanistNonblockingGuard.php
@@ -0,0 +1,56 @@
+<?php
+
+final class ArcanistNonblockingGuard
+ extends Phobject {
+
+ private $stream;
+ private $didSetNonblocking;
+
+ public static function newForStream($stream) {
+ $guard = new self();
+ $guard->stream = $stream;
+
+ if (phutil_is_windows()) {
+
+ // On Windows, we skip this because stdin can not be made nonblocking.
+
+ } else if (!function_exists('pcntl_signal')) {
+
+ // If we can't handle signals, we: can't reset the flag if we're
+ // interrupted; but also don't benefit from setting it in the first
+ // place since it's only relevant for handling interrupts during
+ // prompts. So just skip this.
+
+ } else {
+
+ // See T13649. Note that the "blocked" key identifies whether the
+ // stream is blocking or nonblocking, not whether it will block when
+ // read or written.
+
+ $metadata = stream_get_meta_data($stream);
+ $is_blocking = idx($metadata, 'blocked');
+ if ($is_blocking) {
+ $ok = stream_set_blocking($stream, false);
+ if (!$ok) {
+ throw new Exception(pht('Unable to set stream nonblocking.'));
+ }
+ $guard->didSetNonblocking = true;
+ }
+ }
+
+ return $guard;
+ }
+
+ public function getIsNonblocking() {
+ return $this->didSetNonblocking;
+ }
+
+ public function __destruct() {
+ if ($this->stream && $this->didSetNonblocking) {
+ stream_set_blocking($this->stream, true);
+ }
+
+ $this->stream = null;
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 3, 11:53 PM (5 h, 2 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7088103
Default Alt Text
D21666.id.diff (4 KB)

Event Timeline