1.0.42.43: FD-STREAMS no longer hook into SERVE-EVENT by default

* SOCKET-MAKE-STREAM, and MAKE-FD-STREAM have new keyword
   argument :SERVE-EVENTS which requests that blocking IO on the
   stream should dispatch to SERVE-EVENT. For SOCKET-MAKE-STREAM the
   default is T, for MAKE-FD-STREAM the default it NIL.

 * Don't call SYSREAD-MAY-BLOCK-P at all unless we need to to handle
   events or check for timeout.

 * Make WAIT-UNTIL-FD-USABLE use UNIX-SIMPLE-POLL instead of going
   into SUB-SERVE-EVENT when appropriate:

   ** Explicit requests to not serve events.

   ** Timeout 0.

   ** No other handlers and no periodic polling function.

 * When FD-STREAM-SERVE-EVENTS is false but write returns EWOULDBLOCK,
   don't queue output but wait till poll(2) says we can go.

 * UNIX-SIMPLE-POLL uses poll() only on platforms where a build-time
   test shows it to exist and work as expected. Elsewhere it is built
   on top of good 'ol select().
This commit is contained in:
Nikodemus Siivola 2010-09-19 20:08:47 +00:00
parent 809babfb68
commit 6e02a5455a
11 changed files with 266 additions and 147 deletions

12
NEWS
View file

@ -1,5 +1,13 @@
;;;; -*- coding: utf-8; fill-column: 78 -*- ;;;; -*- coding: utf-8; fill-column: 78 -*-
changes relative to sbcl-1.0.42 changes relative to sbcl-1.0.42
* incompatible change: FD-STREAMS no longer participate in the serve-event
event-loop by default.
** In addition to streams created by explicit calls to MAKE-FD-STREAM this
affects streams from CL:OPEN.
** Streams from SOCKET-MAKE-STREAM still participate in serve-event by
default, but this is liable to change: applications needing serve-event
for socket streams should explicitly request it using :SERVE-EVENTS T
in the call.
* enhancement: SB-EXT:WORD type is provided for use with SB-EXT:ATOMIC-INCF * enhancement: SB-EXT:WORD type is provided for use with SB-EXT:ATOMIC-INCF
&co. &co.
* enhancement: CLOS effective method functions and defclass slot typechecking * enhancement: CLOS effective method functions and defclass slot typechecking
@ -9,8 +17,8 @@ changes relative to sbcl-1.0.42
* enhancement: symbols are printed using fully qualified names in several * enhancement: symbols are printed using fully qualified names in several
error and warning messages which are often associated with package error and warning messages which are often associated with package
conflicts or mixups (lp#622789, thanks to Attila Lendvai) conflicts or mixups (lp#622789, thanks to Attila Lendvai)
* optimization: use poll(2) instead of select(2) to check for blocking IO * optimization: where available, use poll(2) instead of select(2) to check
on a single FD. for blocking IO on a single FD.
* bug fix: SB-BSD-SOCKETS:SOCKET-CONNECT was not thread safe. (lp#505497, * bug fix: SB-BSD-SOCKETS:SOCKET-CONNECT was not thread safe. (lp#505497,
thanks to Andrew Golding) thanks to Andrew Golding)
* bug fix: DOTIMES accepted literal non-integer reals. (lp#619393, thanks to * bug fix: DOTIMES accepted literal non-integer reals. (lp#619393, thanks to

View file

@ -381,12 +381,17 @@ for the stream."))
(buffering :full) (buffering :full)
(external-format :default) (external-format :default)
timeout timeout
auto-close) auto-close
"Default method for SOCKET objects. An ELEMENT-TYPE of :DEFAULT will (serve-events t))
construct a bivalent stream. Acceptable values for BUFFERING are :FULL, :LINE "Default method for SOCKET objects.
An ELEMENT-TYPE of :DEFAULT will construct a bivalent stream, capable of both
binary and character IO. Acceptable values for BUFFERING are :FULL, :LINE
and :NONE. Streams will have no TIMEOUT by default. If AUTO-CLOSE is true, the and :NONE. Streams will have no TIMEOUT by default. If AUTO-CLOSE is true, the
underlying OS socket is automatically closed after the stream and the socket underlying OS socket is automatically closed after the stream and the socket
have been garbage collected. have been garbage collected. If SERVE-EVENTS is true, blocking IO on the
socket will dispatch to the recursive event loop -- the default is currently
true, but this liable to change.
The stream for SOCKET will be cached, and a second invocation of this method The stream for SOCKET will be cached, and a second invocation of this method
will return the same stream. This may lead to oddities if this function is will return the same stream. This may lead to oddities if this function is
@ -407,8 +412,9 @@ and get an output stream in response\)."
:buffering buffering :buffering buffering
:external-format external-format :external-format external-format
:timeout timeout :timeout timeout
:auto-close auto-close))) :auto-close auto-close
(setf (slot-value socket 'stream) stream) :serve-events serve-events))
(setf (slot-value socket 'stream) stream))
(sb-ext:cancel-finalization socket) (sb-ext:cancel-finalization socket)
stream)) stream))

