mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
Fix retain error in uniquing
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@6483 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
c5fb445c87
commit
dc9403d316
3 changed files with 210 additions and 169 deletions
|
@ -198,6 +198,10 @@ static Class NSCountedSet_concrete_class;
|
|||
id o = [self member: anObject];
|
||||
|
||||
[self addObject: anObject];
|
||||
if (o == nil)
|
||||
{
|
||||
o = anObject;
|
||||
}
|
||||
#if !GS_WITH_GC
|
||||
if (o != anObject)
|
||||
{
|
||||
|
|
|
@ -322,7 +322,7 @@
|
|||
if (node == 0)
|
||||
{
|
||||
result = anObject;
|
||||
GSIMapAddPairNoRetain(&map,(GSIMapKey)anObject,(GSIMapVal)(unsigned)1);
|
||||
GSIMapAddPair(&map,(GSIMapKey)anObject,(GSIMapVal)(unsigned)1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Control of executable units within a shared virtual memory space
|
||||
Copyright (C) 1996 Free Software Foundation, Inc.
|
||||
Copyright (C) 1996-2000 Free Software Foundation, Inc.
|
||||
|
||||
Original Author: Scott Christley <scottc@net-community.com>
|
||||
Rewritten by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
||||
|
@ -32,82 +32,51 @@
|
|||
#include <Foundation/NSString.h>
|
||||
#include <Foundation/NSNotificationQueue.h>
|
||||
|
||||
@interface NSThread (Private)
|
||||
- (id) _initWithSelector: (SEL)s toTarget: (id)t withObject: (id)o;
|
||||
- (void) _sendThreadMethod;
|
||||
@end
|
||||
|
||||
/*
|
||||
* A class to defer the use of the real thread method until we have
|
||||
* an autlorelease pool so that target and arg can be safely released
|
||||
* once they are no longer needed.
|
||||
* Flag indicating whether the objc runtime ever went multi-threaded.
|
||||
*/
|
||||
@interface GSThreadLauncher : NSObject
|
||||
{
|
||||
id target;
|
||||
id arg;
|
||||
SEL sel;
|
||||
}
|
||||
+ (GSThreadLauncher*) newWithTarget: (id)t selector: (SEL)s arg: (id)a;
|
||||
- (void) sendThreadMethod;
|
||||
@end
|
||||
static BOOL entered_multi_threaded_state = NO;
|
||||
|
||||
@implementation GSThreadLauncher
|
||||
+ (GSThreadLauncher*) newWithTarget: (id)t selector: (SEL)s arg: (id)a
|
||||
{
|
||||
GSThreadLauncher *l;
|
||||
|
||||
l = (GSThreadLauncher*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
l->target = RETAIN(t);
|
||||
l->arg = RETAIN(a);
|
||||
l->sel = s;
|
||||
return l;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(target);
|
||||
RELEASE(arg);
|
||||
NSDeallocateObject(self);
|
||||
}
|
||||
|
||||
- (void) sendThreadMethod
|
||||
{
|
||||
/*
|
||||
* We are running in the new thread - so we store ourself in the thread
|
||||
* dictionary and release ourself - thus, when the thread exits, we will
|
||||
* be deallocated cleanly.
|
||||
*/
|
||||
[[[NSThread currentThread] threadDictionary] setObject: self
|
||||
forKey: @"GSThreadLauncher"];
|
||||
RELEASE(self);
|
||||
[target performSelector: sel withObject: arg];
|
||||
[NSThread exit];
|
||||
}
|
||||
@end
|
||||
|
||||
// Class variables
|
||||
|
||||
/* Flag indicating whether the objc runtime ever went multi-threaded. */
|
||||
static BOOL entered_multi_threaded_state;
|
||||
/*
|
||||
* Default thread.
|
||||
*/
|
||||
static NSThread *defaultThread = nil;
|
||||
|
||||
/*
|
||||
* Fast access function to get current thread.
|
||||
*/
|
||||
inline NSThread*
|
||||
GSCurrentThread()
|
||||
{
|
||||
id t = (id) objc_thread_get_data ();
|
||||
|
||||
/* If an NSThread object for this thread has already been created
|
||||
and stashed away, return it. This depends on the objc runtime
|
||||
initializing objc_thread_get_data() to 0 for newly-created
|
||||
threads. */
|
||||
if (t)
|
||||
return t;
|
||||
|
||||
/* We haven't yet created an NSThread object for this thread; create
|
||||
it. (Doing this here instead of in +detachNewThread.. not only
|
||||
avoids the race condition, it also nicely provides an NSThread on
|
||||
request for the single thread that exists at application
|
||||
start-up, and for thread's created by calling
|
||||
objc_thread_detach() directly.) */
|
||||
t = [[NSThread alloc] init];
|
||||
return t;
|
||||
if (entered_multi_threaded_state == NO)
|
||||
{
|
||||
/*
|
||||
* If the NSThread class has been initialized, we will have a default
|
||||
* thread set up - otherwise we must make sure the class is initialised.
|
||||
*/
|
||||
if (defaultThread == nil)
|
||||
{
|
||||
return [NSThread currentThread];
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultThread;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (NSThread*)objc_thread_get_data();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast access function for thread dictionary of current thread.
|
||||
*/
|
||||
NSMutableDictionary*
|
||||
GSCurrentThreadDictionary()
|
||||
{
|
||||
|
@ -115,18 +84,24 @@ GSCurrentThreadDictionary()
|
|||
NSMutableDictionary *dict = thread->_thread_dictionary;
|
||||
|
||||
if (dict == nil)
|
||||
dict = [thread threadDictionary];
|
||||
{
|
||||
dict = [thread threadDictionary];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
void gnustep_base_thread_callback()
|
||||
/*
|
||||
* Callback function so send notifications on becoming multi-threaded.
|
||||
*/
|
||||
static void
|
||||
gnustep_base_thread_callback()
|
||||
{
|
||||
/*
|
||||
* Post a notification if this is the first new thread to be created.
|
||||
* Won't work properly if threads are not all created by this class,
|
||||
* but it's better than nothing.
|
||||
*/
|
||||
if (!entered_multi_threaded_state)
|
||||
if (entered_multi_threaded_state == NO)
|
||||
{
|
||||
NSNotification *n;
|
||||
|
||||
|
@ -143,12 +118,98 @@ void gnustep_base_thread_callback()
|
|||
|
||||
@implementation NSThread
|
||||
|
||||
// Class initialization
|
||||
/*
|
||||
* Return the current thread
|
||||
*/
|
||||
+ (NSThread*) currentThread
|
||||
{
|
||||
return GSCurrentThread();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new thread - use this method rather than alloc-init
|
||||
*/
|
||||
+ (void) detachNewThreadSelector: (SEL)aSelector
|
||||
toTarget: (id)aTarget
|
||||
withObject: (id)anArgument
|
||||
{
|
||||
NSThread *thread;
|
||||
|
||||
/*
|
||||
* Make sure the notification is posted BEFORE the new thread starts.
|
||||
*/
|
||||
gnustep_base_thread_callback();
|
||||
|
||||
/*
|
||||
* Create the new thread.
|
||||
*/
|
||||
thread = (NSThread*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
thread = [thread _initWithSelector: aSelector
|
||||
toTarget: aTarget
|
||||
withObject: anArgument];
|
||||
|
||||
/*
|
||||
* Have the runtime detach the thread
|
||||
*/
|
||||
if (objc_thread_detach(@selector(_sendThreadMethod), thread, nil) == NULL)
|
||||
{
|
||||
/* This should probably be an exception */
|
||||
NSLog(@"Unable to detach thread (unknown error)");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminating a thread
|
||||
* What happens if the thread doesn't call +exit - it doesn't terminate!
|
||||
*/
|
||||
+ (void) exit
|
||||
{
|
||||
NSThread *t;
|
||||
|
||||
t = GSCurrentThread();
|
||||
if (t->_active == YES)
|
||||
{
|
||||
NSNotification *n;
|
||||
|
||||
/*
|
||||
* Set the thread to be inactive to avoid any possibility of recursion.
|
||||
*/
|
||||
t->_active = NO;
|
||||
|
||||
/*
|
||||
* Let observers know this thread is exiting.
|
||||
*/
|
||||
n = [NSNotification alloc];
|
||||
n = [n initWithName: NSThreadWillExitNotification
|
||||
object: t
|
||||
userInfo: nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotification: n];
|
||||
RELEASE(n);
|
||||
|
||||
/*
|
||||
* Release anything in our autorelease pools
|
||||
*/
|
||||
[NSAutoreleasePool _endThread];
|
||||
|
||||
/*
|
||||
* destroy the thread object.
|
||||
*/
|
||||
DESTROY(t);
|
||||
|
||||
/*
|
||||
* Tell the runtime to exit the thread
|
||||
*/
|
||||
objc_thread_exit();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class initialization
|
||||
*/
|
||||
+ (void) initialize
|
||||
{
|
||||
if (self == [NSThread class])
|
||||
{
|
||||
entered_multi_threaded_state = NO;
|
||||
/*
|
||||
* The objc runtime calls this callback AFTER creating a new thread -
|
||||
* which is not correct for us, but does at least mean that we can tell
|
||||
|
@ -156,88 +217,28 @@ void gnustep_base_thread_callback()
|
|||
* rather than via the NSThread class.
|
||||
*/
|
||||
objc_set_thread_callback(gnustep_base_thread_callback);
|
||||
|
||||
/*
|
||||
* Ensure that the default thread exists.
|
||||
*/
|
||||
defaultThread
|
||||
= (NSThread*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
||||
defaultThread = [defaultThread _initWithSelector: (SEL)0
|
||||
toTarget: nil
|
||||
withObject: nil];
|
||||
defaultThread->_active = YES;
|
||||
objc_thread_set_data(defaultThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialization
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
TEST_RELEASE(_thread_dictionary);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
/* Make it easy and fast to get this NSThread object from the thread. */
|
||||
objc_thread_set_data (self);
|
||||
|
||||
/* initialize our ivars. */
|
||||
_thread_dictionary = nil; // Initialize this later only when needed
|
||||
_exception_handler = NULL;
|
||||
init_autorelease_thread_vars(&_autorelease_vars);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
// Creating an NSThread
|
||||
|
||||
+ (NSThread*) currentThread
|
||||
{
|
||||
return GSCurrentThread();
|
||||
}
|
||||
|
||||
+ (void) detachNewThreadSelector: (SEL)aSelector
|
||||
toTarget: (id)aTarget
|
||||
withObject: (id)anArgument
|
||||
{
|
||||
GSThreadLauncher *launcher;
|
||||
/*
|
||||
* Make sure the notification is posted BEFORE the new thread starts.
|
||||
*/
|
||||
gnustep_base_thread_callback();
|
||||
/*
|
||||
* Have the runtime detach the thread
|
||||
*/
|
||||
launcher = [GSThreadLauncher newWithTarget: aTarget
|
||||
selector: aSelector
|
||||
arg: anArgument];
|
||||
|
||||
if (objc_thread_detach(@selector(sendThreadMethod), launcher, nil) == NULL)
|
||||
{
|
||||
/* This should probably be an exception */
|
||||
NSLog(@"Unable to detach thread (unknown error)");
|
||||
}
|
||||
|
||||
/* NOTE we can't create the new NSThread object for this thread here
|
||||
because there would be a race condition. The newly created
|
||||
thread might ask for its NSThread object before we got to create
|
||||
it. */
|
||||
}
|
||||
|
||||
|
||||
// Querying a thread
|
||||
|
||||
+ (BOOL) isMultiThreaded
|
||||
{
|
||||
return entered_multi_threaded_state;
|
||||
}
|
||||
|
||||
/* Thread dictionary
|
||||
NB. This cannot be autoreleased, since we cannot be sure that the
|
||||
autorelease pool for the thread will continue to exist for the entire
|
||||
life of the thread!
|
||||
/*
|
||||
* Delaying a thread
|
||||
*/
|
||||
- (NSMutableDictionary*) threadDictionary
|
||||
{
|
||||
if (!_thread_dictionary)
|
||||
_thread_dictionary = [NSMutableDictionary new];
|
||||
return _thread_dictionary;
|
||||
}
|
||||
|
||||
// Delaying a thread
|
||||
+ (void) sleepUntilDate: (NSDate*)date
|
||||
{
|
||||
NSTimeInterval delay;
|
||||
|
@ -278,35 +279,71 @@ void gnustep_base_thread_callback()
|
|||
}
|
||||
}
|
||||
|
||||
// Terminating a thread
|
||||
// What happens if the thread doesn't call +exit?
|
||||
+ (void) exit
|
||||
|
||||
|
||||
/*
|
||||
* Thread instance methods.
|
||||
*/
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
NSThread *t;
|
||||
NSNotification *n;
|
||||
if (_active == YES)
|
||||
{
|
||||
[NSException raise: NSInternalInconsistencyException
|
||||
format: @"Deallocating an active thread without [+exit]!"];
|
||||
}
|
||||
DESTROY(_thread_dictionary);
|
||||
DESTROY(_target);
|
||||
DESTROY(_arg);
|
||||
NSDeallocateObject(self);
|
||||
}
|
||||
|
||||
// the current NSThread
|
||||
t = GSCurrentThread();
|
||||
- (id) init
|
||||
{
|
||||
RELEASE(self);
|
||||
return [NSThread currentThread];
|
||||
}
|
||||
|
||||
// Post the notification
|
||||
n = [NSNotification alloc];
|
||||
n = [n initWithName: NSThreadWillExitNotification
|
||||
object: t
|
||||
userInfo: nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotification: n];
|
||||
RELEASE(n);
|
||||
- (id) _initWithSelector: (SEL)s toTarget: (id)t withObject: (id)o
|
||||
{
|
||||
/* initialize our ivars. */
|
||||
_selector = s;
|
||||
_target = RETAIN(t);
|
||||
_arg = RETAIN(o);
|
||||
_thread_dictionary = nil; // Initialize this later only when needed
|
||||
_exception_handler = NULL;
|
||||
_active = NO;
|
||||
init_autorelease_thread_vars(&_autorelease_vars);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) _sendThreadMethod
|
||||
{
|
||||
/*
|
||||
* Release anything in our autorelease pools
|
||||
* We are running in the new thread - so we store ourself in the thread
|
||||
* dictionary and release ourself - thus, when the thread exits, we will
|
||||
* be deallocated cleanly.
|
||||
*/
|
||||
[NSAutoreleasePool _endThread];
|
||||
objc_thread_set_data(self);
|
||||
_active = YES;
|
||||
[_target performSelector: _selector withObject: _arg];
|
||||
[NSThread exit];
|
||||
}
|
||||
|
||||
RELEASE(t);
|
||||
|
||||
// xxx Clean up any outstanding NSAutoreleasePools here.
|
||||
|
||||
// Tell the runtime to exit the thread
|
||||
objc_thread_exit ();
|
||||
/*
|
||||
* Thread dictionary
|
||||
* NB. This cannot be autoreleased, since we cannot be sure that the
|
||||
* autorelease pool for the thread will continue to exist for the entire
|
||||
* life of the thread!
|
||||
*/
|
||||
- (NSMutableDictionary*) threadDictionary
|
||||
{
|
||||
if (_thread_dictionary == nil)
|
||||
{
|
||||
_thread_dictionary = [NSMutableDictionary new];
|
||||
}
|
||||
return _thread_dictionary;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
Loading…
Reference in a new issue