CherryPy waits for extra thread to end that is stopped later - python

I am building a application that uses CherryPy to serve a REST API, and another thread that does background work (in fact, it reads data from a serial port).
import cherrypy
import threading
class main:
#cherrypy.expose
def index(self):
return "Hello World."
def run():
while running == True:
# read data from serial port and store in a variable
running = True
t = threading.Thread(target = run)
t.start()
if __name__ == '__main__':
cherrypy.quickstart(main())
running = False
Both api.pc_main() and run work fine. The trouble is, I use a running boolean to stop my thread, but that piece of code is never reached, because CherryPy waits for that thread to finish when I push Ctrl-C. I actually have to use kill -9 to stop the process.

I fixed it by making my thread a CherryPy plugin. I used the code found here: Why is CTRL-C not captured and signal_handler called?
from cherrypy.process.plugins import SimplePlugin
class myplugin(SimplePlugin):
running = False
thread = None
def __init__(self, bus):
SimplePlugin.__init__(self, bus)
def start(self):
print "Starting thread."
self.running = True
if not self.thread:
self.thread = threading.Thread(target = self.run)
self.thread.start()
def stop(self):
print "Stopping thread."
self.running = False
if self.thread:
self.thread.join()
self.thread = None
def run(self):
while self.running == True:
print "Thread runs."
time.sleep(1)
then in the main script:
if __name__ == '__main__':
mythread(cherrypy.engine).subscribe()
cherrypy.quickstart(main())

Related

Python Quart Unable to shutdown background task

I am working on a Python app, but I am moving from Flask to Quart. The application needs a background task that runs constantly whilst the application is running.
When I try to stop the process using control-c, the thread doesn't close cleanly and sits in the while loop in the shutdown routine.
while not self._master_thread_class.shutdown_completed:
if not pro:
print('[DEBUG] Thread is not complete')
pro = True
I have followed this Stackoverflow question, but I can't figure out how to cleanly shutdown the background thread so I would love an explanation please as it seems like the Quart Documentation is lacking a bit.
MasterThread class:
import asyncio
class MasterThread:
def __init__(self, shutdown_requested_event):
self._shutdown_completed = False
self._shutdown_requested_event = shutdown_requested_event
self._shutdown_requested = False
def __del__(self):
print('Thread was deleted')
def run(self, loop) -> None:
asyncio.set_event_loop(loop)
loop.run_until_complete(self._async_entrypoint())
#asyncio.coroutine
def _async_entrypoint(self) -> None:
while not self. _shutdown_requested and \
not self._shutdown_requested_event.isSet():
#print('_main_loop()')
pass
if self._shutdown_requested_event.wait(0.1):
self. _shutdown_requested = True
print('[DEBUG] thread has completed....')
self._shutdown_completed = True
def _main_loop(self) -> None:
print('_main_loop()')
Main application module:
import asyncio
import threading
from quart import Quart
from workthr import MasterThread
app = Quart(__name__)
class Service:
def __init__(self):
self._shutdown_thread_event = threading.Event()
self._master_thread = MasterThread(self._shutdown_thread_event)
self._thread = None
def __del__(self):
self.stop()
def start(self):
loop = asyncio.get_event_loop()
self._thread = threading.Thread(target=self._master_thread.run, args=(loop,))
self._thread.start()
return True
def stop(self) -> None:
print('[DEBUG] Stop signal caught...')
self._shutdown_thread_event.set()
while not self._master_thread.shutdown_completed:
print('[DEBUG] Thread is not complete')
print('[DEBUG] Thread has completed')
self._shutdown()
def _shutdown(self):
print('Shutting down...')
service = Service()
service.start()
Quart has startup and shutdown methods that allow something to be started before the server starts serving and stopped when the server finishes serving. If your background task is mostly IO bound I'd recommend just using a coroutine function rather than a thread,
async def background_task():
while True:
...
#app.before_serving
async def startup():
app.background_task = asyncio.ensure_future(background_task())
#app.after_serving
async def shutdown():
app.background_task.cancel() # Or use a variable in the while loop
Or you can do the same with your Service,
#app.before_serving
async def startup():
service.start()
#app.after_serving
async def shutdown():
service.stop()

