Using python multiprocessing and curses, it appears that terminating a Process interferes with curses display.
For example, in the following code, why does terminating the process stops curses from displaying the text ? (pressing b after pressing a)
More precisely, it seems that not only the string "hello" is not displayed anymore but also the whole curses window.
import curses
from multiprocessing import Process
from time import sleep
def display(stdscr):
stdscr.clear()
curses.newwin(0,0)
stdscr.timeout(500)
p = None
while True:
stdscr.addstr(1, 1, "hello")
stdscr.refresh()
key = stdscr.getch()
if key == ord('a') and not p:
p = Process(target = hang)
p.start()
elif key == ord('b') and p:
p.terminate()
def hang():
sleep(100)
if __name__ == '__main__':
curses.wrapper(display)
I'm running python 3.6 under GNU/Linux.
Edit :
I'm still able to reproduce with this more stripped down version which doesn't call sleep(). Now just pressing "a" triggers the bug.
import curses
from multiprocessing import Process
def display(stdscr):
stdscr.clear()
curses.newwin(0,0)
stdscr.timeout(500)
p = None
while True:
stdscr.addstr(1, 1, "hello")
stdscr.refresh()
key = stdscr.getch()
if key == ord('a') and not p:
p = Process(target = hang)
p.start()
p.terminate()
def hang():
while True:
temp = 1 + 1
if __name__ == '__main__':
curses.wrapper(display)
Are you running this on Windows, perhaps? One of the documented requirements of the multiprocessing module on that platform is that all top-level code (the
curses.wrapper(display)
in your case) MUST be inside anif __name__ == '__main__':
block, so that it isn't accidentally executed in your spawned process.I think what's happening here is that your spawned process is initializing curses itself (which involves setting up the console appropriately), and then reverts the console to normal when it terminates - thus undoing the setup done by the original program.
The following code works:
I created a new
Process
before initializing the UI usingcurses.wrapper()
. Okay, why does this work? For this we must know how Proccess works and what exactly it does when you callProcess(target = hang)
:Now, what does it tell us? You where creating a new
Processes
when you already created acurses
screen. What does curses.wrapper() do?Okay, we have a newly created child process that has the exact same resources as its parent. When you call
terminate()
to kill the child, it frees all resources, including the curses wrapper. It restores the previous terminal settings and therefore breaks your UI.To fix this you have to implement your program differently. Create a new process beforehand, use IPC to communicate with your process, use Process Pools if you need multiple processes, Threading or Thread Pools if you have IO bound tasks.