/[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.74 - (show annotations) (download)
Tue Sep 30 18:57:49 2003 UTC (20 years, 6 months ago) by CaS
Branch: MAIN
CVS Tags: base-1_9_0, alex_latest_semistable
Changes since 1.73: +9 -3 lines
Improve warning messages

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

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