0

Let a multithreaded (pthreads) C program have a tree of (joinable/attached) threads like this:

main
└── th0
    ├── th1
    │   ├── th3
    │   └── th4
    │       ├── th5
    │       └── th6
    └── th2
        ├── th7
        │   └── th9
        └── th8

Any thread 'thX' can have up to 2 child threads.

The tree is dynamic: branches are cut, and new threads are created continuously.

When a condition is met, I would like to kill not a thread, but a branch of threads. That is, if I want to kill th2, I also want all these to be killed: th7, th8 & th9.

What would be the best(most efficient/most readable) way to do this?

What would be the best(most efficient/most readable) way to keep track of the tree?

Now I'm using a _Thread_local variable to keep the name (X) of a thread, another one to keep the name of its parent, and an array of lenght 2 to keep the names of the childs, but that only allows me to kill a child, not grand-childs and so on.

I don't know if it is relevant, but I'm using GNU/Linux and GCC6

PS: I do want to force kill the threads. I don't malloc, so leaking isn't a problem.

PS.2: It's part of a Branch & Bound algorithm; and this is the bound part.

  • Why do you have thread that create thread ? That really strange. – Stargateur Oct 13 '17 at 11:29
  • 1
    It's a branch and bound algorithm – alx - recommends codidact Oct 13 '17 at 11:38
  • 2
    What would "***kill** a thread*" mean? Tell it to end itself in a well defined manner or cancel it from the outside using the `pthread_cancel()`-hammer? – alk Oct 13 '17 at 11:44
  • 1
    Also: Are these thread running attached or detached? – alk Oct 13 '17 at 11:45
  • 1: Hammer, Nuke them. 2: They're joinable (attached). – alx - recommends codidact Oct 13 '17 at 11:49
  • However, if it is more efficient to end them politely, I would consider it. – alx - recommends codidact Oct 13 '17 at 11:53
  • after you have created the threads, i would make global variables for main thread setting it equivalent to 1, and add a while loop to each of child `while(theInt != 1){ return; }` and they'd all stop running and finish. (end) – Ahmed Can Unbay Oct 13 '17 at 12:18
  • I forgot to say that the tree is dynamic: branches are cut, and new threads are created continuously. – alx - recommends codidact Oct 13 '17 at 12:21
  • @turmuka I don't see how that could help knowing which thread is dependent on which one. – alx - recommends codidact Oct 13 '17 at 12:25
  • 1
    You cannot reliably and safely kill thread/s from user code runin on another thread. Only your OS can do it reliably, and can only do so safely at process termination. Designs such as you describe, that rely upon 'killing branches', are inherently, and fatally, flawed. – Martin James Oct 13 '17 at 12:36
  • 2
    Could you explain why? The only problem I know is leaking. Is there any problem with using pthread_cancel()? – alx - recommends codidact Oct 13 '17 at 12:57
  • However, as I said, I could consider signaling each one of the threads to be killed. My main problem is to keep track of the tree – alx - recommends codidact Oct 13 '17 at 12:59
  • "*main problem is to keep track of the tree*" as it stands, your questions is about something else. – alk Oct 13 '17 at 13:04
  • I think I have an idea: Create a binary tree (`struct tree`) containing `X`, `pthread_t`, `struct tree child1` & `struct tree child2`. And recursively destroy it from point `X`, canceling every thread as I destroy its struct. Is this the way to go? Any better idea? – alx - recommends codidact Oct 13 '17 at 13:11
  • @CacahueteFrito sure - it does not, and can not, work reliably and safely in the general case: https://stackoverflow.com/q/4760687/758133 – Martin James Oct 13 '17 at 13:48
  • @CacahueteFrito the big problem with unilaterally killing a thread is that if the thread is killed at an arbitrary execution-point, it won't have a chance to release any of the resources it may be holding at that moment. So it's really easy to end up with memory leaks, or locked mutexes that other threads will block forever on if they try to acquire them, or etc. By asking the thread to voluntarily exit, OTOH, you give the thread a chance to clean up and release its resources gracefully before it goes away. – Jeremy Friesner Oct 13 '17 at 14:13
  • @JeremyFriesner Thanks for the explanation! I don't EVER `malloc` so the leaking problem is not a concern. However, a mutex being locked forever can be a problem, so I will think about it. The answer below is a good start. – alx - recommends codidact Oct 13 '17 at 14:32
  • @CacahueteFrito Memory is not the only thing you can leak. – Nic Oct 13 '17 at 15:23
  • @QPaysTaxes Could you please say what else can be leaked? It's my first time with multithreading, and I have no idea. – alx - recommends codidact Oct 14 '17 at 10:27
  • @Cacahuete Anything that isn't automatically freed (which, in C, means everything that isn't on the stack) can be leaked. File handles are what give me trouble normally, but you can also have issues with sockets, library resources, etc. – Nic Oct 14 '17 at 14:02
  • Thank you very much. I think I can safely cancel threads, then. I only use file pointers and that stuff from the main thread. However, I will eventually use file pointers in threads, so I will make sure that I free them. Now, my threads are only used for intensive CPU work, using mostly the heap. – alx - recommends codidact Oct 14 '17 at 15:39

1 Answers1

1

See if this works for you. Each "parent" thread can push cleanup routines which will cancel their children. When you want to cancel the subtree, you do a pthread_cancel() on the parent....

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>


void cancel_child(void *ptr)
{
    printf("cancelling child\n");
    pthread_cancel(*((pthread_t *) ptr));
}

void *runner(void *ptr)
{
    pthread_t thr1, thr2;
    long depth;

    depth = (long) ptr;
    if (!(--depth)) return NULL;

    pthread_create(&thr1, NULL, runner, (void *) depth);
    pthread_cleanup_push(cancel_child, (void *) &thr1);

    pthread_create(&thr2, NULL, runner, (void *) depth);
    pthread_cleanup_push(cancel_child, (void *) &thr2);

    if (depth == 2) {
        sleep(1);
        // cancel the subtree
        pthread_cancel(thr1);
        pthread_cancel(thr2);
    }

    sleep(10 - depth);

    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
}


int main()
{
    runner((void *) 3);
}
  • What's the use of `pthread_cleanup_pop(0)`? Threads are already cancelled, right? I read the documentation of it, but didn't understand it. Does it mean that the thread that gets there doesn't need to do a cleanup anymore because it has arrived to the end? – alx - recommends codidact Oct 13 '17 at 14:45
  • right pop(0) will remove the cleanup function with executing it. – Rohit Rakshe Oct 14 '17 at 05:04