@@ -27,10 +27,11 @@ #import "OFKernelEventObserver.h" #import "OFKernelEventObserver+Private.h" #import "OFKernelEventObserver_kqueue.h" #import "OFDataArray.h" +#import "OFArray.h" #import "OFInitializationFailedException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" @@ -48,10 +49,11 @@ @throw [OFInitializationFailedException exceptionWithClass: [self class]]; _changeList = [[OFDataArray alloc] initWithItemSize: sizeof(struct kevent)]; + _removedArray = [[OFMutableArray alloc] init]; [self OF_addFileDescriptorForReading: _cancelFD[0]]; } @catch (id e) { [self release]; @throw e; @@ -61,11 +63,13 @@ } - (void)dealloc { close(_kernelQueue); + [_changeList release]; + [_removedArray release]; [super dealloc]; } - (void)OF_addFileDescriptorForReading: (int)fd @@ -114,11 +118,15 @@ int i, events, realEvents = 0; timeout.tv_sec = (time_t)timeInterval; timeout.tv_nsec = lrint((timeInterval - timeout.tv_sec) * 1000000000); - [self OF_processQueue]; + /* + * Make sure to keep the streams retained and thus the file descriptors + * valid until the actual change has been performed. + */ + [self OF_processQueueAndStoreRemovedIn: _removedArray]; if ([self OF_processCache]) { objc_autoreleasePoolPop(pool); return true; } @@ -126,10 +134,12 @@ objc_autoreleasePoolPop(pool); events = kevent(_kernelQueue, [_changeList items], (int)[_changeList count], eventList, EVENTLIST_SIZE, (timeInterval == -1 ? NULL : &timeout)); + + [_removedArray removeAllObjects]; if (events < 0) return false; [_changeList removeAllItems]; @@ -148,34 +158,10 @@ realEvents++; pool = objc_autoreleasePoolPush(); - /* - * If a file descriptor has been closed before it is removed - * from the kernel event observer, the file descriptor is not - * valid anymore and causes EBADF. As closing a file descriptor - * automatically removes it from the queue, there is nothing to - * do anymore. - * - * Ideally, a file descriptor should never be closed before it - * is removed from the kernel event observer, as in rare cases, - * it could result in removing the wrong object from the kernel - * event observer, for example if a file descriptor is closed, - * a new one created, added to the kernel event observer and - * then the old one removed, as the new one could now have the - * same file descriptor as the closed one had and thus the new - * one is removed. - * - * For other errors, call the callback like it was successful - * so that the read / write will generate an error and throw an - * exception. - */ - if ((eventList[i].flags & EV_ERROR) && - eventList[i].data == EBADF) - continue; - switch (eventList[i].filter) { case EVFILT_READ: if ([_delegate respondsToSelector: @selector(objectIsReadyForReading:)]) [_delegate objectIsReadyForReading: