Author: ehuelsmann
Date: Mon May 21 16:29:05 2007
New Revision: 250
Modified:
usocket/trunk/backend/armedbear.lisp
Log:
ABCL 'wait-for-input'. Victory\!
Modified: usocket/trunk/backend/armedbear.lisp
==============================================================================
--- usocket/trunk/backend/armedbear.lisp (original)
+++ usocket/trunk/backend/armedbear.lisp Mon May 21 16:29:05 2007
@@ -6,12 +6,32 @@
(in-package :usocket)
-(defmacro jmethod-call (instance (method &rest arg-spec) &rest args)
+(defmacro jstatic-call (class-name (method-name &rest arg-spec)
+ &rest args)
+ (let ((class-sym (gensym)))
+ `(let ((,class-sym ,class-name))
+ (java:jstatic
+ (java:jmethod ,class-sym ,method-name ,@arg-spec)
+ (java:jclass ,class-sym) ,@args))))
+
+(defmacro jmethod-call (instance-and-class (method &rest arg-spec) &rest args)
(let ((isym (gensym)))
- `(let* ((,isym ,instance)
- (class-name (java:jclass-name (java:jclass-of ,isym))))
- (java:jcall (java:jmethod class-name ,method ,@arg-spec)
- ,isym ,@args))))
+ (multiple-value-bind
+ (instance class-name)
+ (if (listp instance-and-class)
+ (values (first instance-and-class)
+ (second instance-and-class))
+ (values instance-and-class))
+ (when (null class-name)
+ (setf class-name `(java:jclass-name (java:jclass-of ,isym))))
+ `(let* ((,isym ,instance))
+ (java:jcall (java:jmethod ,class-name ,method ,@arg-spec)
+ ,isym ,@args)))))
+
+(defun jequals (x y)
+ (jmethod-call (x "java.lang.Object")
+ ("equals" "java.lang.Object")
+ y))
(defmacro jnew-call ((class &rest arg-spec) &rest args)
`(java:jnew (java:jconstructor ,class ,@arg-spec)
@@ -48,7 +68,10 @@
(sock-addr (jnew-call ("java.net.InetSocketAddress"
"java.lang.String" "int")
(host-to-hostname host) port))
- (sock (jnew-call ("java.net.ServerSocket"))))
+ (chan (jstatic-call "java.nio.channels.ServerSocketChannel" ("open")))
+ (sock (java:jcall
+ (java:jmethod "java.nio.channels.ServerSocketChannel"
+ "socket") chan)))
(when reuseaddress
(jmethod-call sock
("setReuseAddress" "boolean")
@@ -102,3 +125,153 @@
(defmethod get-peer-name ((usocket stream-usocket))
(values (get-peer-address usocket)
(get-peer-port usocket)))
+
+
+#|
+Pseudo code version of what we're trying to do:
+
+We're being called with 2 args:
+
+ - sockets (list)
+ - timeout (non-negative real)
+
+Selector := java.nio.channels.Selector.open()
+
+For all usockets
+ get the java socket
+ get its channel
+ register the channel with the selector
+ with ops (operations) OP_READ and OP_ACCEPT
+
+make the selector wait trunc(timeout*1000) miliseconds,
+ unless (null timeout), because then:
+ selectNow()
+
+retrieve the selectedKeys() set from the selector
+ unless select() returned 0 selected keys.
+
+for set-iterator.hasNextKey()
+ with that key
+ retrieve the channel
+ retrieve the channel's socket
+ add the retrieved socket to the list of ready sockets
+
+for all usockets
+ check if the associated java object
+ is in the list of ready sockets
+ it is? add it to the function result list
+
+close() the selector
+
+return the function result list.
+
+|#
+
+(defun jsocket-channel (jsocket)
+ (jmethod-call jsocket ("getChannel")))
+
+(defun jselkey-channel (jselectionkey)
+ (jmethod-call (jselectionkey "java.nio.channels.SelectionKey")
+ ("channel")))
+
+(defun op-read ()
+ (java:jfield (java:jclass "java.nio.channels.SelectionKey")
+ "OP_READ"))
+
+(defun op-accept ()
+ (java:jfield (java:jclass "java.nio.channels.SelectionKey")
+ "OP_ACCEPT"))
+
+(defun op-connect ()
+ (java:jfield (java:jclass "java.nio.channels.SelectionKey")
+ "OP_CONNECT"))
+
+(defun valid-ops (jchannel)
+ (jmethod-call (jchannel "java.nio.channels.SelectableChannel")
+ ("validOps")))
+
+(defun register (jchannel jselector ops)
+ (jmethod-call (jchannel "java.nio.channels.SelectableChannel")
+ ("register" "java.nio.channels.Selector" "int")
+ jselector ops))
+
+(defun toggle-blocking (jchannel mode)
+ (jmethod-call (jchannel "java.nio.channels.SelectableChannel")
+ ("configureBlocking" "boolean")
+ mode))
+
+(defun jselector-select (jselector timeout)
+ (let ((to (truncate (* (or timeout 0) 1000))))
+ (if (/= timeout 0)
+ (jmethod-call (jselector "java.nio.channels.Selector")
+ ("select" "long") to)
+ (jmethod-call (jselector "java.nio.channels.Selector")
+ ("selectNow")))))
+
+(defun jselector-selected-keys (jselector)
+ (jmethod-call (jselector "java.nio.channels.Selector")
+ ("selectedKeys")))
+
+(defun jset-iterator (jset)
+ (jmethod-call (jset "java.util.Set") ("iterator")))
+
+(defun jiterator-has-next (jiterator)
+ (jmethod-call (jiterator "java.util.Iterator") ("hasNext")))
+
+(defun jiterator-next (jiterator)
+ (jmethod-call (jiterator "java.util.Iterator") ("next")))
+
+(defun channel-class (jchannel)
+ (let ((valid-ops (valid-ops jchannel)))
+ (cond ((/= 0 (logand valid-ops (op-connect)))
+ "java.nio.channels.SocketChannel")
+ ((/= 0 (logand valid-ops (op-accept)))
+ "java.nio.channels.ServerSocketChannel")
+ (t
+ "java.nio.channels.DatagramChannel"))))
+
+(defun wait-for-input-internal (sockets &key timeout)
+ (let* ((ops (logior (op-read) (op-accept)))
+ (selector (jstatic-call "java.nio.channels.Selector" ("open")))
+ (channels
+ (mapcar #'(lambda (s)
+ (jsocket-channel (socket s)))
+ sockets)))
+ (unwind-protect
+ (progn
+ (let ((jfalse (java:make-immediate-object nil :boolean)))
+ (dolist (channel channels)
+ (toggle-blocking channel jfalse)
+ (register channel selector (logand ops (valid-ops channel)))))
+ (let ((ready-count
+ (jselector-select selector timeout)))
+ (when (< 0 ready-count)
+ ;; we actually have work to do
+ (let* ((selkeys (jselector-selected-keys selector))
+ (selkey-iterator (jset-iterator selkeys))
+ ready-sockets)
+ (loop while (jiterator-has-next selkey-iterator)
+ do (let* ((key (jiterator-next selkey-iterator))
+ (chan (jselkey-channel key)))
+ (push (jmethod-call (chan (channel-class chan))
+ ("socket"))
+ ready-sockets)))
+ (print ready-sockets)
+ (print (remove-if #'(lambda (s)
+ (not (member (socket s) ready-sockets
+ :test #'jequals)))
+ sockets))))))
+ ;; cancel all Selector registrations
+ (let* ((keys (jmethod-call (selector "java.nio.channels.Selector")
+ ("keys")))
+ (iter (jset-iterator keys)))
+ (loop while (jiterator-has-next iter)
+ do (jmethod-call ((jiterator-next iter)
+ "java.nio.channels.SelectionKey")
+ ("cancel"))))
+ ;; close the selectorx
+ (jmethod-call (selector "java.nio.channels.Selector") ("close"))
+ ;; make all sockets blocking again.
+ (let ((jtrue (java:make-immediate-object t :boolean)))
+ (dolist (chan channels)
+ (toggle-blocking chan jtrue))))))