Stop Python script with ctrl + c

I have a script which uses threads, but it is unable to catch Ctrl + C.
Here it is the sample code to reproduce this error:
import threading
import time
import signal
class DummyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._running = True
signal.signal(signal.SIGINT, self.stop)
signal.signal(signal.SIGTERM, self.stop)
def stop(self, signum=None, frame=None):
self._running = False
def run(self):
while self._running:
time.sleep(1)
print("Running")
if __name__ == "__main__":
try:
t = DummyThread()
t.start()
while True:
print("Main thread running")
time.sleep(0.5)
except KeyboardInterrupt:
print("This never gets printed")
t.stop()
finally:
print("Exit")
When I run python3 script.py it starts running, but it does not catch ctrl+c. I have googled it but I have not found a solution. I have to kill the script with SIGTERM, but I want DummyThread to stop gracefully.
class DummyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._running = True
signal.signal(signal.SIGINT, self.stop)
signal.signal(signal.SIGTERM, self.stop)
The program actually does not work as expected because of those last two lines and would work without them.
The reason is that, if you press Ctrl-C, the SIGINT signal is handled by the signal handler that is set up by signal.signal and self.stop is called. So the thread should actually stop.
But in the main thread, the while True loop is still running. Since the signal has already been handled, there will be no KeyboardInterrupt exception raised by the Python runtime. Therefore you never get to the except part.
if __name__ == "__main__":
try:
t = DummyThread()
t.start()
while True: # you are stuck in this loop
print("Main thread running")
time.sleep(0.5)
except KeyboardInterrupt: # this never happens
print("This never gets printed")
t.stop()
Only one signal handler should be set up to call the stop method. So there are two options to solve the problem:
Handle the signal implicitly by catching the KeyboardInterrupt exception. This is achieved by simply removing the two signal.signal(...) lines.
Set up an explicit signal handler (as you did by using signal.signal in DummyThread.__init__), but remove the while True: loop from the main thread and do not try to handle KeyboardInterrupt. Instead, just wait for the DummyThread to finish on its own by using its join method:
if __name__ == "__main__":
t = DummyThread()
t.start()
t.join()
print("Exit")
The main point is that you can't work with signals in any other Thread except the Main Thread. The Main Thread is the only one which can receive signals and handle them. I can offer the following solution, it is based on Event sync primitive.
According to Python documantation:
Signals and threads
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.
from threading import Thread, Event
import time
class DummyThread(Thread):
def __init__(self, event: Event):
Thread.__init__(self)
self.stop_event = event
def run(self):
# we are monitoring the event in the Main Thread
while not self.stop_event.is_set():
time.sleep(1)
print("Running")
# only Main Thread can make the point reachable
print("I am done !")
if __name__ == "__main__":
try:
e = Event()
t = DummyThread(e)
t.start()
while True:
print("Main thread running")
time.sleep(0.5)
except KeyboardInterrupt:
e.set()
finally:
print("Exit")
Another possible choice is to use daemon Thread for such tasks like in your code example (when you just printing smth in the screen every second, rather than e.g. close database connection or some similar task). If main thread is stoped the daemon Thread will stop too.
As shown in your code, you used KeyboardInterrupt to call stop() function. See how Listener does the same task and stops the execution which was unable to catch from Ctrl + C. You dont have to kill the script with SIGTERM anymore
import threading
import time
import signal
import os
from pynput.keyboard import Key, Listener
class DummyThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self._running = True
signal.signal(signal.SIGINT, self.stop)
signal.signal(signal.SIGTERM, self.stop)
def stop(self, signum=None, frame=None):
self._running = False
print ("Bye Bye . .")
os._exit(1)
def run(self):
while self._running:
time.sleep(1)
print("Running")
if __name__ == "__main__":
t = DummyThread()
def func2():
try:
t.start()
while True:
print("Main thread running")
time.sleep(0.5)
except KeyboardInterrupt:
print("No need for this")
t.stop()
finally:
print("Exit")
def func1():
with Listener(on_press = t.stop) as listener :
listener.join()
threading.Thread(target=func1).start()
threading.Thread(target=func2).start()

Stopping a server in a subprocess with its shutdown method

