Generally, threads don't have a notion of non-cooperative thread termination. This is because (unlike processes) threads share address spaces, and an unexpected termination of a thread can leave memory in arbitrary and unexpected states. (For example, what if one thread was holding a mutex when it got killed?)

The POSIX threading model does include the ability to send a signal to a particular thread using pthread_kill(). What that does is cause the process' globally-registered signal handler for that signal to be invoked in that particular thread. With the default handlers, though, these tend to have process-wide effects; e.g., if you pthread_kill(tid, 15) with the default handler, it will simply kill the process. This doesn't make as much sense in the context of Python's signal handling (which is a layer on top of POSIX signal handling) so signals.pthread_kill() isn't a very useful function.

In practice (and I actually had to deal with exactly this problem in my own code just last week!) there are two approaches that tend to work:

(1) Cooperative scheduling: Your code has an explicit place within the threads where they check for abort signals and handle them appropriately. For example, if your threads are each running event listeners, you might post a "cancel" event on the main bus; or alternately, you could use a threading.Event to signal everyone to shut down.

(2) Separate address spaces: If (1) isn't possible for some reason, and you need to literally be able to kill off a process noncooperatively -- say, the purpose of your code is to be an intermediate "harness" library which runs someone else's long-running functions in a thread, and it's not possible to require all of that code to obey a cooperative protocol -- then use processes instead of threads.

In many cases, you can achieve this with built-in Python libraries like multiprocessing and ProcessPoolExecutor, and in this case, you can terminate processes simply by calling Popen.send_signal() or the like. In a few cases (e.g., if you're using gRPC, whose client library is incompatible with those for complicated reasons) you'll have to fork() and exec() yourself, typically by using the subprocess.Popen library directly.

Yonatan

On Thu, Jun 20, 2019 at 10:16 AM Michael Foord <fuzzyman@gmail.com> wrote:


On Thu, 20 Jun 2019 at 16:33, Guido van Rossum <guido@python.org> wrote:
On Thu, Jun 20, 2019 at 8:21 AM Michael Foord <fuzzyman@gmail.com> wrote:
> It works by raising an exception in the target thread, which the thread is free to handle (usually for cleanup and then reraise).

Sure, those are the right semantics. How does it stop blocking I/O though? Suppose the thread is waiting for a server to return a response which just isn't ever going to come, but the connection somehow is kept open by the other side?



Sorry, resending to list as well. 



It used to be on the CLR getting back control. So it couldn't handle that case. (.NET 1.1). 

It has since been improved. It still blocks on the execution of unmanaged code (or computation in a finally block handling the ThreadAbortException), but blocking IO can be interrupted:


If Abort is called on a thread that is blocked or is sleeping, the thread is interrupted and then aborted.

This SO question on the topic says:


Thread.Abort()

There are 2 things I see you could do. 1 is that if you have started this TcpListener thread from another you can simply call Thread.Abort instance method on the thread which will cause a threadabortexception to be thrown within the blocking call and walk up the stack.

Michael




--
--Guido van Rossum (python.org/~guido)
Pronouns: he/him/his (why is my pronoun here?)
--
Michael Foord
Python Consultant, Contractor and Trainer
https://agileabstractions.com/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ARIZ3WMWXIP6MICQFW3OKE4VY43OKXEK/
Code of Conduct: http://python.org/psf/codeofconduct/