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