View file

@ -185,7 +185,8 @@
(not (equalp-with-case r (vals entry))))) (not (equalp-with-case r (vals entry)))))
(when (pend entry) (when (pend entry)
(let ((*print-circle* *print-circle-on-failure*)) (let ((*print-circle* *print-circle-on-failure*)
(*print-escape* nil))
(format s "~&Test ~:@(~S~) failed~ (format s "~&Test ~:@(~S~) failed~
~%Form: ~S~ ~%Form: ~S~
~%Expected value~P: ~ ~%Expected value~P: ~

View file

@ -1245,7 +1245,7 @@ SB-EXT:PACKAGE-LOCKED-ERROR-SYMBOL."))
(lambda (condition stream) (lambda (condition stream)
(declare (type stream stream)) (declare (type stream stream))
(format stream (format stream
"I/O timeout ~(~A~)ing ~S." "I/O timeout while doing ~(~A~) on ~S."
(io-timeout-direction condition) (io-timeout-direction condition)
(stream-error-stream condition))))) (stream-error-stream condition)))))

View file

@ -169,6 +169,8 @@
(char-pos nil :type (or unsigned-byte null)) (char-pos nil :type (or unsigned-byte null))
;; T if input is waiting on FD. :EOF if we hit EOF. ;; T if input is waiting on FD. :EOF if we hit EOF.
(listen nil :type (member nil t :eof)) (listen nil :type (member nil t :eof))
;; T if serve-event is allowed when this stream blocks
(serve-events nil :type boolean)
;; the input buffer ;; the input buffer
(instead (make-array 0 :element-type 'character :adjustable t :fill-pointer t) :type (array character (*))) (instead (make-array 0 :element-type 'character :adjustable t :fill-pointer t) :type (array character (*)))
@ -262,32 +264,45 @@
(aver (< head tail)) (aver (< head tail))
(%queue-and-replace-output-buffer stream)) (%queue-and-replace-output-buffer stream))
(t (t
;; Try a non-blocking write, queue whatever is left over. ;; Try a non-blocking write, if SERVE-EVENT is allowed, queue
;; whatever is left over. Otherwise wait until we can write.
(aver (< head tail)) (aver (< head tail))
(synchronize-stream-output stream) (synchronize-stream-output stream)
(let ((length (- tail head))) (loop
(multiple-value-bind (count errno) (let ((length (- tail head)))
(sb!unix:unix-write (fd-stream-fd stream) (buffer-sap obuf) (multiple-value-bind (count errno)
head length) (sb!unix:unix-write (fd-stream-fd stream) (buffer-sap obuf)
(cond ((eql count length) head length)
;; Complete write -- we can use the same buffer. (flet ((queue-or-wait ()
(reset-buffer obuf)) (if (fd-stream-serve-events stream)
(count (return (%queue-and-replace-output-buffer stream))
;; Partial write -- update buffer status and queue. (or (wait-until-fd-usable (fd-stream-fd stream) :output
;; Do not use INCF! Another thread might have moved (fd-stream-timeout stream)
;; head... nil)
(setf (buffer-head obuf) (+ count head)) (signal-timeout 'io-timeout
(%queue-and-replace-output-buffer stream)) :stream stream
#!-win32 :direction :output
((eql errno sb!unix:ewouldblock) :seconds (fd-stream-timeout stream))))))
;; Blocking, queue. (cond ((eql count length)
(%queue-and-replace-output-buffer stream)) ;; Complete write -- we can use the same buffer.
(t (return (reset-buffer obuf)))
(simple-stream-perror "Couldn't write to ~s" (count
stream errno))))))))))) ;; Partial write -- update buffer status and
;; queue or wait. Do not use INCF! Another
;; thread might have moved head...
(setf (buffer-head obuf) (+ count head))
(queue-or-wait))
#!-win32
((eql errno sb!unix:ewouldblock)
;; Blocking, queue or wair.
(queue-or-wait))
(t
(simple-stream-perror "Couldn't write to ~s"
stream errno)))))))))))))
;;; Helper for FLUSH-OUTPUT-BUFFER -- returns the new buffer. ;;; Helper for FLUSH-OUTPUT-BUFFER -- returns the new buffer.
(defun %queue-and-replace-output-buffer (stream) (defun %queue-and-replace-output-buffer (stream)
(aver (fd-stream-serve-events stream))
(let ((queue (fd-stream-output-queue stream)) (let ((queue (fd-stream-output-queue stream))
(later (list (or (fd-stream-obuf stream) (bug "Missing obuf.")))) (later (list (or (fd-stream-obuf stream) (bug "Missing obuf."))))
(new (get-buffer))) (new (get-buffer)))
@ -312,6 +327,7 @@
;;; This is called by the FD-HANDLER for the stream when output is ;;; This is called by the FD-HANDLER for the stream when output is
;;; possible. ;;; possible.
(defun write-output-from-queue (stream) (defun write-output-from-queue (stream)
(aver (fd-stream-serve-events stream))
(synchronize-stream-output stream) (synchronize-stream-output stream)
(let (not-first-p) (let (not-first-p)
(tagbody (tagbody
@ -951,10 +967,13 @@
(errno 0) (errno 0)
(count 0)) (count 0))
(tagbody (tagbody
;; Check for blocking input before touching the stream, as if ;; Check for blocking input before touching the stream if we are to
;; we happen to wait we are liable to be interrupted, and the ;; serve events: if the FD is blocking, we don't want to hang on the
;; interrupt handler may use the same stream. ;; write if we are to serve events or notice timeouts.
(if (sysread-may-block-p stream) (if (and (or (fd-stream-serve-events stream)
(fd-stream-timeout stream)
*deadline*)
(sysread-may-block-p stream))
(go :wait-for-input) (go :wait-for-input)
(go :main)) (go :main))
;; These (:CLOSED-FLAME and :READ-ERROR) tags are here so what ;; These (:CLOSED-FLAME and :READ-ERROR) tags are here so what
@ -966,8 +985,11 @@
:wait-for-input :wait-for-input
;; This tag is here so we can unwind outside the WITHOUT-INTERRUPTS ;; This tag is here so we can unwind outside the WITHOUT-INTERRUPTS
;; to wait for input if read tells us EWOULDBLOCK. ;; to wait for input if read tells us EWOULDBLOCK.
(unless (wait-until-fd-usable fd :input (fd-stream-timeout stream)) (unless (wait-until-fd-usable fd :input (fd-stream-timeout stream)
(signal-timeout 'io-timeout :stream stream :direction :read (fd-stream-serve-events stream))
(signal-timeout 'io-timeout
:stream stream
:direction :input
:seconds (fd-stream-timeout stream))) :seconds (fd-stream-timeout stream)))
:main :main
;; Since the read should not block, we'll disable the ;; Since the read should not block, we'll disable the
@ -978,7 +1000,7 @@
;; resulting thunk is stack-allocatable. ;; resulting thunk is stack-allocatable.
((lambda (return-reason) ((lambda (return-reason)
(ecase return-reason (ecase return-reason
((nil)) ; fast path normal cases ((nil)) ; fast path normal cases
((:wait-for-input) (go :wait-for-input)) ((:wait-for-input) (go :wait-for-input))
((:closed-flame) (go :closed-flame)) ((:closed-flame) (go :closed-flame))
((:read-error) (go :read-error)))) ((:read-error) (go :read-error))))
@ -2062,6 +2084,7 @@
(flush-output-buffer stream) (flush-output-buffer stream)
(do () (do ()
((null (fd-stream-output-queue stream))) ((null (fd-stream-output-queue stream)))
(aver (fd-stream-serve-events stream))
(serve-all-events))) (serve-all-events)))
(defun fd-stream-get-file-position (stream) (defun fd-stream-get-file-position (stream)
@ -2163,6 +2186,9 @@
;;; FILE is the name of the file (will be returned by PATHNAME). ;;; FILE is the name of the file (will be returned by PATHNAME).
;;; ;;;
;;; NAME is used to identify the stream when printed. ;;; NAME is used to identify the stream when printed.
;;;
;;; If SERVE-EVENTS is true, SERVE-EVENT machinery is used to
;;; handle blocking IO on the stream.
(defun make-fd-stream (fd (defun make-fd-stream (fd
&key &key
(input nil input-p) (input nil input-p)
@ -2170,6 +2196,7 @@
(element-type 'base-char) (element-type 'base-char)
(buffering :full) (buffering :full)
(external-format :default) (external-format :default)
serve-events
timeout timeout
file file
original original
@ -2198,6 +2225,7 @@
:external-format external-format :external-format external-format
:bivalent-p (eq element-type :default) :bivalent-p (eq element-type :default)
:char-size (external-format-char-size external-format) :char-size (external-format-char-size external-format)
:serve-events serve-events
:timeout :timeout
(if timeout (if timeout
(coerce timeout 'single-float) (coerce timeout 'single-float)
@ -2400,6 +2428,7 @@
:delete-original delete-original :delete-original delete-original
:pathname pathname :pathname pathname
:dual-channel-p nil :dual-channel-p nil
:serve-events nil
:input-buffer-p t :input-buffer-p t
:auto-close t)) :auto-close t))
(:probe (:probe
@ -2474,6 +2503,7 @@
(setf *stdin* (setf *stdin*
(make-fd-stream 0 :name "standard input" :input t :buffering :line (make-fd-stream 0 :name "standard input" :input t :buffering :line
:element-type :default :element-type :default
:serve-events t
:external-format (stdstream-external-format nil))) :external-format (stdstream-external-format nil)))
(setf *stdout* (setf *stdout*
(make-fd-stream 1 :name "standard output" :output t :buffering :line (make-fd-stream 1 :name "standard output" :output t :buffering :line

View file

@ -138,42 +138,81 @@
;;;; SERVE-ALL-EVENTS, SERVE-EVENT, and friends ;;;; SERVE-ALL-EVENTS, SERVE-EVENT, and friends
;;; When a *periodic-polling-function* is defined the server will not
;;; block for more than the maximum event timeout and will call the
;;; polling function if it does time out.
(declaim (type (or null symbol function) *periodic-polling-function*))
(defvar *periodic-polling-function* nil
"Either NIL, or a designator for a function callable without any
arguments. Called when the system has been waiting for input for
longer then *PERIODIC-POLLING-PERIOD* seconds. Shared between all
threads, unless locally bound. EXPERIMENTAL.")
(declaim (real *periodic-polling-period*))
(defvar *periodic-polling-period* 0
"A real number designating the number of seconds to wait for input
at maximum, before calling the *PERIODIC-POLLING-FUNCTION* \(if any.)
Shared between all threads, unless locally bound. EXPERIMENTAL.")
;;; Wait until FD is usable for DIRECTION. The timeout given to serve-event is ;;; Wait until FD is usable for DIRECTION. The timeout given to serve-event is
;;; recalculated each time through the loop so that WAIT-UNTIL-FD-USABLE will ;;; recalculated each time through the loop so that WAIT-UNTIL-FD-USABLE will
;;; timeout at the correct time irrespective of how many events are handled in ;;; timeout at the correct time irrespective of how many events are handled in
;;; the meantime. ;;; the meantime.
(defun wait-until-fd-usable (fd direction &optional timeout) (defun wait-until-fd-usable (fd direction &optional timeout (serve-events t))
#!+sb-doc #!+sb-doc
"Wait until FD is usable for DIRECTION. DIRECTION should be either :INPUT or "Wait until FD is usable for DIRECTION. DIRECTION should be either :INPUT or
:OUTPUT. TIMEOUT, if supplied, is the number of seconds to wait before giving :OUTPUT. TIMEOUT, if supplied, is the number of seconds to wait before giving
up." up. Returns true once the FD is usable, NIL return indicates timeout.
(prog (usable)
If SERVE-EVENTS is true (the default), events on other FDs are served while
waiting."
(tagbody
:restart :restart
(multiple-value-bind (to-sec to-usec stop-sec stop-usec signalp) (multiple-value-bind (to-sec to-usec stop-sec stop-usec signalp)
(decode-timeout timeout) (decode-timeout timeout)
(declare (type (or integer null) to-sec to-usec)) (declare (type (or integer null) to-sec to-usec))
(with-fd-handler (fd direction (lambda (fd) (flet ((maybe-update-timeout ()
(declare (ignore fd)) ;; If we return early, recompute the timeouts, possibly
(setf usable t))) ;; signaling the deadline or returning with NIL to caller.
(loop (multiple-value-bind (sec usec)
(sub-serve-event to-sec to-usec signalp) (decode-internal-time (get-internal-real-time))
(when usable (setf to-sec (- stop-sec sec))
(return-from wait-until-fd-usable t)) (cond ((> usec stop-usec)
(when to-sec (decf to-sec)
(multiple-value-bind (sec usec) (setf to-usec (- (+ stop-usec 1000000) usec)))
(decode-internal-time (get-internal-real-time)) (t
(setf to-sec (- stop-sec sec)) (setf to-usec (- stop-usec usec)))))
(cond ((> usec stop-usec) (when (or (minusp to-sec) (and (zerop to-sec) (not (plusp to-usec))))
(decf to-sec) (cond (signalp
(setf to-usec (- (+ stop-usec 1000000) usec))) (signal-deadline)
(t (go :restart))
(setf to-usec (- stop-usec usec))))) (t
(when (or (minusp to-sec) (minusp to-usec)) (return-from wait-until-fd-usable nil))))))
(if signalp (if (and serve-events
(progn ;; No timeout or non-zero timeout
(signal-deadline) (or (not to-sec)
(go :restart)) (not (= 0 to-sec to-usec)))
(return-from wait-until-fd-usable nil))))))))) ;; Something to do while we wait
(or *descriptor-handlers* *periodic-polling-function*))
;; Loop around SUB-SERVE-EVENT till done.
(dx-let ((usable (list nil)))
(dx-flet ((usable! (fd)
(declare (ignore fd))
(setf (car usable) t)))
(with-fd-handler (fd direction #'usable!)
(loop
(sub-serve-event to-sec to-usec signalp)
(when (car usable)
(return-from wait-until-fd-usable t))
(when to-sec
(maybe-update-timeout))))))
;; If we don't have to serve events, just poll on the single FD instead.
(loop for to-msec = (if (and to-sec to-usec)
(+ (* 1000 to-sec) (truncate to-usec 1000))
-1)
when (sb!unix:unix-simple-poll fd direction to-msec)
do (return-from wait-until-fd-usable t)
else
do (when to-sec (maybe-update-timeout))))))))
;;; Wait for up to timeout seconds for an event to happen. Make sure all ;;; Wait for up to timeout seconds for an event to happen. Make sure all
;;; pending events are processed before returning. ;;; pending events are processed before returning.
@ -201,21 +240,6 @@ happens. Server returns T if something happened and NIL otherwise. Timeout
(declare (ignore stop-sec stop-usec)) (declare (ignore stop-sec stop-usec))
(sub-serve-event to-sec to-usec signalp))) (sub-serve-event to-sec to-usec signalp)))
;;; When a *periodic-polling-function* is defined the server will not
;;; block for more than the maximum event timeout and will call the
;;; polling function if it does time out.
(declaim (type (or null symbol function) *periodic-polling-function*))
(defvar *periodic-polling-function* nil
"Either NIL, or a designator for a function callable without any
arguments. Called when the system has been waiting for input for
longer then *PERIODIC-POLLING-PERIOD* seconds. Shared between all
threads, unless locally bound. EXPERIMENTAL.")
(declaim (real *periodic-polling-period*))
(defvar *periodic-polling-period* 0
"A real number designating the number of seconds to wait for input
at maximum, before calling the *PERIODIC-POLLING-FUNCTION* \(if any.)
Shared between all threads, unless locally bound. EXPERIMENTAL.")
;;; Takes timeout broken into seconds and microseconds, NIL timeout means ;;; Takes timeout broken into seconds and microseconds, NIL timeout means
;;; to wait as long as needed. ;;; to wait as long as needed.
(defun sub-serve-event (to-sec to-usec deadlinep) (defun sub-serve-event (to-sec to-usec deadlinep)

View file

@ -570,8 +570,6 @@ corresponds to NAME, or NIL if there is none."
(slot usage 'ru-nivcsw)) (slot usage 'ru-nivcsw))
who (addr usage)))) who (addr usage))))
;;;; poll.h
(defvar *on-dangerous-wait* :warn) (defvar *on-dangerous-wait* :warn)
;;; Calling select in a bad place can hang in a nasty manner, so it's better ;;; Calling select in a bad place can hang in a nasty manner, so it's better
@ -594,32 +592,36 @@ corresponds to NAME, or NIL if there is none."
type) type)
(sb!debug:backtrace))) (sb!debug:backtrace)))
nil)) nil))
;;;; poll.h
#!+os-provides-poll
(progn
(define-alien-type nil
(struct pollfd
(fd int)
(events short) ; requested events
(revents short))) ; returned events
(define-alien-type nil (defun unix-simple-poll (fd direction to-msec)
(struct pollfd (declare (fixnum fd to-msec))
(fd int) (when (and (minusp to-msec) (not *interrupts-enabled*))
(events short) ; requested events (note-dangerous-wait "poll(2)"))
(revents short))) ; returned events (let ((events (ecase direction
(:input (logior pollin pollpri))
;; Just for a single fd. (:output pollout))))
(defun unix-simple-poll (fd direction to-msec) (with-alien ((fds (struct pollfd)))
(declare (fixnum fd to-msec)) (with-restarted-syscall (count errno)
(when (and (minusp to-msec) (not *interrupts-enabled*)) (progn
(note-dangerous-wait "poll(2)")) (setf (slot fds 'fd) fd
(let ((events (ecase direction (slot fds 'events) events
(:input (logior pollin pollpri)) (slot fds 'revents) 0)
(:output pollout)))) (int-syscall ("poll" (* (struct pollfd)) int int)
(with-alien ((fds (struct pollfd))) (addr fds) 1 to-msec))
(sb!unix:with-restarted-syscall (count errno) (if (zerop errno)
(progn (let ((revents (slot fds 'revents)))
(setf (slot fds 'fd) fd (or (and (eql 1 count) (logtest events revents))
(slot fds 'events) events (logtest pollhup revents)))
(slot fds 'revents) 0) (error "Syscall poll(2) failed: ~A" (strerror))))))))
(int-syscall ("poll" (* (struct pollfd)) int int)
(addr fds) 1 to-msec))
(if (zerop errno)
(and (eql 1 count) (logtest events (slot fds 'revents)))
(error "Syscall poll(2) failed: ~A" (strerror)))))))
;;;; sys/select.h ;;;; sys/select.h
@ -707,6 +709,65 @@ corresponds to NAME, or NIL if there is none."
(fd-set-to-num nfds xpf)) (fd-set-to-num nfds xpf))
nfds (frob rdfds rdf) (frob wrfds wrf) (frob xpfds xpf) nfds (frob rdfds rdf) (frob wrfds wrf) (frob xpfds xpf)
(if to-secs (alien-sap (addr tv)) (int-sap 0)))))) (if to-secs (alien-sap (addr tv)) (int-sap 0))))))
;;; Lisp-side implmentations of FD_FOO macros. Abandon all hope who enters
;;; here...
;;;
(defmacro fd-set (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(setf (deref (slot ,fd-set 'fds-bits) ,word)
(logior (truly-the (unsigned-byte #.sb!vm:n-machine-word-bits)
(ash 1 ,bit))
(deref (slot ,fd-set 'fds-bits) ,word))))))
(defmacro fd-clr (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(setf (deref (slot ,fd-set 'fds-bits) ,word)
(logand (deref (slot ,fd-set 'fds-bits) ,word)
(sb!kernel:word-logical-not
(truly-the (unsigned-byte #.sb!vm:n-machine-word-bits)
(ash 1 ,bit))))))))
(defmacro fd-isset (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(logbitp ,bit (deref (slot ,fd-set 'fds-bits) ,word)))))
(defmacro fd-zero (fd-set)
`(progn
,@(loop for index upfrom 0 below (/ fd-setsize sb!vm:n-machine-word-bits)
collect `(setf (deref (slot ,fd-set 'fds-bits) ,index) 0))))
#!-os-provides-poll
(defun unix-simple-poll (fd direction to-msec)
(multiple-value-bind (to-sec to-usec)
(if (minusp to-msec)
(values nil nil)
(multiple-value-bind (to-sec to-msec2) (truncate to-msec 1000)
(values to-sec (* to-msec2 1000))))
(sb!unix:with-restarted-syscall (count errno)
(sb!alien:with-alien ((fds (sb!alien:struct sb!unix:fd-set)))
(sb!unix:fd-zero fds)
(sb!unix:fd-set fd fds)
(multiple-value-bind (read-fds write-fds)
(ecase direction
(:input
(values (addr fds) nil))
(:output
(values nil (addr fds))))
(sb!unix:unix-fast-select (1+ fd)
read-fds write-fds nil
to-sec to-usec)))
(case count
((1) t)
((0) nil)
(otherwise
(error "Syscall select(2) failed on fd ~D: ~A" fd (strerror)))))))
;;;; sys/stat.h ;;;; sys/stat.h
@ -1121,43 +1182,3 @@ the UNIX epoch (January 1st 1970.)"
;;;; the headers that may or may not be the same thing. To be ;;;; the headers that may or may not be the same thing. To be
;;;; investigated. -- CSR, 2002-03-25 ;;;; investigated. -- CSR, 2002-03-25
(defconstant wstopped #o177) (defconstant wstopped #o177)
;;;; stuff not yet found in the header files
;;;;
;;;; Abandon all hope who enters here...
;;; not checked for linux...
(defmacro fd-set (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(setf (deref (slot ,fd-set 'fds-bits) ,word)
(logior (truly-the (unsigned-byte #.sb!vm:n-machine-word-bits)
(ash 1 ,bit))
(deref (slot ,fd-set 'fds-bits) ,word))))))
;;; not checked for linux...
(defmacro fd-clr (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(setf (deref (slot ,fd-set 'fds-bits) ,word)
(logand (deref (slot ,fd-set 'fds-bits) ,word)
(sb!kernel:word-logical-not
(truly-the (unsigned-byte #.sb!vm:n-machine-word-bits)
(ash 1 ,bit))))))))
;;; not checked for linux...
(defmacro fd-isset (offset fd-set)
(with-unique-names (word bit)
`(multiple-value-bind (,word ,bit) (floor ,offset
sb!vm:n-machine-word-bits)
(logbitp ,bit (deref (slot ,fd-set 'fds-bits) ,word)))))
;;; not checked for linux...
(defmacro fd-zero (fd-set)
`(progn
,@(loop for index upfrom 0 below (/ fd-setsize sb!vm:n-machine-word-bits)
collect `(setf (deref (slot ,fd-set 'fds-bits) ,index) 0))))

View file

@ -9,7 +9,7 @@ cd ./tools-for-build > /dev/null
featurep() { featurep() {
bin="$1-test" bin="$1-test"
rm -f $bin rm -f $bin
$GNUMAKE $bin -I ../src/runtime > /dev/null 2>&1 && ./$bin > /dev/null 2>&1 $GNUMAKE $bin -I ../src/runtime > /dev/null 2>&1 && echo "input" | ./$bin> /dev/null 2>&1
if [ "$?" = 104 ] if [ "$?" = 104 ]
then then
printf " :$1" printf " :$1"
@ -30,3 +30,5 @@ featurep os-provides-blksize-t
featurep os-provides-suseconds-t featurep os-provides-suseconds-t
featurep os-provides-getprotoby-r featurep os-provides-getprotoby-r
featurep os-provides-poll

View file

@ -258,6 +258,7 @@ main(int argc, char *argv[])
defconstant("pollin", POLLIN); defconstant("pollin", POLLIN);
defconstant("pollout", POLLOUT); defconstant("pollout", POLLOUT);
defconstant("pollpri", POLLPRI); defconstant("pollpri", POLLPRI);
defconstant("pollhup", POLLHUP);
DEFTYPE("nfds-t", nfds_t); DEFTYPE("nfds-t", nfds_t);
printf(";;; langinfo\n"); printf(";;; langinfo\n");

View file

@ -0,0 +1,26 @@
/* test to build and run so that we know if we have poll that works on
* stdin and /dev/zero -- which is hopefully a sufficient sample to weed
* out crappy versions like that on Darwin.
*/
#include <fcntl.h>
#include <poll.h>
int main ()
{
struct pollfd fds;
fds.fd = 0;
fds.events = POLLIN|POLLPRI;
fds.revents = 0;
if (!((1 == poll(&fds, 1, -1)) && ((POLLIN|POLLPRI) & fds.revents)))
return 0;
fds.fd = open("/dev/zero", O_RDONLY);
fds.events = POLLIN|POLLPRI;
fds.revents = 0;
if (!((1 == poll(&fds, 1, -1)) && ((POLLIN|POLLPRI) & fds.revents)))
return 0;
return 104;
}

View file

@ -17,4 +17,4 @@
;;; checkins which aren't released. (And occasionally for internal ;;; checkins which aren't released. (And occasionally for internal
;;; versions, especially for internal versions off the main CVS ;;; versions, especially for internal versions off the main CVS
;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".) ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
"1.0.42.42" "1.0.42.43"