2001-12-17 14:31:42 +00:00
|
|
|
|
/** Control of executable units within a shared virtual memory space
|
2000-04-19 12:29:17 +00:00
|
|
|
|
Copyright (C) 1996-2000 Free Software Foundation, Inc.
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
Original Author: Scott Christley <scottc@net-community.com>
|
|
|
|
|
Rewritten by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
|
|
|
|
Created: 1996
|
1999-05-10 06:45:36 +00:00
|
|
|
|
Rewritten by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
to add optimisations features for faster thread access.
|
2000-11-12 07:41:24 +00:00
|
|
|
|
Modified by: Nicola Pero <n.pero@mi.flashnet.it>
|
2005-02-22 11:22:44 +00:00
|
|
|
|
to add GNUstep extensions allowing to interact with threads created
|
2000-11-12 07:41:24 +00:00
|
|
|
|
by external libraries/code (eg, a Java Virtual Machine).
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-02-13 15:40:05 +00:00
|
|
|
|
This file is part of the GNUstep Objective-C Library.
|
|
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-02-13 15:40:05 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
2005-05-22 03:32:16 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSThread class reference</title>
|
|
|
|
|
$Date$ $Revision$
|
2005-02-22 11:22:44 +00:00
|
|
|
|
*/
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "config.h"
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/preface.h"
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_UNISTD_H
|
1999-06-24 19:30:29 +00:00
|
|
|
|
#include <unistd.h>
|
2002-02-20 06:42:05 +00:00
|
|
|
|
#endif
|
2003-07-17 09:00:31 +00:00
|
|
|
|
#ifdef HAVE_NANOSLEEP
|
|
|
|
|
#include <time.h>
|
|
|
|
|
#endif
|
2004-02-17 12:55:02 +00:00
|
|
|
|
#ifdef NeXT_RUNTIME
|
|
|
|
|
#include "thr-mach.h"
|
|
|
|
|
#endif
|
2002-11-02 16:53:48 +00:00
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
2003-09-30 18:19:03 +00:00
|
|
|
|
#include "Foundation/NSException.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "Foundation/NSThread.h"
|
|
|
|
|
#include "Foundation/NSLock.h"
|
|
|
|
|
#include "Foundation/NSString.h"
|
|
|
|
|
#include "Foundation/NSNotificationQueue.h"
|
|
|
|
|
#include "Foundation/NSRunLoop.h"
|
|
|
|
|
#include "Foundation/NSConnection.h"
|
|
|
|
|
#include "Foundation/NSInvocation.h"
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
2006-04-09 16:16:07 +00:00
|
|
|
|
#include "GSRunLoopCtxt.h"
|
|
|
|
|
|
|
|
|
|
@interface NSAutoreleasePool (NSThread)
|
|
|
|
|
+ (void) _endThread: (NSThread*)thread;
|
|
|
|
|
@end
|
2002-11-03 18:56:46 +00:00
|
|
|
|
|
2004-09-27 08:59:04 +00:00
|
|
|
|
typedef struct { @defs(NSThread) } NSThread_ivars;
|
|
|
|
|
|
2001-03-19 12:20:21 +00:00
|
|
|
|
static Class threadClass = Nil;
|
2002-08-07 13:29:31 +00:00
|
|
|
|
static NSNotificationCenter *nc = nil;
|
2001-03-19 12:20:21 +00:00
|
|
|
|
|
2005-07-08 11:48:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* This class performs a dual function ...
|
|
|
|
|
* <p>
|
|
|
|
|
* As a class, it is responsible for handling incoming events from
|
|
|
|
|
* the main runloop on a special inputFd. This consumes any bytes
|
|
|
|
|
* written to wake the main runloop.<br />
|
|
|
|
|
* During initialisation, the default runloop is set up to watch
|
|
|
|
|
* for data arriving on inputFd.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* As instances, each instance retains perform receiver and argument
|
|
|
|
|
* values as long as they are needed, and handles locking to support
|
|
|
|
|
* mthods which want to block until an action has been performed.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* The initialize method of this class is called before any new threads
|
|
|
|
|
* run.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
|
|
|
|
@interface GSPerformHolder : NSObject
|
|
|
|
|
{
|
|
|
|
|
id receiver;
|
|
|
|
|
id argument;
|
|
|
|
|
SEL selector;
|
|
|
|
|
NSArray *modes;
|
|
|
|
|
NSConditionLock *lock; // Not retained.
|
|
|
|
|
}
|
|
|
|
|
+ (BOOL) isValid;
|
|
|
|
|
+ (GSPerformHolder*) newForReceiver: (id)r
|
|
|
|
|
argument: (id)a
|
|
|
|
|
selector: (SEL)s
|
|
|
|
|
modes: (NSArray*)m
|
|
|
|
|
lock: (NSConditionLock*)l;
|
|
|
|
|
+ (void) receivedEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
extra: (void*)extra
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
- (void) fire;
|
|
|
|
|
@end
|
|
|
|
|
|
2003-07-20 06:37:25 +00:00
|
|
|
|
/**
|
|
|
|
|
* Sleep until the current date/time is the specified time interval
|
|
|
|
|
* past the reference date/time.<br />
|
|
|
|
|
* Implemented as a function taking an NSTimeInterval argument in order
|
|
|
|
|
* to avoid objc messaging and object allocation/deallocation (NSDate)
|
|
|
|
|
* overheads.<br />
|
|
|
|
|
* Used to implement [NSThread+sleepUntilDate:]
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
GSSleepUntilIntervalSinceReferenceDate(NSTimeInterval when)
|
|
|
|
|
{
|
2003-12-23 17:41:38 +00:00
|
|
|
|
extern NSTimeInterval GSTimeNow(void);
|
2003-07-20 06:37:25 +00:00
|
|
|
|
NSTimeInterval delay;
|
|
|
|
|
|
|
|
|
|
// delay is always the number of seconds we still need to wait
|
|
|
|
|
delay = when - GSTimeNow();
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_NANOSLEEP
|
|
|
|
|
// Avoid any possibility of overflow by sleeping in chunks.
|
|
|
|
|
while (delay > 32768)
|
|
|
|
|
{
|
|
|
|
|
struct timespec request;
|
|
|
|
|
|
|
|
|
|
request.tv_sec = (time_t)32768;
|
|
|
|
|
request.tv_nsec = (long)0;
|
|
|
|
|
nanosleep(&request, 0);
|
|
|
|
|
delay = when - GSTimeNow();
|
|
|
|
|
}
|
|
|
|
|
if (delay > 0)
|
|
|
|
|
{
|
|
|
|
|
struct timespec request;
|
|
|
|
|
struct timespec remainder;
|
|
|
|
|
|
|
|
|
|
request.tv_sec = (time_t)delay;
|
|
|
|
|
request.tv_nsec = (long)((delay - request.tv_sec) * 1000000000);
|
|
|
|
|
remainder.tv_sec = 0;
|
|
|
|
|
remainder.tv_nsec = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* With nanosleep, we can restart the sleep after a signal by using
|
|
|
|
|
* the remainder information ... so we can be sure to sleep to the
|
|
|
|
|
* desired limit without having to re-generate the delay needed.
|
|
|
|
|
*/
|
|
|
|
|
while (nanosleep(&request, &remainder) < 0
|
|
|
|
|
&& (remainder.tv_sec > 0 || remainder.tv_nsec > 0))
|
|
|
|
|
{
|
|
|
|
|
request.tv_sec = remainder.tv_sec;
|
|
|
|
|
request.tv_nsec = remainder.tv_nsec;
|
|
|
|
|
remainder.tv_sec = 0;
|
|
|
|
|
remainder.tv_nsec = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Avoid integer overflow by breaking up long sleeps.
|
|
|
|
|
*/
|
|
|
|
|
while (delay > 30.0*60.0)
|
|
|
|
|
{
|
|
|
|
|
// sleep 30 minutes
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2003-07-20 06:37:25 +00:00
|
|
|
|
Sleep (30*60*1000);
|
|
|
|
|
#else
|
|
|
|
|
sleep (30*60);
|
|
|
|
|
#endif
|
|
|
|
|
delay = when - GSTimeNow();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* sleeping may return early because of signals, so we need to re-calculate
|
|
|
|
|
* the required delay and check to see if we need to sleep again.
|
|
|
|
|
*/
|
|
|
|
|
while (delay > 0)
|
|
|
|
|
{
|
|
|
|
|
#ifdef HAVE_USLEEP
|
|
|
|
|
usleep ((int)(delay*1000000));
|
|
|
|
|
#else
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2003-07-20 06:37:25 +00:00
|
|
|
|
Sleep (delay*1000);
|
|
|
|
|
#else
|
|
|
|
|
sleep ((int)delay);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
delay = when - GSTimeNow();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
static NSArray *
|
2003-12-23 17:41:38 +00:00
|
|
|
|
commonModes(void)
|
2002-11-03 18:56:46 +00:00
|
|
|
|
{
|
|
|
|
|
static NSArray *modes = nil;
|
|
|
|
|
|
|
|
|
|
if (modes == nil)
|
|
|
|
|
{
|
|
|
|
|
[gnustep_global_lock lock];
|
|
|
|
|
if (modes == nil)
|
|
|
|
|
{
|
|
|
|
|
Class c = NSClassFromString(@"NSApplication");
|
|
|
|
|
SEL s = @selector(allRunLoopModes);
|
|
|
|
|
|
|
|
|
|
if (c != 0 && [c respondsToSelector: s])
|
|
|
|
|
{
|
|
|
|
|
modes = RETAIN([c performSelector: s]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
modes = [[NSArray alloc] initWithObjects:
|
|
|
|
|
NSDefaultRunLoopMode, NSConnectionReplyMode, nil];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
[gnustep_global_lock unlock];
|
|
|
|
|
}
|
|
|
|
|
return modes;
|
|
|
|
|
}
|
|
|
|
|
|
2001-05-31 22:39:16 +00:00
|
|
|
|
#if !defined(HAVE_OBJC_THREAD_ADD) && !defined(NeXT_RUNTIME)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
/* We need to access these private vars in the objc runtime - because
|
|
|
|
|
the objc runtime's API is not enough powerful for the GNUstep
|
|
|
|
|
extensions we want to add. */
|
2001-03-12 02:52:16 +00:00
|
|
|
|
extern objc_mutex_t __objc_runtime_mutex;
|
|
|
|
|
extern int __objc_runtime_threads_alive;
|
2000-11-12 07:41:24 +00:00
|
|
|
|
extern int __objc_is_multi_threaded;
|
|
|
|
|
|
2003-12-23 17:41:38 +00:00
|
|
|
|
inline static void objc_thread_add (void)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
{
|
|
|
|
|
objc_mutex_lock(__objc_runtime_mutex);
|
|
|
|
|
__objc_is_multi_threaded = 1;
|
|
|
|
|
__objc_runtime_threads_alive++;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
objc_mutex_unlock(__objc_runtime_mutex);
|
2000-11-12 07:41:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-12-23 17:41:38 +00:00
|
|
|
|
inline static void objc_thread_remove (void)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
{
|
|
|
|
|
objc_mutex_lock(__objc_runtime_mutex);
|
|
|
|
|
__objc_runtime_threads_alive--;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
objc_mutex_unlock(__objc_runtime_mutex);
|
2000-11-12 07:41:24 +00:00
|
|
|
|
}
|
2001-03-10 21:36:03 +00:00
|
|
|
|
#endif /* not HAVE_OBJC_THREAD_ADD */
|
2000-11-12 07:41:24 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
@interface NSThread (Private)
|
|
|
|
|
- (id) _initWithSelector: (SEL)s toTarget: (id)t withObject: (id)o;
|
|
|
|
|
- (void) _sendThreadMethod;
|
1999-07-14 16:28:43 +00:00
|
|
|
|
@end
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Flag indicating whether the objc runtime ever went multi-threaded.
|
|
|
|
|
*/
|
|
|
|
|
static BOOL entered_multi_threaded_state = NO;
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Default thread.
|
|
|
|
|
*/
|
|
|
|
|
static NSThread *defaultThread = nil;
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
2002-05-03 08:17:04 +00:00
|
|
|
|
/**
|
2002-08-20 10:22:05 +00:00
|
|
|
|
* <p>
|
|
|
|
|
* This function is a GNUstep extension. It pretty much
|
|
|
|
|
* duplicates the functionality of [NSThread +currentThread]
|
|
|
|
|
* but is more efficient and is used internally throughout
|
|
|
|
|
* GNUstep.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* Returns the current thread. Could perhaps return <code>nil</code>
|
|
|
|
|
* if executing a thread that was started outside the GNUstep
|
|
|
|
|
* environment and not registered (this should not happen in a
|
|
|
|
|
* well-coded application).
|
|
|
|
|
* </p>
|
2000-04-19 12:29:17 +00:00
|
|
|
|
*/
|
1999-04-19 14:29:52 +00:00
|
|
|
|
inline NSThread*
|
2003-12-23 17:41:38 +00:00
|
|
|
|
GSCurrentThread(void)
|
1999-04-19 14:29:52 +00:00
|
|
|
|
{
|
2002-05-03 08:17:04 +00:00
|
|
|
|
NSThread *t;
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2002-05-03 08:17:04 +00:00
|
|
|
|
t = [NSThread currentThread];
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-05-03 08:17:04 +00:00
|
|
|
|
t = defaultThread;
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-05-03 08:17:04 +00:00
|
|
|
|
t = (NSThread*)objc_thread_get_data();
|
|
|
|
|
if (t == nil)
|
|
|
|
|
{
|
2003-09-30 18:57:49 +00:00
|
|
|
|
fprintf(stderr,
|
2003-12-23 17:41:38 +00:00
|
|
|
|
"ALERT ... GSCurrentThread() ... objc_thread_get_data() call returned nil!\n"
|
|
|
|
|
"Your application MUST call GSRegisterCurrentThread() before attempting to\n"
|
|
|
|
|
"use any GNUstep code from a thread other than the main GNUstep thread.\n");
|
2002-05-03 08:17:04 +00:00
|
|
|
|
fflush(stderr); // Needed for windoze
|
|
|
|
|
}
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
2002-05-03 08:17:04 +00:00
|
|
|
|
return t;
|
1999-04-19 14:29:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-05-03 08:17:04 +00:00
|
|
|
|
/**
|
2004-09-27 08:59:04 +00:00
|
|
|
|
* Fast access function for thread dictionary of current thread.<br />
|
2004-11-03 13:20:39 +00:00
|
|
|
|
* If there is no dictionary, creates the dictionary.
|
2000-04-19 12:29:17 +00:00
|
|
|
|
*/
|
1999-04-19 14:29:52 +00:00
|
|
|
|
NSMutableDictionary*
|
2002-10-30 07:45:59 +00:00
|
|
|
|
GSDictionaryForThread(NSThread *t)
|
1999-04-19 14:29:52 +00:00
|
|
|
|
{
|
2002-10-30 07:45:59 +00:00
|
|
|
|
if (t == nil)
|
|
|
|
|
{
|
|
|
|
|
t = GSCurrentThread();
|
|
|
|
|
}
|
|
|
|
|
if (t == nil)
|
2002-05-03 08:17:04 +00:00
|
|
|
|
{
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
else
|
2000-04-19 12:29:17 +00:00
|
|
|
|
{
|
2002-10-30 07:45:59 +00:00
|
|
|
|
NSMutableDictionary *dict = t->_thread_dictionary;
|
2002-05-03 08:17:04 +00:00
|
|
|
|
|
2004-11-03 13:20:39 +00:00
|
|
|
|
if (dict == nil)
|
2002-05-03 08:17:04 +00:00
|
|
|
|
{
|
2002-10-30 07:45:59 +00:00
|
|
|
|
dict = [t threadDictionary];
|
2002-05-03 08:17:04 +00:00
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
return dict;
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
1999-04-19 14:29:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
/**
|
|
|
|
|
* Fast access function for thread dictionary of current thread.
|
|
|
|
|
*/
|
|
|
|
|
NSMutableDictionary*
|
2003-12-23 17:41:38 +00:00
|
|
|
|
GSCurrentThreadDictionary(void)
|
2002-10-30 07:45:59 +00:00
|
|
|
|
{
|
|
|
|
|
return GSDictionaryForThread(nil);
|
|
|
|
|
}
|
|
|
|
|
|
2003-04-17 06:20:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* The special timer which we set up in the run loop of the main thread
|
|
|
|
|
* to perform housekeeping duties. NSRunLoop needs to call this private
|
|
|
|
|
* function so it knows about the housekeeping timer and won't keep the
|
|
|
|
|
* loop running just to do housekeeping.
|
|
|
|
|
*
|
|
|
|
|
* The NSUserDefaults system registers as an observer of GSHousekeeping
|
|
|
|
|
* notifications in order to synchronise the in-memory cache and the
|
|
|
|
|
* on-disk database.
|
|
|
|
|
*/
|
|
|
|
|
static NSTimer *housekeeper = nil;
|
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
/**
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* Returns the runloop for the specified thread (or, if t is nil,
|
2004-09-27 08:59:04 +00:00
|
|
|
|
* for the current thread).<br />
|
|
|
|
|
* Creates a new runloop if necessary,
|
|
|
|
|
* as long as the thread dictionary exists.<br />
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* Returns nil on failure.
|
|
|
|
|
*/
|
|
|
|
|
NSRunLoop*
|
|
|
|
|
GSRunLoopForThread(NSThread *t)
|
|
|
|
|
{
|
|
|
|
|
static NSString *key = @"NSRunLoopThreadKey";
|
|
|
|
|
NSMutableDictionary *d = GSDictionaryForThread(t);
|
|
|
|
|
NSRunLoop *r;
|
|
|
|
|
|
|
|
|
|
r = [d objectForKey: key];
|
|
|
|
|
if (r == nil)
|
|
|
|
|
{
|
|
|
|
|
if (d != nil)
|
|
|
|
|
{
|
|
|
|
|
r = [NSRunLoop new];
|
|
|
|
|
[d setObject: r forKey: key];
|
|
|
|
|
RELEASE(r);
|
2003-07-25 09:27:44 +00:00
|
|
|
|
if (housekeeper == nil && (t == nil || t == defaultThread))
|
2003-04-17 06:20:17 +00:00
|
|
|
|
{
|
2003-07-25 09:27:44 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL (arp);
|
2003-04-17 06:20:17 +00:00
|
|
|
|
NSNotificationCenter *ctr;
|
|
|
|
|
NSNotification *not;
|
|
|
|
|
NSInvocation *inv;
|
|
|
|
|
SEL sel;
|
|
|
|
|
|
|
|
|
|
ctr = [NSNotificationCenter defaultCenter];
|
|
|
|
|
not = [NSNotification notificationWithName: @"GSHousekeeping"
|
2003-07-25 09:27:44 +00:00
|
|
|
|
object: nil
|
2003-04-17 06:20:17 +00:00
|
|
|
|
userInfo: nil];
|
|
|
|
|
sel = @selector(postNotification:);
|
|
|
|
|
inv = [NSInvocation invocationWithMethodSignature:
|
|
|
|
|
[ctr methodSignatureForSelector: sel]];
|
|
|
|
|
[inv setTarget: ctr];
|
|
|
|
|
[inv setSelector: sel];
|
|
|
|
|
[inv setArgument: ¬ atIndex: 2];
|
|
|
|
|
[inv retainArguments];
|
|
|
|
|
|
2003-07-25 09:27:44 +00:00
|
|
|
|
housekeeper = [[NSTimer alloc] initWithFireDate: nil
|
|
|
|
|
interval: 30.0
|
|
|
|
|
target: inv
|
|
|
|
|
selector: NULL
|
|
|
|
|
userInfo: nil
|
|
|
|
|
repeats: YES];
|
2006-04-09 16:16:07 +00:00
|
|
|
|
[r _setHousekeeper: housekeeper];
|
|
|
|
|
RELEASE(housekeeper);
|
2003-07-25 09:27:44 +00:00
|
|
|
|
RELEASE(arp);
|
2003-04-17 06:20:17 +00:00
|
|
|
|
}
|
2002-10-30 07:45:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Callback function so send notifications on becoming multi-threaded.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2003-12-23 17:41:38 +00:00
|
|
|
|
gnustep_base_thread_callback(void)
|
1998-05-29 15:25:41 +00:00
|
|
|
|
{
|
1999-06-22 15:06:21 +00:00
|
|
|
|
/*
|
2003-09-30 18:19:03 +00:00
|
|
|
|
* Protect this function with locking ... to avoid any possibility
|
|
|
|
|
* of multiple threads registering with the system simultaneously,
|
|
|
|
|
* and so that all NSWillBecomeMultiThreadedNotifications are sent
|
|
|
|
|
* out before any second thread can interfere with anything.
|
1999-06-22 15:06:21 +00:00
|
|
|
|
*/
|
2000-04-19 12:29:17 +00:00
|
|
|
|
if (entered_multi_threaded_state == NO)
|
1998-05-29 15:25:41 +00:00
|
|
|
|
{
|
2003-09-30 18:19:03 +00:00
|
|
|
|
[gnustep_global_lock lock];
|
|
|
|
|
if (entered_multi_threaded_state == NO)
|
2002-08-07 13:29:31 +00:00
|
|
|
|
{
|
2004-03-12 16:51:30 +00:00
|
|
|
|
/*
|
|
|
|
|
* For apple compatibility ... and to make things easier for
|
|
|
|
|
* code called indirectly within a will-become-multi-threaded
|
|
|
|
|
* notification handler, we set the flag to say we are multi
|
|
|
|
|
* threaded BEFORE sending the notifications.
|
|
|
|
|
*/
|
|
|
|
|
entered_multi_threaded_state = YES;
|
2003-09-30 18:19:03 +00:00
|
|
|
|
NS_DURING
|
|
|
|
|
{
|
|
|
|
|
[GSPerformHolder class]; // Force initialization
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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 (nc == nil)
|
|
|
|
|
{
|
2004-11-10 11:45:08 +00:00
|
|
|
|
nc = RETAIN([NSNotificationCenter defaultCenter]);
|
2003-09-30 18:19:03 +00:00
|
|
|
|
}
|
|
|
|
|
[nc postNotificationName: NSWillBecomeMultiThreadedNotification
|
|
|
|
|
object: nil
|
|
|
|
|
userInfo: nil];
|
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
2003-09-30 18:57:49 +00:00
|
|
|
|
fprintf(stderr,
|
2003-12-23 17:41:38 +00:00
|
|
|
|
"ALERT ... exception while becoming multi-threaded ... system may not be\n"
|
|
|
|
|
"properly initialised.\n");
|
2003-09-30 18:57:49 +00:00
|
|
|
|
fflush(stderr);
|
2003-09-30 18:19:03 +00:00
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
2002-08-07 13:29:31 +00:00
|
|
|
|
}
|
2003-09-30 18:19:03 +00:00
|
|
|
|
[gnustep_global_lock unlock];
|
1998-05-29 15:25:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1996-07-15 18:41:44 +00:00
|
|
|
|
|
2003-04-06 07:57:00 +00:00
|
|
|
|
/**
|
|
|
|
|
* This class encapsulates OpenStep threading. See [NSLock] and its
|
|
|
|
|
* subclasses for handling synchronisation between threads.<br />
|
|
|
|
|
* Each process begins with a main thread and additional threads can
|
|
|
|
|
* be created using NSThread. The GNUstep implementation of OpenStep
|
|
|
|
|
* has been carefully designed so that the internals of the base
|
|
|
|
|
* library do not use threading (except for methods which explicitly
|
|
|
|
|
* deal with threads of course) so that you can write applications
|
2004-06-22 22:40:40 +00:00
|
|
|
|
* without threading. Non-threaded applications are more efficient
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* (no locking is required) and are easier to debug during development.
|
|
|
|
|
*/
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
@implementation NSThread
|
1996-02-13 15:40:05 +00:00
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>
|
|
|
|
|
* Returns the NSThread object corresponding to the current thread.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* NB. In GNUstep the library internals use the GSCurrentThread()
|
|
|
|
|
* function as a more efficient mechanism for doing this job - so
|
|
|
|
|
* you cannot use a category to override this method and expect
|
|
|
|
|
* the library internals to use your implementation.
|
|
|
|
|
* </p>
|
2000-04-19 12:29:17 +00:00
|
|
|
|
*/
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
+ (NSThread*) currentThread
|
|
|
|
|
{
|
2003-07-25 09:27:44 +00:00
|
|
|
|
NSThread *t = nil;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2000-08-07 22:00:31 +00:00
|
|
|
|
if (entered_multi_threaded_state == NO)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* The NSThread class has been initialized - so we will have a default
|
2003-07-25 09:27:44 +00:00
|
|
|
|
* thread set up unless the default thread subsequently exited.
|
2000-08-07 22:00:31 +00:00
|
|
|
|
*/
|
2002-05-03 08:17:04 +00:00
|
|
|
|
t = defaultThread;
|
2000-08-07 22:00:31 +00:00
|
|
|
|
}
|
2003-07-25 09:27:44 +00:00
|
|
|
|
if (t == nil)
|
2000-08-07 22:00:31 +00:00
|
|
|
|
{
|
2002-05-03 08:17:04 +00:00
|
|
|
|
t = (NSThread*)objc_thread_get_data();
|
|
|
|
|
if (t == nil)
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "ALERT ... [NSThread +currentThread] ... the "
|
|
|
|
|
"objc_thread_get_data() call returned nil!");
|
|
|
|
|
fflush(stderr); // Needed for windoze
|
|
|
|
|
}
|
2000-08-07 22:00:31 +00:00
|
|
|
|
}
|
2002-05-03 08:17:04 +00:00
|
|
|
|
return t;
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
}
|
1996-03-19 01:45:25 +00:00
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
2004-06-22 22:40:40 +00:00
|
|
|
|
* <p>Create a new thread - use this method rather than alloc-init. The new
|
|
|
|
|
* thread will begin executing the message given by aSelector, aTarget, and
|
|
|
|
|
* anArgument. This should have no return value, and must set up an
|
|
|
|
|
* autorelease pool if retain/release memory management is used. It should
|
|
|
|
|
* free this pool before it finishes execution.</p>
|
2005-11-01 15:14:59 +00:00
|
|
|
|
*/
|
1999-05-10 06:45:36 +00:00
|
|
|
|
+ (void) detachNewThreadSelector: (SEL)aSelector
|
|
|
|
|
toTarget: (id)aTarget
|
|
|
|
|
withObject: (id)anArgument
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
{
|
2000-04-19 12:29:17 +00:00
|
|
|
|
NSThread *thread;
|
|
|
|
|
|
1999-06-22 15:06:21 +00:00
|
|
|
|
/*
|
|
|
|
|
* Make sure the notification is posted BEFORE the new thread starts.
|
|
|
|
|
*/
|
|
|
|
|
gnustep_base_thread_callback();
|
2000-04-19 12:29:17 +00:00
|
|
|
|
|
1999-06-22 15:06:21 +00:00
|
|
|
|
/*
|
2000-04-19 12:29:17 +00:00
|
|
|
|
* Create the new thread.
|
1999-06-22 15:06:21 +00:00
|
|
|
|
*/
|
2000-04-19 12:29:17 +00:00
|
|
|
|
thread = (NSThread*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
|
|
|
|
thread = [thread _initWithSelector: aSelector
|
|
|
|
|
toTarget: aTarget
|
|
|
|
|
withObject: anArgument];
|
1999-07-14 16:28:43 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Have the runtime detach the thread
|
|
|
|
|
*/
|
|
|
|
|
if (objc_thread_detach(@selector(_sendThreadMethod), thread, nil) == NULL)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-09-30 17:19:35 +00:00
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"Unable to detach thread (unknown error)"];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-11-03 13:20:39 +00:00
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
2005-11-01 15:14:59 +00:00
|
|
|
|
* Terminates the current thread.<br />
|
|
|
|
|
* Normally you don't need to call this method explicitly,
|
|
|
|
|
* since exiting the method with which the thread was detached
|
|
|
|
|
* causes this method to be called automatically.
|
2000-04-19 12:29:17 +00:00
|
|
|
|
*/
|
|
|
|
|
+ (void) exit
|
|
|
|
|
{
|
|
|
|
|
NSThread *t;
|
|
|
|
|
|
|
|
|
|
t = GSCurrentThread();
|
|
|
|
|
if (t->_active == YES)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Set the thread to be inactive to avoid any possibility of recursion.
|
|
|
|
|
*/
|
|
|
|
|
t->_active = NO;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Let observers know this thread is exiting.
|
|
|
|
|
*/
|
2002-08-07 13:29:31 +00:00
|
|
|
|
if (nc == nil)
|
|
|
|
|
{
|
2004-11-10 11:45:08 +00:00
|
|
|
|
nc = RETAIN([NSNotificationCenter defaultCenter]);
|
2002-08-07 13:29:31 +00:00
|
|
|
|
}
|
|
|
|
|
[nc postNotificationName: NSThreadWillExitNotification
|
|
|
|
|
object: t
|
|
|
|
|
userInfo: nil];
|
2000-04-19 12:29:17 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* destroy the thread object.
|
|
|
|
|
*/
|
|
|
|
|
DESTROY(t);
|
|
|
|
|
|
2000-11-22 08:41:07 +00:00
|
|
|
|
objc_thread_set_data (NULL);
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Tell the runtime to exit the thread
|
|
|
|
|
*/
|
|
|
|
|
objc_thread_exit();
|
|
|
|
|
}
|
1996-02-13 15:40:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
/*
|
|
|
|
|
* Class initialization
|
|
|
|
|
*/
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (self == [NSThread class])
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* if we have become multi-threaded due to a call to the runtime directly
|
|
|
|
|
* 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);
|
2001-03-19 12:20:21 +00:00
|
|
|
|
threadClass = self;
|
2000-04-19 12:29:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
2004-09-27 10:24:02 +00:00
|
|
|
|
* Returns a flag to say whether the application is multi-threaded or not.<br />
|
2002-08-27 17:02:05 +00:00
|
|
|
|
* An application is considered to be multi-threaded if any thread other
|
|
|
|
|
* than the main thread has been started, irrespective of whether that
|
2004-09-27 10:24:02 +00:00
|
|
|
|
* thread has since terminated.<br />
|
|
|
|
|
* NB. This method returns YES if called within a handler processing
|
|
|
|
|
* <code>NSWillBecomeMultiThreadedNotification</code>
|
2002-08-27 17:02:05 +00:00
|
|
|
|
*/
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
+ (BOOL) isMultiThreaded
|
1996-02-13 15:40:05 +00:00
|
|
|
|
{
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
return entered_multi_threaded_state;
|
1996-02-13 15:40:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* Set the priority of the current thread. This is a value in the
|
|
|
|
|
* range 0.0 (lowest) to 1.0 (highest) which is mapped to the underlying
|
|
|
|
|
* system priorities. The current gnu objc runtime supports three
|
|
|
|
|
* priority levels which you can obtain using values of 0.0, 0.5, and 1.0
|
|
|
|
|
*/
|
|
|
|
|
+ (void) setThreadPriority: (double)pri
|
|
|
|
|
{
|
|
|
|
|
int p;
|
|
|
|
|
|
|
|
|
|
if (pri <= 0.3)
|
|
|
|
|
p = OBJC_THREAD_LOW_PRIORITY;
|
|
|
|
|
else if (pri <= 0.6)
|
|
|
|
|
p = OBJC_THREAD_BACKGROUND_PRIORITY;
|
|
|
|
|
else
|
|
|
|
|
p = OBJC_THREAD_INTERACTIVE_PRIORITY;
|
|
|
|
|
|
|
|
|
|
objc_thread_set_priority(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Delaying a thread ... pause until the specified date.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
+ (void) sleepUntilDate: (NSDate*)date
|
1996-02-13 15:40:05 +00:00
|
|
|
|
{
|
2003-07-20 06:37:25 +00:00
|
|
|
|
GSSleepUntilIntervalSinceReferenceDate([date timeIntervalSinceReferenceDate]);
|
1996-02-13 15:40:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-07-20 06:37:25 +00:00
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return the priority of the current thread.
|
|
|
|
|
*/
|
|
|
|
|
+ (double) threadPriority
|
|
|
|
|
{
|
|
|
|
|
int p = objc_thread_get_priority();
|
|
|
|
|
|
|
|
|
|
if (p == OBJC_THREAD_LOW_PRIORITY)
|
|
|
|
|
return 0.0;
|
|
|
|
|
else if (p == OBJC_THREAD_BACKGROUND_PRIORITY)
|
|
|
|
|
return 0.5;
|
|
|
|
|
else if (p == OBJC_THREAD_INTERACTIVE_PRIORITY)
|
|
|
|
|
return 1.0;
|
|
|
|
|
else
|
|
|
|
|
return 0.0; // Unknown.
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Thread instance methods.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
1996-02-13 15:40:05 +00:00
|
|
|
|
{
|
2000-04-19 12:29:17 +00:00
|
|
|
|
if (_active == YES)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"Deallocating an active thread without [+exit]!"];
|
|
|
|
|
}
|
|
|
|
|
DESTROY(_thread_dictionary);
|
|
|
|
|
DESTROY(_target);
|
|
|
|
|
DESTROY(_arg);
|
2006-01-26 02:49:59 +00:00
|
|
|
|
if (_autorelease_vars.pool_cache != 0)
|
|
|
|
|
{
|
|
|
|
|
[NSAutoreleasePool _endThread: self];
|
|
|
|
|
}
|
2000-11-22 08:41:07 +00:00
|
|
|
|
|
2001-03-17 10:18:09 +00:00
|
|
|
|
if (_thread_dictionary != nil)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Try again to get rid of thread dictionary.
|
|
|
|
|
*/
|
|
|
|
|
DESTROY(_thread_dictionary);
|
2006-01-26 02:49:59 +00:00
|
|
|
|
if (_autorelease_vars.pool_cache != 0)
|
|
|
|
|
{
|
|
|
|
|
[NSAutoreleasePool _endThread: self];
|
|
|
|
|
}
|
2001-03-17 10:18:09 +00:00
|
|
|
|
if (_thread_dictionary != nil)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Oops - leak - thread dictionary is %@", _thread_dictionary);
|
2006-01-26 02:49:59 +00:00
|
|
|
|
if (_autorelease_vars.pool_cache != 0)
|
|
|
|
|
{
|
|
|
|
|
[NSAutoreleasePool _endThread: self];
|
|
|
|
|
}
|
2001-03-17 10:18:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-07-25 09:27:44 +00:00
|
|
|
|
if (self == defaultThread)
|
|
|
|
|
{
|
|
|
|
|
defaultThread = nil;
|
|
|
|
|
}
|
2000-04-19 12:29:17 +00:00
|
|
|
|
NSDeallocateObject(self);
|
|
|
|
|
}
|
1996-03-19 01:45:25 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
- (id) init
|
|
|
|
|
{
|
|
|
|
|
RELEASE(self);
|
|
|
|
|
return [NSThread currentThread];
|
|
|
|
|
}
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
- (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;
|
|
|
|
|
}
|
(NSBecomingMultiThreaded, NSThreadExiting): Initialize the
notification strings as static strings, not in +initialize.
(thread_id_2_nsthread): Renamed from THREAD_LIST. Keep the collection
of NSThread's as a maptable, not a NSArray that takes linear time to
search!
(thread_lock): Renamed from THREAD_LIST_LOCK.
(entered_multi_threaded_state): Renamed from ENTERED_MULTI-THREADED_STATE.
([NSThread +initialize]): Don't initialize notification strings here.
Don't autorelease the lock!
([NSThread -init]): Initialize _thread_autorelease_pool. Set our
thread data to self, for easy, efficient access to this NSThread
object later. Put ourselves in the thread collection here, not in
+detach...
([NSThread +currentThread]): This will be called often and needs to be
fast. Reimplemented so we don't have to acquire a lock and step
through an NSArray of threads; instead, just look ourselves up with
the objc_thread_get_data(), and furthermore, no lock required.
([NSThread +detachNewThreadSelector:toTarget:withObject:]): Avoid race
condition, don't create new NSThread object here.
([NSThread +sleepUntilDate:]): Call -notImplemented:.
([NSThread +exit]): Properly post NSThreadExiting notification, making
sure not to hold the lock while we do so. Get the NSThread object
efficiently.
([NSThread -threadId]): Removed unnecessary private method.
([NSThread -setThreadId]): Likewise.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1530 72102866-910b-0410-8b05-ffd578937521
1996-05-13 15:53:36 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
- (void) _sendThreadMethod
|
|
|
|
|
{
|
1999-04-21 20:16:25 +00:00
|
|
|
|
/*
|
2000-04-19 12:29:17 +00:00
|
|
|
|
* 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.
|
1999-04-21 20:16:25 +00:00
|
|
|
|
*/
|
2000-04-19 12:29:17 +00:00
|
|
|
|
objc_thread_set_data(self);
|
|
|
|
|
_active = YES;
|
2000-11-12 07:41:24 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Let observers know a new thread is starting.
|
|
|
|
|
*/
|
2002-08-07 13:29:31 +00:00
|
|
|
|
if (nc == nil)
|
|
|
|
|
{
|
2004-11-10 11:45:08 +00:00
|
|
|
|
nc = RETAIN([NSNotificationCenter defaultCenter]);
|
2002-08-07 13:29:31 +00:00
|
|
|
|
}
|
|
|
|
|
[nc postNotificationName: NSThreadDidStartNotification
|
|
|
|
|
object: self
|
|
|
|
|
userInfo: nil];
|
2000-11-12 07:41:24 +00:00
|
|
|
|
|
2000-04-19 12:29:17 +00:00
|
|
|
|
[_target performSelector: _selector withObject: _arg];
|
|
|
|
|
[NSThread exit];
|
|
|
|
|
}
|
1996-03-19 01:45:25 +00:00
|
|
|
|
|
2002-08-27 17:02:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* Return the thread dictionary. This dictionary can be used to store
|
|
|
|
|
* arbitrary thread specific data.<br />
|
2000-04-19 12:29:17 +00:00
|
|
|
|
* 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
|
|
|
|
|
{
|
2001-03-17 10:18:09 +00:00
|
|
|
|
if (_thread_dictionary == nil)
|
2000-04-19 12:29:17 +00:00
|
|
|
|
{
|
|
|
|
|
_thread_dictionary = [NSMutableDictionary new];
|
|
|
|
|
}
|
|
|
|
|
return _thread_dictionary;
|
1996-02-13 15:40:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-19 01:45:25 +00:00
|
|
|
|
@end
|
2000-04-19 12:29:17 +00:00
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation GSPerformHolder
|
|
|
|
|
|
2002-11-02 16:53:48 +00:00
|
|
|
|
static NSLock *subthreadsLock = nil;
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#ifdef __MINGW32__
|
2005-02-23 16:05:09 +00:00
|
|
|
|
static HANDLE event;
|
|
|
|
|
#else
|
2002-11-02 16:53:48 +00:00
|
|
|
|
static int inputFd = -1;
|
|
|
|
|
static int outputFd = -1;
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#endif
|
2002-11-02 16:53:48 +00:00
|
|
|
|
static NSMutableArray *perfArray = nil;
|
|
|
|
|
static NSDate *theFuture;
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
+ (void) initialize
|
2002-11-02 16:53:48 +00:00
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
NSRunLoop *loop = GSRunLoopForThread(defaultThread);
|
|
|
|
|
NSArray *m = commonModes();
|
|
|
|
|
unsigned count = [m count];
|
|
|
|
|
unsigned i;
|
2002-11-02 16:53:48 +00:00
|
|
|
|
|
|
|
|
|
theFuture = RETAIN([NSDate distantFuture]);
|
2005-02-23 16:05:09 +00:00
|
|
|
|
subthreadsLock = [[NSLock alloc] init];
|
|
|
|
|
perfArray = [[NSMutableArray alloc] initWithCapacity: 10];
|
2002-11-02 16:53:48 +00:00
|
|
|
|
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#ifndef __MINGW32__
|
2002-11-13 15:12:39 +00:00
|
|
|
|
{
|
|
|
|
|
int fd[2];
|
|
|
|
|
|
|
|
|
|
if (pipe(fd) == 0)
|
|
|
|
|
{
|
|
|
|
|
inputFd = fd[0];
|
|
|
|
|
outputFd = fd[1];
|
2005-02-23 16:05:09 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"Failed to create pipe to handle perform in main thread"];
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
2006-01-26 02:49:59 +00:00
|
|
|
|
[loop addEvent: (void*)(intptr_t)inputFd
|
2005-02-23 16:05:09 +00:00
|
|
|
|
type: ET_RDESC
|
|
|
|
|
watcher: (id<RunLoopEvents>)self
|
|
|
|
|
forMode: [m objectAtIndex: i]];
|
2002-11-13 15:12:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
{
|
2005-02-23 16:05:09 +00:00
|
|
|
|
if ((event = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
|
format: @"Failed to create event to handle perform in main thread"];
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < count; i++)
|
2002-11-13 15:12:39 +00:00
|
|
|
|
{
|
2005-02-23 16:05:09 +00:00
|
|
|
|
[loop addEvent: (void*)event
|
|
|
|
|
type: ET_HANDLE
|
|
|
|
|
watcher: (id<RunLoopEvents>)self
|
|
|
|
|
forMode: [m objectAtIndex: i]];
|
2002-11-13 15:12:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#endif
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
+ (BOOL) isValid
|
2002-11-02 16:53:48 +00:00
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
return YES;
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
2002-11-03 18:56:46 +00:00
|
|
|
|
|
|
|
|
|
+ (GSPerformHolder*) newForReceiver: (id)r
|
|
|
|
|
argument: (id)a
|
|
|
|
|
selector: (SEL)s
|
|
|
|
|
modes: (NSArray*)m
|
|
|
|
|
lock: (NSConditionLock*)l
|
2002-11-02 16:53:48 +00:00
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
GSPerformHolder *h;
|
|
|
|
|
|
|
|
|
|
h = (GSPerformHolder*)NSAllocateObject(self, 0, NSDefaultMallocZone());
|
|
|
|
|
h->receiver = RETAIN(r);
|
|
|
|
|
h->argument = RETAIN(a);
|
|
|
|
|
h->selector = s;
|
|
|
|
|
h->modes = RETAIN(m);
|
|
|
|
|
h->lock = l;
|
|
|
|
|
|
|
|
|
|
[subthreadsLock lock];
|
2005-02-23 16:05:09 +00:00
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
[perfArray addObject: h];
|
2005-02-23 16:05:09 +00:00
|
|
|
|
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2005-02-23 16:05:09 +00:00
|
|
|
|
if (SetEvent(event) == 0)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Set event failed - %@", GSLastErrorStr(errno));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
if (write(outputFd, "0", 1) != 1)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Write to pipe failed - %@", GSLastErrorStr(errno));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
[subthreadsLock unlock];
|
2002-11-02 16:53:48 +00:00
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
return h;
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
+ (void) receivedEvent: (void*)data
|
2002-11-02 16:53:48 +00:00
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
extra: (void*)extra
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
2005-02-23 16:05:09 +00:00
|
|
|
|
NSArray *toDo;
|
2003-04-06 07:14:50 +00:00
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int c;
|
2002-11-03 18:56:46 +00:00
|
|
|
|
|
2002-11-02 16:53:48 +00:00
|
|
|
|
[subthreadsLock lock];
|
|
|
|
|
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2005-02-23 16:05:09 +00:00
|
|
|
|
if (ResetEvent(event) == 0)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Reset event failed - %@", GSLastErrorStr(errno));
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
if (read(inputFd, &c, 1) != 1)
|
|
|
|
|
{
|
|
|
|
|
NSLog(@"Read pipe failed - %@", GSLastErrorStr(errno));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
toDo = [[NSArray alloc] initWithArray: perfArray];
|
|
|
|
|
[perfArray removeAllObjects];
|
|
|
|
|
[subthreadsLock unlock];
|
|
|
|
|
|
|
|
|
|
c = [toDo count];
|
2003-04-06 07:14:50 +00:00
|
|
|
|
for (i = 0; i < c; i++)
|
2002-11-02 16:53:48 +00:00
|
|
|
|
{
|
2005-02-23 16:05:09 +00:00
|
|
|
|
GSPerformHolder *h = [toDo objectAtIndex: i];
|
2002-11-03 18:56:46 +00:00
|
|
|
|
|
|
|
|
|
[loop performSelector: @selector(fire)
|
|
|
|
|
target: h
|
|
|
|
|
argument: nil
|
|
|
|
|
order: 0
|
|
|
|
|
modes: h->modes];
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
|
RELEASE(toDo);
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
|
|
|
|
DESTROY(receiver);
|
|
|
|
|
DESTROY(argument);
|
|
|
|
|
DESTROY(modes);
|
|
|
|
|
if (lock != nil)
|
|
|
|
|
{
|
|
|
|
|
[lock lock];
|
|
|
|
|
[lock unlockWithCondition: 1];
|
|
|
|
|
lock = nil;
|
|
|
|
|
}
|
|
|
|
|
NSDeallocateObject(self);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) fire
|
|
|
|
|
{
|
|
|
|
|
if (receiver == nil)
|
|
|
|
|
{
|
|
|
|
|
return; // Already fired!
|
|
|
|
|
}
|
|
|
|
|
[GSRunLoopForThread(defaultThread) cancelPerformSelectorsWithTarget: self];
|
|
|
|
|
[receiver performSelector: selector withObject: argument];
|
|
|
|
|
DESTROY(receiver);
|
|
|
|
|
DESTROY(argument);
|
|
|
|
|
DESTROY(modes);
|
|
|
|
|
if (lock == nil)
|
|
|
|
|
{
|
|
|
|
|
RELEASE(self);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-12-08 20:18:34 +00:00
|
|
|
|
NSConditionLock *l = lock;
|
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
[lock lock];
|
|
|
|
|
lock = nil;
|
2002-12-08 20:18:34 +00:00
|
|
|
|
[l unlockWithCondition: 1];
|
2002-11-03 18:56:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@end
|
2002-11-02 16:53:48 +00:00
|
|
|
|
|
2003-04-06 07:57:00 +00:00
|
|
|
|
/**
|
|
|
|
|
* Extra methods to permit messages to be sent to an object such that they
|
|
|
|
|
* are executed in the <em>main</em> thread.<br />
|
|
|
|
|
* The main thread is the thread in which the GNUstep system is started,
|
|
|
|
|
* and where the GNUstep gui is used, it is the thread in which gui
|
|
|
|
|
* drawing operations <strong>must</strong> be performed.
|
|
|
|
|
*/
|
2002-11-02 16:53:48 +00:00
|
|
|
|
@implementation NSObject (NSMainThreadPerformAdditions)
|
2002-10-30 07:45:59 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* <p>This method performs aSelector on the receiver, passing anObject as
|
|
|
|
|
* an argument, but does so in the main thread of the program. The receiver
|
|
|
|
|
* and anObject are both retained until the method is performed.
|
|
|
|
|
* </p>
|
2002-11-03 19:09:39 +00:00
|
|
|
|
* <p>The selector is performed when the runloop of the main thread next
|
2003-04-06 07:21:59 +00:00
|
|
|
|
* runs in one of the modes specified in anArray.<br />
|
|
|
|
|
* Where this method has been called more than once before the runloop
|
|
|
|
|
* of the main thread runs in the required mode, the order in which the
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* operations in the main thread is done is the same as that in which
|
|
|
|
|
* they were added using this method.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>If there are no modes in anArray,
|
2003-04-06 07:21:59 +00:00
|
|
|
|
* the method has no effect and simply returns immediately.
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* </p>
|
|
|
|
|
* <p>The argument aFlag specifies whether the method should wait until
|
|
|
|
|
* the selector has been performed before returning.<br />
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* <strong>NB.</strong> This method does <em>not</em> cause the runloop of
|
|
|
|
|
* the main thread to be run ... so if the runloop is not executed by some
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* code in the main thread, the thread waiting for the perform to complete
|
|
|
|
|
* will block forever.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>As a special case, if aFlag == YES and the current thread is the main
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* thread, the modes array is ignored and the selector is performed immediately.
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* This behavior is necessary to avoid the main thread being blocked by
|
2003-04-06 07:57:00 +00:00
|
|
|
|
* waiting for a perform which will never happen because the runloop is
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* not executing.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
|
|
|
|
- (void) performSelectorOnMainThread: (SEL)aSelector
|
|
|
|
|
withObject: (id)anObject
|
|
|
|
|
waitUntilDone: (BOOL)aFlag
|
2002-11-03 18:56:46 +00:00
|
|
|
|
modes: (NSArray*)anArray
|
2002-10-30 07:45:59 +00:00
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
NSThread *t;
|
2002-10-30 07:45:59 +00:00
|
|
|
|
|
2002-11-03 18:56:46 +00:00
|
|
|
|
if ([anArray count] == 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t = GSCurrentThread();
|
2002-11-02 16:53:48 +00:00
|
|
|
|
if (t == defaultThread)
|
2002-10-30 07:45:59 +00:00
|
|
|
|
{
|
2002-11-02 16:53:48 +00:00
|
|
|
|
if (aFlag == YES)
|
|
|
|
|
{
|
|
|
|
|
[self performSelector: aSelector withObject: anObject];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
[GSRunLoopForThread(t) performSelector: aSelector
|
|
|
|
|
target: self
|
|
|
|
|
argument: anObject
|
|
|
|
|
order: 0
|
|
|
|
|
modes: anArray];
|
2002-11-02 16:53:48 +00:00
|
|
|
|
}
|
2002-10-30 07:45:59 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSPerformHolder *h;
|
2002-11-03 18:56:46 +00:00
|
|
|
|
NSConditionLock *l = nil;
|
2002-10-30 07:45:59 +00:00
|
|
|
|
|
|
|
|
|
if (aFlag == YES)
|
|
|
|
|
{
|
2002-11-02 16:53:48 +00:00
|
|
|
|
l = [[NSConditionLock alloc] init];
|
2002-10-30 07:45:59 +00:00
|
|
|
|
}
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
h = [GSPerformHolder newForReceiver: self
|
2002-11-03 18:56:46 +00:00
|
|
|
|
argument: anObject
|
|
|
|
|
selector: aSelector
|
|
|
|
|
modes: anArray
|
|
|
|
|
lock: l];
|
2002-10-30 07:45:59 +00:00
|
|
|
|
|
|
|
|
|
if (aFlag == YES)
|
|
|
|
|
{
|
2002-11-03 18:56:46 +00:00
|
|
|
|
[l lockWhenCondition: 1];
|
2002-10-30 12:37:21 +00:00
|
|
|
|
RELEASE(h);
|
|
|
|
|
[l unlock];
|
|
|
|
|
RELEASE(l);
|
2002-10-30 07:45:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Invokes -performSelectorOnMainThread:withObject:waitUntilDone:modes:
|
2002-11-03 19:09:39 +00:00
|
|
|
|
* using the supplied arguments and an array containing common modes.<br />
|
|
|
|
|
* These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
|
|
|
|
|
* in an application, the NSApplication modes.
|
2002-10-30 07:45:59 +00:00
|
|
|
|
*/
|
|
|
|
|
- (void) performSelectorOnMainThread: (SEL)aSelector
|
|
|
|
|
withObject: (id)anObject
|
|
|
|
|
waitUntilDone: (BOOL)aFlag
|
|
|
|
|
{
|
|
|
|
|
[self performSelectorOnMainThread: aSelector
|
|
|
|
|
withObject: anObject
|
|
|
|
|
waitUntilDone: aFlag
|
2002-11-03 19:09:39 +00:00
|
|
|
|
modes: commonModes()];
|
2002-10-30 07:45:59 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
2002-11-03 19:09:39 +00:00
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>
|
|
|
|
|
* This function is provided to let threads started by some other
|
|
|
|
|
* software library register themselves to be used with the
|
|
|
|
|
* GNUstep system. All such threads should call this function
|
|
|
|
|
* before attempting to use any GNUstep objects.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* Returns <code>YES</code> if the thread can be registered,
|
|
|
|
|
* <code>NO</code> if it is already registered.
|
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* Sends out a <code>NSWillBecomeMultiThreadedNotification</code>
|
|
|
|
|
* if the process was not already multithreaded.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
2001-03-19 12:20:21 +00:00
|
|
|
|
BOOL
|
|
|
|
|
GSRegisterCurrentThread (void)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
{
|
|
|
|
|
NSThread *thread;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Do nothing and return NO if the thread is known to us.
|
|
|
|
|
*/
|
|
|
|
|
if ((NSThread*)objc_thread_get_data() != nil)
|
|
|
|
|
{
|
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-19 12:20:21 +00:00
|
|
|
|
/*
|
|
|
|
|
* Make sure the Objective-C runtime knows there is an additional thread.
|
|
|
|
|
*/
|
|
|
|
|
objc_thread_add ();
|
|
|
|
|
|
2002-05-03 10:00:53 +00:00
|
|
|
|
if (threadClass == 0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If the threadClass has not been set, NSThread has not been
|
|
|
|
|
* initialised, and there is no default thread. So we must
|
|
|
|
|
* initialise now ... which will make the current thread the default.
|
|
|
|
|
*/
|
|
|
|
|
NSCAssert(entered_multi_threaded_state == NO,
|
|
|
|
|
NSInternalInconsistencyException);
|
|
|
|
|
thread = [NSThread currentThread];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Create the new thread object.
|
|
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
|
thread = (NSThread*)NSAllocateObject (threadClass, 0,
|
2001-03-19 12:20:21 +00:00
|
|
|
|
NSDefaultMallocZone ());
|
2002-05-03 10:00:53 +00:00
|
|
|
|
thread = [thread _initWithSelector: NULL toTarget: nil withObject: nil];
|
|
|
|
|
objc_thread_set_data (thread);
|
|
|
|
|
((NSThread_ivars *)thread)->_active = YES;
|
|
|
|
|
}
|
2000-11-12 07:41:24 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2005-02-22 11:22:44 +00:00
|
|
|
|
* We post the notification after we register the thread.
|
2002-05-03 10:00:53 +00:00
|
|
|
|
* NB. Even if we are the default thread, we do this to register the app
|
|
|
|
|
* as being multi-threaded - this is so that, if this thread is unregistered
|
|
|
|
|
* later, it does not leave us with a bad default thread.
|
2000-11-12 07:41:24 +00:00
|
|
|
|
*/
|
|
|
|
|
gnustep_base_thread_callback();
|
|
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-20 10:22:05 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p>
|
|
|
|
|
* This function is provided to let threads started by some other
|
|
|
|
|
* software library unregister themselves from the GNUstep threading
|
2005-02-22 11:22:44 +00:00
|
|
|
|
* system.
|
2002-08-20 10:22:05 +00:00
|
|
|
|
* </p>
|
|
|
|
|
* <p>
|
|
|
|
|
* Calling this function causes a
|
|
|
|
|
* <code>NSThreadWillExitNotification</code>
|
|
|
|
|
* to be sent out, and destroys the GNUstep NSThread object
|
|
|
|
|
* associated with the thread.
|
|
|
|
|
* </p>
|
|
|
|
|
*/
|
2001-03-19 12:20:21 +00:00
|
|
|
|
void
|
|
|
|
|
GSUnregisterCurrentThread (void)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
{
|
|
|
|
|
NSThread *thread;
|
|
|
|
|
|
|
|
|
|
thread = GSCurrentThread();
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2001-12-19 03:24:13 +00:00
|
|
|
|
if (((NSThread_ivars *)thread)->_active == YES)
|
2000-11-12 07:41:24 +00:00
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Set the thread to be inactive to avoid any possibility of recursion.
|
|
|
|
|
*/
|
2001-12-19 03:24:13 +00:00
|
|
|
|
((NSThread_ivars *)thread)->_active = NO;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2001-03-19 13:15:10 +00:00
|
|
|
|
/*
|
|
|
|
|
* Let observers know this thread is exiting.
|
|
|
|
|
*/
|
2002-08-07 13:29:31 +00:00
|
|
|
|
if (nc == nil)
|
|
|
|
|
{
|
2004-11-10 11:45:08 +00:00
|
|
|
|
nc = RETAIN([NSNotificationCenter defaultCenter]);
|
2002-08-07 13:29:31 +00:00
|
|
|
|
}
|
|
|
|
|
[nc postNotificationName: NSThreadWillExitNotification
|
|
|
|
|
object: thread
|
2005-02-22 11:22:44 +00:00
|
|
|
|
userInfo: nil];
|
2000-11-22 08:41:07 +00:00
|
|
|
|
|
2000-11-12 07:41:24 +00:00
|
|
|
|
/*
|
|
|
|
|
* destroy the thread object.
|
|
|
|
|
*/
|
|
|
|
|
DESTROY (thread);
|
2000-11-22 08:41:07 +00:00
|
|
|
|
|
2000-11-12 07:41:24 +00:00
|
|
|
|
objc_thread_set_data (NULL);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure Objc runtime knows there is a thread less to manage
|
|
|
|
|
*/
|
|
|
|
|
objc_thread_remove ();
|
|
|
|
|
}
|
|
|
|
|
}
|