Libby
Libby

Reputation: 1457

How can I manually kill a thread?

I'm in ghci testing some functions that fork threads, kinda like this:

myForkingFunction = do
  tid <- forkIO (workerFunction)
  putStrLn ("Worker thread: " ++ show tid)
  putStrLn ("...lots of other actions...")

  where workerFunction = do 
          putStrLn "In real life I'm listening for jobs...."
          workerFunction

This results in:

🐢  > myForkingFunction
Worker thread: ThreadId 216
...lots of other actions...
🐢  > In real life I'm listening for jobs....
In real life I'm listening for jobs....
In real life I'm listening for jobs....
In real life I'm listening for jobs....

(and so on)

Then as I'm :r-ing and iterating on the code, I notice that even when I reload, my workerFunction is still going.

So, I thought could just look at the printed output and do killThread (ThreadId 234) or whatever.

But first, I need to import ThreadId(..) from GHC.Conc.Sync?

Then I get this error:

<interactive>:110:22: error:
    • Couldn't match a lifted type with an unlifted type
      When matching types
        Integer :: *
        GHC.Prim.ThreadId# :: TYPE 'GHC.Types.UnliftedRep
    • In the first argument of ‘ThreadId’, namely ‘805’
      In the first argument of ‘killThread’, namely ‘(ThreadId 805)’
      In the expression: killThread (ThreadId 805)

So I think I must be approaching this wrong?

Upvotes: 5

Views: 865

Answers (4)

dfeuer
dfeuer

Reputation: 48581

A ThreadId is not really a number, although it has a number associated with it for identification. ThreadId is a wrapper around ThreadId#, which is a pointer to a thread state object (a TSO, or in the RTS code, StgTSO). That object contains all the actual internal information about a thread. There is no way to turn an Int into a ThreadId (I'm pretty sure the runtime system doesn't even maintain a mapping from thread ID numbers to ThreadId#s, and there's definitely no primop supporting such a conversion), so you can give up on that now. The only way you can killThread or throwTo a thread is if you hold on to the ThreadId that forkIO gave you. Note that killing a thread isn't usually the way you want to stop it. Normally you want to do something more controlled by communicating with it through an MVar. Most people use the tools in the async library to manage concurrency.

Upvotes: 4

Carl
Carl

Reputation: 27003

ThreadId values are treated specially by the runtime. You can't just create one. You need access to a ThreadId either as produced by forkIO when creating the thread or by myThreadId inside the thread.

Just for extra fun, threads don't get garbage collected while any ThreadId from one is still reachable. So you can't just hold on to them in case you need them. You need to actually have a plan to manage threads.

I think you're probably best off using the async library and it's various linking operations to ensure that when the parent thread is killed, it also kills all its child threads. It's a little (or a lot) of an awkward code style, but it's the best way to get correct behavior.

Upvotes: 5

jberryman
jberryman

Reputation: 16635

I don't think there's any way to construct a ThreadId (e.g. from the result of show). See this question so you'll need to hold on to the ThreadId returned from forkIO, e.g. by changing your function to:

myForkingFunction = do
  tid <- forkIO (workerFunction)
  putStrLn ("Worker thread: " ++ show tid)
  putStrLn ("...lots of other actions...")
  return tid

and doing in ghci:

> tid <- myForkingFunction
> killThread tid

The error you get is a little confusing, and I believe has to do with the fact that numeric literals (like 216) are polymorphic; the argument to ThreadId is ThreadId# which is an unlifted type so you get the error you see before the compiler can even complain of "no instance Num ThreadId#"

Upvotes: 5

The problem is in the arguments of the killThread is wrong. You need to write killThread 234 instead of killThread (ThreadId 234). Have you tried that?

Upvotes: -6

Related Questions