Hi Richard, Sorry for resurrecting an essentially dead thread, but I’ve been using libdispatch a bit more lately, and stumbled onto this problem again, and it turns out that it’s not just some annoying warning. The present way of doing things does leak an NSThread object (and potentially file descriptors used by the runloop on that thread) whenever a thread exits that is not managed by NSThread.
> Am 13.07.2015 um 13:02 schrieb Richard Frith-Macdonald > <richardfrithmacdon...@gmail.com>: > > >> On 13 Jul 2015, at 10:40, David Chisnall <thera...@sucs.org> wrote: >> >> On 13 Jul 2015, at 10:32, Richard Frith-Macdonald >> <richardfrithmacdon...@gmail.com> wrote: >>> >>>> >>>> On 13 Jul 2015, at 09:57, David Chisnall <david.chisn...@cl.cam.ac.uk> >>>> wrote: >>>> >>>> On 10 Jul 2015, at 20:18, Riccardo Mottola <riccardo.mott...@libero.it> >>>> wrote: >>>>> >>>>> Hi, >>>>> >>>>> I detach a thread "normally" with: >>>>> >>>>> [updateButton setEnabled:NO]; >>>>> [NSThread detachNewThreadSelector:@selector(updateData:) toTarget:self >>>>> withObject:nil]; >>>>> >>>>> It executes everything as expected, but I continuously get: >>>>> WARNING thread 0x2c63c1a8 terminated without calling +exit! >>>>> >>>>> I have done something similar with other threads and I don't get this >>>>> error. What could be happening? Some sort of premature exit? It executes >>>>> up to the last statement. >>>> >>>> This message also appears for threads that are not created with NSThread, >>>> which is quite annoying. I use some of the C++11 threading support for >>>> some worker threads and I get a message when they exit. >>> […] > We have a documented thread cleanup mechanism for a reason; it lets you write > portable code; which is what gnustep is for. > > Pragmatically speaking, you can’t assume that methods you call won’t call > other methods/classes (so you can’t assume NSThread is not used). > > We can fairly easily detect a thread which hasn’t been registered, and > automatically register it (which is what happens, though I think explicitly > registering is recommended as that lets you easily check to see if you > already registered the thread), but we can’t easily write portable thread > cleanup code which will work after a thread exits; something that works in > the exit handler under one pthreads implementation may not work under another. Can you elaborate on these portability problems? I suppose there is some wriggle room in the pthreads specification, but the only piece of unspecified behaviour that seems related is the following: The spec does not tell you when the TLS key will be set to NULL — is it before or after the destructor runs? I’ve tested this on Mac OS and a couple of glibc versions, and there pthread_getspecific() will no longer retrieve the value assigned to the TLS key once the destructor starts running. So in fact the pthread_setspecific() call at the end of unregisterActiveThread() is a no-op on these platforms, and some of the comments on how destructors are handled lead me to believe that that is the intended behaviour. This of course breaks calling [NSThread currentThread] in observers that are being invoked for the NSThreadWillExitNotification, but this seems to be the only problem with the exit handler (in particular, the pointer to the NSThread object will still be valid at this stage). I think there might be two ways to work around the [NSThread currentThread] issue: 1. According to the specifications, destructors will run up to PTHREAD_DESTRUCTOR_ITERATIONS times (most systems default to 4 here). So you could just reassign the NSThread object to the TLS key before running the cleanup and set a flag so that you don’t do the cleanup again when the destructor runs the second time (instead just deallocating the object). Maybe you could even reuse the existing _active/_finished flags for that. 2. pthread_self() still returns the correct thread while the destructor runs, so you could populate a lookup table of threads currently undergoing cleanup, and use that in +currentThread to look up the NSThread object if you can’t get it via pthread_getspecific(). > So, we have an easy option … app developer calls the function to deregister > the thread before exiting from it I don’t think that’s an easy option in every case. Of course it’s perfectly viable if you are managing threading yourself, but it’s quite suboptimal if you want to use an existing thread pool/work scheduling library such as libdispatch — or really just any C library that is multithreaded and requires you to use callbacks that include Objective-C code. You essentially have to correctly guess implementation details of the library you are calling so that you can wrap things in GSRegisterCurrentThread()/GSUnregisterCurrentThread() calls as needed. > Or we have a hard option … someone figures out how to write portable code to > perform cleanup within the pthread exit handler > > I’d be quite happy if someone wanted to contribute portable thread cleanup > code which would run safely on pthread_exit() of course. I’d like to look into that, but I really need to know about what incompatibilities we need to be concerned about. Cheers, Niels _______________________________________________ Gnustep-dev mailing list Gnustep-dev@gnu.org https://lists.gnu.org/mailman/listinfo/gnustep-dev