I am implementing a Server class in CPython 3.7 on Windows 10 with a Server.serve method that starts the serving forever and a Server.shutdown method that stops the serving. I need to run multiple server instances in subprocesses.
Running a server instance in a subthread stops the instance as expected:
import threading
import time
class Server:
def __init__(self):
self.shutdown_request = False
def serve(self):
print("serving")
while not self.shutdown_request:
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_request = True
if __name__ == "__main__":
server = Server()
threading.Thread(target=server.serve).start()
time.sleep(5)
server.shutdown()
However running a server instance in a subprocess does not stop the instance, unexpectedly:
import multiprocessing
import time
class Server:
def __init__(self):
self.shutdown_request = False
def serve(self):
print("serving")
while not self.shutdown_request:
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_request = True
if __name__ == "__main__":
server = Server()
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
server.shutdown()
I suspect that in the multiprocessing case, the self.shutdown_request attribute is not shared between the parent process and the subprocess, and therefore the server.shutdown() call does not affect the running server instance in the subprocess.
I know I could solve this with multiprocessing.Event:
import multiprocessing
import time
class Server:
def __init__(self, shutdown_event):
self.shutdown_event = shutdown_event
def serve(self):
print("serving")
while not self.shutdown_event.is_set():
print("hello")
time.sleep(1)
print("done")
if __name__ == "__main__":
shutdown_event = multiprocessing.Event()
server = Server(shutdown_event)
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
shutdown_event.set()
But I want to keep the Server.shutdown method instead of changing the Server interface according to its usage (single processing v. multiprocessing) and I don't want clients to deal with multiprocessing.Event.
I have finally figured out a solution by myself:
import multiprocessing
import time
class Server:
def __init__(self):
self.shutdown_event = multiprocessing.Event()
def serve(self):
print("serving")
while not self.shutdown_event.is_set():
print("hello")
time.sleep(1)
print("done")
def shutdown(self):
print("stopping")
self.shutdown_event.set()
if __name__ == "__main__":
server = Server()
multiprocessing.Process(target=server.serve).start()
time.sleep(5)
server.shutdown()
It works in either case: single processing (multithreading) and multiprocessing.
Remark. — With a multiprocessing.Event() in the __init__ method, Server instances are no longer pickable. That might be a problem if one wants to call a Server instance in a process pool (either with multiprocessing.pool.Pool or concurrent.futures.ProcessPoolExecutor). In this case, one should replace multiprocessing.Event() with multiprocessing.Manager().Event() in the __init__ method.

Python terminated does not return to command line

