Index: src/OFRunLoop.h ================================================================== --- src/OFRunLoop.h +++ src/OFRunLoop.h @@ -15,10 +15,14 @@ * file. */ #import "OFObject.h" #import "OFString.h" + +#ifdef OF_AMIGAOS +# include +#endif OF_ASSUME_NONNULL_BEGIN /*! @file */ @@ -99,10 +103,76 @@ * @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; + +#ifdef OF_AMIGAOS +/*! + * @brief Adds an Exec Signal to the run loop. + * + * If a signal is added multiple times, the specified methods will be performed + * in the order added. + * + * @note This is only available on AmigaOS! + * + * @param signal The signal to add + * @param target The target to call when the signal was received + * @param selector The selector to call on the target when the signal was + * received. The selector must have one parameter for the ULONG + * of the signal that was received. + */ +- (void)addExecSignal: (ULONG)signal + target: (id)target + selector: (SEL)selector; + +/*! + * @brief Adds an Exec Signal to the run loop for the specified mode. + * + * If a signal is added multiple times, the specified methods will be performed + * in the order added. + * + * @note This is only available on AmigaOS! + * + * @param signal The signal to add + * @param mode The run loop mode in which to handle the signal + * @param target The target to call when the signal was received + * @param selector The selector to call on the target when the signal was + * received. The selector must have one parameter for the ULONG + * of the signal that was received. + */ +- (void)addExecSignal: (ULONG)signal + forMode: (of_run_loop_mode_t)mode + target: (id)target + selector: (SEL)selector; + +/*! + * @brief Removes the specified Exec Signal with the specified target and + * selector. + * + * @param signal The signal to remove + * @param target The target which was specified when adding the signal + * @param selector The selector which was specified when adding the signal + */ +- (void)removeExecSignal: (ULONG)signal + target: (id)target + selector: (SEL)selector; + +/*! + * @brief Removes the specified Exec Signal from the specified mode with the + * specified target and selector. + * + * @param signal The signal to remove + * @param mode The run loop mode to which the signal was added + * @param target The target which was specified when adding the signal + * @param selector The selector which was specified when adding the signal + */ +- (void)removeExecSignal: (ULONG)signal + forMode: (of_run_loop_mode_t)mode + target: (id)target + selector: (SEL)selector; +#endif /*! * @brief Starts the run loop. */ - (void)run; Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -20,10 +20,11 @@ #include #include #import "OFRunLoop.h" #import "OFRunLoop+Private.h" +#import "OFArray.h" #import "OFData.h" #import "OFDictionary.h" #ifdef OF_HAVE_SOCKETS # import "OFKernelEventObserver.h" # import "OFTCPSocket.h" @@ -60,10 +61,21 @@ #if defined(OF_HAVE_SOCKETS) OFKernelEventObserver *_kernelEventObserver; OFMutableDictionary *_readQueues, *_writeQueues; #elif defined(OF_HAVE_THREADS) OFCondition *_condition; +# ifdef OF_AMIGAOS + ULONG _execSignalMask; +# endif +#endif +#ifdef OF_AMIGAOS + OFMutableData *_execSignals; + OFMutableArray *_execSignalsTargets; + OFMutableData *_execSignalsSelectors; +# ifdef OF_HAVE_THREADS + OFMutex *_execSignalsMutex; +# endif #endif } @end @interface OFRunLoop () @@ -190,10 +202,20 @@ _readQueues = [[OFMutableDictionary alloc] init]; _writeQueues = [[OFMutableDictionary alloc] init]; #elif defined(OF_HAVE_THREADS) _condition = [[OFCondition alloc] init]; +#endif +#ifdef OF_AMIGAOS + _execSignals = [[OFMutableData alloc] + initWithItemSize: sizeof(ULONG)]; + _execSignalsTargets = [[OFMutableArray alloc] init]; + _execSignalsSelectors = [[OFMutableData alloc] + initWithItemSize: sizeof(SEL)]; +# ifdef OF_HAVE_THREADS + _execSignalsMutex = [[OFMutex alloc] init]; +# endif #endif } @catch (id e) { [self release]; @throw e; } @@ -211,10 +233,18 @@ [_kernelEventObserver release]; [_readQueues release]; [_writeQueues release]; #elif defined(OF_HAVE_THREADS) [_condition release]; +#endif +#ifdef OF_AMIGAOS + [_execSignals release]; + [_execSignalsTargets release]; + [_execSignalsSelectors release]; +# ifdef OF_HAVE_THREADS + [_execSignalsMutex release]; +# endif #endif [super dealloc]; } @@ -304,10 +334,59 @@ } @finally { [queue release]; } } #endif + +#ifdef OF_AMIGAOS +- (void)execSignalWasReceived: (ULONG)signalMask +{ + void *pool = objc_autoreleasePoolPush(); + OFData *signals; + OFArray *targets; + OFData *selectors; + const ULONG *signalsItems; + const id *targetsObjects; + const SEL *selectorsItems; + size_t count; + +# ifdef OF_HAVE_THREADS + [_execSignalsMutex lock]; + @try { +# endif + /* + * Create copies, so that signal handlers are allowed to modify + * signals. + */ + signals = [[_execSignals copy] autorelease]; + targets = [[_execSignalsTargets copy] autorelease]; + selectors = [[_execSignalsSelectors copy] autorelease]; +# ifdef OF_HAVE_THREADS + } @finally { + [_execSignalsMutex unlock]; + } +# endif + + signalsItems = signals.items; + targetsObjects = targets.objects; + selectorsItems = selectors.items; + count = signals.count; + + for (size_t i = 0; i < count; i++) { + if (signalMask & (1ul << signalsItems[i])) { + void (*callback)(id, SEL, ULONG) = + (void (*)(id, SEL, ULONG))[targetsObjects[i] + methodForSelector: selectorsItems[i]]; + + callback(targetsObjects[i], selectorsItems[i], + signalsItems[i]); + } + } + + objc_autoreleasePoolPop(pool); +} +#endif @end #ifdef OF_HAVE_SOCKETS @implementation OFRunLoopQueueItem - (bool)handleObject: (id)object @@ -1199,10 +1278,120 @@ } @finally { [state->_timersQueueMutex unlock]; } #endif } + +#ifdef OF_AMIGAOS +- (void)addExecSignal: (ULONG)signal + target: (id)target + selector: (SEL)selector +{ + [self addExecSignal: signal + forMode: of_run_loop_mode_default + target: target + selector: selector]; +} + +- (void)addExecSignal: (ULONG)signal + forMode: (of_run_loop_mode_t)mode + target: (id)target + selector: (SEL)selector +{ + OFRunLoopState *state = [self of_stateForMode: mode + create: true]; + +# ifdef OF_HAVE_THREADS + [state->_execSignalsMutex lock]; + @try { +# endif + [state->_execSignals addItem: &signal]; + [state->_execSignalsTargets addObject: target]; + [state->_execSignalsSelectors addItem: &selector]; + +# ifdef OF_HAVE_SOCKETS + state->_kernelEventObserver.execSignalMask |= (1ul << signal); +# elif defined(OF_HAVE_THREADS) + state->_execSignalMask |= (1ul << signal); +# endif +# ifdef OF_HAVE_THREADS + } @finally { + [state->_execSignalsMutex unlock]; + } +# endif + +# if defined(OF_HAVE_SOCKETS) + [state->_kernelEventObserver cancel]; +# elif defined(OF_HAVE_THREADS) + [state->_condition signal]; +# endif +} + +- (void)removeExecSignal: (ULONG)signal + target: (id)target + selector: (SEL)selector +{ + [self removeExecSignal: signal + forMode: of_run_loop_mode_default + target: target + selector: selector]; +} + +- (void)removeExecSignal: (ULONG)signal + forMode: (of_run_loop_mode_t)mode + target: (id)target + selector: (SEL)selector +{ + OFRunLoopState *state = [self of_stateForMode: mode + create: false]; + + if (state == nil) + return; + +# ifdef OF_HAVE_THREADS + [state->_execSignalsMutex lock]; + @try { +# endif + const ULONG *signals = state->_execSignals.items; + const id *targets = state->_execSignalsTargets.objects; + const SEL *selectors = state->_execSignalsSelectors.items; + size_t count = state->_execSignals.count; + bool found = false; + ULONG newMask = 0; + + for (size_t i = 0; i < count; i++) { + if (!found && signals[i] == signal && + targets[i] == target && selectors[i] == selector) { + [state->_execSignals removeItemAtIndex: i]; + [state->_execSignalsTargets + removeObjectAtIndex: i]; + [state->_execSignalsSelectors + removeItemAtIndex: i]; + + found = true; + } else + newMask |= (1ul << signals[i]); + } + +# ifdef OF_HAVE_SOCKETS + state->_kernelEventObserver.execSignalMask = newMask; +# elif defined(OF_HAVE_THREADS) + state->_execSignalMask = newMask; +# endif +# ifdef OF_HAVE_THREADS + } @finally { + [state->_execSignalsMutex unlock]; + } +# endif + +# if defined(OF_HAVE_SOCKETS) + [state->_kernelEventObserver cancel]; +# elif defined(OF_HAVE_THREADS) + [state->_condition signal]; +# endif +} +#endif - (void)run { [self runUntilDate: nil]; } @@ -1229,10 +1418,13 @@ return; _currentMode = mode; @try { OFDate *nextTimer; +#if defined(OF_AMIGAOS) && !defined(OF_HAVE_SOCKETS) && defined(OF_HAVE_THREADS) + ULONG signalMask; +#endif for (;;) { OFTimer *timer; #ifdef OF_HAVE_THREADS @@ -1301,11 +1493,19 @@ if (e.errNo != EINTR) @throw e; } #elif defined(OF_HAVE_THREADS) [state->_condition lock]; +# ifdef OF_AMIGAOS + signalMask = state->_execSignalMask; + [state->_condition waitForTimeInterval: timeout + orExecSignal: &signalMask]; + if (signalMask != 0) + [state execSignalWasReceived: signalMask]; +# else [state->_condition waitForTimeInterval: timeout]; +# endif [state->_condition unlock]; #else [OFThread sleepForTimeInterval: timeout]; #endif } else { @@ -1321,11 +1521,19 @@ if (e.errNo != EINTR) @throw e; } #elif defined(OF_HAVE_THREADS) [state->_condition lock]; +# ifdef OF_AMIGAOS + signalMask = state->_execSignalMask; + [state->_condition + waitForConditionOrExecSignal: &signalMask]; + if (signalMask != 0) + [state execSignalWasReceived: signalMask]; +# else [state->_condition wait]; +# endif [state->_condition unlock]; #else [OFThread sleepForTimeInterval: 86400]; #endif } Index: src/condition_amiga.m ================================================================== --- src/condition_amiga.m +++ src/condition_amiga.m @@ -104,15 +104,12 @@ waitingTask.next = condition->waitingTasks; condition->waitingTasks = &waitingTask; mask = Wait((1ul << waitingTask.sigBit) | *signalMask); - - if (mask & (1ul << waitingTask.sigBit)) + if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) ret = of_mutex_lock(mutex); - else if (*signalMask &= mask) - ret = true; else { /* * This should not happen - it means something interrupted the * Wait(), so the best we can do is return EINTR. */ @@ -209,18 +206,16 @@ SendIO((struct IORequest *)&request); mask = Wait((1ul << waitingTask.sigBit) | (1ul << port.mp_SigBit) | *signalMask); - if (mask & (1ul << waitingTask.sigBit)) + if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) ret = of_mutex_lock(mutex); else if (mask & (1ul << port.mp_SigBit)) { ret = false; errno = ETIMEDOUT; - } else if (*signalMask &= mask) - ret = true; - else { + } else { /* * This should not happen - it means something interrupted the * Wait(), so the best we can do is return EINTR. */ ret = false;