7

I am writing a multithreaded socket server and I need to know for sure.

Articles about threads say that I should wait for the thread to return, instead of killing it. In some cases though, the user's thread i want to kick/ban, will not be able to return properly (for example, I started to send a big block of data and send() blocks the thread at the moment) so I'll need just to kill it.

Why killing thread functions are dangerous and when can they crash the whole application?

Patryk Czachurski
  • 2,981
  • 5
  • 26
  • 49

6 Answers6

20

Killing a thread means stopping all execution exactly where it is a the moment. In particular, it will not execute any destructors. This means sockets and files won't be closed, dynamically-allocated memory will not be freed, mutexes and semaphores won't be released, etc. Killing a thread is almost guaranteed to cause resource leaks and deadlocks.

Thus, your question is kind of reversed. The real question should read:

When, and under what conditions can I kill a thread?

So, you can kill the thread when you're convinced no leaks and deadlocks can occur, not now, and not when the other thread's code will be modified (thus, it is pretty much impossible to guarantee).


In your specific case, the solution is to use non-blocking sockets and check some thread/user-specific flag between calls to send()and recv(). This will likely complicate your code, which is probably why you've been resisting to do so, but it's the proper way to go about it.

Moreover, you will quickly realize that a thread-per-client approach doesn't scale, so you'll change your architecture and re-write lots of it anyways.

André Caron
  • 41,491
  • 10
  • 58
  • 117
  • About the only good time to kill a thread is if you're doing it on the way to shutting down the entire process. – Steven Sudit Nov 10 '10 at 21:28
  • 5
    +1: In Windows, we all end up with IO completion ports eventually. – John Dibling Nov 10 '10 at 21:32
  • @John: unfortunately, I/O completion ports are not for the faint of heart. I wouldn't recommend it to someone unless they have a fair amount of experience with other socket server designs. A design with a single I/O bound thread scales remarkably well (although it wouldn't give you a C10K server) and is much easier to grasp, especially for students. – André Caron Nov 10 '10 at 21:39
  • @Andre: Agreed. But I also know that comments like "In Windows, we all end up with IO completion ports eventually" were the kinds of things that led me to discover new techniques in many cases. – John Dibling Nov 10 '10 at 22:32
  • One of the main reasons why java 1.4 got java.nio.* was to get rid of the one-blocked-thread-per-socket approach, and thus scale much better. Even if you are not using Java it is instructive to look at how this API is designed. – MarcH Nov 10 '10 at 22:49
  • @John: those comments help me a lot too, that's why I +1'd your comment! – André Caron Nov 10 '10 at 23:18
7

Killing a thread can cause your program to leak resources because the thread did not get a chance to clean up after itself. Consider closing the socket handle the thread is sending on. This will cause the blocking send() to return immediately with an appropriate error code. The thread can then clean up and die peacefully.

Ferruccio
  • 93,779
  • 37
  • 217
  • 294
4

If you kill your thread the hard way it can leak resources.

You can avoid it when you design your thread to support cancelation.

Do not use blocking calls or use blocking calls with a timeout. Receive or send data in smaller chunks or asynchronously.

frast
  • 2,629
  • 1
  • 24
  • 34
2

There's many reasons, but here's an easy one: there's only one heap. If a thread allocates ANYTHING on the heap, and you kill it, whatever it has allocated is around until the process ends. Each thread gets its own stack, and so that MAY be freed (implementation-dependent), but you GUARANTEE leaks on the heap by not letting it shut itself down.

Kevin Anderson
  • 5,815
  • 3
  • 29
  • 52
  • Strictly speaking, you *can* release memory allocated by another thread as long as the memory is still being reference (i.e. it's in a thread-shared data structure). This doesn't prevent the thread to be interrupted between the allocation and the pointer storage, though. – André Caron Nov 10 '10 at 21:34
  • True enough Andre, but of course I was referring to anything that the thread allocated, and is not shared in any way between threads. But strictly speaking, you are correct, in that if it "shared" the pointer, it could be de-allocated, but even then it's UNLIKELY to be de-allocated, since how would the "2nd thread" know that the "1st thread" (the one we killed) didn't still have it? You'd need additional levels of "interesting" programming happening to detect that. – Kevin Anderson Nov 10 '10 at 21:41
  • I was thinking of a more simple use, such as maintaining a list of (dynamically allocated) `Client` instances which have a reference to their own thread. The main thread could periodically check each client and see if the thread has ended (or been terminated) and de-allocate the list at that time. I wasn't proposing this as a viable solution (see my answer), but more as a "it technically *is* doable" kind of comment... – André Caron Nov 10 '10 at 21:55
2

In the case of a thread blocked in I/O you never really need to kill it, instead you have the choice between non-blocking I/O, timeouts, and closing the socket from another thread. Either of these will unblock the thread.

MarcH
  • 16,107
  • 1
  • 26
  • 22
  • Although IIRC on Linux (as opposed to most other Unices), closing the socket from another thread does not unblock the thread, which is very unfortunate. – Ringding Nov 11 '10 at 08:45
  • @Ringding: even after days? If yes I think you should report this as a bug in https://bugzilla.kernel.org/ – MarcH Nov 11 '10 at 09:08
  • @MarcH: I will investigate this. But having to wait for hours or days might not be that helpful in many situations. – Ringding Nov 16 '10 at 11:07
  • @Ringding: there is a big difference between waiting hours versus waiting forever. The former can usually be reconfigured easily while the latter cannot. – MarcH Nov 16 '10 at 14:38
  • 1
    @MarcH: are you talking about send() in particular or any blocking calls on sockets? Because I'm running a test program (from here: ), and so far it hasn't returned, after 36 hours. It completes immediately on Solaris. – Ringding Nov 18 '10 at 17:50
  • The fact that at least some blocking system calls don’t return when you close the socket from under them is also reflected in the OpenJDK JVM, where we can see that a signal is sent only on Linux for the sole purpose of interrupting the blocking system call: http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/dfca90a1c8e3/src/solaris/native/sun/nio/ch/NativeThread.c – Ringding Dec 13 '10 at 21:18
2

You really don't want to do this.

If you kill a thread while it holds a critical section it won't be released which will likely result in your whole application breaking. Certain C library calls like heap memory allocation use critical sections and if you happen to kill your thread while it's doing a "new" then calling new from anywhere else in your program will cause that thread to stop.

You simply can't do this safely without really extreme measures which are much more restrictive than simply signalling the thread to terminate itsself.

jcoder
  • 28,098
  • 16
  • 76
  • 120