Index: src/OFKernelEventObserver+Private.h ================================================================== --- src/OFKernelEventObserver+Private.h +++ src/OFKernelEventObserver+Private.h @@ -19,8 +19,8 @@ @interface OFKernelEventObserver (OF_PRIVATE_CATEGORY) - (void)OF_addFileDescriptorForReading: (int)fd; - (void)OF_addFileDescriptorForWriting: (int)fd; - (void)OF_removeFileDescriptorForReading: (int)fd; - (void)OF_removeFileDescriptorForWriting: (int)fd; -- (void)OF_processQueue; +- (void)OF_processQueueAndStoreRemovedIn: (OFMutableArray*)removed; - (bool)OF_processCache; @end Index: src/OFKernelEventObserver.m ================================================================== --- src/OFKernelEventObserver.m +++ src/OFKernelEventObserver.m @@ -297,11 +297,11 @@ - (void)OF_removeFileDescriptorForWriting: (int)fd { OF_UNRECOGNIZED_SELECTOR } -- (void)OF_processQueue +- (void)OF_processQueueAndStoreRemovedIn: (OFMutableArray*)removed { #ifdef OF_HAVE_THREADS [_mutex lock]; #endif @try { @@ -346,16 +346,18 @@ break; case QUEUE_REMOVE | QUEUE_READ: [self OF_removeFileDescriptorForReading: fd]; + [removed addObject: object]; [_readObjects removeObjectIdenticalTo: object]; break; case QUEUE_REMOVE | QUEUE_WRITE: [self OF_removeFileDescriptorForWriting: fd]; + [removed addObject: object]; [_writeObjects removeObjectIdenticalTo: object]; break; default: assert(0); Index: src/OFKernelEventObserver_kqueue.h ================================================================== --- src/OFKernelEventObserver_kqueue.h +++ src/OFKernelEventObserver_kqueue.h @@ -15,12 +15,14 @@ */ #import "OFKernelEventObserver.h" @class OFDataArray; +@class OFMutableArray; @interface OFKernelEventObserver_kqueue: OFKernelEventObserver { int _kernelQueue; OFDataArray *_changeList; + OFMutableArray *_removedArray; } @end Index: src/OFKernelEventObserver_kqueue.m ================================================================== --- src/OFKernelEventObserver_kqueue.m +++ src/OFKernelEventObserver_kqueue.m @@ -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: Index: src/OFKernelEventObserver_poll.m ================================================================== --- src/OFKernelEventObserver_poll.m +++ src/OFKernelEventObserver_poll.m @@ -131,11 +131,11 @@ { void *pool = objc_autoreleasePoolPush(); struct pollfd *FDs; size_t i, nFDs, realEvents = 0; - [self OF_processQueue]; + [self OF_processQueueAndStoreRemovedIn: nil]; if ([self OF_processCache]) { objc_autoreleasePoolPop(pool); return true; } Index: src/OFKernelEventObserver_select.m ================================================================== --- src/OFKernelEventObserver_select.m +++ src/OFKernelEventObserver_select.m @@ -70,11 +70,11 @@ fd_set readFDs; fd_set writeFDs; struct timeval timeout; size_t i, count, realEvents = 0; - [self OF_processQueue]; + [self OF_processQueueAndStoreRemovedIn: nil]; if ([self OF_processCache]) { objc_autoreleasePoolPop(pool); return true; }