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: ¬ 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 |
} |