Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13964009
D19703.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D19703.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
@@ -718,7 +718,6 @@
'PhutilExecPassthru' => 'future/exec/PhutilExecPassthru.php',
'PhutilExecutableFuture' => 'future/exec/PhutilExecutableFuture.php',
'PhutilExecutionEnvironment' => 'utils/PhutilExecutionEnvironment.php',
- 'PhutilExtensionsTestCase' => 'moduleutils/__tests__/PhutilExtensionsTestCase.php',
'PhutilFacebookAuthAdapter' => 'auth/PhutilFacebookAuthAdapter.php',
'PhutilFatalDaemon' => 'daemon/torture/PhutilFatalDaemon.php',
'PhutilFileLock' => 'filesystem/PhutilFileLock.php',
@@ -1841,7 +1840,6 @@
'PhutilExecPassthru' => 'PhutilExecutableFuture',
'PhutilExecutableFuture' => 'Future',
'PhutilExecutionEnvironment' => 'Phobject',
- 'PhutilExtensionsTestCase' => 'PhutilTestCase',
'PhutilFacebookAuthAdapter' => 'PhutilOAuthAuthAdapter',
'PhutilFatalDaemon' => 'PhutilTortureTestDaemon',
'PhutilFileLock' => 'PhutilLock',
diff --git a/src/toolset/ArcanistWorkflow.php b/src/toolset/ArcanistWorkflow.php
--- a/src/toolset/ArcanistWorkflow.php
+++ b/src/toolset/ArcanistWorkflow.php
@@ -129,9 +129,13 @@
}
final public function executeWorkflow(PhutilArgumentParser $args) {
+ $runtime = $this->getRuntime();
+
$this->arguments = $args;
$caught = null;
+ $runtime->pushWorkflow($this);
+
try {
$err = $this->runWorkflow($args);
} catch (Exception $ex) {
@@ -144,6 +148,8 @@
phlog($ex);
}
+ $runtime->popWorkflow();
+
if ($caught) {
throw $caught;
}
@@ -189,4 +195,12 @@
return $this->getRuntime()->getLogEngine();
}
+ public function canHandleSignal($signo) {
+ return false;
+ }
+
+ public function handleSignal($signo) {
+ throw new PhutilMethodNotImplementedException();
+ }
+
}
diff --git a/src/workflow/ArcanistWeldWorkflow.php b/src/workflow/ArcanistWeldWorkflow.php
--- a/src/workflow/ArcanistWeldWorkflow.php
+++ b/src/workflow/ArcanistWeldWorkflow.php
@@ -1,36 +1,38 @@
<?php
-final class ArcanistWeldWorkflow extends ArcanistWorkflow {
+final class ArcanistWeldWorkflow
+ extends ArcanistWorkflow {
public function getWorkflowName() {
return 'weld';
}
- public function getCommandSynopses() {
- return phutil_console_format(<<<EOTEXT
- **weld** [options] __file__ __file__ ...
+ public function getWorkflowInformation() {
+ $help = pht(<<<EOTEXT
+Robustly fuse two or more files together. The resulting joint is much stronger
+than the one created by tools like __cat__.
EOTEXT
- );
- }
+);
- public function getCommandHelp() {
- return phutil_console_format(<<<EOTEXT
- Robustly fuse two or more files together. The resulting joint is
- much stronger than the one created by tools like __cat__.
-EOTEXT
- );
+ return $this->newWorkflowInformation()
+ ->addExample(pht('**weld** [__options__] __file__ __file__ ...'))
+ ->setHelp($help);
}
- public function getArguments() {
+ public function getWorkflowArguments() {
return array(
- '*' => 'files',
+ $this->newWorkflowArgument('files')
+ ->setIsPathArgument(true)
+ ->setWildcard(true),
);
}
- public function run() {
+
+ public function runWorkflow() {
$files = $this->getArgument('files');
+
if (count($files) < 2) {
- throw new ArcanistUsageException(
+ throw new PhutilArgumentUsageException(
pht('Specify two or more files to weld together.'));
}
diff --git a/support/ArcanistRuntime.php b/support/ArcanistRuntime.php
--- a/support/ArcanistRuntime.php
+++ b/support/ArcanistRuntime.php
@@ -4,6 +4,9 @@
private $workflows;
private $logEngine;
+ private $lastInterruptTime;
+
+ private $stack = array();
public function execute(array $argv) {
@@ -68,6 +71,12 @@
$log->writeTrace(pht('ARGV'), csprintf('%Ls', $argv));
+ // We're installing the signal handler after parsing "--trace" so that it
+ // can emit debugging messages. This means there's a very small window at
+ // startup where signals have no special handling, but we couldn't really
+ // route them or do anything interesting with them anyway.
+ $this->installSignalHandler();
+
$args->parsePartial($config_args, true);
$config_engine = $this->loadConfiguration($args);
@@ -514,4 +523,103 @@
return $argv;
}
+ private function installSignalHandler() {
+ $log = $this->getLogEngine();
+
+ if (!function_exists('pcntl_signal')) {
+ $log->writeTrace(
+ pht('PCNTL'),
+ pht(
+ 'Unable to install signal handler, pcntl_signal() unavailable. '.
+ 'Continuing without signal handling.'));
+ return;
+ }
+
+ // NOTE: SIGHUP, SIGTERM and SIGWINCH are handled by "PhutilSignalRouter".
+ // This logic is largely similar to the logic there, but more specific to
+ // Arcanist workflows.
+
+ pcntl_signal(SIGINT, array($this, 'routeSignal'));
+ }
+
+ public function routeSignal($signo) {
+ switch ($signo) {
+ case SIGINT:
+ $this->routeInterruptSignal($signo);
+ break;
+ }
+ }
+
+ private function routeInterruptSignal($signo) {
+ $log = $this->getLogEngine();
+
+ $last_interrupt = $this->lastInterruptTime;
+ $now = microtime(true);
+ $this->lastInterruptTime = $now;
+
+ $should_exit = false;
+
+ // If we received another SIGINT recently, always exit. This implements
+ // "press ^C twice in quick succession to exit" regardless of what the
+ // workflow may decide to do.
+ $interval = 2;
+ if ($last_interrupt !== null) {
+ if ($now - $last_interrupt < $interval) {
+ $should_exit = true;
+ }
+ }
+
+ $handler = null;
+ if (!$should_exit) {
+
+ // Look for an interrupt handler in the current workflow stack.
+
+ $stack = $this->getWorkflowStack();
+ foreach ($stack as $workflow) {
+ if ($workflow->canHandleSignal($signo)) {
+ $handler = $workflow;
+ break;
+ }
+ }
+
+ // If no workflow in the current execution stack can handle an interrupt
+ // signal, just exit on the first interrupt.
+
+ if (!$handler) {
+ $should_exit = true;
+ }
+ }
+
+ if ($should_exit) {
+ $log->writeHint(
+ pht('INTERRUPT'),
+ pht('Interrupted by SIGINT (^C).'));
+ exit(128 + $signo);
+ }
+
+ $log->writeHint(
+ pht('INTERRUPT'),
+ pht('Press ^C again to exit.'));
+
+ $handler->handleSignal($signo);
+ }
+
+ public function pushWorkflow(ArcanistWorkflow $workflow) {
+ $this->stack[] = $workflow;
+ return $this;
+ }
+
+ public function popWorkflow() {
+ if (!$this->stack) {
+ throw new Exception(pht('Trying to pop an empty workflow stack!'));
+ }
+
+ return array_pop($this->stack);
+ }
+
+ public function getWorkflowStack() {
+ return $this->stack;
+ }
+
+
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Oct 16, 7:14 PM (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6718749
Default Alt Text
D19703.diff (6 KB)
Attached To
Mode
D19703: [Wilds] Handle SIGINT (^C) in ArcanistRuntime in a more formal way
Attached
Detach File
Event Timeline
Log In to Comment