---
 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
Gnustep-dev@gnu.org
https://lists.gnu.org/mailman/listinfo/gnustep-dev

Reply via email to