clucumber 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +1 -1
- data/README.md +4 -9
- data/lib/clucumber/clucumber-bootstrap.lisp +32 -0
- data/lib/clucumber/vendor/cl-interpol/alias.lisp +55 -0
- data/lib/clucumber/vendor/cl-interpol/cl-interpol.asd +56 -0
- data/lib/clucumber/vendor/cl-interpol/load.lisp +53 -0
- data/lib/clucumber/vendor/cl-interpol/packages.lisp +40 -0
- data/lib/clucumber/vendor/cl-interpol/read.lisp +716 -0
- data/lib/clucumber/vendor/cl-interpol/specials.lisp +113 -0
- data/lib/clucumber/vendor/cl-interpol/test/packages.lisp +34 -0
- data/lib/clucumber/vendor/cl-interpol/test/tests.lisp +128 -0
- data/lib/clucumber/vendor/cl-interpol/test.lisp +177 -0
- data/lib/clucumber/vendor/cl-interpol/test2.lisp +6254 -0
- data/lib/clucumber/vendor/cl-interpol/unicode.lisp +13912 -0
- data/lib/clucumber/vendor/cl-interpol/util.lisp +122 -0
- data/lib/clucumber/vendor/cl-ppcre/api.lisp +1262 -0
- data/lib/clucumber/vendor/cl-ppcre/charmap.lisp +152 -0
- data/lib/clucumber/vendor/cl-ppcre/charset.lisp +242 -0
- data/lib/clucumber/vendor/cl-ppcre/chartest.lisp +98 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-test.asd +34 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/packages.lisp +38 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode/resolver.lisp +61 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre-unicode.asd +58 -0
- data/lib/clucumber/vendor/cl-ppcre/cl-ppcre.asd +79 -0
- data/lib/clucumber/vendor/cl-ppcre/closures.lisp +471 -0
- data/lib/clucumber/vendor/cl-ppcre/convert.lisp +875 -0
- data/lib/clucumber/vendor/cl-ppcre/errors.lisp +84 -0
- data/lib/clucumber/vendor/cl-ppcre/lexer.lisp +737 -0
- data/lib/clucumber/vendor/cl-ppcre/lispworks-defsystem.lisp +57 -0
- data/lib/clucumber/vendor/cl-ppcre/load.lisp +67 -0
- data/lib/clucumber/vendor/cl-ppcre/optimize.lisp +578 -0
- data/lib/clucumber/vendor/cl-ppcre/packages.lisp +68 -0
- data/lib/clucumber/vendor/cl-ppcre/parser.lisp +319 -0
- data/lib/clucumber/vendor/cl-ppcre/ppcre-tests.lisp +269 -0
- data/lib/clucumber/vendor/cl-ppcre/regex-class-util.lisp +555 -0
- data/lib/clucumber/vendor/cl-ppcre/regex-class.lisp +259 -0
- data/lib/clucumber/vendor/cl-ppcre/repetition-closures.lisp +833 -0
- data/lib/clucumber/vendor/cl-ppcre/scanner.lisp +506 -0
- data/lib/clucumber/vendor/cl-ppcre/specials.lisp +172 -0
- data/lib/clucumber/vendor/cl-ppcre/test/packages.lisp +37 -0
- data/lib/clucumber/vendor/cl-ppcre/test/perl-tests.lisp +150 -0
- data/lib/clucumber/vendor/cl-ppcre/test/tests.lisp +159 -0
- data/lib/clucumber/vendor/cl-ppcre/test/unicode-tests.lisp +80 -0
- data/lib/clucumber/vendor/cl-ppcre/util.lisp +201 -0
- data/lib/clucumber/vendor/cl-unicode/alias.lisp +135 -0
- data/lib/clucumber/vendor/cl-unicode/api.lisp +412 -0
- data/lib/clucumber/vendor/cl-unicode/build/char-info.lisp +133 -0
- data/lib/clucumber/vendor/cl-unicode/build/dump.lisp +239 -0
- data/lib/clucumber/vendor/cl-unicode/build/read.lisp +280 -0
- data/lib/clucumber/vendor/cl-unicode/build/util.lisp +182 -0
- data/lib/clucumber/vendor/cl-unicode/cl-unicode.asd +90 -0
- data/lib/clucumber/vendor/cl-unicode/conditions.lisp +54 -0
- data/lib/clucumber/vendor/cl-unicode/derived.lisp +120 -0
- data/lib/clucumber/vendor/cl-unicode/hash-tables.lisp +20 -0
- data/lib/clucumber/vendor/cl-unicode/lists.lisp +10 -0
- data/lib/clucumber/vendor/cl-unicode/methods.lisp +15 -0
- data/lib/clucumber/vendor/cl-unicode/packages.lisp +76 -0
- data/lib/clucumber/vendor/cl-unicode/specials.lisp +135 -0
- data/lib/clucumber/vendor/cl-unicode/test/packages.lisp +34 -0
- data/lib/clucumber/vendor/cl-unicode/test/tests.lisp +150 -0
- data/lib/clucumber/vendor/cl-unicode/test-functions.lisp +94 -0
- data/lib/clucumber/vendor/cl-unicode/util.lisp +274 -0
- data/lib/clucumber/vendor/lift/compare/fiveam-tests.lisp +14 -0
- data/lib/clucumber/vendor/lift/compare/lift-tests.lisp +13 -0
- data/lib/clucumber/vendor/lift/compare/rt-tests.lisp +16 -0
- data/lib/clucumber/vendor/lift/compare/xlunit-tests.lisp +15 -0
- data/lib/clucumber/vendor/lift/dev/changes.lisp +61 -0
- data/lib/clucumber/vendor/lift/dev/config.lisp +354 -0
- data/lib/clucumber/vendor/lift/dev/copy-file.lisp +117 -0
- data/lib/clucumber/vendor/lift/dev/introspection.lisp +232 -0
- data/lib/clucumber/vendor/lift/dev/lift-interface.lisp +56 -0
- data/lib/clucumber/vendor/lift/dev/lift-notes.lisp +202 -0
- data/lib/clucumber/vendor/lift/dev/lift-randomized.lisp +45 -0
- data/lib/clucumber/vendor/lift/dev/lift.lisp +2383 -0
- data/lib/clucumber/vendor/lift/dev/macros.lisp +229 -0
- data/lib/clucumber/vendor/lift/dev/measuring.lisp +156 -0
- data/lib/clucumber/vendor/lift/dev/packages.lisp +161 -0
- data/lib/clucumber/vendor/lift/dev/port.lisp +151 -0
- data/lib/clucumber/vendor/lift/dev/prototypes.lisp +282 -0
- data/lib/clucumber/vendor/lift/dev/random-testing.lisp +124 -0
- data/lib/clucumber/vendor/lift/dev/report-locations.lisp +13 -0
- data/lib/clucumber/vendor/lift/dev/reports.lisp +916 -0
- data/lib/clucumber/vendor/lift/dev/utilities.lisp +242 -0
- data/lib/clucumber/vendor/lift/docs/package.lisp +6 -0
- data/lib/clucumber/vendor/lift/docs/setup.lisp +17 -0
- data/lib/clucumber/vendor/lift/examples/basic-examples.lisp +289 -0
- data/lib/clucumber/vendor/lift/examples/random-testing.lisp +32 -0
- data/lib/clucumber/vendor/lift/lift-documentation.asd +28 -0
- data/lib/clucumber/vendor/lift/lift-test.asd +35 -0
- data/lib/clucumber/vendor/lift/lift.asd +77 -0
- data/lib/clucumber/vendor/lift/test/equality-tests.lisp +40 -0
- data/lib/clucumber/vendor/lift/test/finding-tests.lisp +32 -0
- data/lib/clucumber/vendor/lift/test/lift-test.lisp +783 -0
- data/lib/clucumber/vendor/lift/test/order-of-operations.lisp +54 -0
- data/lib/clucumber/vendor/lift/test/packages.lisp +17 -0
- data/lib/clucumber/vendor/lift/test/test-config-files.lisp +20 -0
- data/lib/clucumber/vendor/lift/test/test-dynamic-variables.lisp +57 -0
- data/lib/clucumber/vendor/lift/test/test-maximum-problems.lisp +74 -0
- data/lib/clucumber/vendor/lift/test/test-prototypes.lisp +278 -0
- data/lib/clucumber/vendor/lift/test/test-timeout.lisp +37 -0
- data/lib/clucumber/vendor/lift/test/tests-in-progress.lisp +62 -0
- data/lib/clucumber/vendor/lift/test/testsuite-expects.lisp +60 -0
- data/lib/clucumber/vendor/lift/timeout/package.lisp +13 -0
- data/lib/clucumber/vendor/lift/timeout/with-timeout.lisp +123 -0
- data/lib/clucumber/vendor/lift/website/stuff/Temp.lisp +34 -0
- data/lib/clucumber/vendor/st-json/st-json.asd +3 -0
- data/lib/clucumber/vendor/st-json/st-json.lisp +310 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/backtrace.lisp +127 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/fallback.lisp +10 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/map-backtrace.lisp +103 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/mucking.lisp +75 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/packages.lisp +13 -0
- data/lib/clucumber/vendor/trivial-backtrace/dev/utilities.lisp +104 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/packages.lisp +5 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/test-setup.lisp +4 -0
- data/lib/clucumber/vendor/trivial-backtrace/test/tests.lisp +16 -0
- data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace-test.asd +22 -0
- data/lib/clucumber/vendor/trivial-backtrace/trivial-backtrace.asd +35 -0
- data/lib/clucumber/vendor/usocket/backend/allegro.lisp +199 -0
- data/lib/clucumber/vendor/usocket/backend/armedbear.lisp +491 -0
- data/lib/clucumber/vendor/usocket/backend/clisp.lisp +260 -0
- data/lib/clucumber/vendor/usocket/backend/cmucl.lisp +266 -0
- data/lib/clucumber/vendor/usocket/backend/lispworks.lisp +741 -0
- data/lib/clucumber/vendor/usocket/backend/mcl.lisp +369 -0
- data/lib/clucumber/vendor/usocket/backend/openmcl.lisp +206 -0
- data/lib/clucumber/vendor/usocket/backend/sbcl.lisp +424 -0
- data/lib/clucumber/vendor/usocket/backend/scl.lisp +261 -0
- data/lib/clucumber/vendor/usocket/condition.lisp +227 -0
- data/lib/clucumber/vendor/usocket/package.lisp +82 -0
- data/lib/clucumber/vendor/usocket/server.lisp +45 -0
- data/lib/clucumber/vendor/usocket/test/package.lisp +13 -0
- data/lib/clucumber/vendor/usocket/test/test-usocket.lisp +166 -0
- data/lib/clucumber/vendor/usocket/usocket-test.asd +26 -0
- data/lib/clucumber/vendor/usocket/usocket.asd +37 -0
- data/lib/clucumber/vendor/usocket/usocket.lisp +542 -0
- data/lib/clucumber/vendor/usocket/vendor/kqueue.lisp +1 -0
- data/lib/clucumber/vendor/usocket/vendor/split-sequence.lisp +245 -0
- data/lib/clucumber.rb +29 -7
- metadata +151 -5
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
;;;; $Id: lispworks.lisp 521 2010-02-21 03:38:51Z ctian $
|
|
2
|
+
;;;; $URL: svn://common-lisp.net/project/usocket/svn/usocket/trunk/backend/lispworks.lisp $
|
|
3
|
+
|
|
4
|
+
;;;; See LICENSE for licensing information.
|
|
5
|
+
|
|
6
|
+
(in-package :usocket)
|
|
7
|
+
|
|
8
|
+
(eval-when (:compile-toplevel :load-toplevel :execute)
|
|
9
|
+
(require "comm")
|
|
10
|
+
|
|
11
|
+
#+lispworks3
|
|
12
|
+
(error "LispWorks 3 is not supported by USOCKET."))
|
|
13
|
+
|
|
14
|
+
;;; ---------------------------------------------------------------------------
|
|
15
|
+
;;; Warn if multiprocessing is not running on Lispworks
|
|
16
|
+
|
|
17
|
+
(defun check-for-multiprocessing-started (&optional errorp)
|
|
18
|
+
(unless mp:*current-process*
|
|
19
|
+
(funcall (if errorp 'error 'warn)
|
|
20
|
+
"You must start multiprocessing on Lispworks by calling~
|
|
21
|
+
~%~3t(~s)~
|
|
22
|
+
~%for ~s function properly."
|
|
23
|
+
'mp:initialize-multiprocessing
|
|
24
|
+
'wait-for-input)))
|
|
25
|
+
|
|
26
|
+
(eval-when (:load-toplevel :execute)
|
|
27
|
+
(check-for-multiprocessing-started))
|
|
28
|
+
|
|
29
|
+
#+win32
|
|
30
|
+
(eval-when (:load-toplevel :execute)
|
|
31
|
+
(fli:register-module "ws2_32")
|
|
32
|
+
(comm::ensure-sockets))
|
|
33
|
+
|
|
34
|
+
(fli:define-foreign-function (get-host-name-internal "gethostname" :source)
|
|
35
|
+
((return-string (:reference-return (:ef-mb-string :limit 257)))
|
|
36
|
+
(namelen :int))
|
|
37
|
+
:lambda-list (&aux (namelen 256) return-string)
|
|
38
|
+
:result-type :int
|
|
39
|
+
#+win32 :module
|
|
40
|
+
#+win32 "ws2_32")
|
|
41
|
+
|
|
42
|
+
(defun get-host-name ()
|
|
43
|
+
(multiple-value-bind (retcode name)
|
|
44
|
+
(get-host-name-internal)
|
|
45
|
+
(when (= 0 retcode)
|
|
46
|
+
name)))
|
|
47
|
+
|
|
48
|
+
#+win32
|
|
49
|
+
(defun remap-maybe-for-win32 (z)
|
|
50
|
+
(mapcar #'(lambda (x)
|
|
51
|
+
(cons (mapcar #'(lambda (y)
|
|
52
|
+
(+ 10000 y))
|
|
53
|
+
(car x))
|
|
54
|
+
(cdr x)))
|
|
55
|
+
z))
|
|
56
|
+
|
|
57
|
+
(defparameter +lispworks-error-map+
|
|
58
|
+
#+win32
|
|
59
|
+
(append (remap-maybe-for-win32 +unix-errno-condition-map+)
|
|
60
|
+
(remap-maybe-for-win32 +unix-errno-error-map+))
|
|
61
|
+
#-win32
|
|
62
|
+
(append +unix-errno-condition-map+
|
|
63
|
+
+unix-errno-error-map+))
|
|
64
|
+
|
|
65
|
+
(defun raise-or-signal-socket-error (errno socket)
|
|
66
|
+
(let ((usock-err
|
|
67
|
+
(cdr (assoc errno +lispworks-error-map+ :test #'member))))
|
|
68
|
+
(if usock-err
|
|
69
|
+
(if (subtypep usock-err 'error)
|
|
70
|
+
(error usock-err :socket socket)
|
|
71
|
+
(signal usock-err :socket))
|
|
72
|
+
(error 'unknown-error
|
|
73
|
+
:socket socket
|
|
74
|
+
:real-condition nil))))
|
|
75
|
+
|
|
76
|
+
(defun raise-usock-err (errno socket &optional condition)
|
|
77
|
+
(let* ((usock-err
|
|
78
|
+
(cdr (assoc errno +lispworks-error-map+
|
|
79
|
+
:test #'member))))
|
|
80
|
+
(if usock-err
|
|
81
|
+
(if (subtypep usock-err 'error)
|
|
82
|
+
(error usock-err :socket socket)
|
|
83
|
+
(signal usock-err :socket))
|
|
84
|
+
(error 'unknown-error
|
|
85
|
+
:socket socket
|
|
86
|
+
:real-error condition))))
|
|
87
|
+
|
|
88
|
+
(defun handle-condition (condition &optional (socket nil))
|
|
89
|
+
"Dispatch correct usocket condition."
|
|
90
|
+
(typecase condition
|
|
91
|
+
(simple-error (destructuring-bind (&optional host port err-msg errno)
|
|
92
|
+
(simple-condition-format-arguments condition)
|
|
93
|
+
(declare (ignore host port err-msg))
|
|
94
|
+
(raise-usock-err errno socket condition)))))
|
|
95
|
+
|
|
96
|
+
(defconstant *socket_sock_dgram* 2
|
|
97
|
+
"Connectionless, unreliable datagrams of fixed maximum length.")
|
|
98
|
+
|
|
99
|
+
(defconstant *sockopt_so_rcvtimeo*
|
|
100
|
+
#+(not linux) #x1006
|
|
101
|
+
#+linux 20
|
|
102
|
+
"Socket receive timeout")
|
|
103
|
+
|
|
104
|
+
(fli:define-c-struct timeval
|
|
105
|
+
(tv-sec :long)
|
|
106
|
+
(tv-usec :long))
|
|
107
|
+
|
|
108
|
+
;;; ssize_t
|
|
109
|
+
;;; recvfrom(int socket, void *restrict buffer, size_t length, int flags,
|
|
110
|
+
;;; struct sockaddr *restrict address, socklen_t *restrict address_len);
|
|
111
|
+
(fli:define-foreign-function (%recvfrom "recvfrom" :source)
|
|
112
|
+
((socket :int)
|
|
113
|
+
(buffer (:pointer (:unsigned :byte)))
|
|
114
|
+
(length :int)
|
|
115
|
+
(flags :int)
|
|
116
|
+
(address (:pointer (:struct comm::sockaddr)))
|
|
117
|
+
(address-len (:pointer :int)))
|
|
118
|
+
:result-type :int
|
|
119
|
+
#+win32 :module
|
|
120
|
+
#+win32 "ws2_32")
|
|
121
|
+
|
|
122
|
+
;;; ssize_t
|
|
123
|
+
;;; sendto(int socket, const void *buffer, size_t length, int flags,
|
|
124
|
+
;;; const struct sockaddr *dest_addr, socklen_t dest_len);
|
|
125
|
+
(fli:define-foreign-function (%sendto "sendto" :source)
|
|
126
|
+
((socket :int)
|
|
127
|
+
(buffer (:pointer (:unsigned :byte)))
|
|
128
|
+
(length :int)
|
|
129
|
+
(flags :int)
|
|
130
|
+
(address (:pointer (:struct comm::sockaddr)))
|
|
131
|
+
(address-len :int))
|
|
132
|
+
:result-type :int
|
|
133
|
+
#+win32 :module
|
|
134
|
+
#+win32 "ws2_32")
|
|
135
|
+
|
|
136
|
+
#-win32
|
|
137
|
+
(defun set-socket-receive-timeout (socket-fd seconds)
|
|
138
|
+
"Set socket option: RCVTIMEO, argument seconds can be a float number"
|
|
139
|
+
(declare (type integer socket-fd)
|
|
140
|
+
(type number seconds))
|
|
141
|
+
(multiple-value-bind (sec usec) (truncate seconds)
|
|
142
|
+
(fli:with-dynamic-foreign-objects ((timeout (:struct timeval)))
|
|
143
|
+
(fli:with-foreign-slots (tv-sec tv-usec) timeout
|
|
144
|
+
(setf tv-sec sec
|
|
145
|
+
tv-usec (truncate (* 1000000 usec)))
|
|
146
|
+
(if (zerop (comm::setsockopt socket-fd
|
|
147
|
+
comm::*sockopt_sol_socket*
|
|
148
|
+
*sockopt_so_rcvtimeo*
|
|
149
|
+
(fli:copy-pointer timeout
|
|
150
|
+
:type '(:pointer :void))
|
|
151
|
+
(fli:size-of '(:struct timeval))))
|
|
152
|
+
seconds)))))
|
|
153
|
+
|
|
154
|
+
#+win32
|
|
155
|
+
(defun set-socket-receive-timeout (socket-fd seconds)
|
|
156
|
+
"Set socket option: RCVTIMEO, argument seconds can be a float number.
|
|
157
|
+
On win32, you must bind the socket before use this function."
|
|
158
|
+
(declare (type integer socket-fd)
|
|
159
|
+
(type number seconds))
|
|
160
|
+
(fli:with-dynamic-foreign-objects ((timeout :int))
|
|
161
|
+
(setf (fli:dereference timeout)
|
|
162
|
+
(truncate (* 1000 seconds)))
|
|
163
|
+
(if (zerop (comm::setsockopt socket-fd
|
|
164
|
+
comm::*sockopt_sol_socket*
|
|
165
|
+
*sockopt_so_rcvtimeo*
|
|
166
|
+
(fli:copy-pointer timeout
|
|
167
|
+
:type '(:pointer :char))
|
|
168
|
+
(fli:size-of :int)))
|
|
169
|
+
seconds)))
|
|
170
|
+
|
|
171
|
+
#-win32
|
|
172
|
+
(defmethod get-socket-receive-timeout (socket-fd)
|
|
173
|
+
"Get socket option: RCVTIMEO, return value is a float number"
|
|
174
|
+
(declare (type integer socket-fd))
|
|
175
|
+
(fli:with-dynamic-foreign-objects ((timeout (:struct timeval))
|
|
176
|
+
(len :int))
|
|
177
|
+
(comm::getsockopt socket-fd
|
|
178
|
+
comm::*sockopt_sol_socket*
|
|
179
|
+
*sockopt_so_rcvtimeo*
|
|
180
|
+
(fli:copy-pointer timeout
|
|
181
|
+
:type '(:pointer :void))
|
|
182
|
+
len)
|
|
183
|
+
(fli:with-foreign-slots (tv-sec tv-usec) timeout
|
|
184
|
+
(float (+ tv-sec (/ tv-usec 1000000))))))
|
|
185
|
+
|
|
186
|
+
#+win32
|
|
187
|
+
(defmethod get-socket-receive-timeout (socket-fd)
|
|
188
|
+
"Get socket option: RCVTIMEO, return value is a float number"
|
|
189
|
+
(declare (type integer socket-fd))
|
|
190
|
+
(fli:with-dynamic-foreign-objects ((timeout :int)
|
|
191
|
+
(len :int))
|
|
192
|
+
(comm::getsockopt socket-fd
|
|
193
|
+
comm::*sockopt_sol_socket*
|
|
194
|
+
*sockopt_so_rcvtimeo*
|
|
195
|
+
(fli:copy-pointer timeout
|
|
196
|
+
:type '(:pointer :void))
|
|
197
|
+
len)
|
|
198
|
+
(float (/ (fli:dereference timeout) 1000))))
|
|
199
|
+
|
|
200
|
+
(defun open-udp-socket (&key local-address local-port read-timeout)
|
|
201
|
+
"Open a unconnected UDP socket.
|
|
202
|
+
For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL),
|
|
203
|
+
for binding on random free unused port, set LOCAL-PORT to 0."
|
|
204
|
+
(let ((socket-fd (comm::socket comm::*socket_af_inet* *socket_sock_dgram* comm::*socket_pf_unspec*)))
|
|
205
|
+
(if socket-fd
|
|
206
|
+
(progn
|
|
207
|
+
(when read-timeout (set-socket-receive-timeout socket-fd read-timeout))
|
|
208
|
+
(if local-port
|
|
209
|
+
(fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)))
|
|
210
|
+
(comm::initialize-sockaddr_in client-addr comm::*socket_af_inet*
|
|
211
|
+
local-address local-port "udp")
|
|
212
|
+
(if (comm::bind socket-fd
|
|
213
|
+
(fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
|
|
214
|
+
(fli:pointer-element-size client-addr))
|
|
215
|
+
;; success, return socket fd
|
|
216
|
+
socket-fd
|
|
217
|
+
(progn
|
|
218
|
+
(comm::close-socket socket-fd)
|
|
219
|
+
(error "cannot bind"))))
|
|
220
|
+
socket-fd))
|
|
221
|
+
(error "cannot create socket"))))
|
|
222
|
+
|
|
223
|
+
(defun connect-to-udp-server (hostname service
|
|
224
|
+
&key local-address local-port read-timeout)
|
|
225
|
+
"Something like CONNECT-TO-TCP-SERVER"
|
|
226
|
+
(let ((socket-fd (open-udp-socket :local-address local-address
|
|
227
|
+
:local-port local-port
|
|
228
|
+
:read-timeout read-timeout)))
|
|
229
|
+
(if socket-fd
|
|
230
|
+
(fli:with-dynamic-foreign-objects ((server-addr (:struct comm::sockaddr_in)))
|
|
231
|
+
;; connect to remote address/port
|
|
232
|
+
(comm::initialize-sockaddr_in server-addr comm::*socket_af_inet* hostname service "udp")
|
|
233
|
+
(if (comm::connect socket-fd
|
|
234
|
+
(fli:copy-pointer server-addr :type '(:struct comm::sockaddr))
|
|
235
|
+
(fli:pointer-element-size server-addr))
|
|
236
|
+
;; success, return socket fd
|
|
237
|
+
socket-fd
|
|
238
|
+
;; fail, close socket and return nil
|
|
239
|
+
(progn
|
|
240
|
+
(comm::close-socket socket-fd)
|
|
241
|
+
(error "cannot connect"))))
|
|
242
|
+
(error "cannot create socket"))))
|
|
243
|
+
|
|
244
|
+
;; Register a special free action for closing datagram usocket when being GCed
|
|
245
|
+
(defun usocket-special-free-action (object)
|
|
246
|
+
(when (and (typep object 'datagram-usocket)
|
|
247
|
+
(%open-p object))
|
|
248
|
+
(socket-close object)))
|
|
249
|
+
|
|
250
|
+
(eval-when (:load-toplevel :execute)
|
|
251
|
+
(hcl:add-special-free-action 'usocket-special-free-action))
|
|
252
|
+
|
|
253
|
+
(defun socket-connect (host port &key (protocol :stream) (element-type 'base-char)
|
|
254
|
+
timeout deadline (nodelay t nodelay-specified)
|
|
255
|
+
local-host (local-port #+win32 *auto-port* #-win32 nil))
|
|
256
|
+
(declare (ignorable nodelay))
|
|
257
|
+
|
|
258
|
+
;; What's the meaning of this keyword?
|
|
259
|
+
(when deadline
|
|
260
|
+
(unimplemented 'deadline 'socket-connect))
|
|
261
|
+
|
|
262
|
+
#+(and lispworks4 (not lispworks4.4)) ; < 4.4.5
|
|
263
|
+
(when timeout
|
|
264
|
+
(unsupported 'timeout 'socket-connect :minimum "LispWorks 4.4.5"))
|
|
265
|
+
|
|
266
|
+
#+(or lispworks4 lispworks5.0) ; < 5.1
|
|
267
|
+
(when nodelay-specified
|
|
268
|
+
(unsupported 'nodelay 'socket-connect :minimum "LispWorks 5.1"))
|
|
269
|
+
|
|
270
|
+
#+lispworks4 #+lispworks4
|
|
271
|
+
(when local-host
|
|
272
|
+
(unsupported 'local-host 'socket-connect :minimum "LispWorks 5.0"))
|
|
273
|
+
(when local-port
|
|
274
|
+
(unsupported 'local-port 'socket-connect :minimum "LispWorks 5.0"))
|
|
275
|
+
|
|
276
|
+
(ecase protocol
|
|
277
|
+
(:stream
|
|
278
|
+
(let ((hostname (host-to-hostname host))
|
|
279
|
+
(stream))
|
|
280
|
+
(setf stream
|
|
281
|
+
(with-mapped-conditions ()
|
|
282
|
+
(comm:open-tcp-stream hostname port
|
|
283
|
+
:element-type element-type
|
|
284
|
+
#-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5
|
|
285
|
+
#-(and lispworks4 (not lispworks4.4))
|
|
286
|
+
:timeout timeout
|
|
287
|
+
#-lispworks4 #-lispworks4
|
|
288
|
+
#-lispworks4 #-lispworks4
|
|
289
|
+
:local-address (when local-host (host-to-hostname local-host))
|
|
290
|
+
:local-port local-port
|
|
291
|
+
#-(or lispworks4 lispworks5.0) ; >= 5.1
|
|
292
|
+
#-(or lispworks4 lispworks5.0)
|
|
293
|
+
:nodelay nodelay)))
|
|
294
|
+
(if stream
|
|
295
|
+
(make-stream-socket :socket (comm:socket-stream-socket stream)
|
|
296
|
+
:stream stream)
|
|
297
|
+
(error 'unknown-error))))
|
|
298
|
+
(:datagram
|
|
299
|
+
(let ((usocket (make-datagram-socket
|
|
300
|
+
(if (and host port)
|
|
301
|
+
(connect-to-udp-server host port
|
|
302
|
+
:local-address local-host
|
|
303
|
+
:local-port local-port)
|
|
304
|
+
(open-udp-socket :local-address local-host
|
|
305
|
+
:local-port local-port))
|
|
306
|
+
:connected-p t)))
|
|
307
|
+
(hcl:flag-special-free-action usocket)
|
|
308
|
+
usocket))))
|
|
309
|
+
|
|
310
|
+
(defun socket-listen (host port
|
|
311
|
+
&key reuseaddress
|
|
312
|
+
(reuse-address nil reuse-address-supplied-p)
|
|
313
|
+
(backlog 5)
|
|
314
|
+
(element-type 'base-char))
|
|
315
|
+
#+lispworks4.1
|
|
316
|
+
(unsupported 'host 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
|
|
317
|
+
#+lispworks4.1
|
|
318
|
+
(unsupported 'backlog 'socket-listen :minimum "LispWorks 4.0 or newer than 4.1")
|
|
319
|
+
|
|
320
|
+
(let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress))
|
|
321
|
+
(comm::*use_so_reuseaddr* reuseaddress)
|
|
322
|
+
(hostname (host-to-hostname host))
|
|
323
|
+
(sock (with-mapped-conditions ()
|
|
324
|
+
#-lispworks4.1 (comm::create-tcp-socket-for-service
|
|
325
|
+
port :address hostname :backlog backlog)
|
|
326
|
+
#+lispworks4.1 (comm::create-tcp-socket-for-service port))))
|
|
327
|
+
(make-stream-server-socket sock :element-type element-type)))
|
|
328
|
+
|
|
329
|
+
(defmethod socket-accept ((usocket stream-server-usocket) &key element-type)
|
|
330
|
+
(let* ((sock (with-mapped-conditions (usocket)
|
|
331
|
+
(comm::get-fd-from-socket (socket usocket))))
|
|
332
|
+
(stream (make-instance 'comm:socket-stream
|
|
333
|
+
:socket sock
|
|
334
|
+
:direction :io
|
|
335
|
+
:element-type (or element-type
|
|
336
|
+
(element-type usocket)))))
|
|
337
|
+
#+win32
|
|
338
|
+
(when sock
|
|
339
|
+
(setf (%ready-p usocket) nil))
|
|
340
|
+
(make-stream-socket :socket sock :stream stream)))
|
|
341
|
+
|
|
342
|
+
;; Sockets and their streams are different objects
|
|
343
|
+
;; close the stream in order to make sure buffers
|
|
344
|
+
;; are correctly flushed and the socket closed.
|
|
345
|
+
(defmethod socket-close ((usocket stream-usocket))
|
|
346
|
+
"Close socket."
|
|
347
|
+
(when (wait-list usocket)
|
|
348
|
+
(remove-waiter (wait-list usocket) usocket))
|
|
349
|
+
(close (socket-stream usocket)))
|
|
350
|
+
|
|
351
|
+
(defmethod socket-close ((usocket usocket))
|
|
352
|
+
(when (wait-list usocket)
|
|
353
|
+
(remove-waiter (wait-list usocket) usocket))
|
|
354
|
+
(with-mapped-conditions (usocket)
|
|
355
|
+
(comm::close-socket (socket usocket))))
|
|
356
|
+
|
|
357
|
+
(defmethod socket-close :after ((socket datagram-usocket))
|
|
358
|
+
"Additional socket-close method for datagram-usocket"
|
|
359
|
+
(setf (%open-p socket) nil))
|
|
360
|
+
|
|
361
|
+
(defvar *message-send-buffer*
|
|
362
|
+
(make-array +max-datagram-packet-size+
|
|
363
|
+
:element-type '(unsigned-byte 8)
|
|
364
|
+
:allocation :static))
|
|
365
|
+
|
|
366
|
+
(defvar *message-send-lock*
|
|
367
|
+
(mp:make-lock :name "USOCKET message send lock"))
|
|
368
|
+
|
|
369
|
+
(defun send-message (socket-fd buffer &optional (length (length buffer)) host service)
|
|
370
|
+
"Send message to a socket, using sendto()/send()"
|
|
371
|
+
(declare (type integer socket-fd)
|
|
372
|
+
(type sequence buffer))
|
|
373
|
+
(let ((message *message-send-buffer*))
|
|
374
|
+
(fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
|
|
375
|
+
(len :int
|
|
376
|
+
#-(or lispworks4 lispworks5.0) ; <= 5.0
|
|
377
|
+
:initial-element
|
|
378
|
+
(fli:size-of '(:struct comm::sockaddr_in))))
|
|
379
|
+
(fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
|
|
380
|
+
(mp:with-lock (*message-send-lock*)
|
|
381
|
+
(replace message buffer :end2 length)
|
|
382
|
+
(if (and host service)
|
|
383
|
+
(progn
|
|
384
|
+
(comm::initialize-sockaddr_in client-addr comm::*socket_af_inet* host service "udp")
|
|
385
|
+
(%sendto socket-fd ptr (min length +max-datagram-packet-size+) 0
|
|
386
|
+
(fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
|
|
387
|
+
(fli:dereference len)))
|
|
388
|
+
(comm::%send socket-fd ptr (min length +max-datagram-packet-size+) 0)))))))
|
|
389
|
+
|
|
390
|
+
(defmethod socket-send ((socket datagram-usocket) buffer length &key host port)
|
|
391
|
+
(let ((s (socket socket)))
|
|
392
|
+
(send-message s buffer length (and host (host-to-hbo host)) port)))
|
|
393
|
+
|
|
394
|
+
(defvar *message-receive-buffer*
|
|
395
|
+
(make-array +max-datagram-packet-size+
|
|
396
|
+
:element-type '(unsigned-byte 8)
|
|
397
|
+
:allocation :static))
|
|
398
|
+
|
|
399
|
+
(defvar *message-receive-lock*
|
|
400
|
+
(mp:make-lock :name "USOCKET message receive lock"))
|
|
401
|
+
|
|
402
|
+
(defun receive-message (socket-fd &optional buffer (length (length buffer))
|
|
403
|
+
&key read-timeout (max-buffer-size +max-datagram-packet-size+))
|
|
404
|
+
"Receive message from socket, read-timeout is a float number in seconds.
|
|
405
|
+
|
|
406
|
+
This function will return 4 values:
|
|
407
|
+
1. receive buffer
|
|
408
|
+
2. number of receive bytes
|
|
409
|
+
3. remote address
|
|
410
|
+
4. remote port"
|
|
411
|
+
(declare (type integer socket-fd)
|
|
412
|
+
(type sequence buffer))
|
|
413
|
+
(let ((message *message-receive-buffer*)
|
|
414
|
+
old-timeout)
|
|
415
|
+
(fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))
|
|
416
|
+
(len :int
|
|
417
|
+
#-(or lispworks4 lispworks5.0) ; <= 5.0
|
|
418
|
+
:initial-element
|
|
419
|
+
(fli:size-of '(:struct comm::sockaddr_in))))
|
|
420
|
+
(fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte))
|
|
421
|
+
;; setup new read timeout
|
|
422
|
+
(when read-timeout
|
|
423
|
+
(setf old-timeout (get-socket-receive-timeout socket-fd))
|
|
424
|
+
(set-socket-receive-timeout socket-fd read-timeout))
|
|
425
|
+
(mp:with-lock (*message-receive-lock*)
|
|
426
|
+
(let ((n (%recvfrom socket-fd ptr max-buffer-size 0
|
|
427
|
+
(fli:copy-pointer client-addr :type '(:struct comm::sockaddr))
|
|
428
|
+
len)))
|
|
429
|
+
;; restore old read timeout
|
|
430
|
+
(when (and read-timeout (/= old-timeout read-timeout))
|
|
431
|
+
(set-socket-receive-timeout socket-fd old-timeout))
|
|
432
|
+
(if (plusp n)
|
|
433
|
+
(values (if buffer
|
|
434
|
+
(replace buffer message
|
|
435
|
+
:end1 (min length max-buffer-size)
|
|
436
|
+
:end2 (min n max-buffer-size))
|
|
437
|
+
(subseq message 0 (min n max-buffer-size)))
|
|
438
|
+
(min n max-buffer-size)
|
|
439
|
+
(comm::ntohl (fli:foreign-slot-value
|
|
440
|
+
(fli:foreign-slot-value client-addr
|
|
441
|
+
'comm::sin_addr
|
|
442
|
+
:object-type '(:struct comm::sockaddr_in)
|
|
443
|
+
:type '(:struct comm::in_addr)
|
|
444
|
+
:copy-foreign-object nil)
|
|
445
|
+
'comm::s_addr
|
|
446
|
+
:object-type '(:struct comm::in_addr)))
|
|
447
|
+
(comm::ntohs (fli:foreign-slot-value client-addr
|
|
448
|
+
'comm::sin_port
|
|
449
|
+
:object-type '(:struct comm::sockaddr_in)
|
|
450
|
+
:type '(:unsigned :short)
|
|
451
|
+
:copy-foreign-object nil)))
|
|
452
|
+
(values nil n 0 0))))))))
|
|
453
|
+
|
|
454
|
+
(defmethod socket-receive ((socket datagram-usocket) buffer length &key)
|
|
455
|
+
(let ((s (socket socket)))
|
|
456
|
+
(multiple-value-bind (buffer size host port)
|
|
457
|
+
(receive-message s buffer length)
|
|
458
|
+
(values buffer size host port))))
|
|
459
|
+
|
|
460
|
+
(defmethod get-local-name ((usocket usocket))
|
|
461
|
+
(multiple-value-bind
|
|
462
|
+
(address port)
|
|
463
|
+
(comm:get-socket-address (socket usocket))
|
|
464
|
+
(values (hbo-to-vector-quad address) port)))
|
|
465
|
+
|
|
466
|
+
(defmethod get-peer-name ((usocket stream-usocket))
|
|
467
|
+
(multiple-value-bind
|
|
468
|
+
(address port)
|
|
469
|
+
(comm:get-socket-peer-address (socket usocket))
|
|
470
|
+
(values (hbo-to-vector-quad address) port)))
|
|
471
|
+
|
|
472
|
+
(defmethod get-local-address ((usocket usocket))
|
|
473
|
+
(nth-value 0 (get-local-name usocket)))
|
|
474
|
+
|
|
475
|
+
(defmethod get-peer-address ((usocket stream-usocket))
|
|
476
|
+
(nth-value 0 (get-peer-name usocket)))
|
|
477
|
+
|
|
478
|
+
(defmethod get-local-port ((usocket usocket))
|
|
479
|
+
(nth-value 1 (get-local-name usocket)))
|
|
480
|
+
|
|
481
|
+
(defmethod get-peer-port ((usocket stream-usocket))
|
|
482
|
+
(nth-value 1 (get-peer-name usocket)))
|
|
483
|
+
|
|
484
|
+
(defun get-hosts-by-name (name)
|
|
485
|
+
(with-mapped-conditions ()
|
|
486
|
+
(mapcar #'hbo-to-vector-quad
|
|
487
|
+
(comm:get-host-entry name :fields '(:addresses)))))
|
|
488
|
+
|
|
489
|
+
(defun os-socket-handle (usocket)
|
|
490
|
+
(socket usocket))
|
|
491
|
+
|
|
492
|
+
(defun usocket-listen (usocket)
|
|
493
|
+
(if (stream-usocket-p usocket)
|
|
494
|
+
(when (listen (socket-stream usocket))
|
|
495
|
+
usocket)
|
|
496
|
+
(when (comm::socket-listen (socket usocket))
|
|
497
|
+
usocket)))
|
|
498
|
+
|
|
499
|
+
;;;
|
|
500
|
+
;;; Non Windows implementation
|
|
501
|
+
;;; The Windows implementation needs to resort to the Windows API in order
|
|
502
|
+
;;; to achieve what we want (what we want is waiting without busy-looping)
|
|
503
|
+
;;;
|
|
504
|
+
|
|
505
|
+
#-win32
|
|
506
|
+
(progn
|
|
507
|
+
|
|
508
|
+
(defun %setup-wait-list (wait-list)
|
|
509
|
+
(declare (ignore wait-list)))
|
|
510
|
+
|
|
511
|
+
(defun %add-waiter (wait-list waiter)
|
|
512
|
+
(declare (ignore wait-list waiter)))
|
|
513
|
+
|
|
514
|
+
(defun %remove-waiter (wait-list waiter)
|
|
515
|
+
(declare (ignore wait-list waiter)))
|
|
516
|
+
|
|
517
|
+
(defun wait-for-input-internal (wait-list &key timeout)
|
|
518
|
+
(with-mapped-conditions ()
|
|
519
|
+
;; unfortunately, it's impossible to share code between
|
|
520
|
+
;; non-win32 and win32 platforms...
|
|
521
|
+
;; Can we have a sane -pref. complete [UDP!?]- API next time, please?
|
|
522
|
+
(dolist (x (wait-list-waiters wait-list))
|
|
523
|
+
(mp:notice-fd (os-socket-handle x)))
|
|
524
|
+
(labels ((wait-function (socks)
|
|
525
|
+
(let (rv)
|
|
526
|
+
(dolist (x socks rv)
|
|
527
|
+
(when (usocket-listen x)
|
|
528
|
+
(setf (state x) :READ
|
|
529
|
+
rv t))))))
|
|
530
|
+
(if timeout
|
|
531
|
+
(mp:process-wait-with-timeout "Waiting for a socket to become active"
|
|
532
|
+
(truncate timeout)
|
|
533
|
+
#'wait-function
|
|
534
|
+
(wait-list-waiters wait-list))
|
|
535
|
+
(mp:process-wait "Waiting for a socket to become active"
|
|
536
|
+
#'wait-function
|
|
537
|
+
(wait-list-waiters wait-list))))
|
|
538
|
+
(dolist (x (wait-list-waiters wait-list))
|
|
539
|
+
(mp:unnotice-fd (os-socket-handle x)))
|
|
540
|
+
wait-list))
|
|
541
|
+
|
|
542
|
+
) ; end of block
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
;;;
|
|
546
|
+
;;; The Windows side of the story
|
|
547
|
+
;;; We want to wait without busy looping
|
|
548
|
+
;;; This code only works in threads which don't have (hidden)
|
|
549
|
+
;;; windows which need to receive messages. There are workarounds in the Windows API
|
|
550
|
+
;;; but are those available to 'us'.
|
|
551
|
+
;;;
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
#+win32
|
|
555
|
+
(progn
|
|
556
|
+
|
|
557
|
+
;; LispWorks doesn't provide an interface to wait for a socket
|
|
558
|
+
;; to become ready (under Win32, that is) meaning that we need
|
|
559
|
+
;; to resort to system calls to achieve the same thing.
|
|
560
|
+
;; Luckily, it provides us access to the raw socket handles (as we
|
|
561
|
+
;; wrote the code above.
|
|
562
|
+
|
|
563
|
+
(defconstant fd-read 1)
|
|
564
|
+
(defconstant fd-read-bit 0)
|
|
565
|
+
(defconstant fd-write 2)
|
|
566
|
+
(defconstant fd-write-bit 1)
|
|
567
|
+
(defconstant fd-oob 4)
|
|
568
|
+
(defconstant fd-oob-bit 2)
|
|
569
|
+
(defconstant fd-accept 8)
|
|
570
|
+
(defconstant fd-accept-bit 3)
|
|
571
|
+
(defconstant fd-connect 16)
|
|
572
|
+
(defconstant fd-connect-bit 4)
|
|
573
|
+
(defconstant fd-close 32)
|
|
574
|
+
(defconstant fd-close-bit 5)
|
|
575
|
+
(defconstant fd-qos 64)
|
|
576
|
+
(defconstant fd-qos-bit 6)
|
|
577
|
+
(defconstant fd-group-qos 128)
|
|
578
|
+
(defconstant fd-group-qos-bit 7)
|
|
579
|
+
(defconstant fd-routing-interface 256)
|
|
580
|
+
(defconstant fd-routing-interface-bit 8)
|
|
581
|
+
(defconstant fd-address-list-change 512)
|
|
582
|
+
(defconstant fd-address-list-change-bit 9)
|
|
583
|
+
|
|
584
|
+
(defconstant fd-max-events 10)
|
|
585
|
+
|
|
586
|
+
(defconstant fionread 1074030207)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
;; Note:
|
|
590
|
+
;;
|
|
591
|
+
;; If special finalization has to occur for a given
|
|
592
|
+
;; system resource (handle), an associated object should
|
|
593
|
+
;; be created. A special cleanup action should be added
|
|
594
|
+
;; to the system and a special cleanup action should
|
|
595
|
+
;; be flagged on all objects created for resources like it
|
|
596
|
+
;;
|
|
597
|
+
;; We have 2 functions to do so:
|
|
598
|
+
;; * hcl:add-special-free-action (function-symbol)
|
|
599
|
+
;; * hcl:flag-special-free-action (object)
|
|
600
|
+
;;
|
|
601
|
+
;; Note that the special free action will be called on all
|
|
602
|
+
;; objects which have been flagged for special free, so be
|
|
603
|
+
;; sure to check for the right argument type!
|
|
604
|
+
|
|
605
|
+
(fli:define-foreign-type ws-socket () '(:unsigned :int))
|
|
606
|
+
(fli:define-foreign-type win32-handle () '(:unsigned :int))
|
|
607
|
+
(fli:define-c-struct wsa-network-events (network-events :long)
|
|
608
|
+
(error-code (:c-array :int 10)))
|
|
609
|
+
|
|
610
|
+
(fli:define-foreign-function (wsa-event-create "WSACreateEvent" :source)
|
|
611
|
+
()
|
|
612
|
+
:lambda-list nil
|
|
613
|
+
:result-type :int
|
|
614
|
+
:module "ws2_32")
|
|
615
|
+
|
|
616
|
+
(fli:define-foreign-function (wsa-event-close "WSACloseEvent" :source)
|
|
617
|
+
((event-object win32-handle))
|
|
618
|
+
:result-type :int
|
|
619
|
+
:module "ws2_32")
|
|
620
|
+
|
|
621
|
+
(fli:define-foreign-function (wsa-enum-network-events "WSAEnumNetworkEvents" :source)
|
|
622
|
+
((socket ws-socket)
|
|
623
|
+
(event-object win32-handle)
|
|
624
|
+
(network-events (:reference-return wsa-network-events)))
|
|
625
|
+
:result-type :int
|
|
626
|
+
:module "ws2_32")
|
|
627
|
+
|
|
628
|
+
(fli:define-foreign-function (wsa-event-select "WSAEventSelect" :source)
|
|
629
|
+
((socket ws-socket)
|
|
630
|
+
(event-object win32-handle)
|
|
631
|
+
(network-events :long))
|
|
632
|
+
:result-type :int
|
|
633
|
+
:module "ws2_32")
|
|
634
|
+
|
|
635
|
+
(fli:define-foreign-function (wsa-get-last-error "WSAGetLastError" :source)
|
|
636
|
+
()
|
|
637
|
+
:result-type :int
|
|
638
|
+
:module "ws2_32")
|
|
639
|
+
|
|
640
|
+
(fli:define-foreign-function (wsa-ioctlsocket "ioctlsocket" :source)
|
|
641
|
+
((socket :long) (cmd :long) (argp (:ptr :long)))
|
|
642
|
+
:result-type :int
|
|
643
|
+
:module "ws2_32")
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
;; The Windows system
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
;; Now that we have access to the system calls, this is the plan:
|
|
650
|
+
|
|
651
|
+
;; 1. Receive a wait-list with associated sockets to wait for
|
|
652
|
+
;; 2. Add all those sockets to an event handle
|
|
653
|
+
;; 3. Listen for an event on that handle (we have a LispWorks system:: internal for that)
|
|
654
|
+
;; 4. After listening, detect if there are errors
|
|
655
|
+
;; (this step is different from Unix, where we can have only one error)
|
|
656
|
+
;; 5. If so, raise one of them
|
|
657
|
+
;; 6. If not so, return the sockets which have input waiting for them
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
(defun maybe-wsa-error (rv &optional socket)
|
|
661
|
+
(unless (zerop rv)
|
|
662
|
+
(raise-usock-err (wsa-get-last-error) socket)))
|
|
663
|
+
|
|
664
|
+
(defun bytes-available-for-read (socket)
|
|
665
|
+
(fli:with-dynamic-foreign-objects ((int-ptr :long))
|
|
666
|
+
(let ((rv (wsa-ioctlsocket (os-socket-handle socket) fionread int-ptr)))
|
|
667
|
+
(if (= 0 rv)
|
|
668
|
+
(fli:dereference int-ptr)
|
|
669
|
+
0))))
|
|
670
|
+
|
|
671
|
+
(defun socket-ready-p (socket)
|
|
672
|
+
(if (typep socket 'stream-usocket)
|
|
673
|
+
(< 0 (bytes-available-for-read socket))
|
|
674
|
+
(%ready-p socket)))
|
|
675
|
+
|
|
676
|
+
(defun waiting-required (sockets)
|
|
677
|
+
(notany #'socket-ready-p sockets))
|
|
678
|
+
|
|
679
|
+
(defun wait-for-input-internal (wait-list &key timeout)
|
|
680
|
+
(when (waiting-required (wait-list-waiters wait-list))
|
|
681
|
+
(system:wait-for-single-object (wait-list-%wait wait-list)
|
|
682
|
+
"Waiting for socket activity" timeout))
|
|
683
|
+
(update-ready-and-state-slots (wait-list-waiters wait-list)))
|
|
684
|
+
|
|
685
|
+
(defun map-network-events (func network-events)
|
|
686
|
+
(let ((event-map (fli:foreign-slot-value network-events 'network-events))
|
|
687
|
+
(error-array (fli:foreign-slot-pointer network-events 'error-code)))
|
|
688
|
+
(unless (zerop event-map)
|
|
689
|
+
(dotimes (i fd-max-events)
|
|
690
|
+
(unless (zerop (ldb (byte 1 i) event-map)) ;;### could be faster with ash and logand?
|
|
691
|
+
(funcall func (fli:foreign-aref error-array i)))))))
|
|
692
|
+
|
|
693
|
+
(defun update-ready-and-state-slots (sockets)
|
|
694
|
+
(dolist (socket sockets)
|
|
695
|
+
(if (or (and (stream-usocket-p socket)
|
|
696
|
+
(listen (socket-stream socket)))
|
|
697
|
+
(%ready-p socket))
|
|
698
|
+
(setf (state socket) :READ)
|
|
699
|
+
(multiple-value-bind
|
|
700
|
+
(rv network-events)
|
|
701
|
+
(wsa-enum-network-events (os-socket-handle socket) 0 t)
|
|
702
|
+
(if (zerop rv)
|
|
703
|
+
(map-network-events #'(lambda (err-code)
|
|
704
|
+
(if (zerop err-code)
|
|
705
|
+
(setf (%ready-p socket) t
|
|
706
|
+
(state socket) :READ)
|
|
707
|
+
(raise-usock-err err-code socket)))
|
|
708
|
+
network-events)
|
|
709
|
+
(maybe-wsa-error rv socket))))))
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
;; The wait-list part
|
|
714
|
+
|
|
715
|
+
(defun free-wait-list (wl)
|
|
716
|
+
(when (wait-list-p wl)
|
|
717
|
+
(unless (null (wait-list-%wait wl))
|
|
718
|
+
(wsa-event-close (wait-list-%wait wl)))))
|
|
719
|
+
|
|
720
|
+
(eval-when (:load-toplevel :execute)
|
|
721
|
+
(hcl:add-special-free-action 'free-wait-list))
|
|
722
|
+
|
|
723
|
+
(defun %setup-wait-list (wait-list)
|
|
724
|
+
(hcl:flag-special-free-action wait-list)
|
|
725
|
+
(setf (wait-list-%wait wait-list) (wsa-event-create)))
|
|
726
|
+
|
|
727
|
+
(defun %add-waiter (wait-list waiter)
|
|
728
|
+
(let ((events (etypecase waiter
|
|
729
|
+
(stream-server-usocket (logior fd-connect fd-accept fd-close))
|
|
730
|
+
(stream-usocket (logior fd-connect fd-read fd-oob fd-close))
|
|
731
|
+
(datagram-usocket (logior fd-read)))))
|
|
732
|
+
(maybe-wsa-error
|
|
733
|
+
(wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) events)
|
|
734
|
+
waiter)))
|
|
735
|
+
|
|
736
|
+
(defun %remove-waiter (wait-list waiter)
|
|
737
|
+
(maybe-wsa-error
|
|
738
|
+
(wsa-event-select (os-socket-handle waiter) (wait-list-%wait wait-list) 0)
|
|
739
|
+
waiter))
|
|
740
|
+
|
|
741
|
+
) ; end of WIN32-block
|