Hi Felix & All,

I observed a starvation of some threads, when there's concurrent i/o
going on.

The attached patch improves fairness in most cases by eating up the
(new) current-ready-queue before refilling it.  But it does obviously
not fix 100% of possible cases of the starvation problem.

I have no environment to quantify the improvement.  But the patch avoids
some needless select(2) handling and improves responsiveness to an
extend that I can "feel it in the mouse".

Unfortunately I'm unable to reproduce the starvation without handling
bad fd's too.  Therefor I must regret to include that change in the
attached patch.

best regards

/Jörg
Index: scheduler.scm
===================================================================
--- scheduler.scm	(Revision 11689)
+++ scheduler.scm	(Arbeitskopie)
@@ -33,10 +33,11 @@
   (emit-exports "scheduler.exports")
   (disable-warning var)
   (hide ##sys#ready-queue-head ##sys#ready-queue-tail ##sys#timeout-list
+	##sys#current-ready-queue ##sys#remove-from-current-ready-queue
 	##sys#update-thread-state-buffer ##sys#restore-thread-state-buffer
 	##sys#remove-from-ready-queue ##sys#unblock-threads-for-i/o ##sys#force-primordial
 	##sys#fdset-input-set ##sys#fdset-output-set ##sys#fdset-clear
-	##sys#fdset-select-timeout ##sys#fdset-restore
+	##sys#fdset-select-timeout ##sys#fdset-restore ##sys#handle-bad-fd!
 	##sys#clear-i/o-state-for-thread!) 
   (foreign-declare #<<EOF
 #ifdef HAVE_ERRNO_H
@@ -60,6 +61,7 @@
 # include <sys/types.h>
 # include <sys/time.h>
 # include <time.h>
+# include <sys/stat.h>
 static C_word C_msleep(C_word ms);
 C_word C_msleep(C_word ms) {
 #ifdef __CYGWIN__
@@ -91,10 +93,24 @@
 #;(define-macro (dbg . args)
   `(print "DBG: " ,@args) )
 
+(define ##sys#current-ready-queue '())
 
+(define (##sys#remove-from-current-ready-queue)
+  (let ((h ##sys#current-ready-queue))
+    (or (and (pair? h)
+	     (let ((r (cdr h)))
+	       (set! ##sys#current-ready-queue r)
+	       (car h) ) )
+	(begin
+	  (set! ##sys#current-ready-queue ##sys#ready-queue-head)
+	  (set! ##sys#ready-queue-head '())
+	  (set! ##sys#ready-queue-tail '())
+	  (and (pair? ##sys#current-ready-queue)
+	       (##sys#remove-from-current-ready-queue)))) ) )
+
 (define (##sys#schedule)
   (define (switch thread)
-    (dbg "switching to " thread)
+    (dbg "switching to ~a" thread)
     (set! ##sys#current-thread thread)
     (##sys#setslot thread 3 'running)
     (##sys#restore-thread-state-buffer thread)
@@ -111,7 +127,7 @@
       (##sys#add-to-ready-queue ct) )
     (let loop1 ()
       ;; Unblock threads waiting for timeout:
-      (unless (null? ##sys#timeout-list)
+      (unless (or (pair? ##sys#current-ready-queue)) (null? ##sys#timeout-list)
 	(let ([now (##sys#fudge 16)])
 	  (dbg "timeout (" now ") list: " ##sys#timeout-list)
 	  (let loop ([lst ##sys#timeout-list])
@@ -145,12 +161,11 @@
       ;; Unblock threads blocked by I/O:
       (if eintr
 	  (##sys#force-primordial)
-	  (begin
-	    (unless (null? ##sys#fd-list)
-	      (##sys#unblock-threads-for-i/o) ) ) )
+	  (unless (or (pair? ##sys#current-ready-queue) (null? ##sys#fd-list))
+ 	   (##sys#unblock-threads-for-i/o) ) )
       ;; Fetch and activate next ready thread:
       (let loop2 ()
-	(let ([nt (##sys#remove-from-ready-queue)])
+	(let ([nt (##sys#remove-from-current-ready-queue)])
 	  (cond [(not nt) 
 		 (if (and (null? ##sys#timeout-list) (null? ##sys#fd-list))
 		     (##sys#signal-hook #:runtime-error "deadlock")
@@ -160,6 +175,7 @@
 
 (define (##sys#force-primordial)
   (dbg "primordial thread forced due to interrupt")
+  ;(display "switching to primordial thread\n" debug-port)
   (##sys#thread-unblock! ##sys#primordial-thread) )
 
 (define ##sys#ready-queue-head '())
@@ -341,6 +357,30 @@
   (##sys#setislot t 13 #f)
   (##sys#setslot t 11 (cons fd i/o)) )
 
+(define-foreign-variable error-bad-file int "(errno == EBADF)")
+
+(define (##sys#handle-bad-fd! e)
+  (dbg "check bad" e)
+  (let ((bad ((foreign-lambda*
+	       bool ((integer fd))
+	       "struct stat buf;"
+	       "int i = ( (fstat(fd, &buf) == -1 && errno == EBADF) ? 1 : 0);"
+	       "return(i);")
+	      (car e))))
+    (if bad
+	(for-each
+	 (lambda (thread)
+	   (thread-signal!
+	    thread
+	    (##sys#make-structure
+	     'condition
+	     '(exn i/o) ;; better? '(exn i/o net)
+	     (list '(exn . message) "bad file descriptor"
+		   '(exn . arguments) (car e)
+		   '(exn . location) thread) )))
+	 (cdr e)))
+    bad))
+
 (define (##sys#unblock-threads-for-i/o)
   (dbg "fd-list: " ##sys#fd-list)
   (let* ([to? (pair? ##sys#timeout-list)]
@@ -353,8 +393,23 @@
 		   (fxmax 0 (- tmo1 now)) )
 		 0) ) ] )		; otherwise immediate timeout.
     (dbg n " fds ready")
-    (cond [(eq? -1 n) 
-	   (##sys#force-primordial)]
+    (cond [(eq? -1 n)
+	   (cond
+	    (error-bad-file
+	     (set! ##sys#fd-list
+		   (let loop ((l ##sys#fd-list))
+		     (cond
+		      ((null? l) l)
+		      ((##sys#handle-bad-fd! (car l))
+		       (##sys#fdset-clear (caar l))
+		       ;; This is supposed to be a rare case, catch
+		       ;; them one by one.
+		       ;; (loop (cdr l))
+		       (cdr l))
+		      (else (cons (car l) (loop (cdr l)))))))
+	     (##sys#fdset-restore)
+	     (##sys#unblock-threads-for-i/o))
+	    (else (##sys#force-primordial))) ]
 	  [(fx> n 0)
 	   (set! ##sys#fd-list
 	     (let loop ([n n] [lst ##sys#fd-list])
_______________________________________________
Chicken-users mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/chicken-users

Reply via email to