Add automatic unregistration of threads that have not been

been explicitly unregistered. This works by keeping around 
a map table with all threads currently undergoing cleanup, 
and using that as a fallback if pthread_getspecific would
not return the NSThread object from TLS. 


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@39318 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Niels Grewe 2016-01-29 13:42:07 +00:00
parent 478e376882
commit b0afa17bed
3 changed files with 360 additions and 32 deletions

View file

@ -0,0 +1,129 @@
#import "ObjectTesting.h"
#import <Foundation/NSThread.h>
#import <Foundation/NSLock.h>
#import <Foundation/NSNotification.h>
#include <pthread.h>
@interface ThreadExpectation : NSObject <NSLocking>
{
NSCondition *condition;
NSThread *origThread;
BOOL done;
BOOL deallocated;
}
- (void)onThreadExit: (NSNotification*)n;
- (BOOL)isDone;
@end
@implementation ThreadExpectation
- (id)init
{
if (nil == (self = [super init]))
{
return nil;
}
condition = [NSCondition new];
return self;
}
- (void)inThread: (NSThread*)thread
{
/* We explicitly don't retain this so that we can check that it actually says
* alive until the notification is sent. That check is implicit since
* PASS_EQUAL in the -onThreadExit method will throw or crash if that isn't
* the case.
*/
origThread = thread;
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(onThreadExit:)
name: NSThreadWillExitNotification
object: thread];
}
- (void)onThreadExit: (NSNotification*)thr
{
NSThread *current = [NSThread currentThread];
PASS_EQUAL(origThread,current,
"Correct thread reference can be obtained on exit");
[[NSNotificationCenter defaultCenter] removeObserver: self];
origThread = nil;
[condition lock];
done = YES;
[condition broadcast];
[condition unlock];
}
- (BOOL)isDone
{
return done;
}
- (void)waitUntilDate: (NSDate*)date
{
[condition waitUntilDate: date];
}
- (void)lock
{
[condition lock];
}
- (void)unlock
{
[condition unlock];
}
- (void)dealloc
{
DESTROY(condition);
[super dealloc];
}
@end
void *thread(void *expectation)
{
[(ThreadExpectation*)expectation inThread: [NSThread currentThread]];
return nil;
}
/**
* This test checks whether we can still obtain a reference to the NSThread
* object of a thread that is in the process of exiting without an explicit
* call to [NSThread exit]. To do this, we pass an expectation object to
* a thread created purely using the pthreads API. We then wait on a condition
* until the thread exits and posts the NSThreadWillExitNotification. If that
* does not happen within 5 seconds, we flag the test as failed.
*/
int main(void)
{
pthread_t thr;
pthread_attr_t attr;
void *ret;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
NSAutoreleasePool *arp = [NSAutoreleasePool new];
ThreadExpectation *expectation = [ThreadExpectation new];
pthread_create(&thr, &attr, thread, expectation);
NSDate *start = [NSDate date];
[expectation lock];
while (![expectation isDone] && [start timeIntervalSinceNow] > -5.0f)
{
[expectation waitUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.5f]];
}
PASS([expectation isDone], "Notification for thread exit was sent");
[expectation unlock];
DESTROY(expectation);
DESTROY(arp);
return 0;
}