Finding #3
Kind of a edge case but real nonetheless. Stripped long claude analysis.
Philippe
=====
diff --git a/Source/unix/GSRunLoopCtxt.m b/Source/unix/GSRunLoopCtxt.m
index ab4b894e8..3b4496410 100644
--- a/Source/unix/GSRunLoopCtxt.m
+++ b/Source/unix/GSRunLoopCtxt.m
@@ -28,10 +28,10 @@
#include <poll.h>
#endif
-#ifdef HAVE_POLL
+#ifdef HAVE_POLL_F
typedef struct {
int limit;
- short *index;
+ int *index; // Position of each fd in pollfds, or -1
unsigned refcount;
} pollextra;
#endif
@@ -169,6 +169,10 @@ static void setPollfd(int fd, int event, GSRunLoopCtxtUnix
*ctxt)
struct pollfd *pollfds = ctxt->pollfds;
pollextra *pe = (pollextra*)ctxt->loop->sharedContextInfo;
+ if (fd < 0)
+ {
+ return; // Not a descriptor: there is nothing to watch.
+ }
if (fd >= pe->limit)
{
int oldfd_limit = pe->limit;
diff --git a/Tests/base/NSRunLoop/many-watchers.m
b/Tests/base/NSRunLoop/many-watchers.m
new file mode 100644
index 000000000..55ace6275
--- /dev/null
+++ b/Tests/base/NSRunLoop/many-watchers.m
@@ -0,0 +1,176 @@
+/*
+ * The poll() based run loop keeps, for each descriptor, its position in
+ * the array passed to poll(), so that a descriptor watched for several
+ * events gets one entry. Check that this works with more than 32767
+ * descriptors (the positions were stored in a short), and that a watcher
+ * for a negative descriptor is ignored without disturbing the others.
+ */
+#import <Foundation/Foundation.h>
+#import "Testing.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#define MANY 32800
+
+@interface Watcher : NSObject <RunLoopEvents>
+{
+@public
+ int reads;
+ int writes;
+ int others;
+}
+@end
+
+@implementation Watcher
+- (void) receivedEvent: (void*)data
+ type: (RunLoopEventType)type
+ extra: (void*)extra
+ forMode: (NSString*)mode
+{
+ if (type == ET_RDESC)
+ {
+ char c;
+
+ if (read((int)(intptr_t)data, &c, 1) == 1)
+ {
+ reads++;
+ }
+ else
+ {
+ others++;
+ }
+ }
+ else if (type == ET_WDESC)
+ {
+ writes++;
+ }
+ else
+ {
+ others++;
+ }
+}
+@end
+
+static void
+runUntil(NSRunLoop *rl, Watcher *w)
+{
+ NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 2.0];
+
+ while ((w->reads == 0 || w->writes == 0) && [limit timeIntervalSinceNow] > 0)
+ {
+ [rl runMode: NSDefaultRunLoopMode
+ beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
+ }
+}
+
+int main()
+{
+ NSAutoreleasePool *arp = [NSAutoreleasePool new];
+ NSRunLoop *rl = [NSRunLoop currentRunLoop];
+ Watcher *w;
+ Watcher *ignored;
+ int sv[2];
+
+ START_SET("negative descriptor")
+
+ w = AUTORELEASE([Watcher new]);
+ ignored = AUTORELEASE([Watcher new]);
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ [rl addEvent: (void*)(intptr_t)-1 type: ET_RDESC watcher: ignored
+ forMode: NSDefaultRunLoopMode];
+ [rl addEvent: (void*)(intptr_t)sv[0] type: ET_RDESC watcher: w
+ forMode: NSDefaultRunLoopMode];
+ [rl addEvent: (void*)(intptr_t)sv[0] type: ET_WDESC watcher: w
+ forMode: NSDefaultRunLoopMode];
+ write(sv[1], "x", 1);
+ runUntil(rl, w);
+ PASS(w->reads == 1 && w->writes > 0,
+ "watchers of a real descriptor fire next to one for -1")
+ PASS(ignored->reads == 0 && ignored->writes == 0 && ignored->others == 0,
+ "the watcher for -1 is not called")
+ [rl removeEvent: (void*)(intptr_t)-1 type: ET_RDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ [rl removeEvent: (void*)(intptr_t)sv[0] type: ET_RDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ [rl removeEvent: (void*)(intptr_t)sv[0] type: ET_WDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ close(sv[0]);
+ close(sv[1]);
+
+ END_SET("negative descriptor")
+
+ START_SET("more than 32767 descriptors")
+ {
+ struct rlimit lim;
+ int *fds;
+ int n;
+ int i;
+ Watcher *idle = AUTORELEASE([Watcher new]);
+
+ /* Each idle watcher needs a pipe (two descriptors).
+ */
+ if (getrlimit(RLIMIT_NOFILE, &lim) < 0
+ || (lim.rlim_max != RLIM_INFINITY && lim.rlim_max < 2 * MANY + 100))
+ {
+ SKIP("the descriptor limit is too low for this test")
+ }
+ if (lim.rlim_cur != RLIM_INFINITY && lim.rlim_cur < 2 * MANY + 100)
+ {
+ lim.rlim_cur = 2 * MANY + 100;
+ if (setrlimit(RLIMIT_NOFILE, &lim) < 0)
+ {
+ SKIP("cannot raise the descriptor limit for this test")
+ }
+ }
+
+ w = AUTORELEASE([Watcher new]);
+ socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
+ /* The watchers are looked at from the last added to the first, so
+ * the two for sv[0] come after all the others, and the descriptor
+ * gets a position above 32767 when its first watcher is seen.
+ */
+ [rl addEvent: (void*)(intptr_t)sv[0] type: ET_RDESC watcher: w
+ forMode: NSDefaultRunLoopMode];
+ [rl addEvent: (void*)(intptr_t)sv[0] type: ET_WDESC watcher: w
+ forMode: NSDefaultRunLoopMode];
+ fds = malloc(2 * MANY * sizeof(int));
+ for (n = 0; n < MANY; n++)
+ {
+ if (pipe(&fds[2 * n]) < 0)
+ {
+ break;
+ }
+ [rl addEvent: (void*)(intptr_t)fds[2 * n] type: ET_RDESC watcher: idle
+ forMode: NSDefaultRunLoopMode];
+ }
+ PASS(n == MANY, "%d idle descriptors are watched", n)
+ write(sv[1], "x", 1);
+ runUntil(rl, w);
+ PASS(w->reads == 1,
+ "a read watcher fires for a descriptor also watched for writing")
+ PASS(w->writes > 0, "the write watcher fires too")
+ PASS(idle->reads == 0 && idle->others == 0,
+ "the idle watchers are not called")
+
+ for (i = 0; i < n; i++)
+ {
+ [rl removeEvent: (void*)(intptr_t)fds[2 * i] type: ET_RDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ close(fds[2 * i]);
+ close(fds[2 * i + 1]);
+ }
+ free(fds);
+ [rl removeEvent: (void*)(intptr_t)sv[0] type: ET_RDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ [rl removeEvent: (void*)(intptr_t)sv[0] type: ET_WDESC
+ forMode: NSDefaultRunLoopMode all: YES];
+ close(sv[0]);
+ close(sv[1]);
+ }
+ END_SET("more than 32767 descriptors")
+
+ [arp release];
+ return 0;
+}
--
2.39.5