php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #77377 No way to handle CTRL+C in Windows
Submitted: 2018-12-30 17:14 UTC Modified: 2019-02-13 07:37 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: gadelat at gmail dot com Assigned: ab (profile)
Status: Closed Package: Win32API related
PHP Version: 7.2Git-2018-12-30 (Git) OS: Windows 10
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: gadelat at gmail dot com
New email:
PHP Version: OS:

 

 [2018-12-30 17:14 UTC] gadelat at gmail dot com
Description:
------------
There is currently no way how to handle CTRL+C with PHP in Windows. This makes it the only scripting language I know that cannot handle this and pretty bad choice for writing cross platform compatible CLI applications.

- handler registered via register_shutdown_function is not called for CTR+C
- pcntl_signal relies on PCNTL extension which isn't available on Windows 

Notice that I am trying to avoid word "signal" here as you may argue there are no signals in Windows. Developer still needs a way how to handle CTRL+C press of end user. 

This is important eg. for running cleanup tasks, as not even PHP's tmpfile() cleanup logic is executed when user aborts program via CTRL+C.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-01-05 17:11 UTC] ab@php.net
Thanks for the report. Yes, it is a known issue. It is possible to catch ^C on Windows, but the handler is invoked in a separate thread. That means, it is not safe to run PHP code within the signal callback. If we would want to handle it, we'd have to insert some interrupts into the VM, which would slow down the execution. For now it's most likely a won't fix issue, I'd say.

Thanks.
 [2019-01-05 17:21 UTC] nikic@php.net
@ab: Isn't this the same as with pctnl signal handlers? We can't run PHP code there either, if for other reasons (not async signal safe). We handle this by registering a VM interrupt and thus delaying execution of the PHP signal handler.

The same should be possible for Ctrl+C handling on Windows. All the groundwork in the VM is already there, we just need the interfacing to register the VM interrupt and then call the callback.
 [2019-01-07 00:10 UTC] ab@php.net
@nikic, yeah, EG(vm_interrupt) is also what is used for the timeout handling since 7.1 IIRC. I remember checking this to that times.

It's impossible to pass the user data to the callback. As a consequence, the handling becomes tricky. Especially for the thread safe build. For the normal case, we don't start a new thread and the system sent CTRL+C is process wide. If something like pthreads or libuv is used - that'll need a magic, as potentially any thread could register a handler and i don't see a rationale which thread should then get an interrupt. Perhaps that should be the main thread always.

When I was talking about interrupt, what I had in mind was a mechanism more similar to ticks, which would also need locking and the whole schmear. As an additional issue potential I see is, that one has to access the globals from another thread, whereby more than one signal could be scheduled and thus there might be race conditions. Btw. I think some atomic datatype should be used for the globals on Linux/UNIX as well.

Anyway, thanks for checking. Let me revaluate this once more and do some research. 

Thanks.
 [2019-01-07 00:11 UTC] ab@php.net
-Assigned To: +Assigned To: ab
 [2019-02-10 03:17 UTC] ab@php.net
-Status: Assigned +Status: Feedback
 [2019-02-10 03:17 UTC] ab@php.net
@gadelat at gmail dot com, please check the latest master snapshots. The handling is as simple as the below code

function handler($evt)
{
    echo "Got CTRL+C event\n";
    exit;
}
sapi_windows_set_ctrl_handler('handler');
 
run it and press CTRL+C. An UPGRADING note is to follow yet.

Thanks.
 [2019-02-10 16:23 UTC] gadelat at gmail dot com
-Status: Feedback +Status: Assigned
 [2019-02-10 16:23 UTC] gadelat at gmail dot com
That's greate news! Thank you for implementing this.

Seems I'll wait for binary release though, I've not succeeded with compilation of PHP on Windows.
 [2019-02-10 17:14 UTC] ab@php.net
-Status: Assigned +Status: Feedback
 [2019-02-10 17:14 UTC] ab@php.net
You don't need to compile, just fetch a latest master snapshot here https://windows.php.net/downloads/snaps/master/

Thanks
 [2019-02-10 18:31 UTC] gadelat at gmail dot com
-Status: Feedback +Status: Assigned
 [2019-02-10 18:31 UTC] gadelat at gmail dot com
Allright so I did try this, unfortunately when this function is used, script hangs on CTRL+C and cannot be killed, neither specified callback is executed. Then user can kill the script (as far as I see) only via task manager.
 [2019-02-10 21:13 UTC] ab@php.net
-Status: Assigned +Status: Feedback
 [2019-02-10 21:13 UTC] ab@php.net
