]> git.saurik.com Git - apple/cf.git/blob - RunLoop.subproj/CFMachPort.c
CF-368.1.tar.gz
[apple/cf.git] / RunLoop.subproj / CFMachPort.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFMachPort.c
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 /*
29 [The following dissertation was written mostly for the
30 benefit of open source developers.]
31
32 Writing a run loop source can be a tricky business, but
33 for CFMachPort that part is relatively straightforward.
34 Thus, it makes a good example for study. Particularly
35 interesting for examination is the process of caching
36 the objects in a non-retaining cache, the invalidation
37 and deallocation sequences, locking for thread-safety,
38 and how the invalidation callback is used.
39
40 CFMachPort is a version 1 CFRunLoopSource, implemented
41 by a few functions. See CFMachPortCreateRunLoopSource()
42 for details on how the run loop source is setup. Note
43 how the source is kept track of by the CFMachPort, so
44 that it can be returned again and again from that function.
45 This helps not only reduce the amount of memory expended
46 in run loop source objects, but eliminates redundant
47 registrations with the run loop and the excess time and
48 memory that would consume. It also allows the CFMachPort
49 to propogate its own invalidation to the run loop source
50 representing it.
51
52 CFMachPortCreateWithPort() is the funnel point for the
53 creation of CFMachPort instances. The cache is first
54 probed to see if an instance with that port is already
55 available, and return that. The object is next allocated
56 and mostly initialized, before it is registered for death
57 notification. This is because cleaning up the memory is
58 simpler than trying to get rid of the registration if
59 memory allocation later fails. The new object must be at
60 least partially initialized (into a harmless state) so
61 that it can be safely invalidated/deallocated if something
62 fails later in creation. Any object allocated with
63 _CFRuntimeCreateInstance() may only be disposed by using
64 CFRelease() (never CFAllocatorDeallocate!) so the class
65 deallocation function __CFMachPortDeallocate() must be
66 able to handle that, and initializing the object to have
67 NULL fields and whatnot makes that possible. The creation
68 eventually inserts the new object in the cache.
69
70 A CFMachPort may be explicitly invalidated, autoinvalidated
71 due to the death of the port (that process is not discussed
72 further here), or invalidated as part of the deallocation
73 process when the last reference is released. For
74 convenience, in all cases this is done through
75 CFMachPortInvalidate(). To prevent the CFMachPort from
76 being freed in mid-function due to the callouts, the object
77 is retained at the beginning of the function. But if this
78 invalidation is due to the object being deallocated, a
79 retain and then release at the end of the function would
80 cause a recursive call to __CFMachPortDeallocate(). The
81 retain protection should be immaterial though at that stage.
82 Invalidation also removes the object from the cache; though
83 the object itself is not yet destroyed, invalidation makes
84 it "useless".
85
86 The best way to learn about the locking is to look through
87 the code -- it's fairly straightforward. The one thing
88 worth calling attention to is how locks must be unlocked
89 before invoking any user-defined callout function, and
90 usually retaken after it returns. This supports reentrancy
91 (which is distinct from thread-safety).
92
93 The invalidation callback, if one has been set, is called
94 at invalidation time, but before the object has been torn
95 down so that the port and other properties may be retrieved
96 from the object in the callback. Note that if the callback
97 is attempted to be set after the CFMachPort is invalid,
98 the function is simply called. This helps with certain
99 race conditions where the invalidation notification might
100 be lost. Only the owner/creator of a CFMachPort should
101 really be setting the invalidation callback.
102
103 Now, the CFMachPort is not retained/released around all
104 callouts, but the callout may release the last reference.
105 Also, sometimes it is friendly to retain/release the
106 user-defined "info" around callouts, so that clients
107 don't have to worry about that. These may be some things
108 to think about in the future, but is usually overkill.
109
110 Finally, one tricky bit mostly specific to CFMachPort
111 deserves mention: use of __CFMachPortCurrentPID. Mach
112 ports are not inherited by a child process created with
113 fork(), unlike most other things. Thus, on fork(), in
114 the child, all cached CFMachPorts should be invalidated.
115 However, because the ports were never in the new task's
116 port space, the Mach part of the kernel doesn't send
117 dead-name notifications (which is used to autoinvalidate
118 CFMachPorts) to the new task, which is reasonable. There
119 is also no other notification that a fork() has occurred,
120 though, so how is CFMachPort to deal with this? An
121 atfork() function, similar to atexit(), would be nice,
122 but is not available. So CFMachPort does the best it
123 can, which is to compare the current process's PID with
124 the last PID recorded and if different, invalidate all
125 CFMachPorts, whenever various CFMachPort functions are
126 called. To avoid going insane, I've assumed that clients
127 aren't going to fork() as a result of callouts from this
128 code, which in a couple places might actually cause trouble.
129 It also isn't completely thread-safe.
130
131 In general, with higher level functionalities in the system,
132 it isn't even possible for a process to fork() and the child
133 not exec(), but continue running, since the higher levels
134 have done one-time initializations that aren't going to
135 happen again.
136
137 - Chris Kane
138
139 */
140
141 #if defined(__MACH__)
142
143 #include <CoreFoundation/CFMachPort.h>
144 #include <CoreFoundation/CFRunLoop.h>
145 #include <CoreFoundation/CFDictionary.h>
146 #include <mach/mach.h>
147 #include <mach/mach_error.h>
148 #include <mach/notify.h>
149 #include "CFInternal.h"
150
151 static CFSpinLock_t __CFAllMachPortsLock = 0;
152 static CFMutableDictionaryRef __CFAllMachPorts = NULL;
153 static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL;
154 static CFMachPortRef __CFNotifyMachPort = NULL;
155 static int __CFMachPortCurrentPID = 0;
156
157 struct __CFMachPort {
158 CFRuntimeBase _base;
159 CFSpinLock_t _lock;
160 mach_port_t _port; /* immutable; invalidated */
161 mach_port_t _oldnotify; /* immutable; invalidated */
162 CFRunLoopSourceRef _source; /* immutable, once created; invalidated */
163 CFMachPortInvalidationCallBack _icallout;
164 CFMachPortCallBack _callout; /* immutable */
165 CFMachPortContext _context; /* immutable; invalidated */
166 };
167
168 /* Bit 0 in the base reserved bits is used for invalid state */
169 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
170 /* Bit 2 in the base reserved bits is used for has-send-ref state */
171 /* Bit 3 in the base reserved bits is used for is-deallocing state */
172
173 CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
174 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 0, 0);
175 }
176
177 CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) {
178 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 1);
179 }
180
181 CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) {
182 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 0);
183 }
184
185 CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
186 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 1, 1);
187 }
188
189 CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
190 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 1, 1, 1);
191 }
192
193 CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
194 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 2, 2);
195 }
196
197 CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
198 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 2, 2, 1);
199 }
200
201 CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) {
202 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 3, 3);
203 }
204
205 CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) {
206 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 3, 3, 1);
207 }
208
209 CF_INLINE void __CFMachPortLock(CFMachPortRef mp) {
210 __CFSpinLock(&(mp->_lock));
211 }
212
213 CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) {
214 __CFSpinUnlock(&(mp->_lock));
215 }
216
217 // The only CFMachPort this releases is the notify port, which
218 // no one else should have gotten their grubby hands on. Set the
219 // new PID first, to avoid reentrant invocations of this function.
220 static void __CFMachPortDidFork(void) {
221 CFMachPortRef oldNotify;
222 __CFMachPortCurrentPID = getpid();
223 __CFSpinLock(&__CFAllMachPortsLock);
224 oldNotify = __CFNotifyMachPort;
225 __CFNotifyMachPort = NULL;
226 __CFNotifyRawMachPort = MACH_PORT_NULL;
227 if (NULL != __CFAllMachPorts) {
228 CFIndex idx, cnt;
229 CFMachPortRef *mps, buffer[128];
230 cnt = CFDictionaryGetCount(__CFAllMachPorts);
231 mps = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, cnt * sizeof(CFMachPortRef), 0);
232 CFDictionaryGetKeysAndValues(__CFAllMachPorts, NULL, (const void **)mps);
233 CFRelease(__CFAllMachPorts);
234 __CFAllMachPorts = NULL;
235 __CFSpinUnlock(&__CFAllMachPortsLock);
236 for (idx = 0; idx < cnt; idx++) {
237 // invalidation must be outside the lock
238 CFMachPortInvalidate(mps[idx]);
239 }
240 if (mps != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, mps);
241 } else {
242 __CFSpinUnlock(&__CFAllMachPortsLock);
243 }
244 if (NULL != oldNotify) {
245 // The global is NULL'd before this, just in case.
246 // __CFNotifyMachPort was in the cache and was
247 // invalidated above, but that's harmless.
248 CFRelease(oldNotify);
249 }
250 }
251
252 CF_INLINE void __CFMachPortCheckForFork(void) {
253 if (getpid() != __CFMachPortCurrentPID) {
254 __CFMachPortDidFork();
255 }
256 }
257
258 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
259 CFRunLoopSourceRef source;
260 if (NULL == __CFNotifyMachPort) return;
261 source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __CFNotifyMachPort, -1000);
262 CFRunLoopAddSource(rl, source, mode);
263 CFRelease(source);
264 }
265
266 static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, void *info) {
267 mach_msg_header_t *header = (mach_msg_header_t *)msg;
268 if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
269 mach_port_t dead_port = ((mach_dead_name_notification_t *)msg)->not_port;
270 CFMachPortRef existing;
271 /* If the CFMachPort has already been invalidated, it won't be found here. */
272 __CFSpinLock(&__CFAllMachPortsLock);
273 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
274 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
275 CFRetain(existing);
276 __CFSpinUnlock(&__CFAllMachPortsLock);
277 CFMachPortInvalidate(existing);
278 CFRelease(existing);
279 } else {
280 __CFSpinUnlock(&__CFAllMachPortsLock);
281 }
282 /* Delete port reference we got for this notification */
283 mach_port_deallocate(mach_task_self(), dead_port);
284 } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) {
285 mach_port_t dead_port = ((mach_port_deleted_notification_t *)msg)->not_port;
286 CFMachPortRef existing;
287 /* If the CFMachPort has already been invalidated, it won't be found here. */
288 __CFSpinLock(&__CFAllMachPortsLock);
289 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
290 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
291 CFRetain(existing);
292 __CFSpinUnlock(&__CFAllMachPortsLock);
293 CFMachPortInvalidate(existing);
294 CFRelease(existing);
295 } else {
296 __CFSpinUnlock(&__CFAllMachPortsLock);
297 }
298 /* Delete port reference we got for this notification */
299 // Don't do this, since this always fails, and could cause trouble
300 // mach_port_deallocate(mach_task_self(), dead_port);
301 }
302 }
303
304 static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
305 CFMachPortRef mp1 = (CFMachPortRef)cf1;
306 CFMachPortRef mp2 = (CFMachPortRef)cf2;
307 // __CFMachPortCheckForFork(); do not do this here
308 return (mp1->_port == mp2->_port);
309 }
310
311 static CFHashCode __CFMachPortHash(CFTypeRef cf) {
312 CFMachPortRef mp = (CFMachPortRef)cf;
313 // __CFMachPortCheckForFork(); do not do this here -- can cause strange reentrancies 3843642
314 return (CFHashCode)mp->_port;
315 }
316
317 static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) {
318 CFMachPortRef mp = (CFMachPortRef)cf;
319 CFStringRef result;
320 const char *locked;
321 CFStringRef contextDesc = NULL;
322 locked = mp->_lock ? "Yes" : "No";
323 if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) {
324 contextDesc = mp->_context.copyDescription(mp->_context.info);
325 }
326 if (NULL == contextDesc) {
327 contextDesc = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info);
328 }
329 result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %p, context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, mp->_callout, (NULL != contextDesc ? contextDesc : CFSTR("<no description>")));
330 if (NULL != contextDesc) {
331 CFRelease(contextDesc);
332 }
333 return result;
334 }
335
336 static void __CFMachPortDeallocate(CFTypeRef cf) {
337 CFMachPortRef mp = (CFMachPortRef)cf;
338 __CFMachPortSetIsDeallocing(mp);
339 CFMachPortInvalidate(mp);
340 // MUST deallocate the send right FIRST if necessary,
341 // then the receive right if necessary. Don't ask me why;
342 // if it's done in the other order the port will leak.
343 if (__CFMachPortHasSend(mp)) {
344 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_SEND, -1);
345 }
346 if (__CFMachPortHasReceive(mp)) {
347 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, -1);
348 }
349 __CFMachPortCheckForFork();
350 }
351
352 static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
353
354 static const CFRuntimeClass __CFMachPortClass = {
355 0,
356 "CFMachPort",
357 NULL, // init
358 NULL, // copy
359 __CFMachPortDeallocate,
360 __CFMachPortEqual,
361 __CFMachPortHash,
362 NULL, //
363 __CFMachPortCopyDescription
364 };
365
366 __private_extern__ void __CFMachPortInitialize(void) {
367 __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass);
368 __CFMachPortCurrentPID = getpid();
369 }
370
371 CFTypeID CFMachPortGetTypeID(void) {
372 __CFMachPortCheckForFork();
373 return __kCFMachPortTypeID;
374 }
375
376 CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
377 CFMachPortRef result;
378 mach_port_t port;
379 kern_return_t ret;
380 __CFMachPortCheckForFork();
381 if (shouldFreeInfo) *shouldFreeInfo = true;
382 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
383 if (KERN_SUCCESS != ret) {
384 return NULL;
385 }
386 ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
387 if (KERN_SUCCESS != ret) {
388 mach_port_destroy(mach_task_self(), port);
389 return NULL;
390 }
391 result = CFMachPortCreateWithPort(allocator, port, callout, context, shouldFreeInfo);
392 if (NULL != result) {
393 __CFMachPortSetHasReceive(result);
394 __CFMachPortSetHasSend(result);
395 }
396 return result;
397 }
398
399 /* Note: any receive or send rights that the port contains coming in will
400 * not be cleaned up by CFMachPort; it will increment and decrement
401 * references on the port if the kernel ever allows that in the future,
402 * but will not cleanup any references you got when you got the port. */
403 CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
404 CFMachPortRef memory;
405 SInt32 size;
406 Boolean didCreateNotifyPort = false;
407 CFRunLoopSourceRef source;
408 __CFMachPortCheckForFork();
409 if (shouldFreeInfo) *shouldFreeInfo = true;
410 __CFSpinLock(&__CFAllMachPortsLock);
411 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)port, (const void **)&memory)) {
412 __CFSpinUnlock(&__CFAllMachPortsLock);
413 return (CFMachPortRef)CFRetain(memory);
414 }
415 size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase);
416 memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, __kCFMachPortTypeID, size, NULL);
417 if (NULL == memory) {
418 __CFSpinUnlock(&__CFAllMachPortsLock);
419 return NULL;
420 }
421 __CFMachPortUnsetValid(memory);
422 memory->_lock = 0;
423 memory->_port = port;
424 memory->_source = NULL;
425 memory->_icallout = NULL;
426 memory->_context.info = NULL;
427 memory->_context.retain = NULL;
428 memory->_context.release = NULL;
429 memory->_context.copyDescription = NULL;
430 if (MACH_PORT_NULL == __CFNotifyRawMachPort) {
431 kern_return_t ret;
432 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &__CFNotifyRawMachPort);
433 if (KERN_SUCCESS != ret) {
434 __CFSpinUnlock(&__CFAllMachPortsLock);
435 CFRelease(memory);
436 return NULL;
437 }
438 didCreateNotifyPort = true;
439 }
440 // Do not register for notifications on the notify port
441 if (MACH_PORT_NULL != __CFNotifyRawMachPort && port != __CFNotifyRawMachPort) {
442 mach_port_t old_port;
443 kern_return_t ret;
444 old_port = MACH_PORT_NULL;
445 ret = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, __CFNotifyRawMachPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port);
446 if (ret != KERN_SUCCESS) {
447 __CFSpinUnlock(&__CFAllMachPortsLock);
448 CFRelease(memory);
449 return NULL;
450 }
451 memory->_oldnotify = old_port;
452 }
453 __CFMachPortSetValid(memory);
454 memory->_callout = callout;
455 if (NULL != context) {
456 CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext));
457 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
458 }
459 if (NULL == __CFAllMachPorts) {
460 __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorMallocZone, 0, NULL, NULL); // XXX_PCB make it GC weak.
461 _CFDictionarySetCapacity(__CFAllMachPorts, 20);
462 }
463 CFDictionaryAddValue(__CFAllMachPorts, (void *)port, memory);
464 __CFSpinUnlock(&__CFAllMachPortsLock);
465 if (didCreateNotifyPort) {
466 // __CFNotifyMachPort ends up in cache
467 CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL);
468 __CFMachPortSetHasReceive(mp);
469 __CFNotifyMachPort = mp;
470 }
471 if (NULL != __CFNotifyMachPort) {
472 // We do this so that it gets into each thread's run loop, since
473 // we don't know which run loop is the main thread's, and that's
474 // not necessarily the "right" one anyway. This won't happen for
475 // the call which creates the __CFNotifyMachPort itself, but that's
476 // OK since it will happen in the invocation of this function
477 // from which that call was triggered.
478 source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __CFNotifyMachPort, -1000);
479 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
480 CFRelease(source);
481 }
482 if (shouldFreeInfo) *shouldFreeInfo = false;
483 return memory;
484 }
485
486 mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
487 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort");
488 __CFGenericValidateType(mp, __kCFMachPortTypeID);
489 __CFMachPortCheckForFork();
490 return mp->_port;
491 }
492
493 void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
494 __CFGenericValidateType(mp, __kCFMachPortTypeID);
495 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
496 __CFMachPortCheckForFork();
497 CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext));
498 }
499
500 void CFMachPortInvalidate(CFMachPortRef mp) {
501 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate");
502 __CFGenericValidateType(mp, __kCFMachPortTypeID);
503 __CFMachPortCheckForFork();
504 if (!__CFMachPortIsDeallocing(mp)) {
505 CFRetain(mp);
506 }
507 __CFSpinLock(&__CFAllMachPortsLock);
508 if (NULL != __CFAllMachPorts) {
509 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(mp->_port));
510 }
511 __CFSpinUnlock(&__CFAllMachPortsLock);
512 __CFMachPortLock(mp);
513 if (__CFMachPortIsValid(mp)) {
514 CFRunLoopSourceRef source;
515 void *info;
516 mach_port_t old_port = mp->_oldnotify;
517 CFMachPortInvalidationCallBack callout = mp->_icallout;
518 __CFMachPortUnsetValid(mp);
519 __CFMachPortUnlock(mp);
520 if (NULL != callout) {
521 callout(mp, mp->_context.info);
522 }
523 __CFMachPortLock(mp);
524 // For hashing and equality purposes, cannot get rid of _port here
525 source = mp->_source;
526 mp->_source = NULL;
527 info = mp->_context.info;
528 mp->_context.info = NULL;
529 __CFMachPortUnlock(mp);
530 if (NULL != mp->_context.release) {
531 mp->_context.release(info);
532 }
533 if (NULL != source) {
534 CFRunLoopSourceInvalidate(source);
535 CFRelease(source);
536 }
537 // here we get rid of the previous notify port
538 // [cjk - somehow it is the right thing to do to
539 // hold this until this point, then deallocate it,
540 // though I don't understand what that triggers
541 // with respect to the send-once right, and I
542 // doubt people are doing the right thing about
543 // handling the "death" (CFMachPort included) of
544 // the send-once right.]
545 if (MACH_PORT_NULL != old_port) {
546 mach_port_deallocate(mach_task_self(), old_port);
547 }
548 } else {
549 __CFMachPortUnlock(mp);
550 }
551 if (!__CFMachPortIsDeallocing(mp)) {
552 CFRelease(mp);
553 }
554 }
555
556 Boolean CFMachPortIsValid(CFMachPortRef mp) {
557 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, Boolean, mp, "isValid");
558 __CFGenericValidateType(mp, __kCFMachPortTypeID);
559 __CFMachPortCheckForFork();
560 return __CFMachPortIsValid(mp);
561 }
562
563 CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
564 __CFGenericValidateType(mp, __kCFMachPortTypeID);
565 __CFMachPortCheckForFork();
566 return mp->_icallout;
567 }
568
569 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
570 __CFGenericValidateType(mp, __kCFMachPortTypeID);
571 __CFMachPortCheckForFork();
572 if (!__CFMachPortIsValid(mp) && NULL != callout) {
573 callout(mp, mp->_context.info);
574 } else {
575 mp->_icallout = callout;
576 }
577 }
578
579 /* Returns the number of messages queued for a receive port. */
580 CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
581 mach_port_status_t status;
582 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
583 kern_return_t ret;
584 ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num);
585 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
586 }
587
588 void CFMachPortInvalidateAll(void) {
589 // This function has been removed from the public API;
590 // it was a very bad idea to call it.
591 }
592
593
594 static mach_port_t __CFMachPortGetPort(void *info) {
595 CFMachPortRef mp = info;
596 __CFMachPortCheckForFork();
597 return mp->_port;
598 }
599
600 static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
601 CFMachPortRef mp = info;
602 void *context_info;
603 void (*context_release)(const void *);
604 __CFMachPortCheckForFork();
605 __CFMachPortLock(mp);
606 if (!__CFMachPortIsValid(mp)) {
607 __CFMachPortUnlock(mp);
608 return NULL;
609 }
610 if (NULL != mp->_context.retain) {
611 context_info = (void *)mp->_context.retain(mp->_context.info);
612 context_release = mp->_context.release;
613 } else {
614 context_info = mp->_context.info;
615 context_release = NULL;
616 }
617 __CFMachPortUnlock(mp);
618 mp->_callout(mp, msg, size, mp->_context.info);
619 if (context_release) {
620 context_release(context_info);
621 }
622 return NULL;
623 }
624
625 CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
626 CFRunLoopSourceRef result = NULL;
627 __CFGenericValidateType(mp, __kCFMachPortTypeID);
628 __CFMachPortCheckForFork();
629 __CFMachPortLock(mp);
630 if (!__CFMachPortIsValid(mp)) {
631 __CFMachPortUnlock(mp);
632 return NULL;
633 }
634 #if 0
635 #warning CF: adding ref to receive right is disabled for now -- doesnt work in 1F
636 if (!__CFMachPortHasReceive(mp)) {
637 kern_return_t ret;
638
639 // this fails in 1F with KERN_INVALID_VALUE -- only 0 and -1 are valid for delta
640 ret = mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, +1);
641 if (KERN_SUCCESS != ret) {
642 __CFMachPortUnlock(mp);
643 return NULL;
644 }
645 __CFMachPortSetHasReceive(mp);
646 }
647 #endif
648 if (NULL == mp->_source) {
649 CFRunLoopSourceContext1 context;
650 context.version = 1;
651 context.info = (void *)mp;
652 context.retain = (const void *(*)(const void *))CFRetain;
653 context.release = (void (*)(const void *))CFRelease;
654 context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
655 context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
656 context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
657 context.getPort = __CFMachPortGetPort;
658 context.perform = __CFMachPortPerform;
659 mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
660 }
661 if (NULL != mp->_source) {
662 result = (CFRunLoopSourceRef)CFRetain(mp->_source);
663 }
664 __CFMachPortUnlock(mp);
665 return result;
666 }
667
668 #endif /* __MACH__ */
669