diff --git a/Source/GSRunLoopWatcher.h b/Source/GSRunLoopWatcher.h index 0812692..1e4673d 100644 --- a/Source/GSRunLoopWatcher.h +++ b/Source/GSRunLoopWatcher.h @@ -81,4 +81,15 @@ - (BOOL) runLoopShouldBlock: (BOOL*)trigger; @end +#if GS_HAVE_LIBDISPATCH_COMPAT address@hidden GSDispatchWatcher : GSRunLoopWatcher +{ + @private + BOOL _receivedEventLastTime; + BOOL _mainQueueSafe; +} ++ (GSDispatchWatcher*)sharedInstance; address@hidden +#endif /* GS_HAVE_LIBDISPATCH_COMPAT */ + #endif /* __GSRunLoopWatcher_h_GNUSTEP_BASE_INCLUDE */ diff --git a/Source/GSRunLoopWatcher.m b/Source/GSRunLoopWatcher.m index c8fa8e0..104018e 100644 --- a/Source/GSRunLoopWatcher.m +++ b/Source/GSRunLoopWatcher.m @@ -61,17 +61,17 @@ format: @"NSRunLoop - unknown event type"]; } - if ([anObj respondsToSelector: @selector(runLoopShouldBlock:)]) - { - checkBlocking = YES; - } - if (![anObj respondsToSelector: @selector(receivedEvent:type:extra:forMode:)]) { DESTROY(self); [NSException raise: NSInvalidArgumentException format: @"RunLoop listener has no event handling method"]; } + + if ([anObj respondsToSelector: @selector(runLoopShouldBlock:)]) + { + checkBlocking = YES; + } return self; } @@ -92,3 +92,156 @@ } @end +#if GS_HAVE_LIBDISPATCH_COMPAT +#import "GNUstepBase/NSThread+GNUstepBase.h" + +#ifdef HAVE_POLL_F +#include +#endif + +int _dispatch_get_main_queue_port_4GS(void); +void _dispatch_main_queue_callback_4GS(void); + +static GSDispatchWatcher* _dispatchWatcherSharedInstance; + address@hidden GSDispatchWatcher + ++ (GSDispatchWatcher*)sharedInstance +{ + NSAssert1(GSIsMainThread(), + @"%@", NSInternalInconsistencyException); + + if (_dispatchWatcherSharedInstance == nil) + { + _dispatchWatcherSharedInstance = [[self alloc] init]; + } + return _dispatchWatcherSharedInstance; +} + ++ (id)allocWithZone:(NSZone *)zone +{ + NSAssert1(GSIsMainThread(), + @"%@", NSInternalInconsistencyException); + + if (_dispatchWatcherSharedInstance == nil) + { + _dispatchWatcherSharedInstance = [super allocWithZone:zone]; + return _dispatchWatcherSharedInstance; // assignment and return on first allocation + } + return nil; //on subsequent allocation attempts return nil +} + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +- (id)retain +{ + return self; +} + +- (NSUInteger)retainCount +{ + return UINT_MAX; //denotes an object that cannot be released +} + +- (oneway void)release +{ + //do nothing +} + +- (id)autorelease +{ + return self; +} + +- (id)init +{ + NSAssert1(GSIsMainThread(), + @"%@", NSInternalInconsistencyException); + + int fd = _dispatch_get_main_queue_port_4GS(); + if ((self = [super initWithType:ET_RDESC + receiver:self + data:(void*)(intptr_t)fd])) + { + _receivedEventLastTime = YES; + _mainQueueSafe = YES; + } + return self; +} + +- (BOOL) runLoopShouldBlock: (BOOL*)trigger +{ + NSAssert1(GSIsMainThread(), + @"%@", NSInternalInconsistencyException); + + if (!_mainQueueSafe) + { + *trigger = NO; + return NO; + } + else if (!_receivedEventLastTime) + { +#ifdef HAVE_POLL_F + struct pollfd pfd = + { + .fd = (int)(intptr_t)self->data, + .events = POLLIN, + .revents = 0 + }; + int rc = poll(&pfd, 1, 0); + if (0 < rc) + { + *trigger = YES; + return NO; + } +#else /* HAVE_POLL_F */ + int fd = (int)(intptr_t)self->data; + struct timeval timeout = + { + .tv_sec = 0, + .tv_usec = 0 + }; + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + int rc = select(fd+1, &fds, NULL, NULL, &timeout); + if (0 < rc) + { + *trigger = YES; + return NO; + } +#endif /* HAVE_POLL_F */ + } + + _receivedEventLastTime = NO; + *trigger = NO; + return YES; +} + +- (void)receivedEvent: (void*) __attribute__((unused)) data + type: (RunLoopEventType) __attribute__((unused)) type + extra: (void*) __attribute__((unused)) extra + forMode: (NSString*) __attribute__((unused)) mode +{ + NSAssert1(GSIsMainThread(), + @"%@", NSInternalInconsistencyException); + + /* We don't care how much we read. Dispatch callback will pump all + * the jobs pushed on the main queue. + * The descriptor is non-blocking ... so it's safe to ask for more + * bytes than are available. + */ + char buf[BUFSIZ]; + int inputFd = (int)(intptr_t)self->data; + while (read(inputFd, buf, sizeof(buf)) > 0) {} + + _mainQueueSafe = NO; + _dispatch_main_queue_callback_4GS(); + _mainQueueSafe = YES; + _receivedEventLastTime = YES; +} address@hidden +#endif /* GS_HAVE_LIBDISPATCH_COMPAT */ diff --git a/Source/unix/GSRunLoopCtxt.m b/Source/unix/GSRunLoopCtxt.m index ee2df1b..70d82c1 100644 --- a/Source/unix/GSRunLoopCtxt.m +++ b/Source/unix/GSRunLoopCtxt.m @@ -27,6 +27,10 @@ #include #endif +#if GS_HAVE_LIBDISPATCH_COMPAT +#import "GNUstepBase/NSThread+GNUstepBase.h" +#endif + #define FDCOUNT 1024 #if GS_WITH_GC == 0 @@ -200,6 +204,15 @@ static const NSMapTableValueCallBacks WatcherMapValueCallBacks = WatcherMapValueCallBacks, 0); _wfdMap = NSCreateMapTable (NSIntegerMapKeyCallBacks, WatcherMapValueCallBacks, 0); + +#if GS_HAVE_LIBDISPATCH_COMPAT + if (GSIsMainThread()) + { + /* FIXME: We should add dispatch watcher only for common modes. */ + id w = [GSDispatchWatcher sharedInstance]; + GSIArrayAddItem(watchers, (GSIArrayItem)w); + } +#endif } return self; }