/[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.72 - (show annotations) (download)
Thu Jul 31 23:49:31 2003 UTC (20 years, 8 months ago) by ayers
Branch: MAIN
CVS Tags: base-1_7_3, base-1_7_4, base-1_8_0
Branch point for: freeze_1_8_0
Changes since 1.71: +2 -2 lines
	Header reorganizsateion - Please refer to ChangeLog

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

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