/* * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3.0 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3.0 along with this program. If not, see * . */ #include "config.h" #include #ifdef HAVE_POLL_H # include #endif #import "OFPollKernelEventObserver.h" #import "OFData.h" #import "OFSocket+Private.h" #import "OFObserveKernelEventsFailedException.h" #import "OFOutOfRangeException.h" #ifdef OF_WII # define pollfd pollsd # define fd socket #endif @implementation OFPollKernelEventObserver - (instancetype)init { self = [super init]; @try { struct pollfd p = { _cancelFD[0], POLLIN, 0 }; _FDs = [[OFMutableData alloc] initWithItemSize: sizeof(struct pollfd)]; [_FDs addItem: &p]; _maxFD = _cancelFD[0]; _FDToObject = OFAllocMemory((size_t)_maxFD + 1, sizeof(id)); } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_FDs release]; OFFreeMemory(_FDToObject); [super dealloc]; } static void addObject(OFPollKernelEventObserver *self, id object, int fd, short events) { struct pollfd *FDs; size_t count; bool found; if (fd < 0) @throw [OFObserveKernelEventsFailedException exceptionWithObserver: self errNo: EBADF]; FDs = self->_FDs.mutableItems; count = self->_FDs.count; found = false; for (size_t i = 0; i < count; i++) { if (FDs[i].fd == fd) { FDs[i].events |= events; found = true; break; } } if (!found) { struct pollfd p = { fd, events, 0 }; if (fd > self->_maxFD) { self->_maxFD = fd; self->_FDToObject = OFResizeMemory(self->_FDToObject, (size_t)self->_maxFD + 1, sizeof(id)); } self->_FDToObject[fd] = object; [self->_FDs addItem: &p]; } } static void removeObject(OFPollKernelEventObserver *self, id object, int fd, short events) { struct pollfd *FDs; size_t nFDs; if (fd < 0) @throw [OFObserveKernelEventsFailedException exceptionWithObserver: self errNo: EBADF]; FDs = self->_FDs.mutableItems; nFDs = self->_FDs.count; for (size_t i = 0; i < nFDs; i++) { if (FDs[i].fd == fd) { FDs[i].events &= ~events; if (FDs[i].events == 0) { /* * TODO: Remove from and resize _FDToObject, * adjust _maxFD. */ [self->_FDs removeItemAtIndex: i]; } break; } } } - (void)addObjectForReading: (id )object { addObject(self, object, object.fileDescriptorForReading, POLLIN); [super addObjectForReading: object]; } - (void)addObjectForWriting: (id )object { addObject(self, object, object.fileDescriptorForWriting, POLLOUT); [super addObjectForWriting: object]; } - (void)removeObjectForReading: (id )object { removeObject(self, object, object.fileDescriptorForReading, POLLIN); [super removeObjectForReading: object]; } - (void)removeObjectForWriting: (id )object { removeObject(self, object, object.fileDescriptorForWriting, POLLOUT); [super removeObjectForWriting: object]; } - (void)observeForTimeInterval: (OFTimeInterval)timeInterval { void *pool; struct pollfd *FDs; int events; size_t nFDs; if ([self of_processReadBuffers]) return; pool = objc_autoreleasePoolPush(); FDs = [[[_FDs mutableCopy] autorelease] mutableItems]; nFDs = _FDs.count; #ifdef OPEN_MAX if (nFDs > OPEN_MAX) @throw [OFOutOfRangeException exception]; #endif events = poll(FDs, (nfds_t)nFDs, (int)(timeInterval != -1 ? timeInterval * 1000 : -1)); if (events < 0) @throw [OFObserveKernelEventsFailedException exceptionWithObserver: self errNo: errno]; for (size_t i = 0; i < nFDs; i++) { OFAssert(FDs[i].fd <= _maxFD); if (FDs[i].revents & POLLIN) { void *pool2; if (FDs[i].fd == _cancelFD[0]) { char buffer; #ifdef OF_HAVE_PIPE OFEnsure(read(_cancelFD[0], &buffer, 1) == 1); #else OFEnsure(recvfrom(_cancelFD[0], &buffer, 1, 0, NULL, NULL) == 1); #endif FDs[i].revents = 0; continue; } pool2 = objc_autoreleasePoolPush(); if ([_delegate respondsToSelector: @selector(objectIsReadyForReading:)]) [_delegate objectIsReadyForReading: _FDToObject[FDs[i].fd]]; objc_autoreleasePoolPop(pool2); } if (FDs[i].revents & (POLLOUT | POLLHUP)) { void *pool2 = objc_autoreleasePoolPush(); if ([_delegate respondsToSelector: @selector(objectIsReadyForWriting:)]) [_delegate objectIsReadyForWriting: _FDToObject[FDs[i].fd]]; objc_autoreleasePoolPop(pool2); } FDs[i].revents = 0; } objc_autoreleasePoolPop(pool); } @end