Donkyhotay
Donkyhotay

Reputation: 81

Python3 Non-blocking input or killing threads

Reading through posts of similar questions I strongly suspect there is no way to do what I'm trying to do but figured I'd ask. I have a program using python3 that is designed to run headless, receiving commands from remote users that have logged in. One of the commands of course is a shutdown so that the program can be ended cleanly. This section is working correctly.

However while working on this I realized an option to be able to enter commands directly, without a remote connection, would be useful in the event something unusual happened to prevent remote access. I added a local_control function that runs in it's own thread so that it doesn't interfere with the main loop. This works great for all commands except for the shutdown command.

I have a variable that both loops monitor so that they can end when the shutdown command is sent. Sending the shutdown command from within local_control works fine because the loop ends before getting back to input(). however when sending the shutdown command remotely the program doesn't end until someone presses the enter key locally because that loop remains stuck at input(). As soon as enter is pressed the program continues, successfully breaks the loop and continues with the shutdown as normal. Below is an example of my code.

import threading

self.runserver = True

def local_control(): #system to control server without remote access
    while self.runserver:
        raw_input = input()
        if raw_input == "shutdown":
            self.runserver = False


mythread = threading.Thread(target=local_control) 
mythread.start()


while self.runserver:
    some_input = get_remote_input() #getting command from remote user
    if some_input == "shutdown":
        self.runserver = False

sys.exit(0) #server is shutdown cleanly

Because the program runs primarily headless GUI options such as pygame aren't an option. Other solutions I've found online involve libraries that are not cross-platform such as msvcrt, termios, and curses. Although it's not as clean an option I'd settle for simply killing the thread to end it if I could however there is no way to do that as well. So is there a cross-platform, non-GUI option to have a non-blocking input? Or is there another way to break a blocked loop from another thread?

Upvotes: 0

Views: 419

Answers (2)

Tyler
Tyler

Reputation: 1837

Not a portable solution, but in *nix, you might be able send yourself an interrupt signal from the local_control function to break the blocking input(). You'll need the pthread ID (pthread_self and save it somewhere readable from local_control) for the network control thread so you can call pthread_kill.

Upvotes: 1

user2722968
user2722968

Reputation: 16485

Your network-IO thread is blocking the processing of commands while waiting for remote commands, so it will only evaluate the state of runserver after get_remote_input() returns (and it's command is processed).

You will need three threads:

  • One which loops in local_control(), sending commands to the processing thread.
  • One which loops on get_remote_input(), also sending commands to the processing thread.
  • A processing thread (possibly the main thread).

A queue will probably be helpful here, since you need to avoid the race condition caused by unsynchronized access as currently present with regards to runserver.

Upvotes: 1

Related Questions