]> git.saurik.com Git - apple/cf.git/blob - CFMachPort.c
CF-476.15.tar.gz
[apple/cf.git] / CFMachPort.c
1 /*
2 * Copyright (c) 2008 Apple 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 In general, with higher level functionalities in the system,
111 it isn't even possible for a process to fork() and the child
112 not exec(), but continue running, since the higher levels
113 have done one-time initializations that aren't going to
114 happen again.
115
116 - Chris Kane
117
118 */
119
120 #include <CoreFoundation/CFMachPort.h>
121 #include <CoreFoundation/CFRunLoop.h>
122 #include <CoreFoundation/CFDictionary.h>
123 #include <CoreFoundation/CFByteOrder.h>
124 #include <mach/mach.h>
125 #include <mach/mach_error.h>
126 #include <mach/notify.h>
127 #include <unistd.h>
128 #include "CFInternal.h"
129 #include <dlfcn.h>
130
131 static CFSpinLock_t __CFAllMachPortsLock = CFSpinLockInit;
132 static CFMutableDictionaryRef __CFAllMachPorts = NULL;
133 static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL;
134 static CFMachPortRef __CFNotifyMachPort = NULL;
135
136 struct __CFMachPort {
137 CFRuntimeBase _base;
138 CFSpinLock_t _lock;
139 mach_port_t _port; /* immutable; invalidated */
140 mach_port_t _oldnotify; /* immutable; invalidated */
141 CFRunLoopSourceRef _source; /* immutable, once created; invalidated */
142 CFMachPortInvalidationCallBack _icallout;
143 CFMachPortCallBack _callout; /* immutable */
144 CFMachPortContext _context; /* immutable; invalidated */
145 };
146
147 /* Bit 0 in the base reserved bits is used for invalid state */
148 /* Bit 1 in the base reserved bits is used for has-receive-ref state */
149 /* Bit 2 in the base reserved bits is used for has-send-ref state */
150 /* Bit 3 in the base reserved bits is used for is-deallocing state */
151
152 CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
153 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0);
154 }
155
156 CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) {
157 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
158 }
159
160 CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) {
161 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 0, 0, 0);
162 }
163
164 CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
165 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1);
166 }
167
168 CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
169 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
170 }
171
172 CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
173 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2);
174 }
175
176 CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
177 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
178 }
179
180 CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) {
181 return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3);
182 }
183
184 CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) {
185 __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
186 }
187
188 CF_INLINE void __CFMachPortLock(CFMachPortRef mp) {
189 __CFSpinLock(&(mp->_lock));
190 }
191
192 CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) {
193 __CFSpinUnlock(&(mp->_lock));
194 }
195
196 void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
197 CFRunLoopSourceRef source;
198 if (NULL == __CFNotifyMachPort) return;
199 source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000);
200 CFRunLoopAddSource(rl, source, mode);
201 CFRelease(source);
202 }
203
204 static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, void *info) {
205 mach_msg_header_t *header = (mach_msg_header_t *)msg;
206 mach_port_t dead_port = MACH_PORT_NULL;
207 if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
208 dead_port = ((mach_dead_name_notification_t *)msg)->not_port;
209 if (((mach_dead_name_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) {
210 dead_port = CFSwapInt32(dead_port);
211 }
212 } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) {
213 dead_port = ((mach_port_deleted_notification_t *)msg)->not_port;
214 if (((mach_port_deleted_notification_t *)msg)->NDR.int_rep != NDR_record.int_rep) {
215 dead_port = CFSwapInt32(dead_port);
216 }
217 } else {
218 return;
219 }
220
221 CFMachPortRef existing;
222 /* If the CFMachPort has already been invalidated, it won't be found here. */
223 __CFSpinLock(&__CFAllMachPortsLock);
224 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)dead_port, (const void **)&existing)) {
225 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)dead_port);
226 CFRetain(existing);
227 __CFSpinUnlock(&__CFAllMachPortsLock);
228 __CFMachPortLock(existing);
229 mach_port_t old_port = existing->_oldnotify;
230 existing->_oldnotify = MACH_PORT_NULL;
231 __CFMachPortUnlock(existing);
232 if (MACH_PORT_NULL != old_port) {
233 header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0) | MACH_MSGH_BITS_COMPLEX;
234 header->msgh_local_port = MACH_PORT_NULL;
235 header->msgh_remote_port = old_port;
236 mach_msg(header, MACH_SEND_MSG, header->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
237 }
238 CFMachPortInvalidate(existing);
239 CFRelease(existing);
240 } else {
241 __CFSpinUnlock(&__CFAllMachPortsLock);
242 }
243
244 if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) {
245 /* Delete port reference we got for this notification */
246 mach_port_deallocate(mach_task_self(), dead_port);
247 }
248 }
249
250 static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
251 CFMachPortRef mp1 = (CFMachPortRef)cf1;
252 CFMachPortRef mp2 = (CFMachPortRef)cf2;
253 return (mp1->_port == mp2->_port);
254 }
255
256 static CFHashCode __CFMachPortHash(CFTypeRef cf) {
257 CHECK_FOR_FORK();
258 CFMachPortRef mp = (CFMachPortRef)cf;
259 return (CFHashCode)mp->_port;
260 }
261
262 static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) {
263 CFMachPortRef mp = (CFMachPortRef)cf;
264 CFStringRef result;
265 const char *locked;
266 CFStringRef contextDesc = NULL;
267 locked = mp->_lock ? "Yes" : "No";
268 if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) {
269 contextDesc = mp->_context.copyDescription(mp->_context.info);
270 }
271 if (NULL == contextDesc) {
272 contextDesc = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info);
273 }
274 void *addr = mp->_callout;
275 Dl_info info;
276 const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
277 result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("<CFMachPort %p [%p]>{locked = %s, valid = %s, port = %p, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, (NULL != contextDesc ? contextDesc : CFSTR("<no description>")));
278 if (NULL != contextDesc) {
279 CFRelease(contextDesc);
280 }
281 return result;
282 }
283
284 static void __CFMachPortDeallocate(CFTypeRef cf) {
285 CHECK_FOR_FORK();
286 CFMachPortRef mp = (CFMachPortRef)cf;
287 __CFMachPortSetIsDeallocing(mp);
288 CFMachPortInvalidate(mp);
289 // MUST deallocate the send right FIRST if necessary,
290 // then the receive right if necessary. Don't ask me why;
291 // if it's done in the other order the port will leak.
292 if (__CFMachPortHasSend(mp)) {
293 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_SEND, -1);
294 }
295 if (__CFMachPortHasReceive(mp)) {
296 mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, -1);
297 }
298 }
299
300 static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
301
302 static const CFRuntimeClass __CFMachPortClass = {
303 0,
304 "CFMachPort",
305 NULL, // init
306 NULL, // copy
307 __CFMachPortDeallocate,
308 __CFMachPortEqual,
309 __CFMachPortHash,
310 NULL, //
311 __CFMachPortCopyDescription
312 };
313
314 __private_extern__ void __CFMachPortInitialize(void) {
315 __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass);
316 }
317
318 CFTypeID CFMachPortGetTypeID(void) {
319 return __kCFMachPortTypeID;
320 }
321
322 CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
323 CFMachPortRef result;
324 mach_port_t port;
325 kern_return_t ret;
326 if (shouldFreeInfo) *shouldFreeInfo = true;
327 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
328 if (KERN_SUCCESS != ret) {
329 return NULL;
330 }
331 ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
332 if (KERN_SUCCESS != ret) {
333 mach_port_destroy(mach_task_self(), port);
334 return NULL;
335 }
336 result = CFMachPortCreateWithPort(allocator, port, callout, context, shouldFreeInfo);
337 if (NULL != result) {
338 __CFMachPortSetHasReceive(result);
339 __CFMachPortSetHasSend(result);
340 }
341 return result;
342 }
343
344 /* Note: any receive or send rights that the port contains coming in will
345 * not be cleaned up by CFMachPort; it will increment and decrement
346 * references on the port if the kernel ever allows that in the future,
347 * but will not cleanup any references you got when you got the port. */
348 CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
349 CHECK_FOR_FORK();
350 CFMachPortRef memory;
351 SInt32 size;
352 Boolean didCreateNotifyPort = false;
353 CFRunLoopSourceRef source;
354 if (shouldFreeInfo) *shouldFreeInfo = true;
355 __CFSpinLock(&__CFAllMachPortsLock);
356 if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)(uintptr_t)port, (const void **)&memory)) {
357 CFRetain(memory);
358 __CFSpinUnlock(&__CFAllMachPortsLock);
359 return (CFMachPortRef)(memory);
360 }
361 size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase);
362 memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, __kCFMachPortTypeID, size, NULL);
363 if (NULL == memory) {
364 __CFSpinUnlock(&__CFAllMachPortsLock);
365 return NULL;
366 }
367 __CFMachPortUnsetValid(memory);
368 memory->_lock = CFSpinLockInit;
369 memory->_port = port;
370 memory->_source = NULL;
371 memory->_icallout = NULL;
372 memory->_context.info = NULL;
373 memory->_context.retain = NULL;
374 memory->_context.release = NULL;
375 memory->_context.copyDescription = NULL;
376 if (MACH_PORT_NULL == __CFNotifyRawMachPort) {
377 kern_return_t ret;
378 ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &__CFNotifyRawMachPort);
379 if (KERN_SUCCESS != ret) {
380 __CFSpinUnlock(&__CFAllMachPortsLock);
381 CFRelease(memory);
382 return NULL;
383 }
384 didCreateNotifyPort = true;
385 }
386 // Do not register for notifications on the notify port
387 if (MACH_PORT_NULL != __CFNotifyRawMachPort && port != __CFNotifyRawMachPort) {
388 mach_port_t old_port;
389 kern_return_t ret;
390 old_port = MACH_PORT_NULL;
391 ret = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, __CFNotifyRawMachPort, MACH_MSG_TYPE_MAKE_SEND_ONCE, &old_port);
392 if (ret != KERN_SUCCESS) {
393 __CFSpinUnlock(&__CFAllMachPortsLock);
394 CFRelease(memory);
395 return NULL;
396 }
397 memory->_oldnotify = old_port;
398 }
399 __CFMachPortSetValid(memory);
400 memory->_callout = callout;
401 if (NULL != context) {
402 CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext));
403 memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
404 }
405 if (NULL == __CFAllMachPorts) {
406 __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
407 _CFDictionarySetCapacity(__CFAllMachPorts, 20);
408 }
409 CFDictionaryAddValue(__CFAllMachPorts, (void *)(uintptr_t)port, memory);
410 __CFSpinUnlock(&__CFAllMachPortsLock);
411 if (didCreateNotifyPort) {
412 // __CFNotifyMachPort ends up in cache
413 CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorSystemDefault, __CFNotifyRawMachPort, __CFNotifyDeadMachPort, NULL, NULL);
414 __CFMachPortSetHasReceive(mp);
415 __CFNotifyMachPort = mp;
416 }
417 if (NULL != __CFNotifyMachPort) {
418 // We do this so that it gets into each thread's run loop, since
419 // we don't know which run loop is the main thread's, and that's
420 // not necessarily the "right" one anyway. This won't happen for
421 // the call which creates the __CFNotifyMachPort itself, but that's
422 // OK since it will happen in the invocation of this function
423 // from which that call was triggered.
424 source = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, __CFNotifyMachPort, -1000);
425 CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
426 CFRelease(source);
427 }
428 if (shouldFreeInfo) *shouldFreeInfo = false;
429 return memory;
430 }
431
432 mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
433 CHECK_FOR_FORK();
434 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort");
435 __CFGenericValidateType(mp, __kCFMachPortTypeID);
436 return mp->_port;
437 }
438
439 void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
440 __CFGenericValidateType(mp, __kCFMachPortTypeID);
441 CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
442 CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext));
443 }
444
445 void CFMachPortInvalidate(CFMachPortRef mp) {
446 CHECK_FOR_FORK();
447 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate");
448 __CFGenericValidateType(mp, __kCFMachPortTypeID);
449 if (!__CFMachPortIsDeallocing(mp)) {
450 CFRetain(mp);
451 }
452 __CFSpinLock(&__CFAllMachPortsLock);
453 if (NULL != __CFAllMachPorts) {
454 CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(uintptr_t)(mp->_port));
455 }
456 __CFSpinUnlock(&__CFAllMachPortsLock);
457 __CFMachPortLock(mp);
458 if (__CFMachPortIsValid(mp)) {
459 CFRunLoopSourceRef source;
460 void *info;
461 mach_port_t old_port = mp->_oldnotify;
462 CFMachPortInvalidationCallBack callout = mp->_icallout;
463 __CFMachPortUnsetValid(mp);
464 __CFMachPortUnlock(mp);
465 if (NULL != callout) {
466 callout(mp, mp->_context.info);
467 }
468 __CFMachPortLock(mp);
469 // For hashing and equality purposes, cannot get rid of _port here
470 source = mp->_source;
471 mp->_source = NULL;
472 info = mp->_context.info;
473 mp->_context.info = NULL;
474 __CFMachPortUnlock(mp);
475 if (NULL != mp->_context.release) {
476 mp->_context.release(info);
477 }
478 if (NULL != source) {
479 CFRunLoopSourceInvalidate(source);
480 CFRelease(source);
481 }
482 if (MACH_PORT_NULL != old_port) {
483 mach_port_deallocate(mach_task_self(), old_port);
484 }
485 } else {
486 __CFMachPortUnlock(mp);
487 }
488 if (!__CFMachPortIsDeallocing(mp)) {
489 CFRelease(mp);
490 }
491 }
492
493 Boolean CFMachPortIsValid(CFMachPortRef mp) {
494 CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, Boolean, mp, "isValid");
495 __CFGenericValidateType(mp, __kCFMachPortTypeID);
496 return __CFMachPortIsValid(mp);
497 }
498
499 CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
500 __CFGenericValidateType(mp, __kCFMachPortTypeID);
501 return mp->_icallout;
502 }
503
504 void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
505 __CFGenericValidateType(mp, __kCFMachPortTypeID);
506 if (!__CFMachPortIsValid(mp) && NULL != callout) {
507 callout(mp, mp->_context.info);
508 } else {
509 mp->_icallout = callout;
510 }
511 }
512
513 /* Returns the number of messages queued for a receive port. */
514 CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {
515 CHECK_FOR_FORK();
516 mach_port_status_t status;
517 mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
518 kern_return_t ret;
519 ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num);
520 return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
521 }
522
523 static mach_port_t __CFMachPortGetPort(void *info) {
524 CFMachPortRef mp = info;
525 return mp->_port;
526 }
527
528 static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
529 CHECK_FOR_FORK();
530 CFMachPortRef mp = info;
531 void *context_info;
532 void (*context_release)(const void *);
533 __CFMachPortLock(mp);
534 if (!__CFMachPortIsValid(mp)) {
535 __CFMachPortUnlock(mp);
536 return NULL;
537 }
538 if (NULL != mp->_context.retain) {
539 context_info = (void *)mp->_context.retain(mp->_context.info);
540 context_release = mp->_context.release;
541 } else {
542 context_info = mp->_context.info;
543 context_release = NULL;
544 }
545 __CFMachPortUnlock(mp);
546 mp->_callout(mp, msg, size, mp->_context.info);
547 CHECK_FOR_FORK();
548 if (context_release) {
549 context_release(context_info);
550 }
551 return NULL;
552 }
553
554 CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
555 CHECK_FOR_FORK();
556 CFRunLoopSourceRef result = NULL;
557 __CFGenericValidateType(mp, __kCFMachPortTypeID);
558 __CFMachPortLock(mp);
559 if (!__CFMachPortIsValid(mp)) {
560 __CFMachPortUnlock(mp);
561 return NULL;
562 }
563 if (NULL == mp->_source) {
564 CFRunLoopSourceContext1 context;
565 context.version = 1;
566 context.info = (void *)mp;
567 context.retain = (const void *(*)(const void *))CFRetain;
568 context.release = (void (*)(const void *))CFRelease;
569 context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
570 context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
571 context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
572 context.getPort = __CFMachPortGetPort;
573 context.perform = __CFMachPortPerform;
574 mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
575 }
576 if (NULL != mp->_source) {
577 result = (CFRunLoopSourceRef)CFRetain(mp->_source);
578 }
579 __CFMachPortUnlock(mp);
580 return result;
581 }
582
583