Thank you for this bug report. To properly diagnose the problem, we
need a short but complete example script to be able to reproduce
this bug ourselves. 

A proper reproducing script starts with <?php and ends with ?>,
is max. 10-20 lines long and does not require any external 
resources such as databases, etc. If the script requires a 
database to demonstrate the issue, please make sure it creates 
all necessary tables, stored procedures etc.

Please avoid embedding huge scripts into the report.

Thanks for checking. Please show the exact code. There are internal functions that would block the execution, it would probably be same on other systems. Say a function like scanf(). The code like this, which only uses the ZVM instructions should be ok.

function handler($evt)
{
    echo "Got CTRL+C event\n";
    exit;
}
sapi_windows_set_ctrl_handler('handler');
while(true) sleep(1); // checking the status

I'd need your exact code to tell more.

Thanks.
 [2019-02-10 21:22 UTC] ab@php.net
Perhaps also check this test here http://git.php.net/?p=php-src.git;a=commitdiff;h=12bfd9a5f58c12b8f63011c130ec3bf6605ea33b#patch5 It's a bit more complex example, but it might give a better picture.

Thanks.
 [2019-02-10 21:51 UTC] gadelat at gmail dot com
I did, but it uses sapi_windows_generate_ctrl_event which obviously might not match what happens when ctrl+c is pressed.
 [2019-02-10 22:14 UTC] ab@php.net
The sapi_windows_generate_ctrl_event() can be used to send CTRL+C to a child process opened by proc_open(). It's otherwise same as one would press the keys.

Thansk.
 [2019-02-10 23:35 UTC] gadelat at gmail dot com
Here's reproducer

<?php
sapi_windows_set_ctrl_handler(function () {
    echo 'shutting down';
    exit(0);
});

$server = stream_socket_server('localhost:1337');
stream_set_blocking($server, false);

$read = [$server];
$write = [];
$except = null;

stream_select($read, $write, $except, null, null);
 [2019-02-11 00:42 UTC] ab@php.net
Thanks for sending the code. Yes, the select sticks in a blocking call, because it's given the timeout of zero. The CTRL+C handler can't be called, as no further PHP code can be interpreted. My suggestion you do similar to the following instead

while (true) {
        $read = [$server];
        $write = [];
        $except = null;

        stream_select($read, $write, $except, 0, 5000);
}

The basic idea - just occasionally return from the blocking calls to give the VM a chance to handle the interrupt. It is one of the Windows specific points, as CTRL+* event doesn't automatically cancel all the I/O nor it does anything else except invoking the handler function. Internally, the VM interrupt is set, but because the select blocks, VM doesn't execute.

I'll think about a way to improve this, but I'm pessimistic there can be done much. One could try to close the open descriptors, that might work, but for that one has to record all of them which might be not doable.

Thanks.
 [2019-02-12 18:12 UTC] ab@php.net
@gadelat at gmail dot com, how is it going? :) Any other feedback?

Thanks.
 [2019-02-12 20:20 UTC] gadelat at gmail dot com
stream_select is not in my expertise as I don't use it myself, just streamlined the hang to such peace of code in library. I have created issue https://github.com/amphp/amp/issues/262 and hopefully guys from AMPHP can take over this conversation. Outside of that, this works nicely
 [2019-02-12 20:39 UTC] ab@php.net
-Status: Feedback +Status: Open
 [2019-02-12 20:39 UTC] ab@php.net
Cool, thanks for the info so far and for filing the issue. I'm still gonna document this and then close this one. I expect there'll be more reports closer to the 7.4 final, when more people start using this functionality.

Cheers.
 [2019-02-13 03:17 UTC] ab@php.net
-Status: Assigned +Status: Closed
 [2019-02-13 03:17 UTC] ab@php.net
Added UPGRADING notes. Closing this one for now. Please open separate tickets for any subsequent issues or ideas.

Thanks!
 [2019-02-13 07:37 UTC] bwoebi@php.net
@Anatol: Under linux stream_select is comfortably aborted (signal being received) - is there any way to abort stream_select() from within Windows in that case?
At least the way it is now is a bit stupid - you don't want to rerun stream_select() too often, but also not wait x time units after pressing ctrl+c.
I don't mind if stream_select were calling select() repeatedly every 100 ms or such _internally_ on Windows. But I feel like this is not an userland responsibility.
 [2019-02-16 02:02 UTC] gadelat at gmail dot com
@bwoebi Can you create new report please? This one is closed so apparently won't receive any more feedback. Thanks!
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Wed Aug 21 12:01:26 2019 UTC