9

I am creating a C++ library for both Linux (with PThreads) and Windows (with their built-in WinThreads) which can be attached to any program, and needs to have a function called when the thread is exiting, similar to how atexit works for processes.

I know of pthread_cleanup_push and pthread_cleanup_pop for pthreads, but these do not work for me since they are macros that add another lexical scope, whereas I want to declare this function the first time my library is called into, and then allow the program itself to run its own code however it needs to. I haven't found anything similar in Windows whatsoever.

Note that this doesn't mean I want an outside thread to be alerted when the thread stops, or even that I can change the way the thread will be exited, since that is controlled by the program itself, my library is just attached, along for the ride.

So the question is: What is the best way, in this instance, for me to have a function I've written called when the thread closes, in either Windows or Linux, when I have no control over how the thread is created or destroyed?

For example in main program:

void* threadFunc(void* arg)
{
    printf("Hello world!\n");
    return NULL;
}

int main(int argc, char** argv)
{
    int        numThreads = 1;
    pid_t*     pids       = NULL;
    pids     = (pid_t*)     calloc(sizeof(pid_t), numThreads);

    pthread_create(&ntid, NULL, threadFunc, &nVal);
    pthreads[0] = ntid;

    pthread_join(pthreads[0], NULL);
    return 0;
}

In library:

void callMeOnExit()
{
    printf("Exiting Thread!\n");
}

I would want for callMeOnExit to be called when the thread reaches return NULL; in this case, as well as when the main thread reaches the return 0;. Wrapping pthread_exit would work for other cases, and could be a solution, but I'd like a better one if possible.

If anyone has any ideas on how I might be able to do this, that would be great!

Dave McGinnis
  • 585
  • 4
  • 17
  • You're creating a C++ library? Well not to be rude, but if you're asking something of this nature instead of reading the relevant specification rigorously then maybe you should just use boost or qt. There are already libraries that do what you want. A one man library is about is realistic as a one man boat crew-that amount of work is impossible if you're going to deliver the features you're talking about. What it sounds like would be applicable is if you wrote a wrapper class this specific work-not a library. – Adam Miller Apr 30 '12 at 21:24
  • 1
    However, for an answer, or perhaps to get a feel of what you are wanting to do: a wrapper class could hide the complexity and manage the creation and starting of the threads and be platform agnostic. I'm sure there's some way that you could make the wrapper class call the function that you want before starting and again/a different function when it exits. – Adam Miller Apr 30 '12 at 21:27
  • 4
    The Windows `DLL_THREAD_DETACH` messages sent to a DLL's `Dllmain()` function may help, but I think there are a lot of possible pitfalls as well. – Michael Burr Apr 30 '12 at 21:27
  • 1
    @Adam Miller: That's not particularly helpful, unless you boil down your comment to the content of "I'd suggest you wrap boost or qt threads to achieve this functionality" – AJG85 Apr 30 '12 at 21:28
  • 1
    @Adam: I don't think the question is about a library that's wrapping a thread API - I believe the library in question is not involved in the creation of the threads (but that's not clear - it probably should be made clear if that's the case). – Michael Burr Apr 30 '12 at 21:30
  • Well when I read the original post, I just thought of the person asking the question as authoring his own library, and posting it online. I was just informing him that that's not a good idea unless you have a specific reason to consider the existing libraries unsuitable. I believe that if that is what the writer of the question intended that my response was appropriate because that much is certainly correct. I think it suitable that the person edit the question to clarify in light of this. @AJG85 I'm not trying to cut them down, just to be very specific. – Adam Miller Apr 30 '12 at 21:34
  • I think what @MichaelBurr was talking about is correct. The library I have currently works fine for what it is meant for, and I merely need to get a custom function called whenever the thread exits. I've edited the question to try to make this more clear, and I apologize for not being more clear from the beginning. – Dave McGinnis Apr 30 '12 at 21:35
  • Perhaps an example would be suitable? I'm thinking of a complex but good way, and a simple but gets-the-job-done way. But I have a question: do you want the thread that is going to call a function before it dies to execute in a threaded environment, or are you just thinking having the calling thread wait on the child thread to complete, and then call a function? Because it wouldn't not hard to wrap a thread start with a begin() and end() that will always get called. But then again, one could construct a class with function pointer member variables – Adam Miller Apr 30 '12 at 21:44
  • That could accept an interface such that, when the thread is called, a private static member function of the same thread encapsulating class that satisfies the interface (for the pthread library, as in void *func(void *);) could be used to call the functions for the begin and end. Begin and end would be function pointers set either in the constructor or with setters. – Adam Miller Apr 30 '12 at 21:46
  • **NOTE** all the above thought is with reference to pthread. I'm not familiar with windows, so I don't plan on implementing that in the example, perhaps someone else could fill that in... I'm just thinking of #define statements being used to abstract that part out. – Adam Miller Apr 30 '12 at 21:48
  • While I would rather not do it that way, @AdamMiller, since it wouldn't work in the example I just included, it would work for most other cases, so maybe that's just what we need to do. I think something similar could work for Windows as well. Another note is I'd like to have it so no changes are needed to the program using my library. If there were something like atexit that worked with threads, that would be optimal. – Dave McGinnis Apr 30 '12 at 21:58
  • Well, by the nature of your question, changes are absolutely necessary, but the way I'm thinking of the solution, they would be limited only to the instantiation of the threaded object and the initialization of the functions that it will call. I don't know about any atexit solution, and you're right, some library solution would be much better to use rather than implementing an entire wrapper class and interface. I'm going to look around for you, and if I don't find anything, I'll try and write a wrapper class. – Adam Miller Apr 30 '12 at 22:04
  • `calloc()`? That does not look like C++... – Alexis Wilke May 10 '16 at 00:44

