1

I have a thread waiting on input, but in the event that no input is provided, I need to exit the program. How can i exit the program? in this example the exit should be triggered by keyboard ctrl+c however I would also like to do this without interaction ie via a timeout or other event.

import threading
import signal
import sys
import time

shutdown = False

def shutdownHook(sigNum, currentStackFrame):
        global shutdown
        print('shutdown')
        shutdown = True

def readInput():
        print('readInput')
        print(sys.stdin.readline())
        print('done reading input')

if __name__ == '__main__':
        signal.signal(signal.SIGINT, shutdownHook)
        signal.signal(signal.SIGTERM, shutdownHook)

        inputThread = threading.Thread(name='input', target=readInput)
        inputThread.start()
        print('started input')

        while not shutdown:
                time.sleep(1)
                print('waiting ' + str(shutdown))
        print('current thread' + str(threading.current_thread()))
        print('end of program ' + str(shutdown))
        sys.exit(0)
pstanton
  • 35,033
  • 24
  • 126
  • 168

2 Answers2

1

You may use signal.alarm() to send a SIGALRM to your program after a certain amount of time (define here in second):

if __name__ == '__main__':
    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, shutdownHook)
    signal.alarm(5)

Here is the complete working example from the documentation:

Here is a minimal example program. It uses the alarm() function to limit the time spent waiting to open a file; this is useful if the file is for a serial device that may not be turned on, which would normally cause the os.open() to hang indefinitely. The solution is to set a 5-second alarm before opening the file; if the operation takes too long, the alarm signal will be sent, and the handler raises an exception.

import signal, os

def handler(signum, frame):
    print('Signal handler called with signal', signum)
    raise OSError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm

As for why your program is not quitting is because quoted the doc

Python signal handlers are always executed in the main Python thread, even if the signal was received in another thread. This means that signals can’t be used as a means of inter-thread communication. You can use the synchronization primitives from the threading module instead. Besides, only the main thread is allowed to set a new signal handler.

That means your thread cannot receive no signals the way you design the program. In fact if you try to set a signal in your thread you will receive a ValueError:

ValueError: signal only works in main thread

That's why your program keeps turning after receiving a SIGTERM. Because the thread did not received the signal.

See here: Kill python thread using os for alternative solution.

Kruupös
  • 5,097
  • 3
  • 27
  • 43
-1

Make the thread as Deamon thread, this way it will also shutdown when main thread is exited.

inputThread = threading.Thread(name='input', target=readInput)
inputThread.setDaemon(True) # add this line
inputThread.start()

Also you can add a time lapse for no activity within specified period.

time_limit_for_shutdown_in_secs = 10 
secs = 0
while not shutdown:
        if secs > time_limit_for_shutdown_in_secs: break
        time.sleep(1)
        print('waiting ' + str(shutdown))
        secs += 1

print('current thread' + str(threading.current_thread()))
print('end of program ' + str(shutdown))
sys.exit(0)
johnII
  • 1,423
  • 1
  • 14
  • 20
  • 1
    this is wrong answer, you are not answering OP, you are just breaking the loop! if you want not to be downvoted correct your answer – Mohsen_Fatemi Dec 29 '17 at 07:18
  • the requirement is to exit the program if in case no input was received for a certain amount of time – johnII Dec 29 '17 at 07:21
  • so you have not even run the code just once a time, the code stock when you enter the first input – Mohsen_Fatemi Dec 29 '17 at 08:13
  • the problem is not the loop, it is that sys.exit does not exit since stdin.readline is blocking. – pstanton Dec 29 '17 at 08:16
  • Yes my bad, haven't noticed that it was indeed not exiting, i have updated my answer and this is currently working on my end. – johnII Dec 29 '17 at 08:27
  • it is not an answer yet, the problem is with threads ! not the while loop, it's about concurrency ! not just breaking a loop !!! – Mohsen_Fatemi Dec 29 '17 at 09:21
  • did you even tried running the updated code I provided? the question mentioned that other events include timeout so basically a limit of time should be achieved and program should exit thus the additional logic inside the loop, as for exiting the child thread I already updated my answer for that – johnII Dec 29 '17 at 09:31