1/** Control of executable units within a shared virtual memory space
2   Copyright (C) 1996-2018 Free Software Foundation, Inc.
3
4   Original Author:  Scott Christley <scottc@net-community.com>
5   Rewritten by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
6   Created: 1996
7   Rewritten by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
8   to add optimisations features for faster thread access.
9   Modified by: Nicola Pero <n.pero@mi.flashnet.it>
10   to add GNUstep extensions allowing to interact with threads created
11   by external libraries/code (eg, a Java Virtual Machine).
12
13   This file is part of the GNUstep Objective-C Library.
14
15   This library is free software; you can redistribute it and/or
16   modify it under the terms of the GNU Lesser General Public
17   License as published by the Free Software Foundation; either
18   version 2 of the License, or (at your option) any later version.
19
20   This library is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23   Lesser General Public License for more details.
24
25   You should have received a copy of the GNU Lesser General Public
26   License along with this library; if not, write to the Free
27   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28   Boston, MA 02110 USA.
29
30   <title>NSThread class reference</title>
31   $Date$ $Revision$
32*/
33
34#import "common.h"
35
36#import "GSPThread.h"
37
38// Dummy implementatation
39// cleaner than IFDEF'ing the code everywhere
40#if !(HAVE_PTHREAD_SPIN_LOCK)
41typedef volatile int pthread_spinlock_t;
42int pthread_spin_init(pthread_spinlock_t *lock, int pshared)
43{
44#if DEBUG && !__has_builtin(__sync_bool_compare_and_swap)
45  fprintf(stderr,"NSThread.m: Warning this platform does not support spin locks - init.\n");
46#endif
47  return 0;
48}
49int pthread_spin_lock(pthread_spinlock_t *lock)
50{
51#if __has_builtin(__sync_bool_compare_and_swap)
52  int count = 0;
53  // Set the spin lock value to 1 if it is 0.
54  while(!__sync_bool_compare_and_swap(lock, 0, 1))
55  {
56    count++;
57    if (0 == count % 10)
58    {
59      // If it is already 1, let another thread play with the CPU for a
60      // bit then try again.
61      sleep(0);
62    }
63  }
64#else
65  #warning no spin_locks, using dummy versions
66#endif
67  return 0;
68}
69int pthread_spin_unlock(pthread_spinlock_t *lock)
70{
71#if __has_builtin(__sync_bool_compare_and_swap)
72  __sync_synchronize();
73  *lock = 0;
74#endif
75  return 0;
76}
77int pthread_spin_destroy(pthread_spinlock_t *lock)
78{
79  return 0;
80}
81#endif
82
83/** Structure for holding lock information for a thread.
84 */
85typedef struct {
86  pthread_spinlock_t    spin;   /* protect access to struct members */
87  NSHashTable           *held;  /* all locks/conditions held by thread */
88  id                    wait;   /* the lock/condition we are waiting for */
89} GSLockInfo;
90
91#define	EXPOSE_NSThread_IVARS	1
92#define	GS_NSThread_IVARS \
93  pthread_t             _pthreadID; \
94  NSUInteger            _threadID; \
95  GSLockInfo            _lockInfo
96
97
98#ifdef HAVE_NANOSLEEP
99#  include <time.h>
100#endif
101#ifdef HAVE_SYS_TIME_H
102#  include <sys/time.h>
103#endif
104#ifdef HAVE_SYS_RESOURCE_H
105#  include <sys/resource.h>
106#endif
107
108#if	defined(HAVE_SYS_FILE_H)
109#  include <sys/file.h>
110#endif
111
112#if	defined(HAVE_SYS_FCNTL_H)
113#  include <sys/fcntl.h>
114#elif	defined(HAVE_FCNTL_H)
115#  include <fcntl.h>
116#endif
117
118#if defined(__POSIX_SOURCE)\
119        || defined(__EXT_POSIX1_198808)\
120        || defined(O_NONBLOCK)
121#define NBLK_OPT     O_NONBLOCK
122#else
123#define NBLK_OPT     FNDELAY
124#endif
125
126#import "Foundation/NSException.h"
127#import "Foundation/NSHashTable.h"
128#import "Foundation/NSThread.h"
129#import "Foundation/NSLock.h"
130#import "Foundation/NSMapTable.h"
131#import "Foundation/NSNotification.h"
132#import "Foundation/NSNotificationQueue.h"
133#import "Foundation/NSRunLoop.h"
134#import "Foundation/NSConnection.h"
135#import "Foundation/NSInvocation.h"
136#import "Foundation/NSUserDefaults.h"
137#import "Foundation/NSValue.h"
138
139#import "GSPrivate.h"
140#import "GSRunLoopCtxt.h"
141
142#if defined(HAVE_PTHREAD_NP_H)
143#  include <pthread_np.h>
144#endif
145
146#if defined(HAVE_GETTID)
147#  include <unistd.h>
148#  include <sys/syscall.h>
149#  include <sys/types.h>
150#endif
151
152#define GSInternal      NSThreadInternal
153#include        "GSInternal.h"
154GS_PRIVATE_INTERNAL(NSThread)
155
156#define pthreadID (internal->_pthreadID)
157#define threadID (internal->_threadID)
158#define lockInfo (internal->_lockInfo)
159
160
161#if defined(HAVE_PTHREAD_MAIN_NP)
162#  define IS_MAIN_PTHREAD (pthread_main_np() == 1)
163#elif defined(HAVE_GETTID)
164#  define IS_MAIN_PTHREAD (getpid() == (pid_t)syscall(SYS_gettid))
165#else
166#  define IS_MAIN_PTHREAD (1)
167#endif
168
169/* Return the current thread ID as an unsigned long.
170 * Ideally, we use the operating-system's notion of a thread ID so
171 * that external process monitoring software will be using the same
172 * value that we log.  If we don't know the system's mechanism, we
173 * use the address of the current NSThread object so that, even if
174 * it makes no sense externally, it can still be used to show that
175 * different threads generated different logs.
176 */
177NSUInteger
178GSPrivateThreadID()
179{
180#if defined(_WIN32)
181  return (NSUInteger)GetCurrentThreadId();
182#elif defined(HAVE_GETTID)
183  return (NSUInteger)syscall(SYS_gettid);
184#elif defined(HAVE_PTHREAD_GETTHREADID_NP)
185  return (NSUInteger)pthread_getthreadid_np();
186#else
187  return (NSUInteger)GSCurrentThread();
188#endif
189}
190
191#if 0
192/*
193 * NSThread setName: method for windows.
194 * FIXME ... This is code for the microsoft compiler;
195 * how do we make it work for gcc/clang?
196 */
197#if defined(_WIN32) && defined(HAVE_WINDOWS_H)
198// Usage: SetThreadName (-1, "MainThread");
199#include <windows.h>
200const DWORD MS_VC_EXCEPTION=0x406D1388;
201
202#pragma pack(push,8)
203typedef struct tagTHREADNAME_INFO
204{
205  DWORD dwType; // Must be 0x1000.
206  LPCSTR szName; // Pointer to name (in user addr space).
207  DWORD dwThreadID; // Thread ID (-1=caller thread).
208  DWORD dwFlags; // Reserved for future use, must be zero.
209} THREADNAME_INFO;
210#pragma pack(pop)
211
212static int SetThreadName(DWORD dwThreadID, const char *threadName)
213{
214  THREADNAME_INFO info;
215  int result;
216
217  info.dwType = 0x1000;
218  info.szName = threadName;
219  info.dwThreadID = dwThreadID;
220  info.dwFlags = 0;
221
222  __try
223  {
224    RaiseException(MS_VC_EXCEPTION, 0,
225      sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
226    result = 0;
227  }
228  __except(EXCEPTION_EXECUTE_HANDLER)
229  {
230    result = -1;
231  }
232}
233
234#define PTHREAD_SETNAME(a)  SetThreadName(-1, a)
235
236#endif
237#endif
238
239#ifndef PTHREAD_SETNAME
240#define PTHREAD_SETNAME(a) -1
241#endif
242
243
244// Some older BSD systems used a non-standard range of thread priorities.
245// Use these if they exist, otherwise define standard ones.
246#ifndef PTHREAD_MAX_PRIORITY
247#define PTHREAD_MAX_PRIORITY 31
248#endif
249#ifndef PTHREAD_MIN_PRIORITY
250#define PTHREAD_MIN_PRIORITY 0
251#endif
252
253
254@interface NSThread (Activation)
255- (void) _makeThreadCurrent;
256@end
257
258@interface NSAutoreleasePool (NSThread)
259+ (void) _endThread: (NSThread*)thread;
260@end
261
262static Class                    threadClass = Nil;
263static NSNotificationCenter     *nc = nil;
264static BOOL                     disableTraceLocks = NO;
265
266/**
267 * This class performs a dual function ...
268 * <p>
269 *   As a class, it is responsible for handling incoming events from
270 *   the main runloop on a special inputFd.  This consumes any bytes
271 *   written to wake the main runloop.<br />
272 *   During initialisation, the default runloop is set up to watch
273 *   for data arriving on inputFd.
274 * </p>
275 * <p>
276 *   As instances, each  instance retains perform receiver and argument
277 *   values as long as they are needed, and handles locking to support
278 *   methods which want to block until an action has been performed.
279 * </p>
280 * <p>
281 *   The initialize method of this class is called before any new threads
282 *   run.
283 * </p>
284 */
285@interface GSPerformHolder : NSObject
286{
287  id			receiver;
288  id			argument;
289  SEL			selector;
290  NSConditionLock	*lock;		// Not retained.
291  NSArray		*modes;
292  BOOL                  invalidated;
293@public
294  NSException           *exception;
295}
296+ (GSPerformHolder*) newForReceiver: (id)r
297			   argument: (id)a
298			   selector: (SEL)s
299			      modes: (NSArray*)m
300			       lock: (NSConditionLock*)l;
301- (void) fire;
302- (void) invalidate;
303- (BOOL) isInvalidated;
304- (NSArray*) modes;
305@end
306
307/**
308 * Sleep until the current date/time is the specified time interval
309 * past the reference date/time.<br />
310 * Implemented as a function taking an NSTimeInterval argument in order
311 * to avoid objc messaging and object allocation/deallocation (NSDate)
312 * overheads.<br />
313 * Used to implement [NSThread+sleepUntilDate:]
314 * If the date is in the past, this function simply allows other threads
315 * (if any) to run.
316 */
317void
318GSSleepUntilIntervalSinceReferenceDate(NSTimeInterval when)
319{
320  NSTimeInterval delay;
321
322  // delay is always the number of seconds we still need to wait
323  delay = when - GSPrivateTimeNow();
324  if (delay <= 0.0)
325    {
326      /* We don't need to wait, but since we are willing to wait at this
327       * point, we should let other threads have preference over this one.
328       */
329      sched_yield();
330      return;
331    }
332
333#if     defined(_WIN32)
334  /*
335   * Avoid integer overflow by breaking up long sleeps.
336   */
337  while (delay > 30.0*60.0)
338    {
339      // sleep 30 minutes
340      Sleep (30*60*1000);
341      delay = when - GSPrivateTimeNow();
342    }
343
344  /* Don't use nanosleep (even if available) on mingw ... it's reported no
345   * to work with pthreads.
346   * Sleeping may return early because of signals, so we need to re-calculate
347   * the required delay and check to see if we need to sleep again.
348   */
349  while (delay > 0)
350    {
351#if	defined(HAVE_USLEEP)
352      /* On windows usleep() seems to perform a busy wait ... so we only
353       * use it for short delays ... otherwise use the less accurate Sleep()
354       */
355      if (delay > 0.1)
356	{
357          Sleep ((NSInteger)(delay*1000));
358	}
359      else
360	{
361          usleep ((NSInteger)(delay*1000000));
362	}
363#else
364      Sleep ((NSInteger)(delay*1000));
365#endif	/* HAVE_USLEEP */
366      delay = when - GSPrivateTimeNow();
367    }
368
369#else   /* _WIN32 */
370
371  /*
372   * Avoid integer overflow by breaking up long sleeps.
373   */
374  while (delay > 30.0*60.0)
375    {
376      // sleep 30 minutes
377      sleep(30*60);
378      delay = when - GSPrivateTimeNow();
379    }
380
381#ifdef	HAVE_NANOSLEEP
382  if (delay > 0)
383    {
384      struct timespec request;
385      struct timespec remainder;
386
387      request.tv_sec = (time_t)delay;
388      request.tv_nsec = (long)((delay - request.tv_sec) * 1000000000);
389      remainder.tv_sec = 0;
390      remainder.tv_nsec = 0;
391
392      /*
393       * With nanosleep, we can restart the sleep after a signal by using
394       * the remainder information ... so we can be sure to sleep to the
395       * desired limit without having to re-generate the delay needed.
396       */
397      while (nanosleep(&request, &remainder) < 0
398	&& (remainder.tv_sec > 0 || remainder.tv_nsec > 0))
399	{
400	  request.tv_sec = remainder.tv_sec;
401	  request.tv_nsec = remainder.tv_nsec;
402	  remainder.tv_sec = 0;
403	  remainder.tv_nsec = 0;
404	}
405    }
406#else   /* HAVE_NANOSLEEP */
407
408  /*
409   * sleeping may return early because of signals, so we need to re-calculate
410   * the required delay and check to see if we need to sleep again.
411   */
412  while (delay > 0)
413    {
414#if	defined(HAVE_USLEEP)
415      usleep((NSInteger)(delay*1000000));
416#else	/* HAVE_USLEEP */
417      sleep((NSInteger)delay);
418#endif	/* !HAVE_USLEEP */
419      delay = when - GSPrivateTimeNow();
420    }
421#endif	/* !HAVE_NANOSLEEP */
422#endif	/* !_WIN32 */
423}
424
425static NSArray *
426commonModes(void)
427{
428  static NSArray	*modes = nil;
429
430  if (modes == nil)
431    {
432      [gnustep_global_lock lock];
433      if (modes == nil)
434	{
435	  Class	c = NSClassFromString(@"NSApplication");
436	  SEL	s = @selector(allRunLoopModes);
437
438	  if (c != 0 && [c respondsToSelector: s])
439	    {
440	      modes = RETAIN([c performSelector: s]);
441	    }
442	  else
443	    {
444	      modes = [[NSArray alloc] initWithObjects:
445		NSDefaultRunLoopMode, NSConnectionReplyMode, nil];
446	    }
447	}
448      [gnustep_global_lock unlock];
449    }
450  return modes;
451}
452
453/*
454 * Flag indicating whether the objc runtime ever went multi-threaded.
455 */
456static BOOL	entered_multi_threaded_state = NO;
457
458static NSThread *defaultThread;
459
460static BOOL             keyInitialized = NO;
461static pthread_key_t    thread_object_key;
462
463
464static NSHashTable *_activeBlocked = nil;
465static NSHashTable *_activeThreads = nil;
466static pthread_mutex_t _activeLock = PTHREAD_MUTEX_INITIALIZER;
467
468/**
469 * pthread_t is an opaque type. It might be a scalar type or
470 * some kind of struct depending on the implementation, so we
471 * need to wrap it up in an NSValue object if we want to pass
472 * it around.
473 * This follows the CoreFoundation 'create rule' and returns an object with
474 * a reference count of 1.
475 */
476static inline NSValue* NSValueCreateFromPthread(pthread_t thread)
477{
478  return [[NSValue alloc] initWithBytes: &thread
479                               objCType: @encode(pthread_t)];
480}
481
482/**
483 * Conversely, we need to be able to retrieve the pthread_t
484 * from an NSValue.
485 */
486static inline void
487_getPthreadFromNSValue(const void *value, pthread_t *thread_ptr)
488{
489  const char    *enc;
490
491  NSCAssert(thread_ptr, @"No storage for thread reference");
492# ifndef NS_BLOCK_ASSERTIONS
493  enc = [(NSValue*)value objCType];
494  NSCAssert(enc != NULL && (0 == strcmp(@encode(pthread_t),enc)),
495    @"Invalid NSValue container for thread reference");
496# endif
497  [(NSValue*)value getValue: (void*)thread_ptr];
498}
499
500/**
501 * This is the comparison function for boxed pthreads, as used by the
502 * NSMapTable containing them.
503 */
504static BOOL
505_boxedPthreadIsEqual(NSMapTable *t,
506  const void *boxed,
507  const void *boxedOther)
508{
509  pthread_t thread;
510  pthread_t otherThread;
511
512  _getPthreadFromNSValue(boxed, &thread);
513  _getPthreadFromNSValue(boxedOther, &otherThread);
514  return pthread_equal(thread, otherThread);
515}
516
517/**
518 * Since pthread_t is opaque, we cannot make any assumption about how
519 * to hash it. There are a few problems here:
520 * 1. Functions to obtain the thread ID of an arbitrary thread
521 *    exist in the in the Win32 and some pthread APIs (GetThreadId() and
522 *    pthread_getunique_np(), respectively), but there is no protable solution
523 *    for this problem.
524 * 2. Even where pthread_getunique_np() is available, it might have different
525 *    definitions, so it's not really robust to use it.
526 *
527 * For these reasons, we always return the same hash. That fulfills the API
528 * contract for NSMapTable (key-hash equality as a necessary condition for key
529 * equality), but makes things quite inefficient (linear search over all
530 * elements), so we need to keep the table small.
531 */
532static NSUInteger _boxedPthreadHash(NSMapTable *t, const void *value)
533{
534  return 0;
535}
536
537/**
538 * Retain callback for boxed thread references.
539 */
540static void _boxedPthreadRetain(NSMapTable *t, const void *value)
541{
542  RETAIN((NSValue*)value);
543}
544
545/**
546 * Release callback for boxed thread references.
547 */
548static void _boxedPthreadRelease(NSMapTable *t, void *value)
549{
550  RELEASE((NSValue*)value);
551}
552
553/**
554 * Description callback for boxed thread references.
555 */
556static NSString *_boxedPthreadDescribe(NSMapTable *t, const void *value)
557{
558  return [(NSValue*)value description];
559}
560
561
562static const NSMapTableKeyCallBacks _boxedPthreadKeyCallBacks =
563{
564  _boxedPthreadHash,
565  _boxedPthreadIsEqual,
566  _boxedPthreadRetain,
567  _boxedPthreadRelease,
568  _boxedPthreadDescribe,
569  NULL
570};
571
572
573/**
574 * This map table maintains a list of all threads currently undergoing
575 * cleanup. This is a required so that +currentThread can still find the
576 * thred if called from within the late-cleanup function.
577 */
578static NSMapTable *_exitingThreads = nil;
579static pthread_mutex_t _exitingThreadsLock = PTHREAD_MUTEX_INITIALIZER;
580
581
582/**
583 * Called before late cleanup is run and inserts the NSThread object into the
584 * table that is used by GSCurrentThread to find the thread if it is called
585 * during cleanup. The boxedThread variable contains a boxed reference to
586 * the result of calling pthread_self().
587 */
588static inline void _willLateUnregisterThread(NSValue *boxedThread,
589  NSThread *specific)
590{
591  pthread_mutex_lock(&_exitingThreadsLock);
592  /* The map table is created lazily/late so that the NSThread
593   * +initialize method can be called without causing other
594   * classes to be initialized.
595   * NB this locked section cannot be protected by an exception handler
596   * because the exception handler stores information in the current
597   * thread variables ... which causes recursion.
598   */
599  if (nil == _exitingThreads)
600    {
601      _exitingThreads = NSCreateMapTable(_boxedPthreadKeyCallBacks,
602	NSObjectMapValueCallBacks, 10);
603    }
604  NSMapInsert(_exitingThreads, (const void*)boxedThread,
605    (const void*)specific);
606  pthread_mutex_unlock(&_exitingThreadsLock);
607}
608
609/**
610 * Called after late cleanup has run. Will remove the current thread from
611 * the lookup table again. The boxedThread variable contains a boxed reference
612 * to the result of calling pthread_self().
613 */
614static inline void _didLateUnregisterCurrentThread(NSValue *boxedThread)
615{
616  /* NB this locked section cannot be protected by an exception handler
617   * because the exception handler stores information in the current
618   * thread variables ... which causes recursion.
619   */
620  pthread_mutex_lock(&_exitingThreadsLock);
621  if (nil != _exitingThreads)
622    {
623      NSMapRemove(_exitingThreads, (const void*)boxedThread);
624    }
625  pthread_mutex_unlock(&_exitingThreadsLock);
626}
627
628/*
629 * Forward declaration of the thread unregistration function
630 */
631static void
632unregisterActiveThread(NSThread *thread);
633
634/**
635 * Pthread cleanup call.
636 *
637 * We should normally not get here ... because threads should exit properly
638 * and clean up, so that this function doesn't get called.  However if a
639 * thread terminates for some reason without calling the exit method, we
640 * we add it to a special lookup table that is used by GSCurrentThread() to
641 * obtain the NSThread object.
642 * We need to be a bit careful about this regarding object allocation because
643 * we must not call into NSAutoreleasePool unless the NSThread object can still
644 * be found using GSCurrentThread()
645 */
646static void exitedThread(void *thread)
647{
648  if (thread != defaultThread)
649    {
650      NSValue           *ref;
651
652      if (0 == thread)
653	{
654	  /* On some systems this is called with a null thread pointer,
655	   * so try to get the NSThread object for the current thread.
656	   */
657	  thread = pthread_getspecific(thread_object_key);
658	  if (0 == thread)
659	    {
660	      return;	// no thread info
661	    }
662	}
663      RETAIN((NSThread*)thread);
664      ref = NSValueCreateFromPthread(pthread_self());
665      _willLateUnregisterThread(ref, (NSThread*)thread);
666
667      {
668        CREATE_AUTORELEASE_POOL(arp);
669        NS_DURING
670          {
671            unregisterActiveThread((NSThread*)thread);
672          }
673        NS_HANDLER
674          {
675            DESTROY(arp);
676            _didLateUnregisterCurrentThread(ref);
677            DESTROY(ref);
678            RELEASE((NSThread*)thread);
679          }
680        NS_ENDHANDLER
681        DESTROY(arp);
682      }
683
684      /* At this point threre shouldn't be any autoreleased objects lingering
685       * around anymore. So we may remove the thread from the lookup table.
686       */
687      _didLateUnregisterCurrentThread(ref);
688      DESTROY(ref);
689      RELEASE((NSThread*)thread);
690    }
691}
692
693/**
694 * These functions needed because sending messages to classes is a seriously
695 * slow process with gcc and the gnu runtime.
696 */
697inline NSThread*
698GSCurrentThread(void)
699{
700  NSThread *thr;
701
702  if (NO == keyInitialized)
703    {
704      if (pthread_key_create(&thread_object_key, exitedThread))
705        {
706          [NSException raise: NSInternalInconsistencyException
707                      format: @"Unable to create thread key!"];
708        }
709      keyInitialized = YES;
710    }
711  thr = pthread_getspecific(thread_object_key);
712  if (nil == thr)
713    {
714      NSValue *selfThread = NSValueCreateFromPthread(pthread_self());
715
716      /* NB this locked section cannot be protected by an exception handler
717       * because the exception handler stores information in the current
718       * thread variables ... which causes recursion.
719       */
720      if (nil != _exitingThreads)
721        {
722          pthread_mutex_lock(&_exitingThreadsLock);
723          thr = NSMapGet(_exitingThreads, (const void*)selfThread);
724          pthread_mutex_unlock(&_exitingThreadsLock);
725        }
726      DESTROY(selfThread);
727      if (nil == thr)
728        {
729          GSRegisterCurrentThread();
730          thr = pthread_getspecific(thread_object_key);
731          if ((nil == defaultThread) && IS_MAIN_PTHREAD)
732            {
733              defaultThread = RETAIN(thr);
734            }
735        }
736      assert(nil != thr && "No main thread");
737    }
738  return thr;
739}
740
741NSMutableDictionary*
742GSDictionaryForThread(NSThread *t)
743{
744  if (nil == t)
745    {
746      t = GSCurrentThread();
747    }
748  return [t threadDictionary];
749}
750
751/**
752 * Fast access function for thread dictionary of current thread.
753 */
754NSMutableDictionary*
755GSCurrentThreadDictionary(void)
756{
757  return GSDictionaryForThread(nil);
758}
759
760/*
761 * Callback function to send notifications on becoming multi-threaded.
762 */
763static void
764gnustep_base_thread_callback(void)
765{
766  /*
767   * Protect this function with locking ... to avoid any possibility
768   * of multiple threads registering with the system simultaneously,
769   * and so that all NSWillBecomeMultiThreadedNotifications are sent
770   * out before any second thread can interfere with anything.
771   */
772  if (entered_multi_threaded_state == NO)
773    {
774      [gnustep_global_lock lock];
775      if (entered_multi_threaded_state == NO)
776	{
777	  /*
778	   * For apple compatibility ... and to make things easier for
779	   * code called indirectly within a will-become-multi-threaded
780	   * notification handler, we set the flag to say we are multi
781	   * threaded BEFORE sending the notifications.
782	   */
783	  entered_multi_threaded_state = YES;
784	  NS_DURING
785	    {
786	      [GSPerformHolder class];	// Force initialization
787
788	      /*
789	       * Post a notification if this is the first new thread
790	       * to be created.
791	       * Won't work properly if threads are not all created
792	       * by this class, but it's better than nothing.
793	       */
794	      if (nc == nil)
795		{
796		  nc = RETAIN([NSNotificationCenter defaultCenter]);
797		}
798#if	!defined(HAVE_INITIALIZE)
799	      if (NO == [[NSUserDefaults standardUserDefaults]
800		boolForKey: @"GSSilenceInitializeWarning"])
801		{
802		  NSLog(@"WARNING your program is becoming multi-threaded, but you are using an ObjectiveC runtime library which does not have a thread-safe implementation of the +initialize method. Please see README.initialize for more information.");
803		}
804#endif
805	      [nc postNotificationName: NSWillBecomeMultiThreadedNotification
806				object: nil
807			      userInfo: nil];
808	    }
809	  NS_HANDLER
810	    {
811	      fprintf(stderr,
812"ALERT ... exception while becoming multi-threaded ... system may not be\n"
813"properly initialised.\n");
814	      fflush(stderr);
815	    }
816	  NS_ENDHANDLER
817	}
818      [gnustep_global_lock unlock];
819    }
820}
821
822@implementation NSThread (Activation)
823- (void) _makeThreadCurrent
824{
825  /* NB. We must set up the pointer to the new NSThread instance from
826   * pthread specific memory before we do anything which might need to
827   * check what the current thread is (like getting the ID)!
828   */
829  pthread_setspecific(thread_object_key, self);
830  threadID = GSPrivateThreadID();
831  pthread_mutex_lock(&_activeLock);
832  /* The hash table is created lazily/late so that the NSThread
833   * +initialize method can be called without causing other
834   * classes to be initialized.
835   * NB this locked section cannot be protected by an exception handler
836   * because the exception handler stores information in the current
837   * thread variables ... which causes recursion.
838   */
839  if (nil == _activeThreads)
840    {
841      _activeThreads = NSCreateHashTable(
842	NSNonRetainedObjectHashCallBacks, 100);
843    }
844  NSHashInsert(_activeThreads, (const void*)self);
845  pthread_mutex_unlock(&_activeLock);
846}
847@end
848
849@implementation NSThread
850
851static void
852setThreadForCurrentThread(NSThread *t)
853{
854  [t _makeThreadCurrent];
855  gnustep_base_thread_callback();
856}
857
858static void
859unregisterActiveThread(NSThread *thread)
860{
861  if (thread->_active == YES)
862    {
863      /*
864       * Set the thread to be inactive to avoid any possibility of recursion.
865       */
866      thread->_active = NO;
867      thread->_finished = YES;
868
869      /*
870       * Let observers know this thread is exiting.
871       */
872      if (nc == nil)
873	{
874	  nc = RETAIN([NSNotificationCenter defaultCenter]);
875	}
876      [nc postNotificationName: NSThreadWillExitNotification
877			object: thread
878		      userInfo: nil];
879
880      [(GSRunLoopThreadInfo*)thread->_runLoopInfo invalidate];
881      RELEASE(thread);
882      pthread_setspecific(thread_object_key, nil);
883    }
884}
885
886+ (NSArray*) callStackReturnAddresses
887{
888  GSStackTrace          *stack;
889  NSArray               *addrs;
890
891  stack = [GSStackTrace new];
892  [stack trace];
893  addrs = RETAIN([stack addresses]);
894  RELEASE(stack);
895  return AUTORELEASE(addrs);
896}
897
898+ (BOOL) _createThreadForCurrentPthread
899{
900  NSThread	*t = pthread_getspecific(thread_object_key);
901
902  if (t == nil)
903    {
904      t = [self new];
905      t->_active = YES;
906      [t _makeThreadCurrent];
907      GS_CONSUMED(t);
908      if (defaultThread != nil && t != defaultThread)
909        {
910          gnustep_base_thread_callback();
911        }
912      return YES;
913    }
914  return NO;
915}
916
917+ (NSThread*) currentThread
918{
919  return GSCurrentThread();
920}
921
922+ (void) detachNewThreadSelector: (SEL)aSelector
923		        toTarget: (id)aTarget
924                      withObject: (id)anArgument
925{
926  NSThread	*thread;
927
928  /*
929   * Create the new thread.
930   */
931  thread = [[NSThread alloc] initWithTarget: aTarget
932                                   selector: aSelector
933                                     object: anArgument];
934
935  [thread start];
936  RELEASE(thread);
937}
938
939+ (void) exit
940{
941  NSThread	*t;
942
943  t = GSCurrentThread();
944  if (t->_active == YES)
945    {
946      unregisterActiveThread(t);
947
948      if (t == defaultThread || defaultThread == nil)
949	{
950	  /* For the default thread, we exit the process.
951	   */
952	  exit(0);
953	}
954      else
955	{
956          pthread_exit(NULL);
957	}
958    }
959}
960
961/*
962 * Class initialization
963 */
964+ (void) initialize
965{
966  if (self == [NSThread class])
967    {
968      if (NO == keyInitialized)
969        {
970          if (pthread_key_create(&thread_object_key, exitedThread))
971            {
972              [NSException raise: NSInternalInconsistencyException
973                          format: @"Unable to create thread key!"];
974            }
975          keyInitialized = YES;
976        }
977      /* Ensure that the default thread exists.
978       * It's safe to create a lock here (since [NSObject+initialize]
979       * creates locks, and locks don't depend on any other class),
980       * but we want to avoid initialising other classes while we are
981       * initialising NSThread.
982       */
983      threadClass = self;
984      GSCurrentThread();
985    }
986}
987
988+ (BOOL) isMainThread
989{
990  return (GSCurrentThread() == defaultThread ? YES : NO);
991}
992
993+ (BOOL) isMultiThreaded
994{
995  return entered_multi_threaded_state;
996}
997
998+ (NSThread*) mainThread
999{
1000  return defaultThread;
1001}
1002
1003/**
1004 * Set the priority of the current thread.  This is a value in the
1005 * range 0.0 (lowest) to 1.0 (highest) which is mapped to the underlying
1006 * system priorities.
1007 */
1008+ (void) setThreadPriority: (double)pri
1009{
1010#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING > 0)
1011  int	policy;
1012  struct sched_param param;
1013
1014  // Clamp pri into the required range.
1015  if (pri > 1) { pri = 1; }
1016  if (pri < 0) { pri = 0; }
1017
1018  // Scale pri based on the range of the host system.
1019  pri *= (PTHREAD_MAX_PRIORITY - PTHREAD_MIN_PRIORITY);
1020  pri += PTHREAD_MIN_PRIORITY;
1021
1022  pthread_getschedparam(pthread_self(), &policy, &param);
1023  param.sched_priority = pri;
1024  pthread_setschedparam(pthread_self(), policy, &param);
1025#endif
1026}
1027
1028+ (void) sleepForTimeInterval: (NSTimeInterval)ti
1029{
1030  GSSleepUntilIntervalSinceReferenceDate(GSPrivateTimeNow() + ti);
1031}
1032
1033/**
1034 * Delaying a thread ... pause until the specified date.
1035 */
1036+ (void) sleepUntilDate: (NSDate*)date
1037{
1038  GSSleepUntilIntervalSinceReferenceDate([date timeIntervalSinceReferenceDate]);
1039}
1040
1041
1042/**
1043 * Return the priority of the current thread.
1044 */
1045+ (double) threadPriority
1046{
1047  double pri = 0;
1048#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && (_POSIX_THREAD_PRIORITY_SCHEDULING > 0)
1049  int policy;
1050  struct sched_param param;
1051
1052  pthread_getschedparam(pthread_self(), &policy, &param);
1053  pri = param.sched_priority;
1054  // Scale pri based on the range of the host system.
1055  pri -= PTHREAD_MIN_PRIORITY;
1056  pri /= (PTHREAD_MAX_PRIORITY - PTHREAD_MIN_PRIORITY);
1057
1058#else
1059#warning Your pthread implementation does not support thread priorities
1060#endif
1061  return pri;
1062
1063}
1064
1065
1066
1067/*
1068 * Thread instance methods.
1069 */
1070
1071- (void) cancel
1072{
1073  _cancelled = YES;
1074}
1075
1076- (void) dealloc
1077{
1078  int   retries = 0;
1079
1080  if (_active == YES)
1081    {
1082      [NSException raise: NSInternalInconsistencyException
1083		  format: @"Deallocating an active thread without [+exit]!"];
1084    }
1085  DESTROY(_runLoopInfo);
1086  DESTROY(_thread_dictionary);
1087  DESTROY(_target);
1088  DESTROY(_arg);
1089  DESTROY(_name);
1090  if (_autorelease_vars.pool_cache != 0)
1091    {
1092      [NSAutoreleasePool _endThread: self];
1093    }
1094
1095  while ((_thread_dictionary != nil || _runLoopInfo != nil) && retries++ < 10)
1096    {
1097      /* Try again.
1098       */
1099      DESTROY(_runLoopInfo);
1100      DESTROY(_thread_dictionary);
1101      if (_autorelease_vars.pool_cache != 0)
1102	{
1103	  [NSAutoreleasePool _endThread: self];
1104	}
1105    }
1106
1107  if (_runLoopInfo != nil)
1108    {
1109      NSLog(@"Oops - leak - run loop is %@", _runLoopInfo);
1110      if (_autorelease_vars.pool_cache != 0)
1111        {
1112          [NSAutoreleasePool _endThread: self];
1113        }
1114    }
1115  if (_thread_dictionary != nil)
1116    {
1117      NSLog(@"Oops - leak - thread dictionary is %@", _thread_dictionary);
1118      if (_autorelease_vars.pool_cache != 0)
1119        {
1120          [NSAutoreleasePool _endThread: self];
1121        }
1122    }
1123  DESTROY(_gcontext);
1124  if (_activeThreads)
1125    {
1126      pthread_mutex_lock(&_activeLock);
1127      NSHashRemove(_activeThreads, self);
1128      pthread_mutex_unlock(&_activeLock);
1129    }
1130  if (GS_EXISTS_INTERNAL)
1131    {
1132      pthread_spin_lock(&lockInfo.spin);
1133      DESTROY(lockInfo.held);
1134      lockInfo.wait = nil;
1135      pthread_spin_unlock(&lockInfo.spin);
1136      pthread_spin_destroy(&lockInfo.spin);
1137      if (internal != nil)
1138        {
1139          GS_DESTROY_INTERNAL(NSThread);
1140        }
1141    }
1142  [super dealloc];
1143}
1144
1145- (NSString*) description
1146{
1147  return [NSString stringWithFormat: @"%@{name = %@, num = %"PRIuPTR"}",
1148    [super description], _name, threadID];
1149}
1150
1151- (id) init
1152{
1153  GS_CREATE_INTERNAL(NSThread);
1154  pthread_spin_init(&lockInfo.spin, 0);
1155  lockInfo.held = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 10);
1156  init_autorelease_thread_vars(&_autorelease_vars);
1157  return self;
1158}
1159
1160- (id) initWithTarget: (id)aTarget
1161             selector: (SEL)aSelector
1162               object: (id)anArgument
1163{
1164  if (nil != (self = [self init]))
1165    {
1166      /* initialize our ivars. */
1167      _selector = aSelector;
1168      _target = RETAIN(aTarget);
1169      _arg = RETAIN(anArgument);
1170    }
1171  return self;
1172}
1173
1174- (BOOL) isCancelled
1175{
1176  return _cancelled;
1177}
1178
1179- (BOOL) isExecuting
1180{
1181  return _active;
1182}
1183
1184- (BOOL) isFinished
1185{
1186  return _finished;
1187}
1188
1189- (BOOL) isMainThread
1190{
1191  return (self == defaultThread ? YES : NO);
1192}
1193
1194- (void) main
1195{
1196  if (_active == NO)
1197    {
1198      [NSException raise: NSInternalInconsistencyException
1199                  format: @"[%@-%@] called on inactive thread",
1200        NSStringFromClass([self class]),
1201        NSStringFromSelector(_cmd)];
1202    }
1203
1204  [_target performSelector: _selector withObject: _arg];
1205}
1206
1207- (NSString*) name
1208{
1209  return _name;
1210}
1211
1212- (void) _setName: (NSString *)aName
1213{
1214  if ([aName isKindOfClass: [NSString class]])
1215    {
1216      int       i;
1217      char      buf[200];
1218
1219      if (YES == [aName getCString: buf
1220                         maxLength: sizeof(buf)
1221                          encoding: NSUTF8StringEncoding])
1222        {
1223          i = strlen(buf);
1224        }
1225      else
1226        {
1227          /* Too much for buffer ... truncate on a character boundary.
1228           */
1229          i = sizeof(buf) - 1;
1230          if (buf[i] & 0x80)
1231            {
1232              while (i > 0 && (buf[i] & 0x80))
1233                {
1234                  buf[i--] = '\0';
1235                }
1236            }
1237          else
1238            {
1239              buf[i--] = '\0';
1240            }
1241        }
1242      while (i > 0)
1243        {
1244          if (PTHREAD_SETNAME(buf) == ERANGE)
1245            {
1246              /* Name must be too long ... gnu/linux uses 15 characters
1247               */
1248              if (i > 15)
1249                {
1250                  i = 15;
1251                }
1252              else
1253                {
1254                  i--;
1255                }
1256              /* too long a name ... truncate on a character boundary.
1257               */
1258              if (buf[i] & 0x80)
1259                {
1260                  while (i > 0 && (buf[i] & 0x80))
1261                    {
1262                      buf[i--] = '\0';
1263                    }
1264                }
1265              else
1266                {
1267                  buf[i--] = '\0';
1268                }
1269            }
1270          else
1271            {
1272              break;    // Success or some other error
1273            }
1274        }
1275    }
1276}
1277
1278- (void) setName: (NSString*)aName
1279{
1280  ASSIGN(_name, aName);
1281#ifdef PTHREAD_SETNAME
1282  if (YES == _active)
1283    {
1284      [self performSelector: @selector(_setName:)
1285                   onThread: self
1286                 withObject: aName
1287              waitUntilDone: NO];
1288    }
1289#endif
1290}
1291
1292- (void) setStackSize: (NSUInteger)stackSize
1293{
1294  _stackSize = stackSize;
1295}
1296
1297- (NSUInteger) stackSize
1298{
1299  return _stackSize;
1300}
1301
1302/**
1303 * Trampoline function called to launch the thread
1304 */
1305static void *
1306nsthreadLauncher(void *thread)
1307{
1308  NSThread *t = (NSThread*)thread;
1309
1310  setThreadForCurrentThread(t);
1311
1312  /*
1313   * Let observers know a new thread is starting.
1314   */
1315  if (nc == nil)
1316    {
1317      nc = RETAIN([NSNotificationCenter defaultCenter]);
1318    }
1319  [nc postNotificationName: NSThreadDidStartNotification
1320		    object: t
1321		  userInfo: nil];
1322
1323  [t _setName: [t name]];
1324
1325  [t main];
1326
1327  [NSThread exit];
1328  // Not reached
1329  return NULL;
1330}
1331
1332- (void) start
1333{
1334  pthread_attr_t	attr;
1335
1336  if (_active == YES)
1337    {
1338      [NSException raise: NSInternalInconsistencyException
1339                  format: @"[%@-%@] called on active thread",
1340        NSStringFromClass([self class]),
1341        NSStringFromSelector(_cmd)];
1342    }
1343  if (_cancelled == YES)
1344    {
1345      [NSException raise: NSInternalInconsistencyException
1346                  format: @"[%@-%@] called on cancelled thread",
1347        NSStringFromClass([self class]),
1348        NSStringFromSelector(_cmd)];
1349    }
1350  if (_finished == YES)
1351    {
1352      [NSException raise: NSInternalInconsistencyException
1353                  format: @"[%@-%@] called on finished thread",
1354        NSStringFromClass([self class]),
1355        NSStringFromSelector(_cmd)];
1356    }
1357
1358  /* Make sure the notification is posted BEFORE the new thread starts.
1359   */
1360  gnustep_base_thread_callback();
1361
1362  /* The thread must persist until it finishes executing.
1363   */
1364  RETAIN(self);
1365
1366  /* Mark the thread as active while it's running.
1367   */
1368  _active = YES;
1369
1370  errno = 0;
1371  pthread_attr_init(&attr);
1372  /* Create this thread detached, because we never use the return state from
1373   * threads.
1374   */
1375  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1376  /* Set the stack size when the thread is created.  Unlike the old setrlimit
1377   * code, this actually works.
1378   */
1379  if (_stackSize > 0)
1380    {
1381      pthread_attr_setstacksize(&attr, _stackSize);
1382    }
1383  if (pthread_create(&pthreadID, &attr, nsthreadLauncher, self))
1384    {
1385      DESTROY(self);
1386      [NSException raise: NSInternalInconsistencyException
1387                  format: @"Unable to detach thread (last error %@)",
1388                  [NSError _last]];
1389    }
1390}
1391
1392/**
1393 * Return the thread dictionary.  This dictionary can be used to store
1394 * arbitrary thread specific data.<br />
1395 * NB. This cannot be autoreleased, since we cannot be sure that the
1396 * autorelease pool for the thread will continue to exist for the entire
1397 * life of the thread!
1398 */
1399- (NSMutableDictionary*) threadDictionary
1400{
1401  if (_thread_dictionary == nil)
1402    {
1403      _thread_dictionary = [NSMutableDictionary new];
1404    }
1405  return _thread_dictionary;
1406}
1407
1408@end
1409
1410
1411
1412@implementation NSThread (GSLockInfo)
1413
1414static NSString *
1415lockInfoErr(NSString *str)
1416{
1417  if (disableTraceLocks)
1418    {
1419      return nil;
1420    }
1421  return str;
1422}
1423
1424- (NSString *) mutexDrop: (id)mutex
1425{
1426  if (GS_EXISTS_INTERNAL)
1427    {
1428      GSLockInfo        *li = &lockInfo;
1429      int               err;
1430
1431      if (YES == disableTraceLocks) return nil;
1432      err = pthread_spin_lock(&li->spin);
1433      if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
1434      if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
1435
1436      if (mutex == li->wait)
1437        {
1438          /* The mutex was being waited for ... simply remove it.
1439           */
1440          li->wait = nil;
1441        }
1442      else if (NSHashGet(li->held, (void*)mutex) == (void*)mutex)
1443        {
1444          GSStackTrace  *stck = [mutex stack];
1445
1446          /* The mutex was being held ... if the recursion count was zero
1447           * we remove it (otherwise the count is decreased).
1448           */
1449          if (stck->recursion-- == 0)
1450            {
1451              NSHashRemove(li->held, (void*)mutex);
1452// fprintf(stderr, "%lu: Drop %p (final) %lu\n", (unsigned long)_threadID, mutex, [li->held count]);
1453            }
1454          else
1455            {
1456// fprintf(stderr, "%lu: Drop %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
1457            }
1458        }
1459      else
1460        {
1461// fprintf(stderr, "%lu: Drop %p (bad) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
1462          pthread_spin_unlock(&li->spin);
1463          return lockInfoErr(
1464            @"attempt to unlock mutex not locked by this thread");
1465        }
1466      pthread_spin_unlock(&li->spin);
1467      return nil;
1468    }
1469  return lockInfoErr(@"thread not active");
1470}
1471
1472- (NSString *) mutexHold: (id)mutex
1473{
1474  if (GS_EXISTS_INTERNAL)
1475    {
1476      GSLockInfo        *li = &lockInfo;
1477      int               err;
1478
1479      if (YES == disableTraceLocks) return nil;
1480      err = pthread_spin_lock(&li->spin);
1481      if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
1482      if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
1483      if (nil == mutex)
1484        {
1485          mutex = li->wait;
1486          if (nil == mutex)
1487            {
1488              pthread_spin_unlock(&li->spin);
1489              return lockInfoErr(@"attempt to hold nil mutex");
1490            }
1491        }
1492      else if (nil != li->wait && mutex != li->wait)
1493        {
1494          pthread_spin_unlock(&li->spin);
1495          return lockInfoErr(@"attempt to hold mutex without waiting for it");
1496        }
1497      if (NSHashGet(li->held, (void*)mutex) == NULL)
1498        {
1499          [[mutex stack] trace];                // Get current strack trace
1500          NSHashInsert(li->held, (void*)mutex);
1501// fprintf(stderr, "%lu: Hold %p (initial) %lu\n", (unsigned long)threadID, mutex, [li->held count]);
1502        }
1503      else
1504        {
1505          GSStackTrace  *stck = [mutex stack];
1506
1507          stck->recursion++;
1508// fprintf(stderr, "%lu: Hold %p (%lu) %lu\n", (unsigned long)threadID, mutex, (unsigned long)stck->recursion, [li->held count]);
1509        }
1510      li->wait = nil;
1511      pthread_spin_unlock(&li->spin);
1512      return nil;
1513    }
1514  return lockInfoErr(@"thread not active");
1515}
1516
1517- (NSString *) mutexWait: (id)mutex
1518{
1519  if (GS_EXISTS_INTERNAL)
1520    {
1521      NSMutableArray    *dependencies;
1522      id                want;
1523      BOOL              done;
1524      GSLockInfo        *li = &lockInfo;
1525      BOOL              owned = NO;
1526      int               err;
1527
1528      if (YES == disableTraceLocks) return nil;
1529      err = pthread_spin_lock(&li->spin);
1530      if (EDEADLK == err) return lockInfoErr(@"thread spin lock deadlocked");
1531      if (EINVAL == err) return lockInfoErr(@"thread spin lock invalid");
1532      if (nil != li->wait)
1533        {
1534          NSString      *msg = [NSString stringWithFormat:
1535            @ "trying to lock %@ when already trying to lock %@",
1536            mutex, li->wait];
1537          pthread_spin_unlock(&li->spin);
1538          return lockInfoErr(msg);
1539        }
1540      li->wait = mutex;
1541      if (nil != NSHashGet(li->held, (const void*)mutex))
1542        {
1543          owned = YES;
1544        }
1545      pthread_spin_unlock(&li->spin);
1546// fprintf(stderr, "%lu: Wait %p\n", (unsigned long)_threadID, mutex);
1547      if (YES == owned && [mutex isKindOfClass: [NSRecursiveLock class]])
1548        {
1549          return nil;   // We can't deadlock on a recursive lock we own
1550        }
1551
1552      /* While checking for deadlocks we don't want threads created/destroyed
1553       * So we hold the lock to prevent thread activity changes.
1554       * This also ensures that no more than one thread can be checking for
1555       * deadlocks at a time (no interference between checks).
1556       */
1557      pthread_mutex_lock(&_activeLock);
1558
1559      /* As we isolate dependencies (a thread holding the lock another thread
1560       * is waiting for) we disable locking in each thread and record the
1561       * thread in a hash table.  Once we have determined all the dependencies
1562       * we can re-enable locking in each of the threads.
1563       */
1564      if (nil == _activeBlocked)
1565        {
1566          _activeBlocked = NSCreateHashTable(
1567            NSNonRetainedObjectHashCallBacks, 100);
1568        }
1569
1570      dependencies = nil;
1571      want = mutex;
1572      done = NO;
1573
1574      while (NO == done)
1575        {
1576          NSHashEnumerator	enumerator;
1577          NSThread              *found = nil;
1578          BOOL                  foundWasLocked = NO;
1579          NSThread              *th;
1580
1581          /* Look for a thread which is holding the mutex we are currently
1582           * interested in.  We are only interested in thread which are
1583           * themselves waiting for a lock (if they aren't waiting then
1584           * they can't be part of a deadlock dependency list).
1585           */
1586          enumerator = NSEnumerateHashTable(_activeThreads);
1587          while ((th = NSNextHashEnumeratorItem(&enumerator)) != nil)
1588            {
1589              GSLockInfo        *info = &GSIVar(th, _lockInfo);
1590
1591              if (YES == th->_active && nil != info->wait)
1592                {
1593                  BOOL          wasLocked;
1594                  GSStackTrace  *stck;
1595
1596                  if (th == self
1597                    || NULL != NSHashGet(_activeBlocked, (const void*)th))
1598                    {
1599                      /* Don't lock ... this is the current thread or is
1600                       * already in the set of blocked threads.
1601                       */
1602                      wasLocked = YES;
1603                    }
1604                  else
1605                    {
1606                      pthread_spin_lock(&info->spin);
1607                      wasLocked = NO;
1608                    }
1609                  if (nil != info->wait
1610                    && nil != (stck = NSHashGet(info->held, (const void*)want)))
1611                    {
1612                      /* This thread holds the lock we are interested in and
1613                       * is waiting for another lock.
1614                       * We therefore record the details in the dependency list
1615                       * and will go on to look for the thread this found one
1616                       * depends on.
1617                       */
1618                      found = th;
1619                      foundWasLocked = wasLocked;
1620                      want = info->wait;
1621                      if (nil == dependencies)
1622                        {
1623                          dependencies = [NSMutableArray new];
1624                        }
1625                      [dependencies addObject: found];  // thread
1626                      [dependencies addObject: want];   // mutex
1627                      /* NB. breaking out here holds the spin lock so that
1628                       * the lock state of each dependency thread is
1629                       * preserved (if we don't have a deadlock, we get a
1630                       * consistent snapshot of the threads and their locks).
1631                       * We therefore have to unlock the threads when done.
1632                       */
1633                      break;
1634                    }
1635                  /* This thread did not hold the lock we are interested in,
1636                   * so we can unlock it (if necessary) and check another.
1637                   */
1638                  if (NO == wasLocked)
1639                    {
1640                      pthread_spin_unlock(&info->spin);
1641                    }
1642                }
1643            }
1644          NSEndHashTableEnumeration(&enumerator);
1645          if (nil == found)
1646            {
1647              /* There is no thread blocked on the mutex we are checking,
1648               * so we can't have a deadlock.
1649               */
1650              DESTROY(dependencies);
1651              done = YES;
1652            }
1653          else if (foundWasLocked)
1654            {
1655              /* The found thread is the current one or in the blocked set
1656               * so we have a deadlock.
1657               */
1658              done = YES;
1659            }
1660          else
1661            {
1662              /* Record the found (and locked) thread and continue
1663               * to find the next dependency.
1664               */
1665              NSHashInsert(_activeBlocked, (const void*)found);
1666            }
1667        }
1668
1669      /* Ensure any locked threads are unlocked again.
1670       */
1671      if (NSCountHashTable(_activeBlocked) > 0)
1672        {
1673          NSHashEnumerator	enumerator;
1674          NSThread              *th;
1675
1676          enumerator = NSEnumerateHashTable(_activeThreads);
1677          while ((th = NSNextHashEnumeratorItem(&enumerator)) != nil)
1678            {
1679              GSLockInfo        *info = &GSIVar(th, _lockInfo);
1680
1681              pthread_spin_unlock(&info->spin);
1682            }
1683          NSEndHashTableEnumeration(&enumerator);
1684          NSResetHashTable(_activeBlocked);
1685        }
1686
1687      /* Finished check ... re-enable thread activity changes.
1688       */
1689      pthread_mutex_unlock(&_activeLock);
1690
1691
1692      if (nil != dependencies)
1693        {
1694          GSStackTrace          *stack;
1695          NSUInteger            count;
1696          NSUInteger            index = 0;
1697          NSMutableString       *m;
1698
1699          disableTraceLocks = YES;
1700          m = [NSMutableString stringWithCapacity: 1000];
1701          stack = [GSStackTrace new];
1702          [stack trace];
1703          [m appendFormat: @"Deadlock on %@ at\n  %@\n",
1704            mutex, [stack symbols]];
1705          RELEASE(stack);
1706          count = [dependencies count];
1707          while (index < count)
1708            {
1709              NSArray           *symbols;
1710              NSThread          *thread;
1711              NSUInteger        frameCount;
1712
1713              thread = [dependencies objectAtIndex: index++];
1714              mutex = [dependencies objectAtIndex: index++];
1715              symbols = [[mutex stack] symbols];
1716              frameCount = [symbols count];
1717              if (frameCount > 0)
1718                {
1719                  NSUInteger    i;
1720
1721                  [m appendFormat: @"  depends on %@\n  blocked by %@\n  at\n",
1722                    mutex, thread];
1723                  for (i = 0; i < frameCount; i++)
1724                    {
1725                      [m appendFormat: @"    %@\n", [symbols objectAtIndex: i]];
1726                    }
1727                }
1728              else
1729                {
1730                  [m appendFormat: @"  depends on %@\n  blocked by %@\n",
1731                    mutex, thread];
1732                }
1733            }
1734          DESTROY(dependencies);
1735          /* NB. Return m directly because we have turned off tracing to
1736           * avoid recursion, and don't want lockInfoErr() to stop the
1737           * error being ruturned.
1738           */
1739          return m;
1740        }
1741      return nil;
1742    }
1743  return lockInfoErr(@"thread not active");
1744}
1745
1746@end
1747
1748
1749
1750@implementation GSRunLoopThreadInfo
1751- (void) addPerformer: (id)performer
1752{
1753  BOOL  signalled = NO;
1754
1755  [lock lock];
1756#if defined(_WIN32)
1757  if (INVALID_HANDLE_VALUE != event)
1758    {
1759      if (SetEvent(event) == 0)
1760        {
1761          NSLog(@"Set event failed - %@", [NSError _last]);
1762        }
1763      else
1764        {
1765          signalled = YES;
1766        }
1767    }
1768#else
1769{
1770  NSTimeInterval        start = 0.0;
1771
1772  /* The write could concievably fail if the pipe is full.
1773   * In that case we need to release the lock temporarily to allow the other
1774   * thread to consume data from the pipe.  It's possible that the thread
1775   * and its runloop might stop during that ... so we need to check that
1776   * outputFd is still valid.
1777   */
1778  while (outputFd >= 0
1779    && NO == (signalled = (write(outputFd, "0", 1) == 1) ? YES : NO))
1780    {
1781      NSTimeInterval    now = [NSDate timeIntervalSinceReferenceDate];
1782
1783      if (0.0 == start)
1784        {
1785          start = now;
1786        }
1787      else if (now - start >= 1.0)
1788        {
1789          NSLog(@"Unable to signal %@ within a second; blocked?", self);
1790          break;
1791        }
1792      [lock unlock];
1793      [lock lock];
1794    }
1795}
1796#endif
1797  if (YES == signalled)
1798    {
1799      [performers addObject: performer];
1800    }
1801  [lock unlock];
1802  if (NO == signalled)
1803    {
1804      /* We failed to add the performer ... so we must invalidate it in
1805       * case there is code waiting for it to complete.
1806       */
1807      [performer invalidate];
1808    }
1809}
1810
1811- (void) dealloc
1812{
1813  [self invalidate];
1814  DESTROY(lock);
1815  DESTROY(loop);
1816  [super dealloc];
1817}
1818
1819- (id) init
1820{
1821#ifdef _WIN32
1822  if ((event = CreateEvent(NULL, TRUE, FALSE, NULL)) == INVALID_HANDLE_VALUE)
1823    {
1824      DESTROY(self);
1825      [NSException raise: NSInternalInconsistencyException
1826        format: @"Failed to create event to handle perform in thread"];
1827    }
1828#else
1829  int	fd[2];
1830
1831  if (pipe(fd) == 0)
1832    {
1833      int	e;
1834
1835      inputFd = fd[0];
1836      outputFd = fd[1];
1837      if ((e = fcntl(inputFd, F_GETFL, 0)) >= 0)
1838	{
1839	  e |= NBLK_OPT;
1840	  if (fcntl(inputFd, F_SETFL, e) < 0)
1841	    {
1842	      [NSException raise: NSInternalInconsistencyException
1843		format: @"Failed to set non block flag for perform in thread"];
1844	    }
1845	}
1846      else
1847	{
1848	  [NSException raise: NSInternalInconsistencyException
1849	    format: @"Failed to get non block flag for perform in thread"];
1850	}
1851      if ((e = fcntl(outputFd, F_GETFL, 0)) >= 0)
1852	{
1853	  e |= NBLK_OPT;
1854	  if (fcntl(outputFd, F_SETFL, e) < 0)
1855	    {
1856	      [NSException raise: NSInternalInconsistencyException
1857		format: @"Failed to set non block flag for perform in thread"];
1858	    }
1859	}
1860      else
1861	{
1862	  [NSException raise: NSInternalInconsistencyException
1863	    format: @"Failed to get non block flag for perform in thread"];
1864	}
1865    }
1866  else
1867    {
1868      DESTROY(self);
1869      [NSException raise: NSInternalInconsistencyException
1870        format: @"Failed to create pipe to handle perform in thread"];
1871    }
1872#endif
1873  lock = [NSLock new];
1874  performers = [NSMutableArray new];
1875  return self;
1876}
1877
1878- (void) invalidate
1879{
1880  NSArray       *p;
1881
1882  [lock lock];
1883  p = AUTORELEASE(performers);
1884  performers = nil;
1885#ifdef _WIN32
1886  if (event != INVALID_HANDLE_VALUE)
1887    {
1888      CloseHandle(event);
1889      event = INVALID_HANDLE_VALUE;
1890    }
1891#else
1892  if (inputFd >= 0)
1893    {
1894      close(inputFd);
1895      inputFd = -1;
1896    }
1897  if (outputFd >= 0)
1898    {
1899      close(outputFd);
1900      outputFd = -1;
1901    }
1902#endif
1903  [lock unlock];
1904  [p makeObjectsPerformSelector: @selector(invalidate)];
1905}
1906
1907- (void) fire
1908{
1909  NSArray	*toDo;
1910  unsigned int	i;
1911  unsigned int	c;
1912
1913  [lock lock];
1914#if defined(_WIN32)
1915  if (event != INVALID_HANDLE_VALUE)
1916    {
1917      if (ResetEvent(event) == 0)
1918        {
1919          NSLog(@"Reset event failed - %@", [NSError _last]);
1920        }
1921    }
1922#else
1923  if (inputFd >= 0)
1924    {
1925      char	buf[BUFSIZ];
1926
1927      /* We don't care how much we read.  If there have been multiple
1928       * performers queued then there will be multiple bytes available,
1929       * but we always handle all available performers, so we can also
1930       * read all available bytes.
1931       * The descriptor is non-blocking ... so it's safe to ask for more
1932       * bytes than are available.
1933       */
1934      while (read(inputFd, buf, sizeof(buf)) > 0)
1935	;
1936    }
1937#endif
1938
1939  c = [performers count];
1940  if (0 == c)
1941    {
1942      /* We deal with all available performers each time we fire, so
1943       * it's likely that we will fire when we have no performers left.
1944       * In that case we can skip the copying and emptying of the array.
1945       */
1946      [lock unlock];
1947      return;
1948    }
1949  toDo = [NSArray arrayWithArray: performers];
1950  [performers removeAllObjects];
1951  [lock unlock];
1952
1953  for (i = 0; i < c; i++)
1954    {
1955      GSPerformHolder	*h = [toDo objectAtIndex: i];
1956
1957      [loop performSelector: @selector(fire)
1958		     target: h
1959		   argument: nil
1960		      order: 0
1961		      modes: [h modes]];
1962    }
1963}
1964@end
1965
1966GSRunLoopThreadInfo *
1967GSRunLoopInfoForThread(NSThread *aThread)
1968{
1969  GSRunLoopThreadInfo   *info;
1970
1971  if (aThread == nil)
1972    {
1973      aThread = GSCurrentThread();
1974    }
1975  if (aThread->_runLoopInfo == nil)
1976    {
1977      [gnustep_global_lock lock];
1978      if (aThread->_runLoopInfo == nil)
1979        {
1980          aThread->_runLoopInfo = [GSRunLoopThreadInfo new];
1981	}
1982      [gnustep_global_lock unlock];
1983    }
1984  info = aThread->_runLoopInfo;
1985  return info;
1986}
1987
1988@implementation GSPerformHolder
1989
1990+ (GSPerformHolder*) newForReceiver: (id)r
1991			   argument: (id)a
1992			   selector: (SEL)s
1993			      modes: (NSArray*)m
1994			       lock: (NSConditionLock*)l
1995{
1996  GSPerformHolder	*h;
1997
1998  h = (GSPerformHolder*)NSAllocateObject(self, 0, NSDefaultMallocZone());
1999  h->receiver = RETAIN(r);
2000  h->argument = RETAIN(a);
2001  h->selector = s;
2002  h->modes = RETAIN(m);
2003  h->lock = l;
2004
2005  return h;
2006}
2007
2008- (void) dealloc
2009{
2010  DESTROY(exception);
2011  DESTROY(receiver);
2012  DESTROY(argument);
2013  DESTROY(modes);
2014  if (lock != nil)
2015    {
2016      [lock lock];
2017      [lock unlockWithCondition: 1];
2018      lock = nil;
2019    }
2020  NSDeallocateObject(self);
2021  GSNOSUPERDEALLOC;
2022}
2023
2024- (NSString*) description
2025{
2026  return [[super description] stringByAppendingFormat: @" [%s %s]",
2027    class_getName(object_getClass(receiver)), sel_getName(selector)];
2028}
2029
2030- (void) fire
2031{
2032  GSRunLoopThreadInfo   *threadInfo;
2033
2034  if (receiver == nil)
2035    {
2036      return;	// Already fired!
2037    }
2038  threadInfo = GSRunLoopInfoForThread(GSCurrentThread());
2039  [threadInfo->loop cancelPerformSelectorsWithTarget: self];
2040  NS_DURING
2041    {
2042      [receiver performSelector: selector withObject: argument];
2043    }
2044  NS_HANDLER
2045    {
2046      ASSIGN(exception, localException);
2047      if (nil == lock)
2048        {
2049          NSLog(@"*** NSRunLoop ignoring exception '%@' (reason '%@') "
2050            @"raised during perform in other thread... with receiver %p (%s) "
2051            @"and selector '%s'",
2052            [localException name], [localException reason], receiver,
2053            class_getName(object_getClass(receiver)),
2054            sel_getName(selector));
2055        }
2056    }
2057  NS_ENDHANDLER
2058  DESTROY(receiver);
2059  DESTROY(argument);
2060  DESTROY(modes);
2061  if (lock != nil)
2062    {
2063      NSConditionLock	*l = lock;
2064
2065      [lock lock];
2066      lock = nil;
2067      [l unlockWithCondition: 1];
2068    }
2069}
2070
2071- (void) invalidate
2072{
2073  if (invalidated == NO)
2074    {
2075      invalidated = YES;
2076      DESTROY(receiver);
2077      if (lock != nil)
2078        {
2079          NSConditionLock	*l = lock;
2080
2081          [lock lock];
2082          lock = nil;
2083          [l unlockWithCondition: 1];
2084        }
2085    }
2086}
2087
2088- (BOOL) isInvalidated
2089{
2090  return invalidated;
2091}
2092
2093- (NSArray*) modes
2094{
2095  return modes;
2096}
2097@end
2098
2099@implementation	NSObject (NSThreadPerformAdditions)
2100
2101- (void) performSelectorOnMainThread: (SEL)aSelector
2102			  withObject: (id)anObject
2103		       waitUntilDone: (BOOL)aFlag
2104			       modes: (NSArray*)anArray
2105{
2106  /* It's possible that this method could be called before the NSThread
2107   * class is initialised, so we check and make sure it's initiailised
2108   * if necessary.
2109   */
2110  if (defaultThread == nil)
2111    {
2112      [NSThread currentThread];
2113    }
2114  [self performSelector: aSelector
2115               onThread: defaultThread
2116             withObject: anObject
2117          waitUntilDone: aFlag
2118                  modes: anArray];
2119}
2120
2121- (void) performSelectorOnMainThread: (SEL)aSelector
2122			  withObject: (id)anObject
2123		       waitUntilDone: (BOOL)aFlag
2124{
2125  [self performSelectorOnMainThread: aSelector
2126			 withObject: anObject
2127		      waitUntilDone: aFlag
2128			      modes: commonModes()];
2129}
2130
2131- (void) performSelector: (SEL)aSelector
2132                onThread: (NSThread*)aThread
2133              withObject: (id)anObject
2134           waitUntilDone: (BOOL)aFlag
2135                   modes: (NSArray*)anArray
2136{
2137  GSRunLoopThreadInfo   *info;
2138  NSThread	        *t;
2139
2140  if ([anArray count] == 0)
2141    {
2142      return;
2143    }
2144
2145  t = GSCurrentThread();
2146  if (aThread == nil)
2147    {
2148      aThread = t;
2149    }
2150  info = GSRunLoopInfoForThread(aThread);
2151  if (t == aThread)
2152    {
2153      /* Perform in current thread.
2154       */
2155      if (aFlag == YES || info->loop == nil)
2156	{
2157          /* Wait until done or no run loop.
2158           */
2159	  [self performSelector: aSelector withObject: anObject];
2160	}
2161      else
2162	{
2163          /* Don't wait ... schedule operation in run loop.
2164           */
2165	  [info->loop performSelector: aSelector
2166                               target: self
2167                             argument: anObject
2168                                order: 0
2169                                modes: anArray];
2170	}
2171    }
2172  else
2173    {
2174      GSPerformHolder   *h;
2175      NSConditionLock	*l = nil;
2176
2177      if ([aThread isFinished] == YES)
2178        {
2179          [NSException raise: NSInternalInconsistencyException
2180            format: @"perform [%@-%@] attempted on finished thread (%@)",
2181            NSStringFromClass([self class]),
2182            NSStringFromSelector(aSelector),
2183            aThread];
2184        }
2185      if (aFlag == YES)
2186	{
2187	  l = [[NSConditionLock alloc] init];
2188	}
2189
2190      h = [GSPerformHolder newForReceiver: self
2191				 argument: anObject
2192				 selector: aSelector
2193				    modes: anArray
2194				     lock: l];
2195      [info addPerformer: h];
2196      if (l != nil)
2197	{
2198          [l lockWhenCondition: 1];
2199	  [l unlock];
2200	  RELEASE(l);
2201          if ([h isInvalidated] == NO)
2202            {
2203              /* If we have an exception passed back from the remote thread,
2204               * re-raise it.
2205               */
2206              if (nil != h->exception)
2207                {
2208                  NSException       *e = AUTORELEASE(RETAIN(h->exception));
2209
2210                  RELEASE(h);
2211                  [e raise];
2212                }
2213            }
2214	}
2215      RELEASE(h);
2216    }
2217}
2218
2219- (void) performSelector: (SEL)aSelector
2220                onThread: (NSThread*)aThread
2221              withObject: (id)anObject
2222           waitUntilDone: (BOOL)aFlag
2223{
2224  [self performSelector: aSelector
2225               onThread: aThread
2226             withObject: anObject
2227          waitUntilDone: aFlag
2228                  modes: commonModes()];
2229}
2230
2231- (void) performSelectorInBackground: (SEL)aSelector
2232                          withObject: (id)anObject
2233{
2234  [NSThread detachNewThreadSelector: aSelector
2235                           toTarget: self
2236                         withObject: anObject];
2237}
2238
2239@end
2240
2241/**
2242 * <p>
2243 *   This function is provided to let threads started by some other
2244 *   software library register themselves to be used with the
2245 *   GNUstep system.  All such threads should call this function
2246 *   before attempting to use any GNUstep objects.
2247 * </p>
2248 * <p>
2249 *   Returns <code>YES</code> if the thread can be registered,
2250 *   <code>NO</code> if it is already registered.
2251 * </p>
2252 * <p>
2253 *   Sends out a <code>NSWillBecomeMultiThreadedNotification</code>
2254 *   if the process was not already multithreaded.
2255 * </p>
2256 */
2257BOOL
2258GSRegisterCurrentThread(void)
2259{
2260  return [NSThread _createThreadForCurrentPthread];
2261}
2262
2263/**
2264 * <p>
2265 *   This function is provided to let threads started by some other
2266 *   software library unregister themselves from the GNUstep threading
2267 *   system.
2268 * </p>
2269 * <p>
2270 *   Calling this function causes a
2271 *   <code>NSThreadWillExitNotification</code>
2272 *   to be sent out, and destroys the GNUstep NSThread object
2273 *   associated with the thread (like [NSThread+exit]) but does
2274 *   not exit the underlying thread.
2275 * </p>
2276 */
2277void
2278GSUnregisterCurrentThread(void)
2279{
2280  unregisterActiveThread(GSCurrentThread());
2281}
2282