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, ¶m); 1023 param.sched_priority = pri; 1024 pthread_setschedparam(pthread_self(), policy, ¶m); 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, ¶m); 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