>From 52e581254a713daa2f19693108af35a859ec7ebd Mon Sep 17 00:00:00 2001 From: Peter Bex Date: Fri, 8 Jun 2012 22:08:40 +0200 Subject: [PATCH] Fix scheduler so it can handle file descriptors over FD_SETSIZE (which occurs in practice on systems where ulimit -n has a higher value) --- NEWS | 1 + scheduler.scm | 98 ++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/NEWS b/NEWS index 7ec6431..725dddc 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ - on 64-bit machines the "random" procedure no longer truncates result values (which caused very nonrandom results for very large values). Note that random shouldn't be used for security-critical code. + - Fixed select()-calls to accommodate more than FD_SETSIZE open files. - Build system - version information has been moved into a separate unit to make the diff --git a/scheduler.scm b/scheduler.scm index e3a96bc..c28ed16 100644 --- a/scheduler.scm +++ b/scheduler.scm @@ -31,9 +31,8 @@ (hide ready-queue-head ready-queue-tail ##sys#timeout-list ##sys#update-thread-state-buffer ##sys#restore-thread-state-buffer remove-from-ready-queue ##sys#unblock-threads-for-i/o ##sys#force-primordial - fdset-input-set fdset-output-set fdset-clear - fdset-select-timeout fdset-set fdset-test - create-fdset stderr + current-fdset-size fd-iset fd-oset fdset-extend fdset-clear make-fdset + fdset-select-timeout fdset-set fdset-test create-fdset stderr ##sys#clear-i/o-state-for-thread! ##sys#abandon-mutexes) (not inline ##sys#interrupt-hook) (unsafe) @@ -74,9 +73,16 @@ C_word C_msleep(C_word ms) { return C_SCHEME_TRUE; } #endif +/* These two should be defined by select.h, but are not standardized... */ +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif +#ifndef NFDBITS +#define NFDBITS (sizeof (fd_mask) * 8) +#endif + static fd_set C_fdset_input, C_fdset_output; -#define C_fd_test_input(fd) C_mk_bool(FD_ISSET(C_unfix(fd), &C_fdset_input)) -#define C_fd_test_output(fd) C_mk_bool(FD_ISSET(C_unfix(fd), &C_fdset_output)) +#define C_fd_test(fd,set) C_mk_bool(FD_ISSET(C_unfix(fd), (fd_set *)C_data_pointer(set))) EOF ) ) @@ -344,46 +350,61 @@ EOF (define ##sys#fd-list '()) ; ((FD1 THREAD1 ...) ...) +(define current-fdset-size 64) +(define fd-iset (make-fdset current-fdset-size)) +(define fd-oset (make-fdset current-fdset-size)) + (define (create-fdset) - (fdset-clear) - (let loop ((lst ##sys#fd-list)) - (unless (null? lst) - (let ((fd (caar lst))) - (for-each - (lambda (t) - (let ((p (##sys#slot t 11))) - (fdset-set fd (cdr p)))) - (cdar lst)) - (loop (cdr lst)))))) + (let* ((max (foldl (lambda (max x) (fxmax max (car x))) 0 ##sys#fd-list)) + (size (let lp ((s current-fdset-size)) ; Find next power of two + (if (fx> max s) (lp (fxshl s 1)) s)))) + (when (fx> size current-fdset-size) ; Adjust set sizes if neccessary + (set! current-fdset-size size) + (set! fd-iset (make-fdset size)) + (set! fd-oset (make-fdset size))) + (fdset-clear fd-iset) + (fdset-clear fd-oset) + (let loop ((lst ##sys#fd-list)) + (unless (null? lst) + (let ((fd (caar lst))) + (for-each + (lambda (t) + (let ((p (##sys#slot t 11))) + (fdset-set fd (cdr p)))) + (cdar lst)) + (loop (cdr lst))))) + max)) (define fdset-select-timeout - (foreign-lambda* int ([bool to] [double tm]) + (foreign-lambda* int ((int maxfd) (bool to) (double tm) + (scheme-pointer iset) (scheme-pointer oset)) "struct timeval timeout;" "timeout.tv_sec = tm / 1000;" "timeout.tv_usec = fmod(tm, 1000) * 1000;" - "C_return(select(FD_SETSIZE, &C_fdset_input, &C_fdset_output, NULL, to ? &timeout : NULL));") ) + "C_return(select(maxfd+1, iset, oset, NULL, to ? &timeout : NULL));") ) -(define fdset-clear - (foreign-lambda* void () - "FD_ZERO(&C_fdset_input);" - "FD_ZERO(&C_fdset_output);")) +(define (fdset-clear set) + ((foreign-lambda* void ((scheme-pointer fdset) (int size)) + "memset(fdset, 0, size);") + set (##sys#size set))) -(define fdset-input-set - (foreign-lambda* void ([int fd]) - "FD_SET(fd, &C_fdset_input);" ) ) +(define (make-fdset maxfd) + (let ((size ((foreign-lambda* int ((int maxfd)) + "C_return(howmany(maxfd+1, NFDBITS) * sizeof(fd_mask));") maxfd))) + (make-blob size))) -(define fdset-output-set - (foreign-lambda* void ([int fd]) - "FD_SET(fd, &C_fdset_output);" ) ) +(define fdset-extend + (foreign-lambda* void ((int fd) (scheme-pointer fdset)) + "FD_SET(fd, (fd_set *)fdset);" ) ) (define (fdset-set fd i/o) (dbg "setting fdset for " fd " to " i/o) (case i/o - ((#t #:input) (fdset-input-set fd)) - ((#f #:output) (fdset-output-set fd)) + ((#t #:input) (fdset-extend fd fd-iset)) + ((#f #:output) (fdset-extend fd fd-oset)) ((#:all) - (fdset-input-set fd) - (fdset-output-set fd) ) + (fdset-extend fd fd-iset) + (fdset-extend fd fd-oset) ) (else (panic "fdset-set: invalid i/o direction")))) (define (fdset-test inf outf i/o) @@ -410,8 +431,8 @@ EOF (define (##sys#unblock-threads-for-i/o) (dbg "fd-list: " ##sys#fd-list) - (create-fdset) - (let* ((to? (pair? ##sys#timeout-list)) + (let* ((maxfd (create-fdset)) + (to? (pair? ##sys#timeout-list)) (rq? (pair? ready-queue-head)) (tmo (if (and to? (not rq?)) ; no thread was unblocked by timeout, so wait (let* ((tmo1 (caar ##sys#timeout-list)) @@ -419,12 +440,11 @@ EOF (fpmax 0.0 (fp- tmo1 now)) ) 0.0) ) ) ; otherwise immediate timeout. (dbg "waiting for I/O with timeout " tmo) - (let ((n (fdset-select-timeout ; we use FD_SETSIZE, but really should use max fd - (or rq? to?) - tmo))) + (let ((n (fdset-select-timeout maxfd (or rq? to?) tmo fd-iset fd-oset))) (dbg n " fds ready") (cond [(eq? -1 n) - (dbg "select(2) returned with result -1" ) + (dbg "select(2) returned with result -1, error was: " + (foreign-value "strerror(errno)" c-string)) (##sys#force-primordial)] [(fx> n 0) (set! ##sys#fd-list @@ -433,8 +453,8 @@ EOF lst (let* ([a (car lst)] [fd (car a)] - [inf (##core#inline "C_fd_test_input" fd)] - [outf (##core#inline "C_fd_test_output" fd)]) + [inf (##core#inline "C_fd_test" fd fd-iset)] + [outf (##core#inline "C_fd_test" fd fd-oset)]) (dbg "fd " fd " state: input=" inf ", output=" outf) (if (or inf outf) (let loop2 ((threads (cdr a)) (keep '())) -- 1.7.9.1