UPDATE: I found the bug. The serial port openned in listner should be closed when stopping thread. I was not doing that.
I have a simple python program where in its main, it starts 2 threads by calling their start() function. When the user presses a specific key, the program goes and clears the condition,self.is_running=false, that those 2 threads check everytime, so they would terminate when it's false:
def run(self):
...
while self.is_running:
...
return
Problem is I see threads terminate, but the program never returns to command prompt, and there is no combination of keys, ctrl+x etc. that would make it return to command prompt. I have to manually close the console window and restart a new one if I want to use the terminal console. I even used sys.exit() after threads are terminated but still doesn't make a difference.
Do you know where am I going wrong?
Here is the minimal code:
main.py:
def main():
listner_thread = None
try:
listner_thread = listner.listner()
listner_thread.start();
except:
Message = "Listener Thread Failed To Start"
print (Message)
return
ui_thread=None
try:
ui_thread = ui.ui(listner_thread)
ui_thread.start();
except:
Message = "UI Thread Failed To Start"
print (Message)
return
return
if __name__ == '__main__':
main()
listener.py and ui.py threads:
class listner(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.port=serial.serial('COM11',115200)
def run(self):
...
while self.is_running:
self.port.read(1)
return
Where threads are instructed to be terminated are in ui thread:
ui.py
class ui(threading.Thread):
def __init__(self,listner):
threading.Thread.__init__(self)
self.listner=listner
def run(self):
Message = ('\n Starting Thread (' + self.name + ')\n')
print Message
self.running = True
while self.running:
user = raw_input('\n Q- Quit\n')
if user.isalpha() and user.upper() == 'Q':
self.listner.is_running= False
self.running = False
return
You set running to False on the UI thread instance, but never mutate running on the listener thread instance. Your listener thread will thus never exit.
Try lifting the exit condition to a global, like should_exit = False. The UI thread can then set this True, and, since the global scope is shared across threads, both should see the change.
In listener I had a serial port open. It was not being closed (port.close()) upon thread terminatinon with setting 'is_running=False' , when I added port.close() it exited the thread properly.

Why doesn't a variable of a pipe work as a pipe for inter thread communication in python

During my exploration of IPC, dealing specifically with threads and sockets in python 3.4.1,
i experienced something that's a bit curious and i don't quite understand what's going on.
Currently (and successfully (for now)) i am using an anonymous pipe with os.pipe() to send
a termination signal to a thread holding a socket connection.
My goal was to terminate the thread in a graceful manner. I tried used a boolean flag at first, but since the select call was blocking, i had to send a termination signal to something that select.select could read; a socket, pipe, stdin, etc, hence breaking the select call.
Before I discovered how to use a pipe to communicate with the thread and penetrate the select call, i broke off development into a testing branch.
Let me explain my situation.
Basically this works...:
import os
import threading
import select
class MyThread(threading.Thread):
def __init__(self, pipein):
threading.Thread.__init__(self)
# The pipe to listen for terminate signals on
self.pipein = pipein
self.stopped = False
self.inputs = [self.pipein]
def run(self):
print("Thread-1 Started")
while not self.stopped:
inputready, outputready, errors = select.select(self.inputs, [], [])
for i in inputready:
if i == self.pipein:
signal = os.read(self.pipein, 64)
# 64 An arbitrary length that should be enough in any case
print("The thread received stuff on the pipe: %s" % signal)
if signal == b'stop':
print("Stop command received.")
print("Exiting.")
self.stopped = True
break
if __name__ == "__main__":
# Create the communication pipes
pipe = os.pipe()
# Start the worker thread
print("Starting the worker thread...")
t = MyThread(pipe[0])
t.start()
print("Worker thread started.")
stopped = False
# Enter the main loop
while not stopped:
command = input("Command to send to thread: ")
os.write(pipe[1], bytes(command, 'UTF-8'))
stopped = True
and if i type 'stop' in the terminal i get this:
localhost:ipc.git $ python3 pipes.py
Starting the worker thread...
Thread-1 Started
Worker thread started.
Command to send to thread: stop
The thread received stuff on the pipe: b'stop'
Stop command received.
Exiting.
localhost:ipc.git $ clear
and this doesn't:
import os
import threading
import select
class MyThread(threading.Thread):
def __init__(self, pipein):
threading.Thread.__init__(self)
# The pipe to listen for terminate signals on
self.pipein = pipein
self.stopped = False
self.inputs = [self.pipein]
def run(self):
print("Thread-1 Started")
while not self.stopped:
inputready, outputready, errors = select.select(self.inputs, [], [])
for i in inputready:
if i == self.pipein:
signal = os.read(self.pipein, 64)
# 64 An arbitrary length that should be enough in any case
print("The thread received stuff on the pipe: %s" % signal)
if signal == b'stop':
print("Stop command received.")
print("Exiting.")
self.stopped = True
break
if __name__ == "__main__":
# Create the communication pipes
pipein, pipeout = os.pipe() # Seperate into reader fd and writer fd
# Start the worker thread
print("Starting the worker thread...")
t = MyThread(pipein) # Give the thread the receiver
t.start()
print("Worker thread started.")
stopped = False
# Enter the main loop
while not stopped:
command = input("Command to send to thread: ")
# Write on the variable of pipe[1]: pipeout
os.write(pipeout, bytes(command, 'UTF-8'))
stopped = True
The difference is, is get a
OSError: [Errno 9] Bad file descriptor
when trying to read of write from a variable created from pipe
like:
pipein, pipeout = os.pipe()
or
pipe = os.pipe()
pipein = pipe[0]
pipeout = pipe[1]
however if i use the pipe[0] and pipe[1] to read and write respectively with os.read() and os.write() it works just fine!
So creating any sort of variable to pipe[0] or pipe[1] does not work, and i get and OSError.
Same thing applies if i create a class call Communicator and put the pipe[0] and pipe[1] as instance variables.
Could anyone explain why this is the case? Will i never be able to write to variables of pipe[1], or is this just because i going between threads?
If you know of another way for inter thread communication that can be used within or interupt a select call, i'm all ears.
I tried an instance of io.StringIO or io.{OtherIOHere} but they to don't support a fileno() call so they don't work with select
I would like to create a class to contain my communication pipes for better useability but until i find out why variable of a pipe don't work i cant.
Any input or advice is appreciated.
Edit:
Added some debug tests:
import os
import threading
import time
import select
class MyThread(threading.Thread):
def __init__(self, pipein):
threading.Thread.__init__(self)
self.pipein = pipein
self.stopped = False
self.inputs = [self.pipein]
def run(self):
print("Thread-1 Started")
while not self.stopped:
inputready, outputready, errors = select.select(self.inputs, [], [])
for i in inputready:
if i == self.pipein:
signal = os.read(self.pipein, 64)
print("The thread received stuff on the pipe: %s" % signal)
if signal == b'stop':
print("Stop command received.")
print("Exiting.")
self.stopped = True
break
if __name__ == "__main__":
# Create the communication pipes
pipe = os.pipe()
pipein = pipe[0]
pipeout = pipe[1]
# Some Print debugs
print(type(pipein))
print(type(pipeout))
print(pipein)
print(pipeout)
print(type(pipe))
print(type(pipe[0]))
print(type(pipe[1]))
print(pipe[0])
print(pipe[1])
# Start the worker thread
print("Starting the worker thread...")
t = MyThread(pipein)
t.start()
print("Worker thread started.")
# Enter the main loop
stopped = False
while not stopped:
command = input("Command to send to thread: ")
os.write(pipeout, bytes(command, 'UTF-8'))
stopped = True
# Dave, the funny thing is, this works now and have not the faintest idea why. I did the same this is two different projects. In both cases i couldn't write to a variable of pipe[1]
localhost:ipc.git $ python3 pipes.py
<class 'int'>
<class 'int'>
3
4
<class 'tuple'>
<class 'int'>
<class 'int'>
3
4
Starting the worker thread...
Thread-1 Started
Worker thread started.
Command to send to thread: stop
The thread received stuff on the pipe: b'stop'
Stop command received.
Exiting.
localhost:ipc.git $
Edit 2
Ok I have created the Communicator class to communicate between threads with a pipe. It comes with easy-to-use read() and write() methods. Everything seems to be hunky dory. Wonder why it didn't work before. Must have been a system related thing. Perhaps my works with sockets and threads has it on edge.
Here is the complete functional code:
import os
import threading
import select
class MyThread(threading.Thread):
def __init__(self, comm):
threading.Thread.__init__(self)
self.comm = comm
self.stopped = False
self.inputs = [self.comm.pipein]
def run(self):
print("Thread-1 Started")
while not self.stopped:
inputready, outputready, errors = select.select(self.inputs, [], [])
for i in inputready:
if i == self.comm.pipein:
signal = self.comm.read()
print("The thread received stuff on the pipe: %s" % signal)
if signal == b'stop':
print("Stop command received.")
print("Exiting.")
self.stopped = True
break
class Communicator:
def __init__(self):
self.pipe = os.pipe()
self.pipein = self.pipe[0]
self.pipeout = self.pipe[1]
def write(self, msg):
os.write(self.pipeout, msg)
def read(self):
return os.read(self.pipein, 64)
if __name__ == "__main__":
# Create the communication pipes
#pipe = os.pipe()
#pipein = pipe[0]
#pipeout = pipe[1]
# Use the communicator class
comm = Communicator()
# Some Print debugs
# Start the worker thread
print("Starting the worker thread...")
t = MyThread(comm)
t.start()
print("Worker thread started.")
# Enter the main loop
stopped = False
while not stopped:
command = input("Command to send to thread: ")
comm.write(b'stop')
stopped = True
Thanks for your help guys.
I copied and pasted your two examples of code into 2 files on my macbook, ran them with python 3.4.1 (from macports), entered 'stop', and they both worked.
What operating system are you using?
Edit: Looks like you "fixed" it. Good job. ;)

Categories

Resources