Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -21,96 +21,115 @@ # import "OFUDPSocket.h" #endif OF_ASSUME_NONNULL_BEGIN +@class OFRunLoop_State; + @interface OFRunLoop () + (void)of_setMainRunLoop: (OFRunLoop *)runLoop; #ifdef OF_HAVE_SOCKETS + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)socket + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)socket buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; + (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket buffer: (const void *)buffer length: (size_t)length receiver: (of_socket_address_t)receiver + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (nullable id)context; # ifdef OF_HAVE_BLOCKS + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_block_t)block; + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_block_t)block; + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_line_block_t)block; + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_stream_async_write_block_t)block; + (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)socket + mode: (of_run_loop_mode_t)mode block: (of_tcp_socket_async_accept_block_t) block; + (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)socket buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_udp_socket_async_receive_block_t) block; + (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)socket buffer: (const void *)buffer length: (size_t)length receiver: (of_socket_address_t)receiver + mode: (of_run_loop_mode_t)mode block: (of_udp_socket_async_send_block_t)block; # endif -+ (void)of_cancelAsyncRequestsForObject: (id)object; ++ (void)of_cancelAsyncRequestsForObject: (id)object + mode: (of_run_loop_mode_t)mode; #endif -- (void)of_removeTimer: (OFTimer *)timer; +- (void)of_removeTimer: (OFTimer *)timer + forMode: (of_run_loop_mode_t)mode; @end OF_ASSUME_NONNULL_END Index: src/OFRunLoop.h ================================================================== --- src/OFRunLoop.h +++ src/OFRunLoop.h @@ -14,15 +14,18 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFObject.h" +#import "OFString.h" #ifdef OF_HAVE_SOCKETS # import "OFTCPSocket.h" #endif OF_ASSUME_NONNULL_BEGIN + +/*! @file */ @class OFSortedList OF_GENERIC(ObjectType); #ifdef OF_HAVE_THREADS @class OFMutex; @class OFCondition; @@ -32,37 +35,46 @@ #endif @class OFMutableDictionary OF_GENERIC(KeyType, ObjectType); @class OFTimer; @class OFDate; +/*! + * @brief A mode for an OFRunLoop. + */ +typedef OFConstantString *of_run_loop_mode_t; + +#ifdef __cplusplus +extern "C" { +#endif +/*! + * @brief The default mode for an OFRunLoop. + */ +extern of_run_loop_mode_t of_run_loop_mode_default; +#ifdef __cplusplus +} +#endif + /*! * @class OFRunLoop OFRunLoop.h ObjFW/OFRunLoop.h * * @brief A class providing a run loop for the application and its processes. */ @interface OFRunLoop: OFObject -#ifdef OF_HAVE_SOCKETS - -#endif -{ - OFSortedList OF_GENERIC(OFTimer *) *_timersQueue; -#ifdef OF_HAVE_THREADS - OFMutex *_timersQueueLock; -#endif -#if defined(OF_HAVE_SOCKETS) - OFKernelEventObserver *_kernelEventObserver; - OFMutableDictionary *_readQueues, *_writeQueues; -#elif defined(OF_HAVE_THREADS) - OFCondition *_condition; -#endif +{ + OFMutableDictionary *_states; +#ifdef OF_HAVE_THREADS + OFMutex *_statesMutex; +#endif + of_run_loop_mode_t _currentMode; volatile bool _stop; } #ifdef OF_HAVE_CLASS_PROPERTIES @property (class, readonly, nullable, nonatomic) OFRunLoop *mainRunLoop; @property (class, readonly, nullable, nonatomic) OFRunLoop *currentRunLoop; #endif +@property (readonly, nonatomic) of_run_loop_mode_t currentMode; /*! * @brief Returns the run loop for the main thread. * * @return The run loop for the main thread @@ -81,10 +93,19 @@ * * @param timer The timer to add */ - (void)addTimer: (OFTimer *)timer; +/*! + * @brief Adds an OFTimer to the run loop for the specified mode. + * + * @param timer The timer to add + * @param mode The run loop mode in which to run the timer + */ +- (void)addTimer: (OFTimer *)timer + forMode: (of_run_loop_mode_t)mode; + /*! * @brief Starts the run loop. */ - (void)run; @@ -93,13 +114,23 @@ * * @param deadline The date until which the run loop should run */ - (void)runUntilDate: (nullable OFDate *)deadline; +/*! + * @brief Run the run loop until an event or timer occurs or the specified + * deadline is reached. + * + * @param mode The mode in which to run the run loop + * @param deadline The date until which the run loop should run at the longest + */ +- (void)runMode: (of_run_loop_mode_t)mode + beforeDate: (OFDate *)deadline; + /*! * @brief Stops the run loop. If there is still an operation being executed, it * is finished before the run loop stops. */ - (void)stop; @end OF_ASSUME_NONNULL_END Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -41,11 +41,36 @@ #import "OFObserveFailedException.h" #ifdef OF_HAVE_SOCKETS # import "OFConnectionFailedException.h" #endif +of_run_loop_mode_t of_run_loop_mode_default = @"of_run_loop_mode_default"; static OFRunLoop *mainRunLoop = nil; + +@interface OFRunLoop () +- (OFRunLoop_State *)of_stateForMode: (of_run_loop_mode_t)mode + create: (bool)create; +@end + +@interface OFRunLoop_State: OFObject +#ifdef OF_HAVE_SOCKETS + +#endif +{ +@public + OFSortedList OF_GENERIC(OFTimer *) *_timersQueue; +#ifdef OF_HAVE_THREADS + OFMutex *_timersQueueMutex; +#endif +#if defined(OF_HAVE_SOCKETS) + OFKernelEventObserver *_kernelEventObserver; + OFMutableDictionary *_readQueues, *_writeQueues; +#elif defined(OF_HAVE_THREADS) + OFCondition *_condition; +#endif +} +@end #ifdef OF_HAVE_SOCKETS @interface OFRunLoop_QueueItem: OFObject { @public @@ -132,11 +157,143 @@ const void *_buffer; size_t _length; of_socket_address_t _receiver; } @end +#endif +@implementation OFRunLoop_State +- (instancetype)init +{ + self = [super init]; + + @try { + _timersQueue = [[OFSortedList alloc] init]; + +#if defined(OF_HAVE_SOCKETS) + _kernelEventObserver = [[OFKernelEventObserver alloc] init]; + [_kernelEventObserver setDelegate: self]; + + _readQueues = [[OFMutableDictionary alloc] init]; + _writeQueues = [[OFMutableDictionary alloc] init]; +#elif defined(OF_HAVE_THREADS) + _condition = [[OFCondition alloc] init]; +#endif + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_timersQueue release]; +#if defined(OF_HAVE_SOCKETS) + [_kernelEventObserver release]; + [_readQueues release]; + [_writeQueues release]; +#elif defined(OF_HAVE_THREADS) + [_condition release]; +#endif + + [super dealloc]; +} + +#ifdef OF_HAVE_SOCKETS +- (void)objectIsReadyForReading: (id)object +{ + /* + * Retain the queue so that it doesn't disappear from us because the + * handler called -[cancelAsyncRequests]. + */ + OFList OF_GENERIC(OF_KINDOF(OFRunLoop_ReadQueueItem *)) *queue = + [[_readQueues objectForKey: object] retain]; + + assert(queue != nil); + + @try { + if (![[queue firstObject] handleObject: object]) { + of_list_object_t *listObject = [queue firstListObject]; + + /* + * The handler might have called -[cancelAsyncRequests] + * so that our queue is now empty, in which case we + * should do nothing. + */ + if (listObject != NULL) { + /* + * Make sure we keep the target until after we + * are done removing the object. The reason for + * this is that the target might call + * -[cancelAsyncRequests] in its dealloc. + */ + [[listObject->object retain] autorelease]; + + [queue removeListObject: listObject]; + + if ([queue count] == 0) { + [_kernelEventObserver + removeObjectForReading: object]; + [_readQueues + removeObjectForKey: object]; + } + } + } + } @finally { + [queue release]; + } +} + +- (void)objectIsReadyForWriting: (id)object +{ + /* + * Retain the queue so that it doesn't disappear from us because the + * handler called -[cancelAsyncRequests]. + */ + OFList OF_GENERIC(OF_KINDOF(OFRunLoop_WriteQueueItem *)) *queue = + [[_writeQueues objectForKey: object] retain]; + + assert(queue != nil); + + @try { + if (![[queue firstObject] handleObject: object]) { + of_list_object_t *listObject = [queue firstListObject]; + + /* + * The handler might have called -[cancelAsyncRequests] + * so that our queue is now empty, in which case we + * should do nothing. + */ + if (listObject != NULL) { + /* + * Make sure we keep the target until after we + * are done removing the object. The reason for + * this is that the target might call + * -[cancelAsyncRequests] in its dealloc. + */ + [[listObject->object retain] autorelease]; + + [queue removeListObject: listObject]; + + if ([queue count] == 0) { + [_kernelEventObserver + removeObjectForWriting: object]; + [_writeQueues + removeObjectForKey: object]; + } + } + } + } @finally { + [queue release]; + } +} +#endif +@end + +#ifdef OF_HAVE_SOCKETS @implementation OFRunLoop_QueueItem - (bool)handleObject: (id)object { OF_UNRECOGNIZED_SELECTOR } @@ -495,10 +652,12 @@ # endif @end #endif @implementation OFRunLoop +@synthesize currentMode = _currentMode; + + (OFRunLoop *)mainRunLoop { return mainRunLoop; } @@ -515,45 +674,49 @@ { mainRunLoop = [runLoop retain]; } #ifdef OF_HAVE_SOCKETS -# define ADD_READ(type, object, code) \ +# define ADD_READ(type, object, mode, code) \ void *pool = objc_autoreleasePoolPush(); \ OFRunLoop *runLoop = [self currentRunLoop]; \ - OFList *queue = [runLoop->_readQueues objectForKey: object]; \ + OFRunLoop_State *state = [runLoop of_stateForMode: mode \ + create: true]; \ + OFList *queue = [state->_readQueues objectForKey: object]; \ type *queueItem; \ \ if (queue == nil) { \ queue = [OFList list]; \ - [runLoop->_readQueues setObject: queue \ + [state->_readQueues setObject: queue \ forKey: object]; \ } \ \ if ([queue count] == 0) \ - [runLoop->_kernelEventObserver \ + [state->_kernelEventObserver \ addObjectForReading: object]; \ \ queueItem = [[[type alloc] init] autorelease]; \ code \ [queue appendObject: queueItem]; \ \ objc_autoreleasePoolPop(pool); -# define ADD_WRITE(type, object, code) \ +# define ADD_WRITE(type, object, mode, code) \ void *pool = objc_autoreleasePoolPush(); \ OFRunLoop *runLoop = [self currentRunLoop]; \ - OFList *queue = [runLoop->_writeQueues objectForKey: object]; \ + OFRunLoop_State *state = [runLoop of_stateForMode: mode \ + create: true]; \ + OFList *queue = [state->_writeQueues objectForKey: object]; \ type *queueItem; \ \ if (queue == nil) { \ queue = [OFList list]; \ - [runLoop->_writeQueues setObject: queue \ + [state->_writeQueues setObject: queue \ forKey: object]; \ } \ \ if ([queue count] == 0) \ - [runLoop->_kernelEventObserver \ + [state->_kernelEventObserver \ addObjectForWriting: object]; \ \ queueItem = [[[type alloc] init] autorelease]; \ code \ [queue appendObject: queueItem]; \ @@ -562,15 +725,16 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_READ(OFRunLoop_ReadQueueItem, stream, { + ADD_READ(OFRunLoop_ReadQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_length = length; @@ -579,15 +743,16 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)exactLength + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_READ(OFRunLoop_ExactReadQueueItem, stream, { + ADD_READ(OFRunLoop_ExactReadQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_exactLength = exactLength; @@ -595,15 +760,16 @@ } + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_READ(OFRunLoop_ReadLineQueueItem, stream, { + ADD_READ(OFRunLoop_ReadLineQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_encoding = encoding; }) @@ -611,55 +777,59 @@ + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_WRITE(OFRunLoop_WriteQueueItem, stream, { + ADD_WRITE(OFRunLoop_WriteQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_length = length; }) } + (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)stream + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_WRITE(OFRunLoop_ConnectQueueItem, stream, { + ADD_WRITE(OFRunLoop_ConnectQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; }) } + (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)stream + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_READ(OFRunLoop_AcceptQueueItem, stream, { + ADD_READ(OFRunLoop_AcceptQueueItem, stream, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; }) } + (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)sock buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_READ(OFRunLoop_UDPReceiveQueueItem, sock, { + ADD_READ(OFRunLoop_UDPReceiveQueueItem, sock, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_length = length; @@ -668,15 +838,16 @@ + (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)sock buffer: (const void *)buffer length: (size_t)length receiver: (of_socket_address_t)receiver + mode: (of_run_loop_mode_t)mode target: (id)target selector: (SEL)selector context: (id)context { - ADD_WRITE(OFRunLoop_UDPSendQueueItem, sock, { + ADD_WRITE(OFRunLoop_UDPSendQueueItem, sock, mode, { queueItem->_target = [target retain]; queueItem->_selector = selector; queueItem->_context = [context retain]; queueItem->_buffer = buffer; queueItem->_length = length; @@ -687,13 +858,14 @@ # ifdef OF_HAVE_BLOCKS + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_block_t)block { - ADD_READ(OFRunLoop_ReadQueueItem, stream, { + ADD_READ(OFRunLoop_ReadQueueItem, stream, mode, { queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_length = length; }) } @@ -700,58 +872,63 @@ + (void)of_addAsyncReadForStream: (OFStream *) stream buffer: (void *)buffer exactLength: (size_t)exactLength + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_block_t)block { - ADD_READ(OFRunLoop_ExactReadQueueItem, stream, { + ADD_READ(OFRunLoop_ExactReadQueueItem, stream, mode, { queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_exactLength = exactLength; }) } + (void)of_addAsyncReadLineForStream: (OFStream *) stream encoding: (of_string_encoding_t)encoding + mode: (of_run_loop_mode_t)mode block: (of_stream_async_read_line_block_t)block { - ADD_READ(OFRunLoop_ReadLineQueueItem, stream, { + ADD_READ(OFRunLoop_ReadLineQueueItem, stream, mode, { queueItem->_block = [block copy]; queueItem->_encoding = encoding; }) } + (void)of_addAsyncWriteForStream: (OFStream *) stream buffer: (const void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_stream_async_write_block_t)block { - ADD_WRITE(OFRunLoop_WriteQueueItem, stream, { + ADD_WRITE(OFRunLoop_WriteQueueItem, stream, mode, { queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_length = length; }) } + (void)of_addAsyncAcceptForTCPSocket: (OFTCPSocket *)stream + mode: (of_run_loop_mode_t)mode block: (of_tcp_socket_async_accept_block_t)block { - ADD_READ(OFRunLoop_AcceptQueueItem, stream, { + ADD_READ(OFRunLoop_AcceptQueueItem, stream, mode, { queueItem->_block = [block copy]; }) } + (void)of_addAsyncReceiveForUDPSocket: (OFUDPSocket *)sock buffer: (void *)buffer length: (size_t)length + mode: (of_run_loop_mode_t)mode block: (of_udp_socket_async_receive_block_t) block { - ADD_READ(OFRunLoop_UDPReceiveQueueItem, sock, { + ADD_READ(OFRunLoop_UDPReceiveQueueItem, sock, mode, { queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_length = length; }) } @@ -758,13 +935,14 @@ + (void)of_addAsyncSendForUDPSocket: (OFUDPSocket *)sock buffer: (const void *)buffer length: (size_t)length receiver: (of_socket_address_t)receiver + mode: (of_run_loop_mode_t)mode block: (of_udp_socket_async_send_block_t)block { - ADD_WRITE(OFRunLoop_UDPSendQueueItem, sock, { + ADD_WRITE(OFRunLoop_UDPSendQueueItem, sock, mode, { queueItem->_block = [block copy]; queueItem->_buffer = buffer; queueItem->_length = length; queueItem->_receiver = receiver; }) @@ -772,39 +950,45 @@ # endif # undef ADD_READ # undef ADD_WRITE + (void)of_cancelAsyncRequestsForObject: (id)object + mode: (of_run_loop_mode_t)mode { void *pool = objc_autoreleasePoolPush(); OFRunLoop *runLoop = [self currentRunLoop]; + OFRunLoop_State *state = [runLoop of_stateForMode: mode + create: false]; OFList *queue; - if ((queue = [runLoop->_writeQueues objectForKey: object]) != nil) { + if (state == nil) + return; + + if ((queue = [state->_writeQueues objectForKey: object]) != nil) { assert([queue count] > 0); /* * Clear the queue now, in case this has been called from a * handler, as otherwise, we'd do the cleanups below twice. */ [queue removeAllObjects]; - [runLoop->_kernelEventObserver removeObjectForWriting: object]; - [runLoop->_writeQueues removeObjectForKey: object]; + [state->_kernelEventObserver removeObjectForWriting: object]; + [state->_writeQueues removeObjectForKey: object]; } - if ((queue = [runLoop->_readQueues objectForKey: object]) != nil) { + if ((queue = [state->_readQueues objectForKey: object]) != nil) { assert([queue count] > 0); /* * Clear the queue now, in case this has been called from a * handler, as otherwise, we'd do the cleanups below twice. */ [queue removeAllObjects]; - [runLoop->_kernelEventObserver removeObjectForReading: object]; - [runLoop->_readQueues removeObjectForKey: object]; + [state->_kernelEventObserver removeObjectForReading: object]; + [state->_readQueues removeObjectForKey: object]; } objc_autoreleasePoolPop(pool); } #endif @@ -812,23 +996,24 @@ - (instancetype)init { self = [super init]; @try { - _timersQueue = [[OFSortedList alloc] init]; + OFRunLoop_State *state; + + _states = [[OFMutableDictionary alloc] init]; + + state = [[OFRunLoop_State alloc] init]; + @try { + [_states setObject: state + forKey: of_run_loop_mode_default]; + } @finally { + [state release]; + } + #ifdef OF_HAVE_THREADS - _timersQueueLock = [[OFMutex alloc] init]; -#endif - -#if defined(OF_HAVE_SOCKETS) - _kernelEventObserver = [[OFKernelEventObserver alloc] init]; - [_kernelEventObserver setDelegate: self]; - - _readQueues = [[OFMutableDictionary alloc] init]; - _writeQueues = [[OFMutableDictionary alloc] init]; -#elif defined(OF_HAVE_THREADS) - _condition = [[OFCondition alloc] init]; + _statesMutex = [[OFMutex alloc] init]; #endif } @catch (id e) { [self release]; @throw e; } @@ -836,158 +1021,108 @@ return self; } - (void)dealloc { - [_timersQueue release]; + [_states release]; #ifdef OF_HAVE_THREADS - [_timersQueueLock release]; -#endif -#if defined(OF_HAVE_SOCKETS) - [_kernelEventObserver release]; - [_readQueues release]; - [_writeQueues release]; -#elif defined(OF_HAVE_THREADS) - [_condition release]; + [_statesMutex release]; #endif [super dealloc]; } + +- (OFRunLoop_State *)of_stateForMode: (of_run_loop_mode_t)mode + create: (bool)create +{ + OFRunLoop_State *state; + +#ifdef OF_HAVE_THREADS + [_statesMutex lock]; + @try { +#endif + state = [_states objectForKey: mode]; + + if (create && state == nil) { + state = [[OFRunLoop_State alloc] init]; + @try { + [_states setObject: state + forKey: mode]; + } @finally { + [state release]; + } + } +#ifdef OF_HAVE_THREADS + } @finally { + [_statesMutex unlock]; + } +#endif + + return state; +} + +- (void)addTimer: (OFTimer *)timer +{ + [self addTimer: timer + forMode: of_run_loop_mode_default]; +} - (void)addTimer: (OFTimer *)timer + forMode: (of_run_loop_mode_t)mode { + OFRunLoop_State *state = [self of_stateForMode: mode + create: true]; + #ifdef OF_HAVE_THREADS - [_timersQueueLock lock]; + [state->_timersQueueMutex lock]; @try { #endif - [_timersQueue insertObject: timer]; + [state->_timersQueue insertObject: timer]; #ifdef OF_HAVE_THREADS } @finally { - [_timersQueueLock unlock]; + [state->_timersQueueMutex unlock]; } #endif - [timer of_setInRunLoop: self]; + [timer of_setInRunLoop: self + mode: mode]; #if defined(OF_HAVE_SOCKETS) - [_kernelEventObserver cancel]; + [state->_kernelEventObserver cancel]; #elif defined(OF_HAVE_THREADS) - [_condition signal]; + [state->_condition signal]; #endif } - (void)of_removeTimer: (OFTimer *)timer + forMode: (of_run_loop_mode_t)mode { + OFRunLoop_State *state = [self of_stateForMode: mode + create: false]; + + if (state == nil) + return; + #ifdef OF_HAVE_THREADS - [_timersQueueLock lock]; + [state->_timersQueueMutex lock]; @try { #endif of_list_object_t *iter; - for (iter = [_timersQueue firstListObject]; iter != NULL; + for (iter = [state->_timersQueue firstListObject]; iter != NULL; iter = iter->next) { if ([iter->object isEqual: timer]) { - [_timersQueue removeListObject: iter]; + [state->_timersQueue removeListObject: iter]; break; } } #ifdef OF_HAVE_THREADS } @finally { - [_timersQueueLock unlock]; - } -#endif -} - -#ifdef OF_HAVE_SOCKETS -- (void)objectIsReadyForReading: (id)object -{ - /* - * Retain the queue so that it doesn't disappear from us because the - * handler called -[cancelAsyncRequests]. - */ - OFList OF_GENERIC(OF_KINDOF(OFRunLoop_ReadQueueItem *)) *queue = - [[_readQueues objectForKey: object] retain]; - - assert(queue != nil); - - @try { - if (![[queue firstObject] handleObject: object]) { - of_list_object_t *listObject = [queue firstListObject]; - - /* - * The handler might have called -[cancelAsyncRequests] - * so that our queue is now empty, in which case we - * should do nothing. - */ - if (listObject != NULL) { - /* - * Make sure we keep the target until after we - * are done removing the object. The reason for - * this is that the target might call - * -[cancelAsyncRequests] in its dealloc. - */ - [[listObject->object retain] autorelease]; - - [queue removeListObject: listObject]; - - if ([queue count] == 0) { - [_kernelEventObserver - removeObjectForReading: object]; - [_readQueues - removeObjectForKey: object]; - } - } - } - } @finally { - [queue release]; - } -} - -- (void)objectIsReadyForWriting: (id)object -{ - /* - * Retain the queue so that it doesn't disappear from us because the - * handler called -[cancelAsyncRequests]. - */ - OFList OF_GENERIC(OF_KINDOF(OFRunLoop_WriteQueueItem *)) *queue = - [[_writeQueues objectForKey: object] retain]; - - assert(queue != nil); - - @try { - if (![[queue firstObject] handleObject: object]) { - of_list_object_t *listObject = [queue firstListObject]; - - /* - * The handler might have called -[cancelAsyncRequests] - * so that our queue is now empty, in which case we - * should do nothing. - */ - if (listObject != NULL) { - /* - * Make sure we keep the target until after we - * are done removing the object. The reason for - * this is that the target might call - * -[cancelAsyncRequests] in its dealloc. - */ - [[listObject->object retain] autorelease]; - - [queue removeListObject: listObject]; - - if ([queue count] == 0) { - [_kernelEventObserver - removeObjectForWriting: object]; - [_writeQueues - removeObjectForKey: object]; - } - } - } - } @finally { - [queue release]; - } -} -#endif + [state->_timersQueueMutex unlock]; + } +#endif +} - (void)run { [self runUntilDate: nil]; } @@ -994,55 +1129,73 @@ - (void)runUntilDate: (OFDate *)deadline { _stop = false; - for (;;) { - void *pool = objc_autoreleasePoolPush(); - OFDate *now = [OFDate date]; + while (!_stop && + (deadline == nil || [deadline timeIntervalSinceNow] >= 0)) + [self runMode: of_run_loop_mode_default + beforeDate: deadline]; +} + +- (void)runMode: (of_run_loop_mode_t)mode + beforeDate: (OFDate *)deadline +{ + void *pool = objc_autoreleasePoolPush(); + OFRunLoop_State *state = [self of_stateForMode: mode + create: false]; + + if (state == nil) + return; + + _currentMode = mode; + @try { OFDate *nextTimer; for (;;) { OFTimer *timer; #ifdef OF_HAVE_THREADS - [_timersQueueLock lock]; + [state->_timersQueueMutex lock]; @try { #endif of_list_object_t *listObject = - [_timersQueue firstListObject]; + [state->_timersQueue firstListObject]; if (listObject != NULL && [[listObject->object - fireDate] compare: now] != - OF_ORDERED_DESCENDING) { + fireDate] timeIntervalSinceNow] <= 0) { timer = [[listObject->object retain] autorelease]; - [_timersQueue removeListObject: - listObject]; + [state->_timersQueue + removeListObject: listObject]; - [timer of_setInRunLoop: nil]; + [timer of_setInRunLoop: nil + mode: nil]; } else break; #ifdef OF_HAVE_THREADS } @finally { - [_timersQueueLock unlock]; + [state->_timersQueueMutex unlock]; } #endif - if ([timer isValid]) + if ([timer isValid]) { [timer fire]; + return; + } } #ifdef OF_HAVE_THREADS - [_timersQueueLock lock]; + [state->_timersQueueMutex lock]; @try { #endif - nextTimer = [[_timersQueue firstObject] fireDate]; + nextTimer = [[state->_timersQueue + firstObject] fireDate]; #ifdef OF_HAVE_THREADS } @finally { - [_timersQueueLock unlock]; + [state->_timersQueueMutex unlock]; } #endif /* Watch for I/O events until the next timer is due */ if (nextTimer != nil || deadline != nil) { @@ -1059,20 +1212,20 @@ if (timeout < 0) timeout = 0; #if defined(OF_HAVE_SOCKETS) @try { - [_kernelEventObserver + [state->_kernelEventObserver observeForTimeInterval: timeout]; } @catch (OFObserveFailedException *e) { if ([e errNo] != EINTR) @throw e; } #elif defined(OF_HAVE_THREADS) - [_condition lock]; - [_condition waitForTimeInterval: timeout]; - [_condition unlock]; + [state->_condition lock]; + [state->_condition waitForTimeInterval: timeout]; + [state->_condition unlock]; #else [OFThread sleepForTimeInterval: timeout]; #endif } else { /* @@ -1080,41 +1233,42 @@ * until we get an event. If a timer is added by * another thread, it cancels the observe. */ #if defined(OF_HAVE_SOCKETS) @try { - [_kernelEventObserver observe]; + [state->_kernelEventObserver observe]; } @catch (OFObserveFailedException *e) { if ([e errNo] != EINTR) @throw e; } #elif defined(OF_HAVE_THREADS) - [_condition lock]; - [_condition wait]; - [_condition unlock]; + [state->_condition lock]; + [state->_condition wait]; + [state->_condition unlock]; #else [OFThread sleepForTimeInterval: 86400]; #endif } - if (_stop || (deadline != nil && - [deadline compare: now] != OF_ORDERED_DESCENDING)) { - objc_autoreleasePoolPop(pool); - break; - } - - objc_autoreleasePoolPop(pool); - } - - _stop = false; + objc_autoreleasePoolPop(pool); + } @finally { + _currentMode = nil; + } } - (void)stop { + OFRunLoop_State *state = [self of_stateForMode: of_run_loop_mode_default + create: false]; + _stop = true; + + if (state == nil) + return; + #if defined(OF_HAVE_SOCKETS) - [_kernelEventObserver cancel]; + [state->_kernelEventObserver cancel]; #elif defined(OF_HAVE_THREADS) - [_condition signal]; + [state->_condition signal]; #endif } @end Index: src/OFStream.m ================================================================== --- src/OFStream.m +++ src/OFStream.m @@ -205,10 +205,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer length: length + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -222,10 +223,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer exactLength: length + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -238,10 +240,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer length: length + mode: of_run_loop_mode_default block: block]; } - (void)asyncReadIntoBuffer: (void *)buffer exactLength: (size_t)length @@ -251,10 +254,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncReadForStream: stream buffer: buffer exactLength: length + mode: of_run_loop_mode_default block: block]; } # endif #endif @@ -850,10 +854,11 @@ OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadLineForStream: stream encoding: encoding + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -870,10 +875,11 @@ OFStream *stream = (OFStream *)self; [OFRunLoop of_addAsyncReadLineForStream: stream encoding: encoding + mode: of_run_loop_mode_default block: block]; } # endif #endif @@ -1132,10 +1138,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncWriteForStream: stream buffer: buffer length: length + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -1148,10 +1155,11 @@ (OFStream *)self; [OFRunLoop of_addAsyncWriteForStream: stream buffer: buffer length: length + mode: of_run_loop_mode_default block: block]; } # endif #endif @@ -1748,11 +1756,12 @@ } #ifdef OF_HAVE_SOCKETS - (void)cancelAsyncRequests { - [OFRunLoop of_cancelAsyncRequestsForObject: self]; + [OFRunLoop of_cancelAsyncRequestsForObject: self + mode: of_run_loop_mode_default]; } #endif - (void)unreadFromBuffer: (const void *)buffer length: (size_t)length Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -292,12 +292,14 @@ if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) { if (errNo == EINPROGRESS) { SEL selector = @selector(socketDidConnect:context: exception:); + of_run_loop_mode_t mode = of_run_loop_mode_default; [OFRunLoop of_addAsyncConnectForTCPSocket: _socket + mode: mode target: self selector: selector context: nil]; return; } else { @@ -1065,19 +1067,21 @@ - (void)asyncAcceptWithTarget: (id)target selector: (SEL)selector context: (id)context { [OFRunLoop of_addAsyncAcceptForTCPSocket: self + mode: of_run_loop_mode_default target: target selector: selector context: context]; } #ifdef OF_HAVE_BLOCKS - (void)asyncAcceptWithBlock: (of_tcp_socket_async_accept_block_t)block { [OFRunLoop of_addAsyncAcceptForTCPSocket: self + mode: of_run_loop_mode_default block: block]; } #endif - (const of_socket_address_t *)remoteAddress Index: src/OFTimer+Private.h ================================================================== --- src/OFTimer+Private.h +++ src/OFTimer+Private.h @@ -18,10 +18,10 @@ #import "OFTimer.h" OF_ASSUME_NONNULL_BEGIN @interface OFTimer () -@property OF_NULLABLE_PROPERTY (retain, nonatomic, setter=of_setInRunLoop:) - OFRunLoop *of_inRunLoop; +- (void)of_setInRunLoop: (nullable OFRunLoop *)runLoop + mode: (nullable of_run_loop_mode_t)mode; @end OF_ASSUME_NONNULL_END Index: src/OFTimer.h ================================================================== --- src/OFTimer.h +++ src/OFTimer.h @@ -14,18 +14,18 @@ * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #import "OFObject.h" +#import "OFRunLoop.h" OF_ASSUME_NONNULL_BEGIN /*! @file */ @class OFTimer; @class OFDate; -@class OFRunLoop; #ifdef OF_HAVE_THREADS @class OFCondition; #endif #ifdef OF_HAVE_BLOCKS @@ -58,10 +58,11 @@ #ifdef OF_HAVE_THREADS OFCondition *_condition; bool _done; #endif OFRunLoop *_Nullable _inRunLoop; + of_run_loop_mode_t _Nullable _inRunLoopMode; } /*! * @brief The time interval in which the timer will repeat, if it is a * repeating timer. Index: src/OFTimer.m ================================================================== --- src/OFTimer.m +++ src/OFTimer.m @@ -32,11 +32,10 @@ #import "OFInvalidArgumentException.h" @implementation OFTimer @synthesize timeInterval = _interval, repeating = _repeats, valid = _valid; -@synthesize of_inRunLoop = _inRunLoop; + (instancetype)scheduledTimerWithTimeInterval: (of_time_interval_t)timeInterval target: (id)target selector: (SEL)selector repeats: (bool)repeats @@ -488,10 +487,11 @@ /* * The run loop references the timer, so it should never be deallocated * if it is still in a run loop. */ assert(_inRunLoop == nil); + assert(_inRunLoopMode == nil); [_fireDate release]; [_target release]; [_object1 release]; [_object2 release]; @@ -516,10 +516,23 @@ timer = (OFTimer *)object; return [_fireDate compare: timer->_fireDate]; } + +- (void)of_setInRunLoop: (OFRunLoop *)runLoop + mode: (of_run_loop_mode_t)mode +{ + OFRunLoop *oldInRunLoop = _inRunLoop; + of_run_loop_mode_t oldInRunLoopMode = _inRunLoopMode; + + _inRunLoop = [runLoop retain]; + [oldInRunLoop release]; + + _inRunLoopMode = [mode copy]; + [oldInRunLoopMode release]; +} - (void)fire { void *pool = objc_autoreleasePoolPush(); id target = [[_target retain] autorelease]; @@ -532,10 +545,11 @@ if (_repeats && _valid) { int64_t missedIntervals = -[_fireDate timeIntervalSinceNow] / _interval; of_time_interval_t newFireDate; + OFRunLoop *runLoop; /* In case the clock was changed backwards */ if (missedIntervals < 0) missedIntervals = 0; @@ -544,11 +558,13 @@ [_fireDate release]; _fireDate = [[OFDate alloc] initWithTimeIntervalSince1970: newFireDate]; - [[OFRunLoop currentRunLoop] addTimer: self]; + runLoop = [OFRunLoop currentRunLoop]; + [runLoop addTimer: self + forMode: [runLoop currentMode]]; } else [self invalidate]; #ifdef OF_HAVE_BLOCKS if (_block != NULL) @@ -609,17 +625,19 @@ [self retain]; @try { @synchronized (self) { OFDate *old; - [_inRunLoop of_removeTimer: self]; + [_inRunLoop of_removeTimer: self + forMode: _inRunLoopMode]; old = _fireDate; _fireDate = [fireDate copy]; [old release]; - [_inRunLoop addTimer: self]; + [_inRunLoop addTimer: self + forMode: _inRunLoopMode]; } } @finally { [self release]; } } Index: src/OFUDPSocket.m ================================================================== --- src/OFUDPSocket.m +++ src/OFUDPSocket.m @@ -516,10 +516,11 @@ context: (id)context { [OFRunLoop of_addAsyncReceiveForUDPSocket: self buffer: buffer length: length + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -529,10 +530,11 @@ block: (of_udp_socket_async_receive_block_t)block { [OFRunLoop of_addAsyncReceiveForUDPSocket: self buffer: buffer length: length + mode: of_run_loop_mode_default block: block]; } #endif - (void)sendBuffer: (const void *)buffer @@ -586,10 +588,11 @@ { [OFRunLoop of_addAsyncSendForUDPSocket: self buffer: buffer length: length receiver: receiver + mode: of_run_loop_mode_default target: target selector: selector context: context]; } @@ -601,17 +604,19 @@ { [OFRunLoop of_addAsyncSendForUDPSocket: self buffer: buffer length: length receiver: receiver + mode: of_run_loop_mode_default block: block]; } #endif - (void)cancelAsyncRequests { - [OFRunLoop of_cancelAsyncRequestsForObject: self]; + [OFRunLoop of_cancelAsyncRequestsForObject: self + mode: of_run_loop_mode_default]; } - (int)fileDescriptorForReading { #ifndef OF_WINDOWS