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

monitor: make Ctrl-] work when running on WSL (v2) #1209

Closed
wants to merge 3 commits into from
Closed

monitor: make Ctrl-] work when running on WSL (v2) #1209

wants to merge 3 commits into from

Conversation

enrikb
Copy link

@enrikb enrikb commented Nov 1, 2017

Because the TIOCSTI termios ioctl is not supported on WSL (bash on
windows) at this time, another way for cancelling a blocking read on
stdin is needed. Also, setting a timeout using termios.c_cc[VMIN] = 0
with termios.c_cc[VTIME] = 1 does not currently work for WSL.

A first approach to mimic the Windows kludge based on kbhit() by using
ioctl(TIOCINQ) for peeking at the input queue before calling read()
basically worked but failed badly for fast multi character input like
escape sequences sent back by the terminal. This can be easily
reproduced using the console example (examples/system/console). The root
cause was identified to be the buffering involved reading and decoding
stdin in python.

The current solution uses pthread_kill() via ctypes (python 2) to
unblock getkey() in ConsoleReader which seems to be working well.
However, even in this case stdin must have been re-opened without
buffering. Otherwise, the read() may be restarted internally after
seeing EINTR.

This change has been successfully tested on WSL with the console
example, escape sequences (UP, DOWN keys), Ctrl-] to leave the monitor
and Ctrl-T/Ctrl-A for re-flashing. Unfortunately, testing on other
systems was not possible at this time.

@enrikb
Copy link
Author

enrikb commented Nov 1, 2017

Please note that I'm a python beginner. If there are better ways e.g. to 'unbuffer' stdin etc., please let me know.

Because the TIOCSTI termios ioctl is not supported on WSL (bash on
windows) at this time, another way for cancelling a blocking read on
stdin is needed. Also, setting a timeout using termios.c_cc[VMIN] = 0
with termios.c_cc[VTIME] = 1 does not currently work for WSL.

A first approach to mimic the Windows kludge based on kbhit() by using
ioctl(TIOCINQ) for peeking at the input queue before calling read()
basically worked but failed badly for fast multi character input like
escape sequences sent back by the terminal. This can be easily
reproduced using the console example (examples/system/console). The root
cause was identified to be the buffering involved reading and decoding
stdin in python.

The current solution uses pthread_kill() via ctypes (python 2) to
unblock getkey() in ConsoleReader which seems to be working well.
However, even in this case stdin must have been re-opened without
buffering. Otherwise, the read() may be restarted internally after
seeing EINTR.

This change has been successfully tested on WSL with the console
example, escape sequences (UP, DOWN keys), Ctrl-] to leave the monitor
and Ctrl-T/Ctrl-A for re-flashing. Unfortunately, testing on other
systems was not possible at this time.
Copy link
Contributor

@projectgus projectgus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work finding a solution for this. Sorry it took me a while to test it. I finally got WSL installed myself and had a poke around.

I don't really like this as a bunch of hacks to get around WSL limitations, but at the same time it seems to work and I don't know a better way to unblock stdin (except possibly to poll using select() and have a timeout, which seems icky in its own way).

I was a bit skeptical about killing a pthread inside of the Python interpreter, but in my tests I couldn't break anything and could run multiple gdb sessions in a row, etc, so it seems harmless enough... and the existing code is going to crash on WSL anyhow, so anything that's less crashy is probably an improvement. ;)

I left a couple of minor comments about code style.


def run(self):
if self.pthread_self != None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typical Python style is to use "is not None" instead of "!= None".

In this case (and in the same check below) I'd be more comfortable using if running_on_wsl everywhere as this makes it easier to trace all the of the WSL workarounds (which can hopefully some day be removed!)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also if you don't need to test whether self.pthread_self is None, you can remove the self.pthread_self = None default value settings from the constructor)

except IOError:
if not self.alive:
# expected during _cancel when killed
break
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code swallows the IOError if self.alive is true. Does it make more sense to re-raise it?

@@ -220,6 +245,20 @@ class Monitor(object):
"""
def __init__(self, serial_instance, elf_file, make="make", toolchain_prefix=DEFAULT_TOOLCHAIN_PREFIX, eol="CRLF"):
super(Monitor, self).__init__()
posix_is_wsl = False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about naming this running_on_wsl to match the field used in the Console instance? Another change that makes it easier to search for all of the WSL-related hacks at once. :)

@enrikb
Copy link
Author

enrikb commented Nov 23, 2017

Thank you very much for the valuable feedback! I will take care ASAP, but not before 2017-11-26 as I will be travelling till Sunday.

Instead of unconditionally swallowing IOError on stdin, re-raise the
exception when monitor is still active. Only suppress it during
unblocking of stdin, when it is expected.

(Suggested by projectgus.)
- rename posix_is_wsl to running_on_wsl
- conditions for WSL specific code depend on 'running_on_wsl'

(Suggested by projectgus.)
@enrikb
Copy link
Author

enrikb commented Nov 26, 2017

Hi, I tried to implemented the requested changes.
What is stille really unclean is the signal handler registration in ConsoleReader init.
Wouldn't it be better to do this in main() to have a better defined lifetime of the handler?

@enrikb
Copy link
Author

enrikb commented Nov 26, 2017

Anyway, I will try to find a select() based solution, now that it is clear that `unbuffered stdin' seems to be key. I would also be more comfortable without having to use pthread_kill() ;-)

@projectgus
Copy link
Contributor

projectgus commented Nov 26, 2017

What is stille really unclean is the signal handler registration in ConsoleReader init.
Wouldn't it be better to do this in main() to have a better defined lifetime of the handler?

This seems like a good idea. You can probalby make the running_in_wsl flag a module-level variable, and set it at import time (ie run the test of /proc/cpuinfo at the top level of the module source.)

Anyway, I will try to find a select() based solution, now that it is clear that `unbuffered stdin' seems to be key. I would also be more comfortable without having to use pthread_kill() ;-)

OK, that sounds great. I will hold off on doing anything more with this PR until I hear how you get on. :)

@projectgus
Copy link
Contributor

Closing in favour of new approach in #1324.

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

Successfully merging this pull request may close these issues.

None yet

2 participants