]> git.saurik.com Git - apple/cf.git/blob - RunLoop.subproj/CFMachPort.c
CF-368.18.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 <CoreFoundation/CFByteOrder.h>
147 #include <mach/mach.h>
148 #include <mach/mach_error.h>
149 #include <mach/notify.h>
150 #include "CFInternal.h"
151
152 static CFSpinLock_t __CFAllMachPortsLock = 0;
153 static CFMutableDictionaryRef __CFAllMachPorts = NULL;
154 static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL;
155 static CFMachPortRef __CFNotifyMachPort = NULL;
156 static int __CFMachPortCurrentPID = 0;
157
158 struct __CFMachPort {
159 CFRuntimeBase _base;
160 CFSpinLock_t _lock;
161 mach_port_t _port; /* immutable; invalidated */
162 mach_port_t _oldnotify; /* immutable; invalidated */
163 CFRunLoopSourceRef _source; /* immutable, once created; invalidated */
164 CFMachPortInvalidationCallBack _icallout;
165 CFMachPortCallBack _callout; /* immutable */
166 CFMachPortContext _context; /* immutable; invalidated */
167 };
168
169 /* Bit 0 in the base reserved bits is used for invalid state */
170 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
171 /* Bit 2 in the base reserved bits is used for has-send-ref state */
172 /* Bit 3 in the base reserved bits is used for is-deallocing state */
173
174 CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
175 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 0, 0);
176 }
177
178 CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) {
179 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 1);
180 }
181
182 CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) {
183 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 0);
184 }
185
186 CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
187 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 1, 1);
188 }
189
190 CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
191 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 1, 1, 1);
192 }
193
194 CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
195 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 2, 2);
196 }
197
198 CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
199 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 2, 2, 1);
200 }
201
202 CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) {
203 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 3, 3);
204 }
205
206 CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) {
207 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 3, 3, 1);
208 }
209
210 CF_INLINE void __CFMachPortLock(CFMachPortRef mp) {
211 __CFSpinLock(&(mp->_lock));
212 }
213
214 CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) {
215 __CFSpinUnlock(&(mp->_lock));
216 }
217
218 // The only CFMachPort this releases is the notify port, which
219 // no one else should have gotten their grubby hands on. Set the
220 // new PID first, to avoid reentrant invocations of this function.
221 static void __CFMachPortDidFork(void) {
222 CFMachPortRef oldNotify;
223 __CFMachPortCurrentPID = getpid();
224 __CFSpinLock(&__CFAllMachPortsLock);
225 oldNotify = __CFNotifyMachPort;
226 __CFNotifyMachPort = NULL;
227 __CFNotifyRawMachPort = MACH_PORT_NULL;
228 if (NULL != __CFAllMachPorts) {
229 CFIndex idx, cnt;
230 CFMachPortRef *mps, buffer[128];
231 cnt = CFDictionaryGetCount(__CFAllMachPorts);
232 mps = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, cnt * sizeof(CFMachPortRef), 0);
233 CFDictionaryGetKeysAndValues(__CFAllMachPorts, NULL, (const void **)mps);
234 CFRelease(__CFAllMachPorts);
235 __CFAllMachPorts = NULL;
236 __CFSpinUnlock(&__CFAllMachPortsLock);
237 for (idx = 0; idx < cnt; idx++) {
238 // invalidation must be outside the lock
239 CFMachPortInvalidate(mps[idx]);
240 }
241 if (mps != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, mps);
242 } else {
243 __CFSpinUnlock(&__CFAllMachPortsLock);
244 }
245 if (NULL != oldNotify) {
246 // The global is NULL'd before this, just in case.
247 // __CFNotifyMachPort was in the cache and was
248 // invalidated above, but that's harmless.
249 CFRelease(oldNotify);
250 }
251 }
252
253 CF_INLINE void __CFMachPortCheckForFork(void) {
254 if (getpid() != __CFMachPortCurrentPID) {
255 __CFMachPortDidFork();
256 }
257 }
258
259 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
260 CFRunLoopSourceRef source;
261 if (NULL == __CFNotifyMachPort) return;
262 source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __CFNotifyMachPort, -1000);
263 CFRunLoopAddSource(rl, source, mode);
264 CFRelease(source);
265 }
266
267 static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, void *info) {
268 mach_msg_header_t *header = (mach_msg_header_t *)msg;
269 if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
270 mach_port_t dead_port = ((mach_dead_name_notification_t *)msg)->not_port;
271 if (((mach_dead_name_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) {
272 dead_port = CFSwapInt32(dead_port);
273 }
274 CFMachPortRef existing;
275 /* If the CFMachPort has already been invalidated, it won't be found here. */
276 __CFSpinLock(&__CFAllMachPortsLock);
277 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
278 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
279 CFRetain(existing);
280 __CFSpinUnlock(&__CFAllMachPortsLock);
281 CFMachPortInvalidate(existing);
282 CFRelease(existing);
283 } else {
284 __CFSpinUnlock(&__CFAllMachPortsLock);
285 }
286 /* Delete port reference we got for this notification */
287 mach_port_deallocate(mach_task_self(), dead_port);
288 } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) {
289 mach_port_t dead_port = ((mach_port_deleted_notification_t *)msg)->not_port;
290 if (((mach_dead_name_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) {
291 dead_port = CFSwapInt32(dead_port);
292 }
293 CFMachPortRef existing;
294 /* If the CFMachPort has already been invalidated, it won't be found here. */
295 __CFSpinLock(&__CFAllMachPortsLock);
296 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) {
297 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port);
298 CFRetain(existing);
299 __CFSpinUnlock(&__CFAllMachPortsLock);
300 CFMachPortInvalidate(existing);
301 CFRelease(existing);
302 } else {
303 __CFSpinUnlock(&__CFAllMachPortsLock);
304 }
305 /* Delete port reference we got for this notification */
306 // Don't do this, since this always fails, and could cause trouble
307 // mach_port_deallocate(mach_task_self(), dead_port);
308 }
309 }
310
311 static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
312 CFMachPortRef mp1 = (CFMachPortRef)cf1;
313 CFMachPortRef mp2 = (CFMachPortRef)cf2;
314 // __CFMachPortCheckForFork(); do not do this here
315 return (mp1->_port == mp2->_port);
316 }
317
318 static CFHashCode __CFMachPortHash(CFTypeRef cf) {
319 CFMachPortRef mp = (CFMachPortRef)cf;
320 // __CFMachPortCheckForFork(); do not do this here -- can cause strange reentrancies 3843642
321 return (CFHashCode)mp->_port;
322 }
323
324 static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) {
325 CFMachPortRef mp = (CFMachPortRef)cf;
326 CFStringRef result;
327 const char *locked;
328 CFStringRef contextDesc = NULL;
329 locked = mp->_lock ? "Yes" : "No";
330 if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) {
331 contextDesc = mp->_context.copyDescription(mp->_context.info);
332 }
333 if (NULL == contextDesc) {
334 contextDesc = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info);
335 }
336 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>")));
337 if (NULL != contextDesc) {
338 CFRelease(contextDesc);
339 }
340 return result;
341 }
342
343 static void __CFMachPortDeallocate(CFTypeRef cf) {
344 CFMachPortRef mp = (CFMachPortRef)cf;
345 __CFMachPortSetIsDeallocing(mp);
346 CFMachPortInvalidate(mp);
347 // MUST deallocate the send right FIRST if necessary,
348 // then the receive right if necessary. Don't ask me why;
349 // if it's done in the other order the port will leak.
350 if (__CFMachPortHasSend(mp)) {
351 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_SEND, -1);
352 }
353 if (__CFMachPortHasReceive(mp)) {
354 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, -1);
355 }
356 __CFMachPortCheckForFork();
357 }
358
359 static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
360
361 static const CFRuntimeClass __CFMachPortClass = {
362 0,
363 "CFMachPort",
364 NULL, // init
365 NULL, // copy
366 __CFMachPortDeallocate,
367 __CFMachPortEqual,
368 __CFMachPortHash,
369 NULL, //
370 __CFMachPortCopyDescription
371 };
372
373 __private_extern__ void __CFMachPortInitialize(void) {
374 __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass);
375 __CFMachPortCurrentPID = getpid();
376 }
377
378 CFTypeID CFMachPortGetTypeID(void) {
379 __CFMachPortCheckForFork();
380 return __kCFMachPortTypeID;
381 }
382
383 CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
384 CFMachPortRef result;
385 mach_port_t port;
386 kern_return_t ret;
387 __CFMachPortCheckForFork();
388 if (shouldFreeInfo) *shouldFreeInfo = true;
389 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
390 if (KERN_SUCCESS != ret) {
391 return NULL;
392 }
393 ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
394 if (KERN_SUCCESS != ret) {
395 mach_port_destroy(mach_task_self(), port);
396 return NULL;
397 }
398 result = CFMachPortCreateWithPort(allocator, port, callout, context, shouldFreeInfo);
399 if (NULL != result) {
400 __CFMachPortSetHasReceive(result);
401 __CFMachPortSetHasSend(result);
402 }
403 return result;
404 }
405
406 /* Note: any receive or send rights that the port contains coming in will
407 * not be cleaned up by CFMachPort; it will increment and decrement
408 * references on the port if the kernel ever allows that in the future,
409 * but will not cleanup any references you got when you got the port. */
410 CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
411 CFMachPortRef memory;
412 SInt32 size;
413 Boolean didCreateNotifyPort = false;
414 CFRunLoopSourceRef source;
415 __CFMachPortCheckForFork();
416 if (shouldFreeInfo) *shouldFreeInfo = true;
417 __CFSpinLock(&__CFAllMachPortsLock);
418 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)port, (const void **)&memory)) {
419 __CFSpinUnlock(&__CFAllMachPortsLock);
420 return (CFMachPortRef)CFRetain(memory);
421 }
422 size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase);
423 memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, __kCFMachPortTypeID, size, NULL);
424 if (NULL == memory) {
425 __CFSpinUnlock(&__CFAllMachPortsLock);
426 return NULL;
427 }
428 __CFMachPortUnsetValid(memory);
429 memory->_lock = 0;
430 memory->_port = port;
431 memory->_source = NULL;
432 memory->_icallout = NULL;
433 memory->_context.info = NULL;
434 memory->_context.retain = NULL;
435 memory->_context.release = NULL;
436 memory->_context.copyDescription = NULL;
437 if (MACH_PORT_NULL == __CFNotifyRawMachPort) {
438 kern_return_t ret;
439 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &__CFNotifyRawMachPort);
440 if (KERN_SUCCESS != ret) {
441 __CFSpinUnlock(&__CFAllMachPortsLock);
442 CFRelease(memory);
443 return NULL;
444 }
445 didCreateNotifyPort = true;
446 }
447 // Do not register for notifications on the notify port
448 if (MACH_PORT_NULL != __CFNotifyRawMachPort && port != __CFNotifyRawMachPort) {
449 mach_port_t old_port;
450 kern_return_t ret;
451 old_port = MACH_PORT_NULL;
452 ret = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, __CFNotifyRawMachPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port);
453 if (ret != KERN_SUCCESS) {
454 __CFSpinUnlock(&__CFAllMachPortsLock);
455 CFRelease(memory);
456 return NULL;
457 }
458 memory->_oldnotify = old_port;
459 }
460 __CFMachPortSetValid(memory);
461 memory->_callout = callout;
462 if (NULL != context) {
463 CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext));
464 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
465 }
466 if (NULL == __CFAllMachPorts) {
467 __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorMallocZone, 0, NULL, NULL); // XXX_PCB make it GC weak.
468 _CFDictionarySetCapacity(__CFAllMachPorts, 20);
469 }
470 CFDictionaryAddValue(__CFAllMachPorts, (void *)port, memory);
471 __CFSpinUnlock(&__CFAllMachPortsLock);
472 if (didCreateNotifyPort) {
473 // __CFNotifyMachPort ends up in cache
474 CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL);
475 __CFMachPortSetHasReceive(mp);
476 __CFNotifyMachPort = mp;
477 }
478 if (NULL != __CFNotifyMachPort) {
479 // We do this so that it gets into each thread's run loop, since
480 // we don't know which run loop is the main thread's, and that's
481 // not necessarily the "right" one anyway. This won't happen for
482 // the call which creates the __CFNotifyMachPort itself, but that's
483 // OK since it will happen in the invocation of this function
484 // from which that call was triggered.
485 source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __CFNotifyMachPort, -1000);
486 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
487 CFRelease(source);
488 }
489 if (shouldFreeInfo) *shouldFreeInfo = false;
490 return memory;
491 }
492
493 mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
494 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort");
495 __CFGenericValidateType(mp, __kCFMachPortTypeID);
496 __CFMachPortCheckForFork();
497 return mp->_port;
498 }
499
500 void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
501 __CFGenericValidateType(mp, __kCFMachPortTypeID);
502 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
503 __CFMachPortCheckForFork();
504 CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext));
505 }
506
507 void CFMachPortInvalidate(CFMachPortRef mp) {
508 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate");
509 __CFGenericValidateType(mp, __kCFMachPortTypeID);
510 __CFMachPortCheckForFork();
511 if (!__CFMachPortIsDeallocing(mp)) {
512 CFRetain(mp);
513 }
514 __CFSpinLock(&__CFAllMachPortsLock);
515 if (NULL != __CFAllMachPorts) {
516 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(mp->_port));
517 }
518 __CFSpinUnlock(&__CFAllMachPortsLock);
519 __CFMachPortLock(mp);
520 if (__CFMachPortIsValid(mp)) {
521 CFRunLoopSourceRef source;
522 void *info;
523 mach_port_t old_port = mp->_oldnotify;
524 CFMachPortInvalidationCallBack callout = mp->_icallout;
525 __CFMachPortUnsetValid(mp);
526 __CFMachPortUnlock(mp);
527 if (NULL != callout) {
528 callout(mp, mp->_context.info);
529 }
530 __CFMachPortLock(mp);
531 // For hashing and equality purposes, cannot get rid of _port here
532 source = mp->_source;
533 mp->_source = NULL;
534 info = mp->_context.info;
535 mp->_context.info = NULL;
536 __CFMachPortUnlock(mp);
537 if (NULL != mp->_context.release) {
538 mp->_context.release(info);
539 }
540 if (NULL != source) {
541 CFRunLoopSourceInvalidate(source);
542 CFRelease(source);
543 }
544 // here we get rid of the previous notify port
545 // [cjk - somehow it is the right thing to do to
546 // hold this until this point, then deallocate it,
547 // though I don't understand what that triggers
548 // with respect to the send-once right, and I
549 // doubt people are doing the right thing about
550 // handling the "death" (CFMachPort included) of
551 // the send-once right.]
552 if (MACH_PORT_NULL != old_port) {
553 mach_port_deallocate(mach_task_self(), old_port);
554 }
555 } else {
556 __CFMachPortUnlock(mp);
557 }
558 if (!__CFMachPortIsDeallocing(mp)) {
559 CFRelease(mp);
560 }
561 }
562
563 Boolean CFMachPortIsValid(CFMachPortRef mp) {
564 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, Boolean, mp, "isValid");
565 __CFGenericValidateType(mp, __kCFMachPortTypeID);
566 __CFMachPortCheckForFork();
567 return __CFMachPortIsValid(mp);
568 }
569
570 CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
571 __CFGenericValidateType(mp, __kCFMachPortTypeID);
572 __CFMachPortCheckForFork();
573 return mp->_icallout;
574 }
575
576 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
577 __CFGenericValidateType(mp, __kCFMachPortTypeID);
578 __CFMachPortCheckForFork();
579 if (!__CFMachPortIsValid(mp) && NULL != callout) {
580 callout(mp, mp->_context.info);
581 } else {
582 mp->_icallout = callout;
583 }
584 }
585
586 /* Returns the number of messages queued for a receive port. */
587 CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
588 mach_port_status_t status;
589 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
590 kern_return_t ret;
591 ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num);
592 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
593 }
594
595 void CFMachPortInvalidateAll(void) {
596 // This function has been removed from the public API;
597 // it was a very bad idea to call it.
598 }
599
600
601 static mach_port_t __CFMachPortGetPort(void *info) {
602 CFMachPortRef mp = info;
603 __CFMachPortCheckForFork();
604 return mp->_port;
605 }
606
607 static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
608 CFMachPortRef mp = info;
609 void *context_info;
610 void (*context_release)(const void *);
611 __CFMachPortCheckForFork();
612 __CFMachPortLock(mp);
613 if (!__CFMachPortIsValid(mp)) {
614 __CFMachPortUnlock(mp);
615 return NULL;
616 }
617 if (NULL != mp->_context.retain) {
618 context_info = (void *)mp->_context.retain(mp->_context.info);
619 context_release = mp->_context.release;
620 } else {
621 context_info = mp->_context.info;
622 context_release = NULL;
623 }
624 __CFMachPortUnlock(mp);
625 mp->_callout(mp, msg, size, mp->_context.info);
626 if (context_release) {
627 context_release(context_info);
628 }
629 return NULL;
630 }
631
632 CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
633 CFRunLoopSourceRef result = NULL;
634 __CFGenericValidateType(mp, __kCFMachPortTypeID);
635 __CFMachPortCheckForFork();
636 __CFMachPortLock(mp);
637 if (!__CFMachPortIsValid(mp)) {
638 __CFMachPortUnlock(mp);
639 return NULL;
640 }
641 #if 0
642 #warning CF: adding ref to receive right is disabled for now -- doesnt work in 1F
643 if (!__CFMachPortHasReceive(mp)) {
644 kern_return_t ret;
645
646 // this fails in 1F with KERN_INVALID_VALUE -- only 0 and -1 are valid for delta
647 ret = mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, +1);
648 if (KERN_SUCCESS != ret) {
649 __CFMachPortUnlock(mp);
650 return NULL;
651 }
652 __CFMachPortSetHasReceive(mp);
653 }
654 #endif
655 if (NULL == mp->_source) {
656 CFRunLoopSourceContext1 context;
657 context.version = 1;
658 context.info = (void *)mp;
659 context.retain = (const void *(*)(const void *))CFRetain;
660 context.release = (void (*)(const void *))CFRelease;
661 context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
662 context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
663 context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
664 context.getPort = __CFMachPortGetPort;
665 context.perform = __CFMachPortPerform;
666 mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
667 }
668 if (NULL != mp->_source) {
669 result = (CFRunLoopSourceRef)CFRetain(mp->_source);
670 }
671 __CFMachPortUnlock(mp);
672 return result;
673 }
674
675 #endif /* __MACH__ */
676