gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: abnormal thread termination


From: Niels Grewe
Subject: Re: abnormal thread termination
Date: Fri, 15 Jan 2016 10:51:21 +0000

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 <address@hidden>:
> 
> 
>> On 13 Jul 2015, at 10:40, David Chisnall <address@hidden> wrote:
>> 
>> On 13 Jul 2015, at 10:32, Richard Frith-Macdonald <address@hidden> wrote:
>>> 
>>>> 
>>>> On 13 Jul 2015, at 09:57, David Chisnall <address@hidden> wrote:
>>>> 
>>>> On 10 Jul 2015, at 20:18, Riccardo Mottola <address@hidden> 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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]