[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
- Re: abnormal thread termination,
Niels Grewe <=