tsbcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://hg6vgqziawt5s4dj.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) LICENSE
       ---
       tsbcl.lisp (34399B)
       ---
            1 ;;;; -*- Mode: Common-Lisp -*-
            2 
            3 ;;;; See LICENSE for licensing information.
            4 
            5 (in-package :usocket)
            6 
            7 #+sbcl
            8 (progn
            9   #-win32
           10   (defun get-host-name ()
           11     (sb-unix:unix-gethostname))
           12 
           13   ;; we assume winsock has already been loaded, after all,
           14   ;; we already loaded sb-bsd-sockets and sb-alien
           15   #+win32
           16   (defun get-host-name ()
           17     (sb-alien:with-alien ((buf (sb-alien:array sb-alien:char 256)))
           18        (let ((result (sb-alien:alien-funcall
           19                       (sb-alien:extern-alien "gethostname"
           20                                              (sb-alien:function sb-alien:int
           21                                                                 (* sb-alien:char)
           22                                                                 sb-alien:int))
           23                       (sb-alien:cast buf (* sb-alien:char))
           24                       256)))
           25          (when (= result 0)
           26            (sb-alien:cast buf sb-alien:c-string))))))
           27 
           28 #+(and ecl (not ecl-bytecmp))
           29 (progn
           30   #-:wsock
           31   (ffi:clines
           32    "#include <errno.h>"
           33    "#include <sys/socket.h>"
           34    "#include <unistd.h>")
           35   #+:wsock
           36   (ffi:clines
           37    "#ifndef FD_SETSIZE"
           38    "#define FD_SETSIZE 1024"
           39    "#endif"
           40    "#include <winsock2.h>")
           41 
           42   (ffi:clines
           43    #+:msvc "#include <time.h>"
           44    #-:msvc "#include <sys/time.h>"
           45    "#include <ecl/ecl-inl.h>")
           46 #|
           47   #+:prefixed-api
           48   (ffi:clines
           49    "#define CONS(x, y) ecl_cons((x), (y))"
           50    "#define MAKE_INTEGER(x) ecl_make_integer((x))")
           51   #-:prefixed-api
           52   (ffi:clines
           53    "#define CONS(x, y) make_cons((x), (y))"
           54    "#define MAKE_INTEGER(x) make_integer((x))")
           55 |#
           56 
           57   (defun cerrno ()
           58     (ffi:c-inline () () :int
           59      "errno" :one-liner t))
           60 
           61   (defun fd-setsize ()
           62     (ffi:c-inline () () :fixnum
           63      "FD_SETSIZE" :one-liner t))
           64 
           65   (defun fdset-alloc ()
           66     (ffi:c-inline () () :pointer-void
           67      "ecl_alloc_atomic(sizeof(fd_set))" :one-liner t))
           68 
           69   (defun fdset-zero (fdset)
           70     (ffi:c-inline (fdset) (:pointer-void) :void
           71      "FD_ZERO((fd_set*)#0)" :one-liner t))
           72 
           73   (defun fdset-set (fdset fd)
           74     (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :void
           75      "FD_SET(#1,(fd_set*)#0)" :one-liner t))
           76 
           77   (defun fdset-clr (fdset fd)
           78     (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :void
           79      "FD_CLR(#1,(fd_set*)#0)" :one-liner t))
           80 
           81   (defun fdset-fd-isset (fdset fd)
           82     (ffi:c-inline (fdset fd) (:pointer-void :fixnum) :bool
           83      "FD_ISSET(#1,(fd_set*)#0)" :one-liner t))
           84 
           85   (declaim (inline cerrno
           86                    fd-setsize
           87                    fdset-alloc
           88                    fdset-zero
           89                    fdset-set
           90                    fdset-clr
           91                    fdset-fd-isset))
           92 
           93   (defun get-host-name ()
           94     (ffi:c-inline
           95      () () :object
           96      "{ char *buf = (char *) ecl_alloc_atomic(257);
           97 
           98         if (gethostname(buf,256) == 0)
           99           @(return) = make_simple_base_string(buf);
          100         else
          101           @(return) = Cnil;
          102       }" :one-liner nil :side-effects nil))
          103 
          104   (defun read-select (wl to-secs &optional (to-musecs 0))
          105     (let* ((sockets (wait-list-waiters wl))
          106            (rfds (wait-list-%wait wl))
          107            (max-fd (reduce #'(lambda (x y)
          108                                (let ((sy (sb-bsd-sockets:socket-file-descriptor
          109                                           (socket y))))
          110                                  (if (< x sy) sy x)))
          111                            (cdr sockets)
          112                            :initial-value (sb-bsd-sockets:socket-file-descriptor
          113                                            (socket (car sockets))))))
          114       (fdset-zero rfds)
          115       (dolist (sock sockets)
          116         (fdset-set rfds (sb-bsd-sockets:socket-file-descriptor
          117                          (socket sock))))
          118       (let ((count
          119              (ffi:c-inline (to-secs to-musecs rfds max-fd)
          120                            (t :unsigned-int :pointer-void :int)
          121                            :int
          122       "
          123           int count;
          124           struct timeval tv;
          125 
          126           if (#0 != Cnil) {
          127             tv.tv_sec = fixnnint(#0);
          128             tv.tv_usec = #1;
          129           }
          130         @(return) = select(#3 + 1, (fd_set*)#2, NULL, NULL,
          131                            (#0 != Cnil) ? &tv : NULL);
          132 " :one-liner nil)))
          133         (cond
          134           ((= 0 count)
          135            (values nil nil))
          136           ((< count 0)
          137            ;; check for EINTR and EAGAIN; these should not err
          138            (values nil (cerrno)))
          139           (t
          140            (dolist (sock sockets)
          141              (when (fdset-fd-isset rfds (sb-bsd-sockets:socket-file-descriptor
          142                                          (socket sock)))
          143                (setf (state sock) :READ))))))))
          144 ) ; progn
          145 
          146 (defun map-socket-error (sock-err)
          147   (map-errno-error (sb-bsd-sockets::socket-error-errno sock-err)))
          148 
          149 (defparameter +sbcl-condition-map+
          150   '((interrupted-error . interrupted-condition)))
          151 
          152 (defparameter +sbcl-error-map+
          153   `((sb-bsd-sockets:address-in-use-error . address-in-use-error)
          154     (sb-bsd-sockets::no-address-error . address-not-available-error)
          155     (sb-bsd-sockets:bad-file-descriptor-error . bad-file-descriptor-error)
          156     (sb-bsd-sockets:connection-refused-error . connection-refused-error)
          157     (sb-bsd-sockets:invalid-argument-error . invalid-argument-error)
          158     (sb-bsd-sockets:no-buffers-error . no-buffers-error)
          159     (sb-bsd-sockets:operation-not-supported-error
          160      . operation-not-supported-error)
          161     (sb-bsd-sockets:operation-not-permitted-error
          162      . operation-not-permitted-error)
          163     (sb-bsd-sockets:protocol-not-supported-error
          164      . protocol-not-supported-error)
          165     #-ecl
          166     (sb-bsd-sockets:unknown-protocol
          167      . protocol-not-supported-error)
          168     (sb-bsd-sockets:socket-type-not-supported-error
          169      . socket-type-not-supported-error)
          170     (sb-bsd-sockets:network-unreachable-error . network-unreachable-error)
          171     (sb-bsd-sockets:operation-timeout-error . timeout-error)
          172     #-ecl
          173     (sb-sys:io-timeout . timeout-error)
          174     #+sbcl
          175     (sb-ext:timeout . timeout-error)
          176     (sb-bsd-sockets:socket-error . ,#'map-socket-error)
          177 
          178     ;; Nameservice errors: mapped to unknown-error
          179     #-ecl
          180     (sb-bsd-sockets:no-recovery-error . ns-no-recovery-error)
          181     #-ecl
          182     (sb-bsd-sockets:try-again-error . ns-try-again-condition)
          183     #-ecl
          184     (sb-bsd-sockets:host-not-found-error . ns-host-not-found-error)))
          185 
          186 (defun handle-condition (condition &optional (socket nil))
          187   "Dispatch correct usocket condition."
          188   (typecase condition
          189     (serious-condition
          190      (let* ((usock-error (cdr (assoc (type-of condition) +sbcl-error-map+)))
          191             (usock-error (if (functionp usock-error)
          192                              (funcall usock-error condition)
          193                            usock-error)))
          194        (when usock-error
          195          (error usock-error :socket socket))))
          196     (condition (let* ((usock-cond (cdr (assoc (type-of condition)
          197                                               +sbcl-condition-map+)))
          198                       (usock-cond (if (functionp usock-cond)
          199                                       (funcall usock-cond condition)
          200                                     usock-cond)))
          201                  (if usock-cond
          202                      (signal usock-cond :socket socket))))))
          203 
          204 ;;; "The socket stream ends up with a bogus name as it is created before
          205 ;;; the socket is connected, making things harder to debug than they need
          206 ;;; to be." -- Nikodemus Siivola <nikodemus@random-state.net>
          207 
          208 (defvar *dummy-stream*
          209   (let ((stream (make-broadcast-stream)))
          210     (close stream)
          211     stream))
          212 
          213 ;;; Amusingly, neither SBCL's own, nor GBBopen's WITH-TIMEOUT is asynch
          214 ;;; unwind safe. The one I posted is -- that's what the WITHOUT-INTERRUPTS
          215 ;;; and WITH-LOCAL-INTERRUPTS were for. :) But yeah, it's miles saner than
          216 ;;; the SB-EXT:WITH-TIMEOUT. -- Nikodemus Siivola <nikodemus@random-state.net>
          217 
          218 #+(and sbcl (not win32))
          219 (defmacro %with-timeout ((seconds timeout-form) &body body)
          220   "Runs BODY as an implicit PROGN with timeout of SECONDS. If
          221 timeout occurs before BODY has finished, BODY is unwound and
          222 TIMEOUT-FORM is executed with its values returned instead.
          223 
          224 Note that BODY is unwound asynchronously when a timeout occurs,
          225 so unless all code executed during it -- including anything
          226 down the call chain -- is asynch unwind safe, bad things will
          227 happen. Use with care."
          228   (let ((exec (gensym)) (unwind (gensym)) (timer (gensym))
          229         (timeout (gensym)) (block (gensym)))
          230     `(block ,block
          231        (tagbody
          232           (flet ((,unwind ()
          233                    (go ,timeout))
          234                  (,exec ()
          235                    ,@body))
          236             (declare (dynamic-extent #',exec #',unwind))
          237             (let ((,timer (sb-ext:make-timer #',unwind)))
          238               (declare (dynamic-extent ,timer))
          239               (sb-sys:without-interrupts
          240                   (unwind-protect
          241                        (progn
          242                          (sb-ext:schedule-timer ,timer ,seconds)
          243                          (return-from ,block
          244                            (sb-sys:with-local-interrupts
          245                                (,exec))))
          246                     (sb-ext:unschedule-timer ,timer)))))
          247           ,timeout
          248           (return-from ,block ,timeout-form)))))
          249 
          250 (defun get-hosts-by-name (name)
          251   (with-mapped-conditions ()
          252     (multiple-value-bind (host4 host6)
          253         (sb-bsd-sockets:get-host-by-name name)
          254       (let ((addr4 (when host4
          255                      (sb-bsd-sockets::host-ent-addresses host4)))
          256             (addr6 (when host6
          257                      (sb-bsd-sockets::host-ent-addresses host6))))
          258         (append addr4 addr6)))))
          259 
          260 (defun socket-connect (host port &key (protocol :stream) (element-type 'character)
          261                        timeout deadline (nodelay t nodelay-specified)
          262                        local-host local-port
          263                        &aux
          264                        (sockopt-tcp-nodelay-p
          265                         (fboundp 'sb-bsd-sockets::sockopt-tcp-nodelay)))
          266   (when deadline (unsupported 'deadline 'socket-connect))
          267   #+ecl
          268   (when timeout (unsupported 'timeout 'socket-connect))
          269   (when (and nodelay-specified
          270              ;; 20080802: ECL added this function to its sockets
          271              ;; package today. There's no guarantee the functions
          272              ;; we need are available, but we can make sure not to
          273              ;; call them if they aren't
          274              (not (eq nodelay :if-supported))
          275              (not sockopt-tcp-nodelay-p))
          276     (unsupported 'nodelay 'socket-connect))
          277   (when (eq nodelay :if-supported)
          278     (setf nodelay t))
          279 
          280   (let* ((remote (when host
          281                    (car (get-hosts-by-name (host-to-hostname host)))))
          282          (local (when local-host
          283                   (car (get-hosts-by-name (host-to-hostname local-host)))))
          284          (ipv6 (or (and remote (= 16 (length remote)))
          285                    (and local (= 16 (length local)))))
          286          (socket (make-instance #+sbcl (if ipv6
          287                                            'sb-bsd-sockets::inet6-socket
          288                                            'sb-bsd-sockets:inet-socket)
          289                                 #+ecl 'sb-bsd-sockets:inet-socket
          290                                 :type protocol
          291                                 :protocol (case protocol
          292                                             (:stream :tcp)
          293                                             (:datagram :udp))))
          294          usocket
          295          ok)
          296 
          297     (unwind-protect
          298          (progn
          299            (ecase protocol
          300              (:stream
          301               ;; If make a real socket stream before the socket is
          302               ;; connected, it gets a misleading name so supply a
          303               ;; dummy value to start with.
          304               (setf usocket (make-stream-socket :socket socket :stream *dummy-stream*))
          305               ;; binghe: use SOCKOPT-TCP-NODELAY as internal symbol
          306               ;;         to pass compilation on ECL without it.
          307               (when (and nodelay-specified sockopt-tcp-nodelay-p)
          308                 (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) nodelay))
          309               (when (or local-host local-port)
          310                 (sb-bsd-sockets:socket-bind socket
          311                                             (if ipv6
          312                                                 (or local (ipv6-host-to-vector "::0"))
          313                                                 (or local (host-to-vector-quad *wildcard-host*)))
          314                                             (or local-port *auto-port*)))
          315 
          316               (with-mapped-conditions (usocket)
          317                 #+(and sbcl (not win32))
          318                 (labels ((connect ()
          319                            (sb-bsd-sockets:socket-connect socket remote port)))
          320                   (if timeout
          321                       (%with-timeout (timeout (error 'sb-ext:timeout)) (connect))
          322                       (connect)))
          323                 #+(or ecl (and sbcl win32))
          324                 (sb-bsd-sockets:socket-connect socket remote port)
          325                 ;; Now that we're connected make the stream.
          326                 (setf (socket-stream usocket)
          327                       (sb-bsd-sockets:socket-make-stream socket
          328                         :input t :output t :buffering :full
          329                         :element-type element-type
          330                         ;; Robert Brown <robert.brown@gmail.com> said on Aug 4, 2011:
          331                         ;; ... This means that SBCL streams created by usocket have a true
          332                         ;; serve-events property.  When writing large amounts of data to several
          333                         ;; streams, the kernel will eventually stop accepting data from SBCL.
          334                         ;; When this happens, SBCL either waits for I/O to be possible on
          335                         ;; the file descriptor it's writing to or queues the data to be flushed later.
          336                         ;; Because usocket streams specify serve-events as true, SBCL
          337                         ;; always queues.  Instead, it should wait for I/O to be available and
          338                         ;; write the remaining data to the socket.  That's what serve-events
          339                         ;; equal to NIL gets you.
          340                         ;;
          341                         ;; Nikodemus Siivola <nikodemus@random-state.net> said on Aug 8, 2011:
          342                         ;; It's set to T for purely historical reasons, and will soon change to
          343                         ;; NIL in SBCL. (The docstring has warned of T being a temporary default
          344                         ;; for as long as the :SERVE-EVENTS keyword argument has existed.)
          345                         :serve-events nil))))
          346              (:datagram
          347               (when (or local-host local-port)
          348                 (sb-bsd-sockets:socket-bind socket
          349                                             (if ipv6
          350                                                 (or local (ipv6-host-to-vector "::0"))
          351                                                 (or local (host-to-vector-quad *wildcard-host*)))
          352                                             (or local-port *auto-port*)))
          353               (setf usocket (make-datagram-socket socket))
          354               (when (and host port)
          355                 (with-mapped-conditions (usocket)
          356                   (sb-bsd-sockets:socket-connect socket remote port)
          357                   (setf (connected-p usocket) t)))))
          358            (setf ok t))
          359       ;; Clean up in case of an error.
          360       (unless ok
          361         (sb-bsd-sockets:socket-close socket :abort t)))
          362     usocket))
          363 
          364 (defun socket-listen (host port
          365                            &key reuseaddress
          366                            (reuse-address nil reuse-address-supplied-p)
          367                            (backlog 5)
          368                            (element-type 'character))
          369   (let* (#+sbcl
          370          (local (when host
          371                   (car (get-hosts-by-name (host-to-hostname host)))))
          372          #+sbcl
          373          (ipv6 (and local (= 16 (length local))))
          374          (reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
          375          (ip #+sbcl (if (and local (not (eq host *wildcard-host*)))
          376                         local
          377                         (hbo-to-vector-quad sb-bsd-sockets-internal::inaddr-any))
          378              #+ecl (host-to-vector-quad host))
          379          (sock (make-instance #+sbcl (if ipv6
          380                                          'sb-bsd-sockets::inet6-socket
          381                                          'sb-bsd-sockets:inet-socket)
          382                               #+ecl 'sb-bsd-sockets:inet-socket
          383                               :type :stream
          384                               :protocol :tcp)))
          385     (handler-case
          386         (with-mapped-conditions ()
          387           (setf (sb-bsd-sockets:sockopt-reuse-address sock) reuseaddress)
          388           (sb-bsd-sockets:socket-bind sock ip port)
          389           (sb-bsd-sockets:socket-listen sock backlog)
          390           (make-stream-server-socket sock :element-type element-type))
          391       (t (c)
          392         ;; Make sure we don't leak filedescriptors
          393         (sb-bsd-sockets:socket-close sock)
          394         (error c)))))
          395 
          396 ;;; "2. SB-BSD-SOCKETS:SOCKET-ACCEPT method returns NIL for EAGAIN/EINTR,
          397 ;;; instead of raising a condition. It's always possible for
          398 ;;; SOCKET-ACCEPT on non-blocking socket to fail, even after the socket
          399 ;;; was detected to be ready: connection might be reset, for example.
          400 ;;;
          401 ;;; "I had to redefine SOCKET-ACCEPT method of STREAM-SERVER-USOCKET to
          402 ;;; handle this situation. Here is the redefinition:" -- Anton Kovalenko <anton@sw4me.com>
          403 
          404 (defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
          405   (with-mapped-conditions (usocket)
          406     (let ((socket (sb-bsd-sockets:socket-accept (socket usocket))))
          407       (when socket
          408         (prog1
          409           (make-stream-socket
          410            :socket socket
          411            :stream (sb-bsd-sockets:socket-make-stream
          412                     socket
          413                     :input t :output t :buffering :full
          414                     :element-type (or element-type
          415                                       (element-type usocket))))
          416 
          417           ;; next time wait for event again if we had EAGAIN/EINTR
          418           ;; or else we'd enter a tight loop of failed accepts
          419           #+win32
          420           (setf (%ready-p usocket) nil))))))
          421 
          422 ;; Sockets and their associated streams are modelled as
          423 ;; different objects. Be sure to close the stream (which
          424 ;; closes the socket too) when closing a stream-socket.
          425 (defmethod socket-close ((usocket usocket))
          426   (when (wait-list usocket)
          427      (remove-waiter (wait-list usocket) usocket))
          428   (with-mapped-conditions (usocket)
          429     (sb-bsd-sockets:socket-close (socket usocket))))
          430 
          431 (defmethod socket-close ((usocket stream-usocket))
          432   (when (wait-list usocket)
          433      (remove-waiter (wait-list usocket) usocket))
          434   (with-mapped-conditions (usocket)
          435     (close (socket-stream usocket))))
          436 
          437 #+sbcl
          438 (defmethod socket-shutdown ((usocket stream-usocket) direction)
          439   (with-mapped-conditions (usocket)
          440     (sb-bsd-sockets::socket-shutdown (socket usocket) :direction direction)))
          441 
          442 #+ecl
          443 (defmethod socket-shutdown ((usocket stream-usocket) direction)
          444   (let ((sock-fd (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
          445         (direction-flag (ecase direction
          446                           (:input 0)
          447                           (:output 1))))
          448     (unless (zerop (ffi:c-inline (sock-fd direction-flag) (:int :int) :int
          449                                "shutdown(#0, #1)" :one-liner t))
          450       (error (map-errno-error (cerrno))))))
          451 
          452 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0))
          453   (let ((remote (when host
          454                   (car (get-hosts-by-name (host-to-hostname host))))))
          455     (with-mapped-conditions (usocket)
          456       (let* ((s (socket usocket))
          457              (dest (if (and host port) (list remote port) nil))
          458              (real-buffer (if (zerop offset)
          459                               buffer
          460                               (subseq buffer offset (+ offset size)))))
          461         (sb-bsd-sockets:socket-send s real-buffer size :address dest)))))
          462 
          463 (defmethod socket-receive ((socket datagram-usocket) buffer length
          464                            &key (element-type '(unsigned-byte 8)))
          465   #+sbcl
          466   (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer
          467                    (integer 0)                          ; size
          468                    (simple-array (unsigned-byte 8) (*)) ; host
          469                    (unsigned-byte 16)))                 ; port
          470   (with-mapped-conditions (socket)
          471     (let ((s (socket socket)))
          472       (sb-bsd-sockets:socket-receive s buffer length :element-type element-type))))
          473 
          474 (defmethod get-local-name ((usocket usocket))
          475   (sb-bsd-sockets:socket-name (socket usocket)))
          476 
          477 (defmethod get-peer-name ((usocket stream-usocket))
          478   (sb-bsd-sockets:socket-peername (socket usocket)))
          479 
          480 (defmethod get-local-address ((usocket usocket))
          481   (nth-value 0 (get-local-name usocket)))
          482 
          483 (defmethod get-peer-address ((usocket stream-usocket))
          484   (nth-value 0 (get-peer-name usocket)))
          485 
          486 (defmethod get-local-port ((usocket usocket))
          487   (nth-value 1 (get-local-name usocket)))
          488 
          489 (defmethod get-peer-port ((usocket stream-usocket))
          490   (nth-value 1 (get-peer-name usocket)))
          491 
          492 (defun get-host-by-address (address)
          493   (with-mapped-conditions ()
          494     (sb-bsd-sockets::host-ent-name
          495         (sb-bsd-sockets:get-host-by-address address))))
          496 
          497 #+(and sbcl (not win32))
          498 (progn
          499   (defun %setup-wait-list (wait-list)
          500     (declare (ignore wait-list)))
          501 
          502   (defun %add-waiter (wait-list waiter)
          503     (push (socket waiter) (wait-list-%wait wait-list)))
          504 
          505   (defun %remove-waiter (wait-list waiter)
          506     (setf (wait-list-%wait wait-list)
          507           (remove (socket waiter) (wait-list-%wait wait-list))))
          508 
          509   (defun wait-for-input-internal (sockets &key timeout)
          510     (with-mapped-conditions ()
          511       (sb-alien:with-alien ((rfds (sb-alien:struct sb-unix:fd-set)))
          512          (sb-unix:fd-zero rfds)
          513          (dolist (socket (wait-list-%wait sockets))
          514            (sb-unix:fd-set
          515             (sb-bsd-sockets:socket-file-descriptor socket)
          516             rfds))
          517          (multiple-value-bind
          518              (secs musecs)
          519              (split-timeout (or timeout 1))
          520            (multiple-value-bind
          521                (count err)
          522                (sb-unix:unix-fast-select
          523                 (1+ (reduce #'max (wait-list-%wait sockets)
          524                             :key #'sb-bsd-sockets:socket-file-descriptor))
          525                 (sb-alien:addr rfds) nil nil
          526                 (when timeout secs) (when timeout musecs))
          527              (if (null count)
          528                  (unless (= err sb-unix:EINTR)
          529                    (error (map-errno-error err)))
          530                  (when (< 0 count)
          531                    ;; process the result...
          532                    (dolist (x (wait-list-waiters sockets))
          533                      (when (sb-unix:fd-isset
          534                             (sb-bsd-sockets:socket-file-descriptor
          535                              (socket x))
          536                             rfds)
          537                        (setf (state x) :READ))))))))))
          538 ) ; progn
          539 
          540 ;;; WAIT-FOR-INPUT support for SBCL on Windows platform (Chun Tian (binghe))
          541 ;;; Based on LispWorks version written by Erik Huelsmann.
          542 
          543 #+win32 ; shared by ECL and SBCL
          544 (eval-when (:compile-toplevel :load-toplevel :execute)
          545   (defconstant +wsa-wait-failed+ #xffffffff)
          546   (defconstant +wsa-infinite+ #xffffffff)
          547   (defconstant +wsa-wait-event-0+ 0)
          548   (defconstant +wsa-wait-timeout+ 258))
          549 
          550 #+win32 ; shared by ECL and SBCL
          551 (progn
          552   (defconstant fd-read 1)
          553   (defconstant fd-read-bit 0)
          554   (defconstant fd-write 2)
          555   (defconstant fd-write-bit 1)
          556   (defconstant fd-oob 4)
          557   (defconstant fd-oob-bit 2)
          558   (defconstant fd-accept 8)
          559   (defconstant fd-accept-bit 3)
          560   (defconstant fd-connect 16)
          561   (defconstant fd-connect-bit 4)
          562   (defconstant fd-close 32)
          563   (defconstant fd-close-bit 5)
          564   (defconstant fd-qos 64)
          565   (defconstant fd-qos-bit 6)
          566   (defconstant fd-group-qos 128)
          567   (defconstant fd-group-qos-bit 7)
          568   (defconstant fd-routing-interface 256)
          569   (defconstant fd-routing-interface-bit 8)
          570   (defconstant fd-address-list-change 512)
          571   (defconstant fd-address-list-change-bit 9)
          572   (defconstant fd-max-events 10)
          573   (defconstant fionread 1074030207)
          574 
          575   ;; Note: for ECL, socket-handle will return raw Windows Handle,
          576   ;;       while SBCL returns OSF Handle instead.
          577   (defun socket-handle (usocket)
          578     (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
          579 
          580   (defun socket-ready-p (socket)
          581     (if (typep socket 'stream-usocket)
          582         (plusp (bytes-available-for-read socket))
          583       (%ready-p socket)))
          584 
          585   (defun waiting-required (sockets)
          586     (notany #'socket-ready-p sockets))
          587 
          588   (defun raise-usock-err (errno &optional socket)
          589     (error 'unknown-error
          590            :socket socket
          591            :real-error errno))
          592 
          593   (defun wait-for-input-internal (wait-list &key timeout)
          594     (when (waiting-required (wait-list-waiters wait-list))
          595       (let ((rv (wsa-wait-for-multiple-events 1 (wait-list-%wait wait-list)
          596                                               nil
          597                                               (if timeout
          598                                                   (truncate (* 1000 timeout))
          599                                                   +wsa-infinite+)
          600                                               nil)))
          601         (ecase rv
          602           ((#.+wsa-wait-event-0+)
          603            (update-ready-and-state-slots (wait-list-waiters wait-list)))
          604           ((#.+wsa-wait-timeout+)) ; do nothing here
          605           ((#.+wsa-wait-failed+)
          606            (maybe-wsa-error rv))))))
          607 
          608   (defun %add-waiter (wait-list waiter)
          609     (let ((events (etypecase waiter
          610                     (stream-server-usocket (logior fd-connect fd-accept fd-close))
          611                     (stream-usocket (logior fd-read))
          612                     (datagram-usocket (logior fd-read)))))
          613       (maybe-wsa-error
          614        (wsa-event-select (os-socket-handle waiter) (os-wait-list-%wait wait-list) events)
          615        waiter)))
          616 
          617   (defun %remove-waiter (wait-list waiter)
          618     (maybe-wsa-error
          619      (wsa-event-select (os-socket-handle waiter) (os-wait-list-%wait wait-list) 0)
          620      waiter))
          621 ) ; progn
          622 
          623 #+(and sbcl win32)
          624 (progn
          625   ;; "SOCKET is defined as intptr_t in Windows headers; however, WS-SOCKET
          626   ;; is defined as unsigned-int, i.e. 32-bit even on 64-bit platform.  It
          627   ;; seems to be a good thing to redefine WS-SOCKET as SB-ALIEN:SIGNED,
          628   ;; which is always machine word-sized (exactly as intptr_t;
          629   ;; N.B. as of Windows/x64, long and signed-long are 32-bit, and thus not
          630   ;; enough -- potentially)."
          631   ;; -- Anton Kovalenko <anton@sw4me.com>, Mar 22, 2011
          632   (sb-alien:define-alien-type ws-socket sb-alien:signed)
          633 
          634   (sb-alien:define-alien-type ws-dword sb-alien:unsigned-long)
          635   (sb-alien:define-alien-type ws-event sb-alien::hinstance)
          636 
          637   (sb-alien:define-alien-type nil
          638     (sb-alien:struct wsa-network-events
          639       (network-events sb-alien:long)
          640       (error-code (array sb-alien:int 10)))) ; 10 = fd-max-events
          641 
          642   (sb-alien:define-alien-routine ("WSACreateEvent" wsa-event-create)
          643       ws-event) ; return type only
          644 
          645   (sb-alien:define-alien-routine ("WSACloseEvent" wsa-event-close)
          646       (boolean #.sb-vm::n-machine-word-bits)
          647     (event-object ws-event))
          648 
          649   (sb-alien:define-alien-routine ("WSAEnumNetworkEvents" wsa-enum-network-events)
          650       sb-alien:int
          651     (socket ws-socket)
          652     (event-object ws-event)
          653     (network-events (* (sb-alien:struct wsa-network-events))))
          654 
          655   (sb-alien:define-alien-routine ("WSAEventSelect" wsa-event-select)
          656       sb-alien:int
          657     (socket ws-socket)
          658     (event-object ws-event)
          659     (network-events sb-alien:long))
          660 
          661   (sb-alien:define-alien-routine ("WSAWaitForMultipleEvents" wsa-wait-for-multiple-events)
          662       ws-dword
          663     (number-of-events ws-dword)
          664     (events (* ws-event))
          665     (wait-all-p (boolean #.sb-vm::n-machine-word-bits))
          666     (timeout ws-dword)
          667     (alertable-p (boolean #.sb-vm::n-machine-word-bits)))
          668 
          669   (sb-alien:define-alien-routine ("ioctlsocket" wsa-ioctlsocket)
          670       sb-alien:int
          671     (socket ws-socket)
          672     (cmd sb-alien:long)
          673     (argp (* sb-alien:unsigned-long)))
          674 
          675   (defun maybe-wsa-error (rv &optional socket)
          676     (unless (zerop rv)
          677       (raise-usock-err (sockint::wsa-get-last-error) socket)))
          678 
          679   (defun os-socket-handle (usocket)
          680     (sb-bsd-sockets:socket-file-descriptor (socket usocket)))
          681 
          682   (defun bytes-available-for-read (socket)
          683     (sb-alien:with-alien ((int-ptr sb-alien:unsigned-long))
          684       (maybe-wsa-error (wsa-ioctlsocket (os-socket-handle socket) fionread (sb-alien:addr int-ptr))
          685                        socket)
          686       (prog1 int-ptr
          687         (when (plusp int-ptr)
          688           (setf (state socket) :read)))))
          689 
          690   (defun map-network-events (func network-events)
          691     (let ((event-map (sb-alien:slot network-events 'network-events))
          692           (error-array (sb-alien:slot network-events 'error-code)))
          693       (unless (zerop event-map)
          694         (dotimes (i fd-max-events)
          695           (unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand?
          696             (funcall func (sb-alien:deref error-array i)))))))
          697 
          698   (defun update-ready-and-state-slots (sockets)
          699     (dolist (socket sockets)
          700       (if (%ready-p socket)
          701           (progn
          702             (setf (state socket) :READ))
          703         (sb-alien:with-alien ((network-events (sb-alien:struct wsa-network-events)))
          704           (let ((rv (wsa-enum-network-events (os-socket-handle socket) 0
          705                                              (sb-alien:addr network-events))))
          706             (if (zerop rv)
          707                 (map-network-events
          708                  #'(lambda (err-code)
          709                      (if (zerop err-code)
          710                          (progn
          711                            (setf (state socket) :READ)
          712                            (when (stream-server-usocket-p socket)
          713                              (setf (%ready-p socket) t)))
          714                        (raise-usock-err err-code socket)))
          715                  network-events)
          716               (maybe-wsa-error rv socket)))))))
          717 
          718   (defun os-wait-list-%wait (wait-list)
          719     (sb-alien:deref (wait-list-%wait wait-list)))
          720 
          721   (defun (setf os-wait-list-%wait) (value wait-list)
          722     (setf (sb-alien:deref (wait-list-%wait wait-list)) value))
          723 
          724   ;; "Event handles are leaking in current SBCL backend implementation,
          725   ;; because of SBCL-unfriendly usage of finalizers.
          726   ;;
          727   ;; "SBCL never calls a finalizer that closes over a finalized object: a
          728   ;; reference from that closure prevents its collection forever. That's
          729   ;; the case with USOCKET in %SETUP-WAIT-LIST.
          730   ;;
          731   ;; "I use the following redefinition of %SETUP-WAIT-LIST: 
          732   ;;
          733   ;; "Of course it may be rewritten with more clarity, but you can see the
          734   ;; core idea: I'm closing over those components of WAIT-LIST that I need
          735   ;; for finalization, not the wait-list itself. With the original
          736   ;; %SETUP-WAIT-LIST, hunchentoot stops working after ~100k accepted
          737   ;; connections; it doesn't happen with redefined %SETUP-WAIT-LIST."
          738   ;;
          739   ;; -- Anton Kovalenko <anton@sw4me.com>, Mar 22, 2011
          740 
          741   (defun %setup-wait-list (wait-list)
          742     (setf (wait-list-%wait wait-list) (sb-alien:make-alien ws-event))
          743     (setf (os-wait-list-%wait wait-list) (wsa-event-create))
          744     (sb-ext:finalize wait-list
          745                      (let ((event-handle (os-wait-list-%wait wait-list))
          746                            (alien (wait-list-%wait wait-list)))
          747                        #'(lambda ()
          748                            (wsa-event-close event-handle)
          749                            (unless (null alien)
          750                              (sb-alien:free-alien alien))))))
          751 
          752 ) ; progn
          753 
          754 #+(and ecl (not win32))
          755 (progn
          756   (defun wait-for-input-internal (wl &key timeout)
          757     (with-mapped-conditions ()
          758       (multiple-value-bind (secs usecs)
          759           (split-timeout (or timeout 1))
          760         (multiple-value-bind (result-fds err)
          761             (read-select wl (when timeout secs) usecs)
          762           (declare (ignore result-fds))
          763           (unless (null err)
          764             (error (map-errno-error err)))))))
          765 
          766   (defun %setup-wait-list (wl)
          767     (setf (wait-list-%wait wl)
          768           (fdset-alloc)))
          769 
          770   (defun %add-waiter (wl w)
          771     (declare (ignore wl w)))
          772 
          773   (defun %remove-waiter (wl w)
          774     (declare (ignore wl w)))
          775 ) ; progn
          776 
          777 #+(and ecl win32 (not ecl-bytecmp))
          778 (progn
          779   (defun maybe-wsa-error (rv &optional syscall)
          780     (unless (zerop rv)
          781       (sb-bsd-sockets::socket-error syscall)))
          782 
          783   (defun %setup-wait-list (wl)
          784     (setf (wait-list-%wait wl)
          785           (ffi:c-inline () () :int
          786            "WSAEVENT event;
          787             event = WSACreateEvent();
          788             @(return) = event;")))
          789 
          790   (defun %add-waiter (wait-list waiter)
          791     (let ((events (etypecase waiter
          792                     (stream-server-usocket (logior fd-connect fd-accept fd-close))
          793                     (stream-usocket (logior fd-read))
          794                     (datagram-usocket (logior fd-read)))))
          795       (maybe-wsa-error
          796        (ffi:c-inline ((socket-handle waiter) (wait-list-%wait wait-list) events)
          797                      (:fixnum :fixnum :fixnum) :fixnum
          798         "int result;
          799          result = WSAEventSelect((SOCKET)#0, (WSAEVENT)#1, (long)#2);
          800          @(return) = result;")
          801        '%add-waiter)))
          802 
          803   (defun %remove-waiter (wait-list waiter)
          804     (maybe-wsa-error
          805      (ffi:c-inline ((socket-handle waiter) (wait-list-%wait wait-list))
          806                    (:fixnum :fixnum) :fixnum
          807       "int result;
          808        result = WSAEventSelect((SOCKET)#0, (WSAEVENT)#1, 0L);
          809        @(return) = result;")
          810      '%remove-waiter))
          811 
          812   ;; TODO: how to handle error (result) in this call?
          813   (declaim (inline %bytes-available-for-read))
          814   (defun %bytes-available-for-read (socket)
          815     (ffi:c-inline ((socket-handle socket)) (:fixnum) :fixnum
          816      "u_long nbytes;
          817       int result;
          818       nbytes = 0L;
          819       result = ioctlsocket((SOCKET)#0, FIONREAD, &nbytes);
          820       @(return) = nbytes;"))
          821 
          822   (defun bytes-available-for-read (socket)
          823     (let ((nbytes (%bytes-available-for-read socket)))
          824       (when (plusp nbytes)
          825         (setf (state socket) :read))
          826       nbytes))
          827 
          828   (defun update-ready-and-state-slots (sockets)
          829     (dolist (socket sockets)
          830       (if (%ready-p socket)
          831           (setf (state socket) :READ)
          832         (let ((events (etypecase socket
          833                         (stream-server-usocket (logior fd-connect fd-accept fd-close))
          834                         (stream-usocket (logior fd-read))
          835                         (datagram-usocket (logior fd-read)))))
          836           ;; TODO: check the iErrorCode array
          837           (multiple-value-bind (valid-p ready-p)
          838               (ffi:c-inline ((socket-handle socket) events) (:fixnum :fixnum)
          839                                                             (values :bool :bool)
          840                 "WSANETWORKEVENTS network_events;
          841                  int i, result;
          842                  result = WSAEnumNetworkEvents((SOCKET)#0, 0, &network_events);
          843                  if (!result) {
          844                    @(return 0) = Ct;
          845                    @(return 1) = (#1 & network_events.lNetworkEvents)? Ct : Cnil;
          846                  } else {
          847                    @(return 0) = Cnil;
          848                    @(return 1) = Cnil;
          849                  }")
          850             (if valid-p
          851                 (when ready-p
          852                   (setf (state socket) :READ)
          853                   (when (stream-server-usocket-p socket)
          854                     (setf (%ready-p socket) t)))
          855               (sb-bsd-sockets::socket-error 'update-ready-and-state-slots)))))))
          856 
          857   (defun wait-for-input-internal (wait-list &key timeout)
          858     (when (waiting-required (wait-list-waiters wait-list))
          859       (let ((rv (ffi:c-inline ((wait-list-%wait wait-list)
          860                                (if timeout
          861                                    (truncate (* 1000 timeout))
          862                                    +wsa-infinite+))
          863                               (:fixnum :fixnum) :fixnum
          864                  "DWORD result;
          865                   WSAEVENT events[1];
          866                   events[0] = (WSAEVENT)#0;
          867                   result = WSAWaitForMultipleEvents(1, events, NULL, #1, NULL);
          868                   @(return) = result;")))
          869         (ecase rv
          870           ((#.+wsa-wait-event-0+)
          871            (update-ready-and-state-slots (wait-list-waiters wait-list)))
          872           ((#.+wsa-wait-timeout+)) ; do nothing here
          873           ((#.+wsa-wait-failed+)
          874            (sb-bsd-sockets::socket-error 'wait-for-input-internal))))))
          875 
          876 ) ; progn