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