/[gnustep]/gnustep/core/base/Source/NSThread.m
ViewVC logotype

Contents of /gnustep/core/base/Source/NSThread.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.73 - (show annotations) (download)
Tue Sep 30 18:19:03 2003 UTC (20 years, 7 months ago) by CaS
Branch: MAIN
Changes since 1.72: +32 -13 lines
Thread safety fix ... ensure notifications are sent before we become
multithrteaded.

1 /** Control of executable units within a shared virtual memory space
2 Copyright (C) 1996-2000 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 Library 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 Library General Public License for more details.
24
25 You should have received a copy of the GNU Library General Public
26 License along with this library; if not, write to the Free
27 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
28
29 <title>NSThread class reference</title>
30 $Date: 2003/07/31 23:49:31 $ $Revision: 1.72 $
31 */
32
33 #include "config.h"
34 #include "GNUstepBase/preface.h"
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef HAVE_NANOSLEEP
39 #include <time.h>
40 #endif
41
42 #include "Foundation/NSException.h"
43 #include "Foundation/NSThread.h"
44 #include "Foundation/NSLock.h"
45 #include "Foundation/NSString.h"
46 #include "Foundation/NSNotificationQueue.h"
47 #include "Foundation/NSRunLoop.h"
48 #include "Foundation/NSConnection.h"
49 #include "Foundation/NSInvocation.h"
50
51 @class GSPerformHolder;
52
53 static Class threadClass = Nil;
54 static NSNotificationCenter *nc = nil;
55
56 /**
57 * Sleep until the current date/time is the specified time interval
58 * past the reference date/time.<br />
59 * Implemented as a function taking an NSTimeInterval argument in order
60 * to avoid objc messaging and object allocation/deallocation (NSDate)
61 * overheads.<br />
62 * Used to implement [NSThread+sleepUntilDate:]
63 */
64 void
65 GSSleepUntilIntervalSinceReferenceDate(NSTimeInterval when)
66 {
67 extern NSTimeInterval GSTimeNow();
68 NSTimeInterval delay;
69
70 // delay is always the number of seconds we still need to wait
71 delay = when - GSTimeNow();
72
73 #ifdef HAVE_NANOSLEEP
74 // Avoid any possibility of overflow by sleeping in chunks.
75 while (delay > 32768)
76 {
77 struct timespec request;
78
79 request.tv_sec = (time_t)32768;
80 request.tv_nsec = (long)0;
81 nanosleep(&request, 0);
82 delay = when - GSTimeNow();
83 }
84 if (delay > 0)
85 {
86 struct timespec request;
87 struct timespec remainder;
88
89 request.tv_sec = (time_t)delay;
90 request.tv_nsec = (long)((delay - request.tv_sec) * 1000000000);
91 remainder.tv_sec = 0;
92 remainder.tv_nsec = 0;
93
94 /*
95 * With nanosleep, we can restart the sleep after a signal by using
96 * the remainder information ... so we can be sure to sleep to the
97 * desired limit without having to re-generate the delay needed.
98 */
99 while (nanosleep(&request, &remainder) < 0
100 && (remainder.tv_sec > 0 || remainder.tv_nsec > 0))
101 {
102 request.tv_sec = remainder.tv_sec;
103 request.tv_nsec = remainder.tv_nsec;
104 remainder.tv_sec = 0;
105 remainder.tv_nsec = 0;
106 }
107 }
108 #else
109
110 /*
111 * Avoid integer overflow by breaking up long sleeps.
112 */
113 while (delay > 30.0*60.0)
114 {
115 // sleep 30 minutes
116 #if defined(__MINGW__)
117 Sleep (30*60*1000);
118 #else
119 sleep (30*60);
120 #endif
121 delay = when - GSTimeNow();
122 }
123
124 /*
125 * sleeping may return early because of signals, so we need to re-calculate
126 * the required delay and check to see if we need to sleep again.
127 */
128 while (delay > 0)
129 {
130 #ifdef HAVE_USLEEP
131 usleep ((int)(delay*1000000));
132 #else
133 #if defined(__MINGW__)
134 Sleep (delay*1000);
135 #else
136 sleep ((int)delay);
137 #endif
138 #endif
139 delay = when - GSTimeNow();
140 }
141 #endif
142 }
143
144 static NSArray *
145 commonModes()
146 {
147 static NSArray *modes = nil;
148
149 if (modes == nil)
150 {
151 [gnustep_global_lock lock];
152 if (modes == nil)
153 {
154 Class c = NSClassFromString(@"NSApplication");
155 SEL s = @selector(allRunLoopModes);
156
157 if (c != 0 && [c respondsToSelector: s])
158 {
159 modes = RETAIN([c performSelector: s]);
160 }
161 else
162 {
163 modes = [[NSArray alloc] initWithObjects:
164 NSDefaultRunLoopMode, NSConnectionReplyMode, nil];
165 }
166 }
167 [gnustep_global_lock unlock];
168 }
169 return modes;
170 }
171
172 #if !defined(HAVE_OBJC_THREAD_ADD) && !defined(NeXT_RUNTIME)
173 /* We need to access these private vars in the objc runtime - because
174 the objc runtime's API is not enough powerful for the GNUstep
175 extensions we want to add. */
176 extern objc_mutex_t __objc_runtime_mutex;
177 extern int __objc_runtime_threads_alive;
178 extern int __objc_is_multi_threaded;
179
180 inline static void objc_thread_add ()
181 {
182 objc_mutex_lock(__objc_runtime_mutex);
183 __objc_is_multi_threaded = 1;
184 __objc_runtime_threads_alive++;
185 objc_mutex_unlock(__objc_runtime_mutex);
186 }
187
188 inline static void objc_thread_remove ()
189 {
190 objc_mutex_lock(__objc_runtime_mutex);
191 __objc_runtime_threads_alive--;
192 objc_mutex_unlock(__objc_runtime_mutex);
193 }
194 #endif /* not HAVE_OBJC_THREAD_ADD */
195
196 @interface NSThread (Private)
197 - (id) _initWithSelector: (SEL)s toTarget: (id)t withObject: (id)o;
198 - (void) _sendThreadMethod;
199 @end
200
201 /*
202 * Flag indicating whether the objc runtime ever went multi-threaded.
203 */
204 static BOOL entered_multi_threaded_state = NO;
205
206 /*
207 * Default thread.
208 */
209 static NSThread *defaultThread = nil;
210
211 /**
212 * <p>
213 * This function is a GNUstep extension. It pretty much
214 * duplicates the functionality of [NSThread +currentThread]
215 * but is more efficient and is used internally throughout
216 * GNUstep.
217 * </p>
218 * <p>
219 * Returns the current thread. Could perhaps return <code>nil</code>
220 * if executing a thread that was started outside the GNUstep
221 * environment and not registered (this should not happen in a
222 * well-coded application).
223 * </p>
224 */
225 inline NSThread*
226 GSCurrentThread()
227 {
228 NSThread *t;
229
230 if (entered_multi_threaded_state == NO)
231 {
232 /*
233 * If the NSThread class has been initialized, we will have a default
234 * thread set up - otherwise we must make sure the class is initialised.
235 */
236 if (defaultThread == nil)
237 {
238 t = [NSThread currentThread];
239 }
240 else
241 {
242 t = defaultThread;
243 }
244 }
245 else
246 {
247 t = (NSThread*)objc_thread_get_data();
248 if (t == nil)
249 {
250 fprintf(stderr, "ALERT ... GSCurrentThread() ... the "
251 "objc_thread_get_data() call returned nil!");
252 fflush(stderr); // Needed for windoze
253 }
254 }
255 return t;
256 }
257
258 /**
259 * Fast access function for thread dictionary of current thread.
260 */
261 NSMutableDictionary*
262 GSDictionaryForThread(NSThread *t)
263 {
264 if (t == nil)
265 {
266 t = GSCurrentThread();
267 }
268 if (t == nil)
269 {
270 return nil;
271 }
272 else
273 {
274 NSMutableDictionary *dict = t->_thread_dictionary;
275
276 if (dict == nil)
277 {
278 dict = [t threadDictionary];
279 }
280 return dict;
281 }
282 }
283
284 /**
285 * Fast access function for thread dictionary of current thread.
286 */
287 NSMutableDictionary*
288 GSCurrentThreadDictionary()
289 {
290 return GSDictionaryForThread(nil);
291 }
292
293 /*
294 * The special timer which we set up in the run loop of the main thread
295 * to perform housekeeping duties. NSRunLoop needs to call this private
296 * function so it knows about the housekeeping timer and won't keep the
297 * loop running just to do housekeeping.
298 *
299 * The NSUserDefaults system registers as an observer of GSHousekeeping
300 * notifications in order to synchronise the in-memory cache and the
301 * on-disk database.
302 */
303 static NSTimer *housekeeper = nil;
304 NSTimer *GSHousekeeper()
305 {
306 return housekeeper;
307 }
308
309 /**
310 * Returns the runloop for the specified thread (or, if t is nil,
311 * for the current thread). Creates a new runloop if necessary.<br />
312 * Returns nil on failure.
313 */
314 NSRunLoop*
315 GSRunLoopForThread(NSThread *t)
316 {
317 static NSString *key = @"NSRunLoopThreadKey";
318 NSMutableDictionary *d = GSDictionaryForThread(t);
319 NSRunLoop *r;
320
321 r = [d objectForKey: key];
322 if (r == nil)
323 {
324 if (d != nil)
325 {
326 r = [NSRunLoop new];
327 [d setObject: r forKey: key];
328 RELEASE(r);
329 if (housekeeper == nil && (t == nil || t == defaultThread))
330 {
331 CREATE_AUTORELEASE_POOL (arp);
332 NSNotificationCenter *ctr;
333 NSNotification *not;
334 NSInvocation *inv;
335 SEL sel;
336
337 ctr = [NSNotificationCenter defaultCenter];
338 not = [NSNotification notificationWithName: @"GSHousekeeping"
339 object: nil
340 userInfo: nil];
341 sel = @selector(postNotification:);
342 inv = [NSInvocation invocationWithMethodSignature:
343 [ctr methodSignatureForSelector: sel]];
344 [inv setTarget: ctr];
345 [inv setSelector: sel];
346 [inv setArgument: &not atIndex: 2];
347 [inv retainArguments];
348
349 housekeeper = [[NSTimer alloc] initWithFireDate: nil
350 interval: 30.0
351 target: inv
352 selector: NULL
353 userInfo: nil
354 repeats: YES];
355 [r addTimer: housekeeper forMode: NSDefaultRunLoopMode];
356 RELEASE(arp);
357 }
358 }
359 }
360 return r;
361 }
362
363 /*
364 * Callback function so send notifications on becoming multi-threaded.
365 */
366 static void
367 gnustep_base_thread_callback()
368 {
369 /*
370 * Protect this function with locking ... to avoid any possibility
371 * of multiple threads registering with the system simultaneously,
372 * and so that all NSWillBecomeMultiThreadedNotifications are sent
373 * out before any second thread can interfere with anything.
374 */
375 if (entered_multi_threaded_state == NO)
376 {
377 [gnustep_global_lock lock];
378 if (entered_multi_threaded_state == NO)
379 {
380 NS_DURING
381 {
382 [GSPerformHolder class]; // Force initialization
383
384 /*
385 * Post a notification if this is the first new thread
386 * to be created.
387 * Won't work properly if threads are not all created
388 * by this class, but it's better than nothing.
389 */
390 if (nc == nil)
391 {
392 nc = [NSNotificationCenter defaultCenter];
393 }
394 [nc postNotificationName: NSWillBecomeMultiThreadedNotification
395 object: nil
396 userInfo: nil];
397 }
398 NS_HANDLER
399 {
400 }
401 NS_ENDHANDLER
402 entered_multi_threaded_state = YES;
403 }
404 [gnustep_global_lock unlock];
405 }
406 }
407
408
409 /**
410 * This class encapsulates OpenStep threading. See [NSLock] and its
411 * subclasses for handling synchronisation between threads.<br />
412 * Each process begins with a main thread and additional threads can
413 * be created using NSThread. The GNUstep implementation of OpenStep
414 * has been carefully designed so that the internals of the base
415 * library do not use threading (except for methods which explicitly
416 * deal with threads of course) so that you can write applications
417 * without threading. Non-threaded applications re more efficient
418 * (no locking is required) and are easier to debug during development.
419 */
420 @implementation NSThread
421
422 /**
423 * <p>
424 * Returns the NSThread object corresponding to the current thread.
425 * </p>
426 * <p>
427 * NB. In GNUstep the library internals use the GSCurrentThread()
428 * function as a more efficient mechanism for doing this job - so
429 * you cannot use a category to override this method and expect
430 * the library internals to use your implementation.
431 * </p>
432 */
433 + (NSThread*) currentThread
434 {
435 NSThread *t = nil;
436
437 if (entered_multi_threaded_state == NO)
438 {
439 /*
440 * The NSThread class has been initialized - so we will have a default
441 * thread set up unless the default thread subsequently exited.
442 */
443 t = defaultThread;
444 }
445 if (t == nil)
446 {
447 t = (NSThread*)objc_thread_get_data();
448 if (t == nil)
449 {
450 fprintf(stderr, "ALERT ... [NSThread +currentThread] ... the "
451 "objc_thread_get_data() call returned nil!");
452 fflush(stderr); // Needed for windoze
453 }
454 }
455 return t;
456 }
457
458 /**
459 * Create a new thread - use this method rather than alloc-init
460 */
461 + (void) detachNewThreadSelector: (SEL)aSelector
462 toTarget: (id)aTarget
463 withObject: (id)anArgument
464 {
465 NSThread *thread;
466
467 /*
468 * Make sure the notification is posted BEFORE the new thread starts.
469 */
470 gnustep_base_thread_callback();
471
472 /*
473 * Create the new thread.
474 */
475 thread = (NSThread*)NSAllocateObject(self, 0, NSDefaultMallocZone());
476 thread = [thread _initWithSelector: aSelector
477 toTarget: aTarget
478 withObject: anArgument];
479
480 /*
481 * Have the runtime detach the thread
482 */
483 if (objc_thread_detach(@selector(_sendThreadMethod), thread, nil) == NULL)
484 {
485 entered_multi_threaded_state = NO;
486 [NSException raise: NSInternalInconsistencyException
487 format: @"Unable to detach thread (unknown error)"];
488 }
489 }
490
491 /**
492 * Terminating a thread
493 * What happens if the thread doesn't call +exit - it doesn't terminate!
494 */
495 + (void) exit
496 {
497 NSThread *t;
498
499 t = GSCurrentThread();
500 if (t->_active == YES)
501 {
502 /*
503 * Set the thread to be inactive to avoid any possibility of recursion.
504 */
505 t->_active = NO;
506
507 /*
508 * Let observers know this thread is exiting.
509 */
510 if (nc == nil)
511 {
512 nc = [NSNotificationCenter defaultCenter];
513 }
514 [nc postNotificationName: NSThreadWillExitNotification
515 object: t
516 userInfo: nil];
517
518 /*
519 * destroy the thread object.
520 */
521 DESTROY(t);
522
523 objc_thread_set_data (NULL);
524
525 /*
526 * Tell the runtime to exit the thread
527 */
528 objc_thread_exit();
529 }
530 }
531
532 /*
533 * Class initialization
534 */
535 + (void) initialize
536 {
537 if (self == [NSThread class])
538 {
539 /*
540 * The objc runtime calls this callback AFTER creating a new thread -
541 * which is not correct for us, but does at least mean that we can tell
542 * if we have become multi-threaded due to a call to the runtime directly
543 * rather than via the NSThread class.
544 */
545 objc_set_thread_callback(gnustep_base_thread_callback);
546
547 /*
548 * Ensure that the default thread exists.
549 */
550 defaultThread
551 = (NSThread*)NSAllocateObject(self, 0, NSDefaultMallocZone());
552 defaultThread = [defaultThread _initWithSelector: (SEL)0
553 toTarget: nil
554 withObject: nil];
555 defaultThread->_active = YES;
556 objc_thread_set_data(defaultThread);
557 threadClass = self;
558 }
559 }
560
561 /**
562 * Returns a flag to say whether the application is multi-threaded or not.
563 * An application is considered to be multi-threaded if any thread other
564 * than the main thread has been started, irrespective of whether that
565 * thread has since terminated.
566 */
567 + (BOOL) isMultiThreaded
568 {
569 return entered_multi_threaded_state;
570 }
571
572 /**
573 * Set the priority of the current thread. This is a value in the
574 * range 0.0 (lowest) to 1.0 (highest) which is mapped to the underlying
575 * system priorities. The current gnu objc runtime supports three
576 * priority levels which you can obtain using values of 0.0, 0.5, and 1.0
577 */
578 + (void) setThreadPriority: (double)pri
579 {
580 int p;
581
582 if (pri <= 0.3)
583 p = OBJC_THREAD_LOW_PRIORITY;
584 else if (pri <= 0.6)
585 p = OBJC_THREAD_BACKGROUND_PRIORITY;
586 else
587 p = OBJC_THREAD_INTERACTIVE_PRIORITY;
588
589 objc_thread_set_priority(p);
590 }
591
592 /**
593 * Delaying a thread ... pause until the specified date.
594 */
595 + (void) sleepUntilDate: (NSDate*)date
596 {
597 GSSleepUntilIntervalSinceReferenceDate([date timeIntervalSinceReferenceDate]);
598 }
599
600
601 /**
602 * Return the priority of the current thread.
603 */
604 + (double) threadPriority
605 {
606 int p = objc_thread_get_priority();
607
608 if (p == OBJC_THREAD_LOW_PRIORITY)
609 return 0.0;
610 else if (p == OBJC_THREAD_BACKGROUND_PRIORITY)
611 return 0.5;
612 else if (p == OBJC_THREAD_INTERACTIVE_PRIORITY)
613 return 1.0;
614 else
615 return 0.0; // Unknown.
616 }
617
618
619
620 /*
621 * Thread instance methods.
622 */
623
624 - (void) dealloc
625 {
626 if (_active == YES)
627 {
628 [NSException raise: NSInternalInconsistencyException
629 format: @"Deallocating an active thread without [+exit]!"];
630 }
631 DESTROY(_thread_dictionary);
632 DESTROY(_target);
633 DESTROY(_arg);
634 [NSAutoreleasePool _endThread: self];
635
636 if (_thread_dictionary != nil)
637 {
638 /*
639 * Try again to get rid of thread dictionary.
640 */
641 init_autorelease_thread_vars(&_autorelease_vars);
642 DESTROY(_thread_dictionary);
643 [NSAutoreleasePool _endThread: self];
644 if (_thread_dictionary != nil)
645 {
646 init_autorelease_thread_vars(&_autorelease_vars);
647 NSLog(@"Oops - leak - thread dictionary is %@", _thread_dictionary);
648 [NSAutoreleasePool _endThread: self];
649 }
650 }
651 if (self == defaultThread)
652 {
653 defaultThread = nil;
654 }
655 NSDeallocateObject(self);
656 }
657
658 - (id) init
659 {
660 RELEASE(self);
661 return [NSThread currentThread];
662 }
663
664 - (id) _initWithSelector: (SEL)s toTarget: (id)t withObject: (id)o
665 {
666 /* initialize our ivars. */
667 _selector = s;
668 _target = RETAIN(t);
669 _arg = RETAIN(o);
670 _thread_dictionary = nil; // Initialize this later only when needed
671 _exception_handler = NULL;
672 _active = NO;
673 init_autorelease_thread_vars(&_autorelease_vars);
674 return self;
675 }
676
677 - (void) _sendThreadMethod
678 {
679 /*
680 * We are running in the new thread - so we store ourself in the thread
681 * dictionary and release ourself - thus, when the thread exits, we will
682 * be deallocated cleanly.
683 */
684 objc_thread_set_data(self);
685 _active = YES;
686
687 /*
688 * Let observers know a new thread is starting.
689 */
690 if (nc == nil)
691 {
692 nc = [NSNotificationCenter defaultCenter];
693 }
694 [nc postNotificationName: NSThreadDidStartNotification
695 object: self
696 userInfo: nil];
697
698 [_target performSelector: _selector withObject: _arg];
699 [NSThread exit];
700 }
701
702 /**
703 * Return the thread dictionary. This dictionary can be used to store
704 * arbitrary thread specific data.<br />
705 * NB. This cannot be autoreleased, since we cannot be sure that the
706 * autorelease pool for the thread will continue to exist for the entire
707 * life of the thread!
708 */
709 - (NSMutableDictionary*) threadDictionary
710 {
711 if (_thread_dictionary == nil)
712 {
713 _thread_dictionary = [NSMutableDictionary new];
714 }
715 return _thread_dictionary;
716 }
717
718 @end
719
720
721
722 /**
723 * This class performs a dual function ...
724 * <p>
725 * As a class, it is responsible for handling incoming events from
726 * the main runloop on a special inputFd. This consumes any bytes
727 * written to wake the main runloop.<br />
728 * During initialisation, the default runloop is set up to watch
729 * for data arriving on inputFd.
730 * </p>
731 * <p>
732 * As instances, each instance retains perform receiver and argument
733 * values as long as they are needed, and handles locking to support
734 * mthods which want to block until an action has been performed.
735 * </p>
736 * <p>
737 * The initialize method of this class is called before any new threads
738 * run.
739 * </p>
740 */
741 @interface GSPerformHolder : NSObject
742 {
743 id receiver;
744 id argument;
745 SEL selector;
746 NSArray *modes;
747 NSConditionLock *lock; // Not retained.
748 }
749 + (BOOL) isValid;
750 + (GSPerformHolder*) newForReceiver: (id)r
751 argument: (id)a
752 selector: (SEL)s
753 modes: (NSArray*)m
754 lock: (NSConditionLock*)l;
755 + (void) receivedEvent: (void*)data
756 type: (RunLoopEventType)type
757 extra: (void*)extra
758 forMode: (NSString*)mode;
759 + (NSDate*) timedOutEvent: (void*)data
760 type: (RunLoopEventType)type
761 forMode: (NSString*)mode;
762 - (void) fire;
763 @end
764
765 @implementation GSPerformHolder
766
767 static NSLock *subthreadsLock = nil;
768 static int inputFd = -1;
769 static int outputFd = -1;
770 static NSMutableArray *perfArray = nil;
771 static NSDate *theFuture;
772
773 + (void) initialize
774 {
775 NSRunLoop *loop = GSRunLoopForThread(defaultThread);
776 NSArray *m = commonModes();
777 unsigned count = [m count];
778 unsigned i;
779 BOOL pipeOK = NO;
780
781 theFuture = RETAIN([NSDate distantFuture]);
782
783 #ifndef __MINGW__
784 {
785 int fd[2];
786
787 if (pipe(fd) == 0)
788 {
789 inputFd = fd[0];
790 outputFd = fd[1];
791 pipeOK = YES;
792 }
793 }
794 #else
795 {
796 HANDLE readh, writeh;
797
798 if (CreatePipe(&readh, &writeh, NULL, 0) != 0)
799 {
800 inputFd = _open_osfhandle((int)readh, 0);
801 outputFd = _open_osfhandle((int)writeh, 0);
802 pipeOK = YES;
803 }
804 }
805 #endif
806 if (pipeOK == NO)
807 {
808 [NSException raise: NSInternalInconsistencyException
809 format: @"Failed to create pipe to handle perform in main thread"];
810 }
811
812 subthreadsLock = [[NSLock alloc] init];
813
814 perfArray = [[NSMutableArray alloc] initWithCapacity: 10];
815
816 for (i = 0; i < count; i++ )
817 {
818 [loop addEvent: (void*)inputFd
819 type: ET_RDESC
820 watcher: (id<RunLoopEvents>)self
821 forMode: [m objectAtIndex: i]];
822 }
823 }
824
825 + (BOOL) isValid
826 {
827 return YES;
828 }
829
830 + (GSPerformHolder*) newForReceiver: (id)r
831 argument: (id)a
832 selector: (SEL)s
833 modes: (NSArray*)m
834 lock: (NSConditionLock*)l
835 {
836 GSPerformHolder *h;
837
838 h = (GSPerformHolder*)NSAllocateObject(self, 0, NSDefaultMallocZone());
839 h->receiver = RETAIN(r);
840 h->argument = RETAIN(a);
841 h->selector = s;
842 h->modes = RETAIN(m);
843 h->lock = l;
844
845 [subthreadsLock lock];
846 [perfArray addObject: h];
847 write(outputFd, "0", 1);
848 [subthreadsLock unlock];
849
850 return h;
851 }
852
853 + (void) receivedEvent: (void*)data
854 type: (RunLoopEventType)type
855 extra: (void*)extra
856 forMode: (NSString*)mode
857 {
858 NSRunLoop *loop = [NSRunLoop currentRunLoop];
859 unsigned int i;
860 unsigned int c;
861 char dummy;
862
863 read(inputFd, &dummy, 1);
864
865 [subthreadsLock lock];
866
867 c = [perfArray count];
868 for (i = 0; i < c; i++)
869 {
870 GSPerformHolder *h = [perfArray objectAtIndex: i];
871
872 [loop performSelector: @selector(fire)
873 target: h
874 argument: nil
875 order: 0
876 modes: h->modes];
877 }
878 [perfArray removeAllObjects];
879
880 [subthreadsLock unlock];
881 }
882
883 + (NSDate*) timedOutEvent: (void*)data
884 type: (RunLoopEventType)type
885 forMode: (NSString*)mode
886 {
887 return theFuture;
888 }
889
890 - (void) dealloc
891 {
892 DESTROY(receiver);
893 DESTROY(argument);
894 DESTROY(modes);
895 if (lock != nil)
896 {
897 [lock lock];
898 [lock unlockWithCondition: 1];
899 lock = nil;
900 }
901 NSDeallocateObject(self);
902 }
903
904 - (void) fire
905 {
906 if (receiver == nil)
907 {
908 return; // Already fired!
909 }
910 [GSRunLoopForThread(defaultThread) cancelPerformSelectorsWithTarget: self];
911 [receiver performSelector: selector withObject: argument];
912 DESTROY(receiver);
913 DESTROY(argument);
914 DESTROY(modes);
915 if (lock == nil)
916 {
917 RELEASE(self);
918 }
919 else
920 {
921 NSConditionLock *l = lock;
922
923 [lock lock];
924 lock = nil;
925 [l unlockWithCondition: 1];
926 }
927 }
928 @end
929
930 /**
931 * Extra methods to permit messages to be sent to an object such that they
932 * are executed in the <em>main</em> thread.<br />
933 * The main thread is the thread in which the GNUstep system is started,
934 * and where the GNUstep gui is used, it is the thread in which gui
935 * drawing operations <strong>must</strong> be performed.
936 */
937 @implementation NSObject (NSMainThreadPerformAdditions)
938
939 /**
940 * <p>This method performs aSelector on the receiver, passing anObject as
941 * an argument, but does so in the main thread of the program. The receiver
942 * and anObject are both retained until the method is performed.
943 * </p>
944 * <p>The selector is performed when the runloop of the main thread next
945 * runs in one of the modes specified in anArray.<br />
946 * Where this method has been called more than once before the runloop
947 * of the main thread runs in the required mode, the order in which the
948 * operations in the main thread is done is the same as that in which
949 * they were added using this method.
950 * </p>
951 * <p>If there are no modes in anArray,
952 * the method has no effect and simply returns immediately.
953 * </p>
954 * <p>The argument aFlag specifies whether the method should wait until
955 * the selector has been performed before returning.<br />
956 * <strong>NB.</strong> This method does <em>not</em> cause the runloop of
957 * the main thread to be run ... so if the runloop is not executed by some
958 * code in the main thread, the thread waiting for the perform to complete
959 * will block forever.
960 * </p>
961 * <p>As a special case, if aFlag == YES and the current thread is the main
962 * thread, the modes array is ignored and the selector is performed immediately.
963 * This behavior is necessary to avoid the main thread being blocked by
964 * waiting for a perform which will never happen because the runloop is
965 * not executing.
966 * </p>
967 */
968 - (void) performSelectorOnMainThread: (SEL)aSelector
969 withObject: (id)anObject
970 waitUntilDone: (BOOL)aFlag
971 modes: (NSArray*)anArray
972 {
973 NSThread *t;
974
975 if ([anArray count] == 0)
976 {
977 return;
978 }
979
980 t = GSCurrentThread();
981 if (t == defaultThread)
982 {
983 if (aFlag == YES)
984 {
985 [self performSelector: aSelector withObject: anObject];
986 }
987 else
988 {
989 [GSRunLoopForThread(t) performSelector: aSelector
990 target: self
991 argument: anObject
992 order: 0
993 modes: anArray];
994 }
995 }
996 else
997 {
998 GSPerformHolder *h;
999 NSConditionLock *l = nil;
1000
1001 if (aFlag == YES)
1002 {
1003 l = [[NSConditionLock alloc] init];
1004 }
1005
1006 h = [GSPerformHolder newForReceiver: self
1007 argument: anObject
1008 selector: aSelector
1009 modes: anArray
1010 lock: l];
1011
1012 if (aFlag == YES)
1013 {
1014 [l lockWhenCondition: 1];
1015 RELEASE(h);
1016 [l unlock];
1017 RELEASE(l);
1018 }
1019 }
1020 }
1021
1022 /**
1023 * Invokes -performSelectorOnMainThread:withObject:waitUntilDone:modes:
1024 * using the supplied arguments and an array containing common modes.<br />
1025 * These modes consist of NSRunLoopMode, NSConnectionreplyMode, and if
1026 * in an application, the NSApplication modes.
1027 */
1028 - (void) performSelectorOnMainThread: (SEL)aSelector
1029 withObject: (id)anObject
1030 waitUntilDone: (BOOL)aFlag
1031 {
1032 [self performSelectorOnMainThread: aSelector
1033 withObject: anObject
1034 waitUntilDone: aFlag
1035 modes: commonModes()];
1036 }
1037 @end
1038
1039 typedef struct { @defs(NSThread) } NSThread_ivars;
1040
1041
1042 /**
1043 * <p>
1044 * This function is provided to let threads started by some other
1045 * software library register themselves to be used with the
1046 * GNUstep system. All such threads should call this function
1047 * before attempting to use any GNUstep objects.
1048 * </p>
1049 * <p>
1050 * Returns <code>YES</code> if the thread can be registered,
1051 * <code>NO</code> if it is already registered.
1052 * </p>
1053 * <p>
1054 * Sends out a <code>NSWillBecomeMultiThreadedNotification</code>
1055 * if the process was not already multithreaded.
1056 * </p>
1057 */
1058 BOOL
1059 GSRegisterCurrentThread (void)
1060 {
1061 NSThread *thread;
1062
1063 /*
1064 * Do nothing and return NO if the thread is known to us.
1065 */
1066 if ((NSThread*)objc_thread_get_data() != nil)
1067 {
1068 return NO;
1069 }
1070
1071 /*
1072 * Make sure the Objective-C runtime knows there is an additional thread.
1073 */
1074 objc_thread_add ();
1075
1076 if (threadClass == 0)
1077 {
1078 /*
1079 * If the threadClass has not been set, NSThread has not been
1080 * initialised, and there is no default thread. So we must
1081 * initialise now ... which will make the current thread the default.
1082 */
1083 NSCAssert(entered_multi_threaded_state == NO,
1084 NSInternalInconsistencyException);
1085 thread = [NSThread currentThread];
1086 }
1087 else
1088 {
1089 /*
1090 * Create the new thread object.
1091 */
1092 thread = (NSThread*)NSAllocateObject (threadClass, 0,
1093 NSDefaultMallocZone ());
1094 thread = [thread _initWithSelector: NULL toTarget: nil withObject: nil];
1095 objc_thread_set_data (thread);
1096 ((NSThread_ivars *)thread)->_active = YES;
1097 }
1098
1099 /*
1100 * We post the notification after we register the thread.
1101 * NB. Even if we are the default thread, we do this to register the app
1102 * as being multi-threaded - this is so that, if this thread is unregistered
1103 * later, it does not leave us with a bad default thread.
1104 */
1105 gnustep_base_thread_callback();
1106
1107 return YES;
1108 }
1109
1110 /**
1111 * <p>
1112 * This function is provided to let threads started by some other
1113 * software library unregister themselves from the GNUstep threading
1114 * system.
1115 * </p>
1116 * <p>
1117 * Calling this function causes a
1118 * <code>NSThreadWillExitNotification</code>
1119 * to be sent out, and destroys the GNUstep NSThread object
1120 * associated with the thread.
1121 * </p>
1122 */
1123 void
1124 GSUnregisterCurrentThread (void)
1125 {
1126 NSThread *thread;
1127
1128 thread = GSCurrentThread();
1129
1130 if (((NSThread_ivars *)thread)->_active == YES)
1131 {
1132 /*
1133 * Set the thread to be inactive to avoid any possibility of recursion.
1134 */
1135 ((NSThread_ivars *)thread)->_active = NO;
1136
1137 /*
1138 * Let observers know this thread is exiting.
1139 */
1140 if (nc == nil)
1141 {
1142 nc = [NSNotificationCenter defaultCenter];
1143 }
1144 [nc postNotificationName: NSThreadWillExitNotification
1145 object: thread
1146 userInfo: nil];
1147
1148 /*
1149 * destroy the thread object.
1150 */
1151 DESTROY (thread);
1152
1153 objc_thread_set_data (NULL);
1154
1155 /*
1156 * Make sure Objc runtime knows there is a thread less to manage
1157 */
1158 objc_thread_remove ();
1159 }
1160 }

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26