dabeaz / curio

Good Curio!
Other
4.04k stars 243 forks source link

kernel does not respond to Ctrl-C on windows #201

Closed goldcode closed 7 years ago

goldcode commented 7 years ago

i have been avoiding this problem like the plague since weeks but i'm afraid sooner or later i have to grab the bull by the horns.

Am i safe in assuming for windows (10) systems, that the kernel cannot/does not react on keyboard interrupt Ctrl-C at await points?

As a test:

import time
import curio
import time
async def main():
    time.sleep(5)
    await curio.sleep(5)

if __name__ == "__main__":
   try:
      curio.run(main())
   except KeyboardInterrupt:
      print('arrived!')

from the code above, if Ctrl-C is pressed at time.sleep, it exits immediately with the call stack below. this seems sensible.

Task 2 crashed
Traceback (most recent call last):
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\kernel.py", line 835, in _run_coro
    trap = current._send(current.next_value)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\task.py", line 96, in _task_runner
    return await coro
  File "C:\Evobase2005\Main\EvoPro\PyTools\tests\sandbox.py", line 19, in main
    time.sleep(5)
KeyboardInterrupt
arrived!

however if Ctrl-C is pressed at curio.sleep, it remains non-responsive and then after 5 secs, keyboard interrupt is handled.

i have tried examples/echoserv.py also with similar results. Also had a look at #72, which seemed related and a unix-centric (await on signal) solution was provided.

dabeaz commented 7 years ago

Entirely possible that Windows select() can't be interrupted by Control-C. I would try using a SignalEvent (or a SignalQueue) and see if it actually works. For example::

async def waitkb():
      goodbye = SignalEvent(signal.SIGINT)
      await goodbye.wait()
      print("Arrived!")

async def main():
     await spawn(waitkb)
     await sleep(30)

run(main)

Other options: Try running curio.run() in a different thread. Launch a task that does nothing but sleep on short-periodic timer so that the kernel wakes up to check Control-C on a regular interval.

goldcode commented 7 years ago

does not help either. here's the output. exploring other options now.

Arrived!
Task(id=3, name='waitkb', <coroutine object waitkb at 0x000001F41A222518>, state='TERMINATED') never joined
goldcode commented 7 years ago

this works...

from curio import *
import signal
async def waitkb():
    while True:
      await sleep(1)
      print("Arrived!")

async def main():
     await spawn(waitkb)
     await sleep(30)
run(main)

with following output:

Arrived!
Arrived!
Arrived!
Arrived!
Traceback (most recent call last):
  File "C:\Evobase2005\Main\EvoPro\PyTools\tests\sandbox.py", line 11, in <module>
    run(main)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\kernel.py", line 882, in run
    return kernel.run(corofunc, *args, timeout=timeout)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\kernel.py", line 168, in run
    ret_val, ret_exc = self._runner.send((coro, timeout))
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\kernel.py", line 747, in _run_coro
    events = selector_select(timeout)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\selectors.py", line 323, in select
    r, w, _ = self._select(self._readers, self._writers, [], timeout)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\selectors.py", line 314, in _select
    r, w, x = select.select(r, w, w, timeout)
KeyboardInterrupt
dabeaz commented 7 years ago

I may have omitted too much detail in haste. In the example with the SignalEvent:

async def waitkb():
      goodbye = SignalEvent(signal.SIGINT)
      await goodbye.wait()
      print("Arrived!")

async def main():
     await spawn(waitkb)
     await sleep(30)

run(main)

when did the "Arrived!" message display? Did it show up immediately when you pressed Control-C? Or was it delayed for 30 seconds? If it showed up immediately, you could probably cancel the main task and bail out. Or, maybe even manually raise the KeyboardInterrupt exception (need to double check the Curio handling of that). For example:

async def waitkb():
      goodbye = SignalEvent(signal.SIGINT)
      await goodbye.wait()
      print("Arrived!")
      raise KeyboardInterrupt()
goldcode commented 7 years ago

Or was it delayed for 30 seconds? yes. it was delayed.

We are restrained to windows systems for hardware centric applications due to tiny companies here in Germany which cannot put in much resources to dish out linux drivers. till now its worked pretty seamlessly though. I wonder how many people actively use curio in a windows environment? Perhaps all have their homegrown solution for shutting down. a point for the tutorial or examples perhaps? ok i know its marked somewhere that windows is not really supported...

dabeaz commented 7 years ago

So, I did a small experiment on my Windows machine here. I got this to work:

import signal
from curio import *

async def waitkb():
       async with SignalQueue(signal.SIGINT) as goodbye:
              await goodbye.get()
              raise KeyboardInterrupt()

async def main():
    await spawn(waitkb, daemon=True)
    await sleep(30)

run(main)

This code died instantly the second I hit Control-C. This was on Windows 10.

goldcode commented 7 years ago

can confirm the same on Windows 10. Many Thanks.

Traceback (most recent call last):
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\kernel.py", line 835, in _run_coro
    trap = current._send(current.next_value)
  File "c:\Users\DSARN\AppData\Local\Programs\Python\Python36\lib\site-packages\curio\task.py", line 96, in _task_runner
    return await coro
  File "C:\Evobase2005\Main\EvoPro\PyTools\apps\ngen.py", line 84, in waitkb
    raise KeyboardInterrupt()
KeyboardInterrupt
2017-03-Task(id=49, name='waitkb', <coroutine object waitkb at 0x00000216E8925048>, state='TERMINATED') never joined