0

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?

3
  • you always want to take the input as "shutdown" or something else Commented Jul 23, 2017 at 4:33
  • The program accepts many different commands besides "shutdown", both locally and remotely so input() could be almost anything. The example I gave is a trimmed down version that doesn't have all the other commands since shutdown is the only one that gets stuck with input() blocking the thread. Commented Jul 23, 2017 at 4:44
  • Your problem is that you don't want to press enter after entering a command Commented Jul 23, 2017 at 4:48

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

The network-IO thread doesn't block anything. A local shutdown command works fine because both loops recognize self.runserver = False breaking the loops. However a remote shutdown command gets stuck on the local loop because the local stops at raw_input = input() waiting for an input while the remote loop ends as expected. If after giving a shutdown command remotely I simply press enter on the keyboard locally it will reach the end of the loop, recognize that self.runserver is now False, end the local loop, and continue with the shutdown process without any further problems.
1

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.

1 Comment

This is definitely dirtier than user2722968's answer, though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.