4 Answers4

9

So after a few code reviews, we were able to find a much more elegant way to do this in Linux, which matches both what Windows does with Fibers (as Neeraj points out) as well as what I expected to find when I started looking into this issue.

The key is that pthread_key_create takes in, as the second argument, a pointer to a destructor, which is called when any thread which has initialized this TLS data dies. I was already using TLS elsewhere per thread, but a simple store into TLS would get you this feature as well to ensure it was called.

Dave McGinnis
  • 585
  • 4
  • 17
  • Exactly what I was looking for! It makes much sense since cleaning up TLS data is typically the thing one wants to do in the first place when one thinks "thread cleanup routine". –  Feb 28 '14 at 15:02
4

Change this:

pthread_create(&ntid, NULL, threadFunc, &nVal);

into:

struct exitInformData
{
   void* (CB*)(void*);
   void* data;
   exitInformData(void* (cp*)(void*), void* dp): CB(cp) data(dp) {}
};
pthread_create(&ntid, NULL, exitInform, new exitInformData(&threadFunc, &nVal));

Then Add:

void* exitInform(void* data)
{
    exitInformData* ei = reinterpret_cast<exitInformData*>(data);

    void* r = (ei.CB)(ei.data);   // Calls the function you want.
    callMeOnExit();               // Calls the exit notification.
    delete ei;
    return r;
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • This is nearly what I use. I actually have a thread object and a thread runner. The runner has a virtual function `run()` which is where the user writes his code. The function that calls the `run()` function can prepare and cleanup the thread as required. – Alexis Wilke May 10 '16 at 00:39
  • @AlexisWilke: This advice is 4 years old. The new version of C++ has a thread object `std::thread` which handles all this for you. Check out http://en.cppreference.com/w/cpp/thread/thread – Martin York May 10 '16 at 02:32
  • Yeah, changing your old code takes time... I wrote that something like 5 or 6 years ago... no C++11 (or 14 or 17...) – Alexis Wilke May 10 '16 at 06:01
4

For Windows, you could try Fls Callbacks. They FLS system can be used to allocate per thread (ignore the 'fiber' part, each thread contains one fiber) storage. You get this callback to free the storage, but can do other things in the callback as well.

Neeraj Singh
  • 589
  • 3
  • 6
  • FLS callbacks work well for this, but one thing should be noted. If the FLS slot is released before the last thread using it exits then you will get an immediate callback for each of the threads that have used the FLS index and that are still active and this callback occurs on the thread that released the FLS index. So, as long as your index outlives all threads that you are tracking you're fine and you will get a callback on the thread when it exits. – Len Holgate May 08 '21 at 09:37
0

I found out that this has already been asked, although the solution given then may not be the same as what you want...

Another idea might be to simply extend from the pthread_t class/struct, and override the pthread_exit call to call another function as you want it to, then call the superclass pthread_exit

Community
  • 1
  • 1
Adam Miller
  • 1,756
  • 1
  • 25
  • 44
  • That link seems to assume there is an external thread that needs to be notified when the program ends, not the program itself. That isn't really an option with this, so I'm not sure it helps at all (although it is telling that no one mentioned anything else like what I'm looking for). I assumed the pthread object class was final/const, is that not the case with it? If not, that might be the way to go, and then just call into the pthread_exit function once I finish my work. – Dave McGinnis Apr 30 '12 at 22:11
  • Yeah, sorry about the link, I didn't read it entirely, but I thought that it might be of some help because you might adapt it to your needs, but I guess not. And if pthread_t isn't an object, then I'll be surprised, I'm used to thinking of everything as an object. I was pretty sure that it was though, it's possible that pthread is not an object, in which case the solution I discussed before will be applicable. – Adam Miller Apr 30 '12 at 22:15
  • I think that the contents of pthread_t (which I've always assumed to be a struct, since it is usable from C as well as C++) is implementation-dependent and closed, but we could redefine pthread_exit with some nice #define magic, and then just have that call into the real pthread_exit once I have finished my code, thus we wouldn't have to worry about the real contents of pthread_t, and a similar approach could be done for Windows. – Dave McGinnis Apr 30 '12 at 22:18
  • Structs are a lot like classes: see [the selected answer here](http://stackoverflow.com/questions/3574040/c-can-a-struct-inherit-from-a-class)-they can be inherited from. Plus, you can call new on a pthread_t object, so creating a subclass seems like the most efficient way to go... although Idk if overriding the pthread_exit function will be a pain in the patoot or not-is it declared virtual? will that matter? – Adam Miller Apr 30 '12 at 22:21
  • It's always worth it to try and extend, the only thing that would happen is you get a compiler error, and have to do it the other way, which sounds significantly harder. – Adam Miller Apr 30 '12 at 22:28
  • This solution, along with the solution by @LokiAstari, both solved my issue, since his solution covers if I return from the function, and your solution covers if they call pthread_exit. Thanks for all the help on this problem, and the solutions posed! – Dave McGinnis May 01 '12 at 00:26