/ CFMachPort.c
CFMachPort.c
  1  /*
  2   * Copyright (c) 2015 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  
 24  /*	CFMachPort.c
 25  	Copyright (c) 1998-2014, Apple Inc. All rights reserved.
 26  	Responsibility: Christopher Kane
 27  */
 28  
 29  #include <CoreFoundation/CFMachPort.h>
 30  #include <CoreFoundation/CFRunLoop.h>
 31  #include <CoreFoundation/CFArray.h>
 32  #include <dispatch/dispatch.h>
 33  #include <dispatch/private.h>
 34  #include <mach/mach.h>
 35  #include <dlfcn.h>
 36  #include <stdio.h>
 37  #include "CFInternal.h"
 38  
 39  
 40  #define AVOID_WEAK_COLLECTIONS 1
 41  
 42  #if !AVOID_WEAK_COLLECTIONS
 43  #import "CFPointerArray.h"
 44  #endif
 45  
 46  static dispatch_queue_t _CFMachPortQueue() {
 47      static volatile dispatch_queue_t __CFMachPortQueue = NULL;
 48      static dispatch_once_t onceToken;
 49      dispatch_once(&onceToken, ^{
 50          dispatch_queue_attr_t dqattr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0);
 51          __CFMachPortQueue = dispatch_queue_create("com.apple.CFMachPort", dqattr);
 52      });
 53      return __CFMachPortQueue;
 54  }
 55  
 56  
 57  enum {
 58      kCFMachPortStateReady = 0,
 59      kCFMachPortStateInvalidating = 1,
 60      kCFMachPortStateInvalid = 2,
 61      kCFMachPortStateDeallocating = 3
 62  };
 63  
 64  struct __CFMachPort {
 65      CFRuntimeBase _base;
 66      int32_t _state;
 67      mach_port_t _port;                          /* immutable */
 68      dispatch_source_t _dsrc;                    /* protected by _lock */
 69      dispatch_semaphore_t _dsrc_sem;             /* protected by _lock */
 70      CFMachPortInvalidationCallBack _icallout;   /* protected by _lock */
 71      CFRunLoopSourceRef _source;                 /* immutable, once created */
 72      CFMachPortCallBack _callout;                /* immutable */
 73      CFMachPortContext _context;                 /* immutable */
 74      CFLock_t _lock;
 75      const void *(*retain)(const void *info); // use these to store the real callbacks
 76      void        (*release)(const void *info);
 77  };
 78  
 79  /* Bit 1 in the base reserved bits is used for has-receive-ref state */
 80  /* Bit 2 in the base reserved bits is used for has-send-ref state */
 81  /* Bit 3 in the base reserved bits is used for has-send-ref2 state */
 82  
 83  CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) {
 84      return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1);
 85  }
 86  
 87  CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) {
 88      __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
 89  }
 90  
 91  CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) {
 92      return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2);
 93  }
 94  
 95  CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) {
 96      __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 2, 2, 1);
 97  }
 98  
 99  CF_INLINE Boolean __CFMachPortHasSend2(CFMachPortRef mp) {
100      return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3);
101  }
102  /*
103   //TODO we should either use this or delete the entire Send2 flag concept
104  CF_INLINE void __CFMachPortSetHasSend2(CFMachPortRef mp) {
105      __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
106  }
107  */
108  CF_INLINE Boolean __CFMachPortIsValid(CFMachPortRef mp) {
109      return kCFMachPortStateReady == mp->_state;
110  }
111  
112  
113  void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) {
114  }
115  
116  static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) {
117      CFMachPortRef mp1 = (CFMachPortRef)cf1;
118      CFMachPortRef mp2 = (CFMachPortRef)cf2;
119      return (mp1->_port == mp2->_port);
120  }
121  
122  static CFHashCode __CFMachPortHash(CFTypeRef cf) {
123      CFMachPortRef mp = (CFMachPortRef)cf;
124      return (CFHashCode)mp->_port;
125  }
126  
127  static CFStringRef __CFMachPortCopyDescription(CFTypeRef cf) {
128      CFMachPortRef mp = (CFMachPortRef)cf;
129      CFStringRef contextDesc = NULL;
130      if (NULL != mp->_context.info && NULL != mp->_context.copyDescription) {
131          contextDesc = mp->_context.copyDescription(mp->_context.info);
132      }
133      if (NULL == contextDesc) {
134          contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort context %p>"), mp->_context.info);
135      }
136      Dl_info info;
137      void *addr = mp->_callout;
138      const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???";
139      CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMachPort %p [%p]>{valid = %s, port = %x, source = %p, callout = %s (%p), context = %@}"), cf, CFGetAllocator(mp), (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, name, addr, contextDesc);
140      if (NULL != contextDesc) {
141          CFRelease(contextDesc);
142      }
143      return result;
144  }
145  
146  // Only call with mp->_lock locked
147  CF_INLINE void __CFMachPortInvalidateLocked(CFRunLoopSourceRef source, CFMachPortRef mp) {
148      CFMachPortInvalidationCallBack cb = mp->_icallout;
149      if (cb) {
150          __CFUnlock(&mp->_lock);
151          cb(mp, mp->_context.info);
152          __CFLock(&mp->_lock);
153      }
154      if (NULL != source) {
155          __CFUnlock(&mp->_lock);
156          CFRunLoopSourceInvalidate(source);
157          CFRelease(source);
158          __CFLock(&mp->_lock);
159      }
160      void *info = mp->_context.info;
161      void (*release)(const void *info) = mp->release;
162      mp->_context.info = NULL;
163      if (release) {
164          __CFUnlock(&mp->_lock);
165          release(info);
166          __CFLock(&mp->_lock);
167      }
168      mp->_state = kCFMachPortStateInvalid;
169      OSMemoryBarrier();
170  }
171  
172  static void __CFMachPortDeallocate(CFTypeRef cf) {
173      CHECK_FOR_FORK_RET();
174      CFMachPortRef mp = (CFMachPortRef)cf;
175  
176      // CFMachPortRef is invalid before we get here, except under GC
177      __CFLock(&mp->_lock);
178      CFRunLoopSourceRef source = NULL;
179      Boolean wasReady = (mp->_state == kCFMachPortStateReady);
180      if (wasReady) {
181          mp->_state = kCFMachPortStateInvalidating;
182          OSMemoryBarrier();
183          if (mp->_dsrc) {
184              dispatch_source_cancel(mp->_dsrc);
185              mp->_dsrc = NULL;
186          }
187          source = mp->_source;
188          mp->_source = NULL;
189      }    
190      if (wasReady) {
191          __CFMachPortInvalidateLocked(source, mp);
192      }
193      mp->_state = kCFMachPortStateDeallocating;
194  
195      // hand ownership of the port and semaphores to the block below
196      mach_port_t port = mp->_port;
197      dispatch_semaphore_t sem1 = mp->_dsrc_sem;
198      Boolean doSend2 = __CFMachPortHasSend2(mp), doSend = __CFMachPortHasSend(mp), doReceive = __CFMachPortHasReceive(mp);
199  
200      __CFUnlock(&mp->_lock);
201  
202      dispatch_async(__CFDispatchQueueGetGenericBackground(), ^{
203              if (sem1) {
204                  dispatch_semaphore_wait(sem1, DISPATCH_TIME_FOREVER);
205                  // immediate release is only safe if dispatch_semaphore_signal() does not touch the semaphore after doing the signal bit
206                  dispatch_release(sem1);
207              }
208  
209              // MUST deallocate the send right FIRST if necessary,
210              // then the receive right if necessary.  Don't ask me why;
211              // if it's done in the other order the port will leak.
212              if (doSend2) {
213                  mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1);
214              }
215              if (doSend) {
216                  mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1);
217              }
218              if (doReceive) {
219                  mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
220              }
221          });
222  }
223  
224  // This lock protects __CFAllMachPorts. Take before any instance-specific lock.
225  static CFLock_t __CFAllMachPortsLock = CFLockInit;
226  
227  #if AVOID_WEAK_COLLECTIONS
228  static CFMutableArrayRef __CFAllMachPorts = NULL;
229  #else
230  static __CFPointerArray *__CFAllMachPorts = nil;
231  #endif
232  
233  static Boolean __CFMachPortCheck(mach_port_t) __attribute__((noinline));
234  static Boolean __CFMachPortCheck(mach_port_t port) {
235      mach_port_type_t type = 0;
236      kern_return_t ret = mach_port_type(mach_task_self(), port, &type);
237      return (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) ? false : true;
238  }
239  
240  static void __CFMachPortChecker(Boolean fromTimer) {
241      __CFLock(&__CFAllMachPortsLock); // take this lock first before any instance-specific lock
242  #if AVOID_WEAK_COLLECTIONS
243      for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
244          CFMachPortRef mp = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
245  #else
246      for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
247          CFMachPortRef mp = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
248  #endif
249          if (!mp) continue;
250          // second clause cleans no-longer-wanted CFMachPorts out of our strong table
251          if (!__CFMachPortCheck(mp->_port) || (!kCFUseCollectableAllocator && 1 == CFGetRetainCount(mp))) {
252              CFRunLoopSourceRef source = NULL;
253              Boolean wasReady = (mp->_state == kCFMachPortStateReady);
254              if (wasReady) {
255                  __CFLock(&mp->_lock); // take this lock second
256                  mp->_state = kCFMachPortStateInvalidating;
257                  OSMemoryBarrier();
258                  if (mp->_dsrc) {
259                      dispatch_source_cancel(mp->_dsrc);
260                      mp->_dsrc = NULL;
261                  }
262                  source = mp->_source;
263                  mp->_source = NULL;
264                  CFRetain(mp);
265                  __CFUnlock(&mp->_lock);
266                  dispatch_async(dispatch_get_main_queue(), ^{
267                      // We can grab the mach port-specific spin lock here since we're no longer on the same thread as the one taking the all mach ports spin lock.
268                      // But be sure to release it during callouts
269                      __CFLock(&mp->_lock);
270                      __CFMachPortInvalidateLocked(source, mp);
271                      __CFUnlock(&mp->_lock);
272                      CFRelease(mp);
273                  });
274              }
275  #if AVOID_WEAK_COLLECTIONS
276              CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx);
277  #else
278              [__CFAllMachPorts removePointerAtIndex:idx];
279  #endif
280              idx--;
281              cnt--;
282          }
283      }
284  #if !AVOID_WEAK_COLLECTIONS
285      [__CFAllMachPorts compact];
286  #endif
287      __CFUnlock(&__CFAllMachPortsLock);
288  };
289  
290  
291  static CFTypeID __kCFMachPortTypeID = _kCFRuntimeNotATypeID;
292  
293  static const CFRuntimeClass __CFMachPortClass = {
294      0,
295      "CFMachPort",
296      NULL,      // init
297      NULL,      // copy
298      __CFMachPortDeallocate,
299      __CFMachPortEqual,
300      __CFMachPortHash,
301      NULL,      // 
302      __CFMachPortCopyDescription
303  };
304  
305  CFTypeID CFMachPortGetTypeID(void) {
306      static dispatch_once_t initOnce;
307      dispatch_once(&initOnce, ^{ __kCFMachPortTypeID = _CFRuntimeRegisterClass(&__CFMachPortClass); });
308      return __kCFMachPortTypeID;
309  }
310  
311  /* Note: any receive or send rights that the port contains coming in will
312   * not be cleaned up by CFMachPort; it will increment and decrement
313   * references on the port if the kernel ever allows that in the future,
314   * but will not cleanup any references you got when you got the port. */
315  CFMachPortRef _CFMachPortCreateWithPort2(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo, Boolean deathWatch) {
316      if (shouldFreeInfo) *shouldFreeInfo = true;
317      CHECK_FOR_FORK_RET(NULL);
318  
319      mach_port_type_t type = 0;
320      kern_return_t ret = mach_port_type(mach_task_self(), port, &type);
321      if (KERN_SUCCESS != ret || (0 == (type & MACH_PORT_TYPE_PORT_RIGHTS))) {
322          if (type & ~MACH_PORT_TYPE_DEAD_NAME) {
323              CFLog(kCFLogLevelError, CFSTR("*** CFMachPortCreateWithPort(): bad Mach port parameter (0x%lx) or unsupported mysterious kind of Mach port (%d, %ld)"), (unsigned long)port, ret, (unsigned long)type);
324          }
325          return NULL;
326      }
327  
328  #if 0
329      static dispatch_source_t timerSource = NULL;
330      static dispatch_once_t onceToken;
331      dispatch_once(&onceToken, ^{
332          timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_INTERVAL, 60 * 1000 /* milliseconds */, 0, _CFMachPortQueue());
333          dispatch_source_set_event_handler(timerSource, ^{
334              __CFMachPortChecker(true);
335          });
336          dispatch_resume(timerSource);
337      });
338  #endif
339  
340      CFMachPortRef mp = NULL;
341      __CFLock(&__CFAllMachPortsLock);
342  #if AVOID_WEAK_COLLECTIONS
343      for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
344          CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
345          if (p && p->_port == port) {
346              CFRetain(p);
347              mp = p;
348              break;
349          }
350      }
351  #else                
352      for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
353          CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
354          if (p && p->_port == port) {
355              CFRetain(p);
356              mp = p;
357              break;
358          }
359      }
360  #endif
361      __CFUnlock(&__CFAllMachPortsLock);
362      
363      if (!mp) {
364          CFIndex size = sizeof(struct __CFMachPort) - sizeof(CFRuntimeBase);
365          CFMachPortRef memory = (CFMachPortRef)_CFRuntimeCreateInstance(allocator, CFMachPortGetTypeID(), size, NULL);
366          if (NULL == memory) {
367              return NULL;
368          }
369          memory->_port = port;
370          memory->_dsrc = NULL;
371          memory->_dsrc_sem = NULL;
372          memory->_icallout = NULL;
373          memory->_source = NULL;
374          memory->_context.info = NULL;
375          memory->_context.retain = NULL;
376          memory->_context.release = NULL;
377          memory->_context.copyDescription = NULL;
378          memory->retain = NULL;
379          memory->release = NULL;
380          memory->_callout = callout;
381          memory->_lock = CFLockInit;
382          if (NULL != context) {
383              objc_memmove_collectable(&memory->_context, context, sizeof(CFMachPortContext));
384              memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info;
385              memory->retain = context->retain;
386              memory->release = context->release;
387  	    memory->_context.retain = (void *)0xAAAAAAAAAACCCAAA;
388              memory->_context.release = (void *)0xAAAAAAAAAABBBAAA;
389          }
390          memory->_state = kCFMachPortStateReady;
391          __CFLock(&__CFAllMachPortsLock);
392      #if AVOID_WEAK_COLLECTIONS
393          if (!__CFAllMachPorts) __CFAllMachPorts = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
394          CFArrayAppendValue(__CFAllMachPorts, memory);
395      #else
396          if (!__CFAllMachPorts) __CFAllMachPorts = [[__CFPointerArray alloc] initWithOptions:(kCFUseCollectableAllocator ? CFPointerFunctionsZeroingWeakMemory : CFPointerFunctionsStrongMemory)];
397          [__CFAllMachPorts addPointer:memory];
398      #endif
399          __CFUnlock(&__CFAllMachPortsLock);
400          mp = memory;
401          if (shouldFreeInfo) *shouldFreeInfo = false;
402  
403          if (type & MACH_PORT_TYPE_SEND_RIGHTS) {
404              dispatch_source_t theSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, port, DISPATCH_MACH_SEND_DEAD, _CFMachPortQueue());
405  	    if (theSource) {
406                  dispatch_semaphore_t sem = dispatch_semaphore_create(0);
407                  dispatch_retain(sem);
408                  dispatch_source_set_cancel_handler(theSource, ^{ dispatch_semaphore_signal(sem); dispatch_release(sem); dispatch_release(theSource); });
409                  dispatch_source_set_event_handler(theSource, ^{ __CFMachPortChecker(false); });
410                  memory->_dsrc_sem = sem;
411                  memory->_dsrc = theSource;
412                  dispatch_resume(theSource);
413  	    }
414          }
415      }
416      
417      if (mp && !CFMachPortIsValid(mp)) { // must do this outside lock to avoid deadlock
418          CFRelease(mp);
419          mp = NULL;
420      }
421      return mp;
422  }
423  
424  CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t port, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
425      return _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
426  }
427  
428  CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) {
429      if (shouldFreeInfo) *shouldFreeInfo = true;
430      CHECK_FOR_FORK_RET(NULL);
431      mach_port_t port = MACH_PORT_NULL;
432      kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
433      if (KERN_SUCCESS == ret) {
434          ret = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
435      }
436      if (KERN_SUCCESS != ret) {
437          if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
438          return NULL;
439      }
440      CFMachPortRef result = _CFMachPortCreateWithPort2(allocator, port, callout, context, shouldFreeInfo, true);
441      if (NULL == result) {
442          if (MACH_PORT_NULL != port) mach_port_destroy(mach_task_self(), port);
443          return NULL;
444      }
445      __CFMachPortSetHasReceive(result);
446      __CFMachPortSetHasSend(result);
447      return result;
448  }
449  
450  void CFMachPortInvalidate(CFMachPortRef mp) {
451      CHECK_FOR_FORK_RET();
452      CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), void, (NSMachPort *)mp, invalidate);
453      __CFGenericValidateType(mp, CFMachPortGetTypeID());
454      CFRetain(mp);
455      CFRunLoopSourceRef source = NULL;
456      Boolean wasReady = false;
457      __CFLock(&__CFAllMachPortsLock); // take this lock first
458      __CFLock(&mp->_lock);
459      wasReady = (mp->_state == kCFMachPortStateReady);
460      if (wasReady) {
461          mp->_state = kCFMachPortStateInvalidating;
462          OSMemoryBarrier();
463  #if AVOID_WEAK_COLLECTIONS
464          for (CFIndex idx = 0, cnt = __CFAllMachPorts ? CFArrayGetCount(__CFAllMachPorts) : 0; idx < cnt; idx++) {
465              CFMachPortRef p = (CFMachPortRef)CFArrayGetValueAtIndex(__CFAllMachPorts, idx);
466              if (p == mp) {
467                  CFArrayRemoveValueAtIndex(__CFAllMachPorts, idx);
468                  break;
469              }
470          }
471  #else
472          for (CFIndex idx = 0, cnt = __CFAllMachPorts ? [__CFAllMachPorts count] : 0; idx < cnt; idx++) {
473              CFMachPortRef p = (CFMachPortRef)[__CFAllMachPorts pointerAtIndex:idx];
474              if (p == mp) {
475                  [__CFAllMachPorts removePointerAtIndex:idx];
476                  break;
477              }
478          }
479  #endif        
480          if (mp->_dsrc) {
481              dispatch_source_cancel(mp->_dsrc);
482              mp->_dsrc = NULL;
483          }
484          source = mp->_source;
485          mp->_source = NULL;
486      }
487      __CFUnlock(&mp->_lock);
488      __CFUnlock(&__CFAllMachPortsLock); // release this lock last
489      if (wasReady) {
490          __CFLock(&mp->_lock);
491          __CFMachPortInvalidateLocked(source, mp);
492          __CFUnlock(&mp->_lock);
493      }
494      CFRelease(mp);
495  }
496  
497  mach_port_t CFMachPortGetPort(CFMachPortRef mp) {
498      CHECK_FOR_FORK_RET(0);
499      CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), mach_port_t, (NSMachPort *)mp, machPort);
500      __CFGenericValidateType(mp, CFMachPortGetTypeID());
501      return mp->_port;
502  }
503  
504  void CFMachPortGetContext(CFMachPortRef mp, CFMachPortContext *context) {
505      __CFGenericValidateType(mp, CFMachPortGetTypeID());
506      CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__);
507      objc_memmove_collectable(context, &mp->_context, sizeof(CFMachPortContext));
508  }
509  
510  Boolean CFMachPortIsValid(CFMachPortRef mp) {
511      CF_OBJC_FUNCDISPATCHV(CFMachPortGetTypeID(), Boolean, (NSMachPort *)mp, isValid);
512      __CFGenericValidateType(mp, CFMachPortGetTypeID());
513      if (!__CFMachPortIsValid(mp)) return false;
514      mach_port_type_t type = 0;
515      kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type);
516      if (KERN_SUCCESS != ret || (type & ~(MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_SEND_ONCE|MACH_PORT_TYPE_RECEIVE|MACH_PORT_TYPE_DNREQUEST))) {
517  	return false;
518      }
519      return true;
520  }
521  
522  CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) {
523      __CFGenericValidateType(mp, CFMachPortGetTypeID());
524      __CFLock(&mp->_lock);
525      CFMachPortInvalidationCallBack cb = mp->_icallout;
526      __CFUnlock(&mp->_lock);
527      return cb;
528  }
529  
530  /* After the CFMachPort has started going invalid, or done invalid, you can't change this, and
531     we'll only do the callout directly on a transition from NULL to non-NULL. */
532  void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) {
533      CHECK_FOR_FORK_RET();
534      __CFGenericValidateType(mp, CFMachPortGetTypeID());
535      if (callout) {
536  	mach_port_type_t type = 0;
537  	kern_return_t ret = mach_port_type(mach_task_self(), mp->_port, &type);
538  	if (KERN_SUCCESS != ret || 0 == (type & MACH_PORT_TYPE_SEND_RIGHTS)) {
539  	    CFLog(kCFLogLevelError, CFSTR("*** WARNING: CFMachPortSetInvalidationCallBack() called on a CFMachPort with a Mach port (0x%x) which does not have any send rights.  This is not going to work.  Callback function: %p"), mp->_port, callout);
540  	}
541      }
542      __CFLock(&mp->_lock);
543      if (__CFMachPortIsValid(mp) || !callout) {
544          mp->_icallout = callout;
545      } else if (!mp->_icallout && callout) {
546          __CFUnlock(&mp->_lock);
547          callout(mp, mp->_context.info);
548          __CFLock(&mp->_lock);
549      } else {
550          CFLog(kCFLogLevelWarning, CFSTR("CFMachPortSetInvalidationCallBack(): attempt to set invalidation callback (%p) on invalid CFMachPort (%p) thwarted"), callout, mp);
551      }
552      __CFUnlock(&mp->_lock);
553  }
554  
555  /* Returns the number of messages queued for a receive port. */
556  CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp) {  
557      CHECK_FOR_FORK_RET(0);
558      __CFGenericValidateType(mp, CFMachPortGetTypeID());
559      mach_port_status_t status;
560      mach_msg_type_number_t num = MACH_PORT_RECEIVE_STATUS_COUNT;
561      kern_return_t ret = mach_port_get_attributes(mach_task_self(), mp->_port, MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &num);
562      return (KERN_SUCCESS != ret) ? 0 : status.mps_msgcount;
563  }
564  
565  static mach_port_t __CFMachPortGetPort(void *info) {
566      CFMachPortRef mp = (CFMachPortRef)info;
567      return mp->_port;
568  }
569  
570  CF_PRIVATE void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) {
571      CHECK_FOR_FORK_RET(NULL);
572      CFMachPortRef mp = (CFMachPortRef)info;
573      __CFLock(&mp->_lock);
574      Boolean isValid = __CFMachPortIsValid(mp);
575      void *context_info = NULL;
576      void (*context_release)(const void *) = NULL;
577      if (isValid) {
578          if (mp->retain) {
579              context_info = (void *)mp->retain(mp->_context.info);
580              context_release = mp->release;
581          } else {
582              context_info = mp->_context.info;
583          }
584      }
585      __CFUnlock(&mp->_lock);
586      if (isValid) {
587          mp->_callout(mp, msg, size, context_info);
588  
589          if (context_release) {
590              context_release(context_info);
591          }
592          CHECK_FOR_FORK_RET(NULL);
593      }
594      return NULL;
595  }
596  
597  
598      
599      
600  CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) {
601      CHECK_FOR_FORK_RET(NULL);
602      __CFGenericValidateType(mp, CFMachPortGetTypeID());
603      if (!CFMachPortIsValid(mp)) return NULL;
604      CFRunLoopSourceRef result = NULL;
605      __CFLock(&mp->_lock);
606      if (__CFMachPortIsValid(mp)) {
607          if (NULL != mp->_source && !CFRunLoopSourceIsValid(mp->_source)) {
608              CFRelease(mp->_source);
609              mp->_source = NULL;
610          }
611          if (NULL == mp->_source) {
612              CFRunLoopSourceContext1 context;
613              context.version = 1;
614              context.info = (void *)mp;
615              context.retain = (const void *(*)(const void *))CFRetain;
616              context.release = (void (*)(const void *))CFRelease;
617              context.copyDescription = (CFStringRef (*)(const void *))__CFMachPortCopyDescription;
618              context.equal = (Boolean (*)(const void *, const void *))__CFMachPortEqual;
619              context.hash = (CFHashCode (*)(const void *))__CFMachPortHash;
620              context.getPort = __CFMachPortGetPort;
621              context.perform = __CFMachPortPerform;
622              mp->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context);
623          }
624          result = mp->_source ? (CFRunLoopSourceRef)CFRetain(mp->_source) : NULL;
625      }
626      __CFUnlock(&mp->_lock);
627      return result;
628  }
629