def _watchFD(self, fd, descr, flag): """ Register a file descriptor with the C{CFRunLoop}, or modify its state so that it's listening for both notifications (read and write) rather than just one; used to implement C{addReader} and C{addWriter}. @param fd: The file descriptor. @type fd: L{int} @param descr: the L{IReadDescriptor} or L{IWriteDescriptor} @param flag: the flag to register for callbacks on, either C{kCFSocketReadCallBack} or C{kCFSocketWriteCallBack} """ if fd == -1: raise RuntimeError("Invalid file descriptor.") if fd in self._fdmap: src, cfs, gotdescr, rw = self._fdmap[fd] # do I need to verify that it's the same descr? else: ctx = [] ctx.append(fd) cfs = CFSocketCreateWithNative( kCFAllocatorDefault, fd, kCFSocketReadCallBack | kCFSocketWriteCallBack | kCFSocketConnectCallBack, self._socketCallback, ctx, ) CFSocketSetSocketFlags( cfs, kCFSocketAutomaticallyReenableReadCallBack | kCFSocketAutomaticallyReenableWriteCallBack | # This extra flag is to ensure that CF doesn't (destructively, # because destructively is the only way to do it) retrieve # SO_ERROR and thereby break twisted.internet.tcp.BaseClient, # which needs SO_ERROR to tell it whether or not it needs to # call connect_ex a second time. _preserveSOError, ) src = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfs, 0) ctx.append(src) CFRunLoopAddSource(self._cfrunloop, src, kCFRunLoopCommonModes) CFSocketDisableCallBacks( cfs, kCFSocketReadCallBack | kCFSocketWriteCallBack | kCFSocketConnectCallBack, ) rw = [False, False] self._idmap[id(descr)] = fd self._fdmap[fd] = src, cfs, descr, rw rw[self._flag2idx(flag)] = True CFSocketEnableCallBacks(cfs, flag)
def register(self, fileobj, events, data=None): fileno = fileobj if isinstance(fileobj, int) else fileobj.fileno() key = SelectorKey(fileobj, fileno, events, data) self._mappings[fileobj] = key entry = _FDEntry() entry.cf_fd = CFFileDescriptorCreate(kCFAllocatorDefault, fileno, 0, self._fdCallback, key) self._fd_events_to_enable[entry.cf_fd] = events entry.cf_source = CFFileDescriptorCreateRunLoopSource( kCFAllocatorDefault, entry.cf_fd, 0) CFRunLoopAddSource(self._runloop, entry.cf_source, kCFRunLoopCommonModes) self._registered_fds[fileobj] = entry
def wait_for(self, notification=None, filter_=None, timeout=5): self.callback_result = None @PAXObserverCallback def _callback(observer, element, notification, refcon): logger.debug("CALLBACK") logger.debug("%s, %s, %s, %s" % (observer, element, notification, refcon)) ret_element = self.ref.__class__(element) if filter_(ret_element): self.callback_result = ret_element observer = PAXObserverCreate(self.ref.pid, _callback) PAXObserverAddNotification(observer, self.ref.ref, notification, id(self.ref.ref)) # Add observer source to run loop CFRunLoopAddSource( CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), NSDefaultRunLoopMode, ) def event_stopper(): end_time = time.time() + timeout while time.time() < end_time: if self.callback_result is not None: break AppHelper.callAfter(AppHelper.stopEventLoop) event_watcher = threading.Thread(target=event_stopper) event_watcher.daemon = True event_watcher.start() # Set the signal handlers prior to running the run loop oldSigIntHandler = MachSignals.signal(signal.SIGINT, _sigHandler) AppHelper.runConsoleEventLoop() MachSignals.signal(signal.SIGINT, oldSigIntHandler) PAXObserverRemoveNotification(observer, self.ref.ref, notification) return self.callback_result