sbcl.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
HTML 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