---
Source/GSRunLoopWatcher.h | 11 +++
Source/GSRunLoopWatcher.m | 163 ++++++++++++++++++++++++++++++++++++++++++--
Source/unix/GSRunLoopCtxt.m | 13 ++++
3 files changed, 182 insertions(+), 5 deletions(-)
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
+@interface GSDispatchWatcher : GSRunLoopWatcher
+{
+ @private
+ BOOL _receivedEventLastTime;
+ BOOL _mainQueueSafe;
+}
++ (GSDispatchWatcher*)sharedInstance;
+@end
+#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 <poll.h>
+#endif
+
+int _dispatch_get_main_queue_port_4GS(void);
+void _dispatch_main_queue_callback_4GS(void);
+
+static GSDispatchWatcher* _dispatchWatcherSharedInstance;
+
+@implementation 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;
+}
+@end
+#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 <poll.h>
#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;
}
_______________________________________________
Gnustep-dev mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/gnustep-dev