Skip to content
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

Graceful Ctrl+C handling #47

Open
thekid opened this issue Apr 4, 2016 · 9 comments
Open

Graceful Ctrl+C handling #47

thekid opened this issue Apr 4, 2016 · 9 comments

Comments

@thekid
Copy link
Contributor

thekid commented Apr 4, 2016

Can we make PHP handle Ctrl+C gracefully by intercepting it inside .NET? Something like installing a signal handler for SIGINT but platform independent.

See https://msdn.microsoft.com/en-us/library/system.console.cancelkeypress.aspx

@thekid
Copy link
Contributor Author

thekid commented Apr 4, 2016

Handling Ctrl+C inside C#:

using System;
using System.Threading;

public class CtrlC
{
    public static void Main()
    {
        Console.WriteLine("Started");
        Console.CancelKeyPress += (sender, args) => {
            Console.WriteLine("Interrupted");
            args.Cancel = true;
        };
        Thread.Sleep(5000);
        Console.WriteLine("Ended");
    }
}

Inside the handler we'd need to call into the PHP subprocess - but how?

@thekid
Copy link
Contributor Author

thekid commented Jul 17, 2016

Seems like Windows PHP would need to use SetConsoleCtrlHandler and not just cleanup and exit, but instead e.g. raise an exception. According to MSDN it's exeucted in a new thread - could that work or would it completely mess up PHP?

@thekid
Copy link
Contributor Author

thekid commented Sep 28, 2016

See https://nomadphp.com/rfcs-future-tick-talk/ - for Unix systems w/ PCNTL, this will be easy and maybe the framework can take care of it. For Windows, we need a different solution - maybe based on ticks + IPC?

@thekid
Copy link
Contributor Author

thekid commented Apr 7, 2018

For Cygwin, we'll need to wrap xp inside something like this:

#!/bin/sh 

_term() { 
  echo "Caught SIGINT signal!" 
  kill -TERM "$child" 2>/dev/null
}

trap _term SIGINT

/home/friebe/bin/xp "$@" &

child=$! 
wait "$child"

See https://unix.stackexchange.com/questions/146756/forward-sigterm-to-child-in-bash

@thekid
Copy link
Contributor Author

thekid commented May 7, 2018

For Cygwin, using winpty -- xp should also work.

@thekid
Copy link
Contributor Author

thekid commented Aug 7, 2018

For Cygwin, using winpty -- xp should also work.

The wrapper above doesn't, actually only winpty works as expected:

diff --git a/src/xp.runner/exec/ExecutionModel.cs b/src/xp.runner/exec/ExecutionModel.cs
index 6caa29a..82657d2 100755
--- a/src/xp.runner/exec/ExecutionModel.cs
+++ b/src/xp.runner/exec/ExecutionModel.cs
@@ -18,6 +18,13 @@ namespace Xp.Runners.Exec
         /// <summary>Run the process and return its exitcode</summary>
         protected int Run(Process proc, Encoding encoding, Func<int> wait = null)
         {
+            Console.CancelKeyPress += (sender, args) => {
+                args.Cancel = true;
+
+                Console.WriteLine("Killing PHP");
+                proc.Kill();
+            };
+
             using (new Output())
             {
                 proc.StartInfo.RedirectStandardOutput = false;
@@ -29,11 +36,14 @@ namespace Xp.Runners.Exec

                     if (null == wait)
                     {
+                Console.WriteLine("WaitForExit");
                         proc.WaitForExit();
+                Console.WriteLine("Waited {0}", proc.ExitCode);
                         return proc.ExitCode;
                     }
                     else
                     {
+                Console.WriteLine("wait");
                         return wait();
                     }
                 }
$ cat xp.sh
#!/bin/bash

./winpty ./xp.exe "$@"

# Without interrupting
$ sh xp.sh -e 'sleep(2)'
WaitForExit
Waited 0

# With ^C
$ sh xp.sh -e 'sleep(2)'
WaitForExit
Killing PHP
Waited -1073741510

$ echo $?
58

@thekid
Copy link
Contributor Author

thekid commented Aug 7, 2018

WinPTY can be obtained by downloading https://github.com/rprichard/winpty/releases

@thekid
Copy link
Contributor Author

thekid commented Feb 5, 2019

<?php

$ffi= FFI::cdef('
  typedef bool (close_handler)(int reason);
  bool SetConsoleCtrlHandler(close_handler handler, bool add);
', 'kernel32.dll');

$f= function($reason) {
  echo "Exiting with $reason...\n";
  return false;
};
var_dump($ffi->SetConsoleCtrlHandler($f, true));
sleep(10);
$ XP_RT=master winpty xp ffi.php
bool(true)
# Press Ctrl+C here
Exiting with 0...

@thekid
Copy link
Contributor Author

thekid commented Sep 28, 2019

Using Windows Terminal (Preview):

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant