def _unwatchFD(self, fd, descr, flag): """ Unregister a file descriptor with the C{CFRunLoop}, or modify its state so that it's listening for only one notification (read or write) as opposed to both; used to implement C{removeReader} and C{removeWriter}. @param fd: a file descriptor @type fd: C{int} @param descr: an L{IReadDescriptor} or L{IWriteDescriptor} @param flag: C{kCFSocketWriteCallBack} C{kCFSocketReadCallBack} """ if id(descr) not in self._idmap: return if fd == -1: # need to deal with it in this case, I think. realfd = self._idmap[id(descr)] else: realfd = fd src, cfs, descr, rw = self._fdmap[realfd] CFSocketDisableCallBacks(cfs, flag) rw[self._flag2idx(flag)] = False if not rw[_READ] and not rw[_WRITE]: del self._idmap[id(descr)] del self._fdmap[realfd] CFRunLoopRemoveSource(self._cfrunloop, src, kCFRunLoopCommonModes) CFSocketInvalidate(cfs)
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)