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