From ctian at common-lisp.net Sun Jan 3 07:29:08 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Sun, 03 Jan 2010 02:29:08 -0500 Subject: [usocket-cvs] r505 - usocket/trunk/backend Message-ID: Author: ctian Date: Sun Jan 3 02:29:07 2010 New Revision: 505 Log: Initial MCL backend support from Terje Norderhaug Added: usocket/trunk/backend/mcl.lisp (contents, props changed) Added: usocket/trunk/backend/mcl.lisp ============================================================================== --- (empty file) +++ usocket/trunk/backend/mcl.lisp Sun Jan 3 02:29:07 2010 @@ -0,0 +1,258 @@ +;; MCL backend for USOCKET 0.4.1 +;; Terje Norderhaug , January 1, 2009 + +(in-package :usocket) + +(defun handle-condition (condition &optional socket) + ; incomplete, needs to handle additional conditions + (flet ((raise-error (&optional socket-condition) + (error (or socket-condition 'unknown-error) :socket socket :real-error condition))) + (typecase condition + (ccl:host-stopped-responding + (raise-error 'host-down-error)) + (ccl:host-not-responding + (raise-error 'host-unreachable-error)) + (ccl:connection-reset + (raise-error 'connection-reset-error)) + (ccl:connection-timed-out + (raise-error 'timeout-error)) + (ccl:opentransport-protocol-error + (raise-error ''protocol-not-supported-error)) + (otherwise + (raise-error))))) + +(defun socket-connect (host port &key (element-type 'character) timeout deadline nodelay + local-host local-port) + (let* ((socket + (make-instance 'active-socket + :remote-host (when host (host-to-hostname host)) + :remote-port port + :local-host (when local-host (host-to-hostname local-host)) + :local-port local-port + :deadline deadline + :nodelay nodelay + :connect-timeout (and timeout (round (* timeout 60))) + :element-type element-type)) + (stream (socket-open-stream socket))) + (make-stream-socket :socket socket :stream stream))) + +(defun socket-listen (host port + &key reuseaddress + (reuse-address nil reuse-address-supplied-p) + (backlog 5) + (element-type 'character)) + (declare (ignore reuseaddress reuse-address-supplied-p)) + (let ((socket (make-instance 'passive-socket + :local-port port + :local-host host + :reuse-address reuse-address + :backlog backlog))) + (make-stream-server-socket socket :element-type element-type))) + +(defmethod socket-accept ((usocket stream-server-usocket) &key element-type) + (let* ((socket (socket usocket)) + (stream (socket-accept socket :element-type element-type))) + (make-stream-socket :socket socket :stream stream))) + +(defmethod socket-close ((usocket usocket)) + (with-mapped-conditions (usocket) + (socket-close (socket usocket)))) + +(defmethod ccl::stream-close ((usocket usocket)) + (socket-close usocket)) + +(defun get-hosts-by-name (name) + (with-mapped-conditions () + (list (hbo-to-vector-quad (ccl::get-host-address + (host-to-hostname name)))))) + +(defun get-host-by-address (address) + (with-mapped-conditions () + (ccl::inet-host-name (host-to-hbo address)))) + +(defmethod get-local-name ((usocket usocket)) + (values (get-local-address usocket) + (get-local-port usocket))) + +(defmethod get-peer-name ((usocket stream-usocket)) + (values (get-peer-address usocket) + (get-peer-port usocket))) + +(defmethod get-local-address ((usocket usocket)) + (hbo-to-vector-quad (ccl::get-host-address (or (local-host (socket usocket)) "")))) + +(defmethod get-local-port ((usocket usocket)) + (local-port (socket usocket))) + +(defmethod get-peer-address ((usocket stream-usocket)) + (hbo-to-vector-quad (ccl::get-host-address (remote-host (socket usocket))))) + +(defmethod get-peer-port ((usocket stream-usocket)) + (remote-port (socket usocket))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; BASIC MCL SOCKET IMPLEMENTATION + +(require :opentransport) + +(defclass socket () + ((local-port :reader local-port :initarg :local-port) + (local-host :reader local-host :initarg :local-host) + (element-type :reader element-type :initform 'ccl::base-character :initarg :element-type))) + +(defclass active-socket (socket) + ((remote-host :reader remote-host :initarg :remote-host) + (remote-port :reader remote-port :initarg :remote-port) + (deadline :initarg :deadline) + (nodelay :initarg :nodelay) + (connect-timeout :reader connect-timeout :initform NIL :initarg :connect-timeout + :type (or null fixnum) :documentation "ticks (60th of a second)"))) + +(defmethod socket-open-stream ((socket active-socket)) + (ccl::open-tcp-stream (or (remote-host socket)(ccl::local-interface-ip-address)) (remote-port socket) + :element-type (if (subtypep (element-type socket) 'character) 'ccl::base-character 'unsigned-byte) + :connect-timeout (connect-timeout socket))) + +(defmethod socket-close ((socket active-socket)) + NIL) + +(defclass passive-socket (socket) + ((streams :accessor socket-streams :type list :initform NIL + :documentation "Circular list of streams with first element the next to open") + (reuse-address :reader reuse-address :initarg :reuse-address))) + +(defmethod initialize-instance :after ((socket passive-socket) &key backlog) + (loop repeat backlog + collect (socket-open-listener socket) into streams + finally (setf (socket-streams socket) + (cdr (rplacd (last streams) streams)))) + (when (zerop (local-port socket)) + (setf (slot-value socket 'local-port) + (or (ccl::process-wait-with-timeout "binding port" (* 10 60) + #'ccl::stream-local-port (car (socket-streams socket))) + (error "timeout"))))) + +(defmethod socket-accept ((socket passive-socket) &key element-type) + (flet ((connection-established-p (stream) + (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil)) + (let ((state (ccl::opentransport-stream-connection-state stream))) + (not (eq :unbnd state)))))) + (with-mapped-conditions () + (let* ((new (socket-open-listener socket element-type)) + (connection (car (socket-streams socket)))) + (assert connection) + (rplaca (socket-streams socket) new) + (setf (socket-streams socket) + (cdr (socket-streams socket))) + (ccl::process-wait "Socket Accept" #'connection-established-p connection) ; expensive polling... + connection)))) + +(defmethod socket-close ((socket passive-socket)) + (loop + with streams = (socket-streams socket) + for (stream tail) on streams + do (close stream :abort T) + until (eq tail streams) + finally (setf (socket-streams socket) NIL))) + +(defmethod socket-open-listener (socket &optional element-type) + ; see http://code.google.com/p/mcl/issues/detail?id=28 + (let* ((ccl::*passive-interface-address* (local-host socket)) + (new (ccl::open-tcp-stream NIL (or (local-port socket) #$kOTAnyInetAddress) + :reuse-local-port-p (reuse-address socket) + :element-type (if (subtypep (or element-type (element-type socket)) + 'character) + 'ccl::base-character + 'unsigned-byte)))) + (declare (special ccl::*passive-interface-address*)) + new)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +#| TEST (from test-usocket.lisp) + + +(defparameter +non-existing-host+ "192.168.1.1") +(defparameter +unused-local-port+ 15213) +(defparameter *soc1* (usocket::make-stream-socket :socket :my-socket + :stream :my-stream)) +(defparameter +common-lisp-net+ #(208 72 159 207)) ;; common-lisp.net IP + + +(usocket:socket *soc1*) + +(usocket:socket-connect "127.0.0.0" +unused-local-port+) + +(usocket:socket-connect #(127 0 0 0) +unused-local-port+) + +(usocket:socket-connect 2130706432 +unused-local-port+) + + (let ((sock (usocket:socket-connect "common-lisp.net" 80))) + (unwind-protect + (typep sock 'usocket:usocket) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) + (unwind-protect + (typep sock 'usocket:usocket) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect (usocket::host-byte-order +common-lisp-net+) 80))) + (unwind-protect + (typep sock 'usocket:usocket) + (usocket:socket-close sock))) + +(let ((sock (usocket:socket-connect "common-lisp.net" 80))) + (unwind-protect + (progn + (format (usocket:socket-stream sock) + "GET / HTTP/1.0~A~A~A~A" + #\Return #\Linefeed #\Return #\Linefeed) + (force-output (usocket:socket-stream sock)) + (read-line (usocket:socket-stream sock))) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) + (unwind-protect + (usocket::get-peer-address sock) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) + (unwind-protect + (usocket::get-peer-port sock) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) + (unwind-protect + (usocket::get-peer-name sock) + (usocket:socket-close sock))) + + (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) + (unwind-protect + (usocket::get-local-address sock) + (usocket:socket-close sock))) + +|# + + +#| + +(defun socket-server (host port) + (let ((socket (socket-listen host port))) + (unwind-protect + (loop + (with-open-stream (stream (socket-stream (socket-accept socket))) + (ccl::telnet-write-line stream "~A" + (reverse (ccl::telnet-read-line stream))) + (ccl::force-output stream))) + (close socket)))) + +(ccl::process-run-function "Socket Server" #'socket-server NIL 4088) + +(let* ((sock (socket-connect nil 4088)) + (stream (usocket:socket-stream sock))) + (assert (streamp stream)) + (ccl::telnet-write-line stream "hello ~A" (random 10)) + (ccl::force-output stream) + (ccl::telnet-read-line stream)) + +|# From ctian at common-lisp.net Sun Jan 3 07:37:23 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Sun, 03 Jan 2010 02:37:23 -0500 Subject: [usocket-cvs] r506 - usocket/trunk Message-ID: Author: ctian Date: Sun Jan 3 02:37:22 2010 New Revision: 506 Log: Add MCL support into usocket.asd Modified: usocket/trunk/usocket.asd Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Sun Jan 3 02:37:22 2010 @@ -40,4 +40,6 @@ :depends-on ("condition")) #+armedbear (:file "armedbear" :pathname "backend/armedbear" :depends-on ("condition")) + #+mcl (:file "mcl" :pathname "backend/armedbear" + :depends-on ("condition")) )) From ctian at common-lisp.net Sun Jan 3 08:54:59 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Sun, 03 Jan 2010 03:54:59 -0500 Subject: [usocket-cvs] r507 - in usocket/trunk: . backend Message-ID: Author: ctian Date: Sun Jan 3 03:54:58 2010 New Revision: 507 Log: ASDF system definition changes for MCL, also make it smaller Modified: usocket/trunk/backend/mcl.lisp usocket/trunk/usocket.asd Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Sun Jan 3 03:54:58 2010 @@ -3,6 +3,9 @@ (in-package :usocket) +(eval-when (:compile-toplevel :load-toplevel :execute) + (require :opentransport)) + (defun handle-condition (condition &optional socket) ; incomplete, needs to handle additional conditions (flet ((raise-error (&optional socket-condition) @@ -93,8 +96,6 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BASIC MCL SOCKET IMPLEMENTATION -(require :opentransport) - (defclass socket () ((local-port :reader local-port :initarg :local-port) (local-host :reader local-host :initarg :local-host) Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Sun Jan 3 03:54:58 2010 @@ -24,22 +24,14 @@ :depends-on ("package")) (:file "condition" :depends-on ("usocket")) - #+clisp (:file "clisp" :pathname "backend/clisp" - :depends-on ("condition")) - #+cmu (:file "cmucl" :pathname "backend/cmucl" - :depends-on ("condition")) - #+scl (:file "scl" :pathname "backend/scl" - :depends-on ("condition")) - #+(or sbcl ecl) (:file "sbcl" :pathname "backend/sbcl" - :depends-on ("condition")) - #+lispworks (:file "lispworks" :pathname "backend/lispworks" - :depends-on ("condition")) - #+openmcl (:file "openmcl" :pathname "backend/openmcl" - :depends-on ("condition")) - #+allegro (:file "allegro" :pathname "backend/allegro" - :depends-on ("condition")) - #+armedbear (:file "armedbear" :pathname "backend/armedbear" - :depends-on ("condition")) - #+mcl (:file "mcl" :pathname "backend/armedbear" - :depends-on ("condition")) - )) + (:module "backend" + :depends-on ("condition") + :components (#+clisp (:file "clisp") + #+cmu (:file "cmucl") + #+scl (:file "scl") + #+(or sbcl ecl) (:file "sbcl") + #+lispworks (:file "lispworks") + #+mcl (:file "mcl") + #+openmcl (:file "openmcl") + #+allegro (:file "allegro") + #+armedbear (:file "armedbear"))))) From ctian at common-lisp.net Mon Jan 4 07:16:11 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Mon, 04 Jan 2010 02:16:11 -0500 Subject: [usocket-cvs] r508 - in usocket/trunk: . backend doc vendor Message-ID: Author: ctian Date: Mon Jan 4 02:16:10 2010 New Revision: 508 Log: Initial import of MCL's wait-for-input implementation, submit by Terje Norderhaug Added: usocket/trunk/vendor/ usocket/trunk/vendor/kqueue.lisp (contents, props changed) Modified: usocket/trunk/backend/mcl.lisp usocket/trunk/doc/backends.txt usocket/trunk/usocket.asd Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Mon Jan 4 02:16:10 2010 @@ -168,6 +168,59 @@ (declare (special ccl::*passive-interface-address*)) new)) + +(defun wait-for-input-internal (wait-list &key timeout &aux result) + (macrolet ((when-io-buffer-lock-grabbed ((lock &optional multiple-value-p) &body body) + "Evaluates the body if and only if the lock is successfully grabbed" + ;; like with-io-buffer-lock-grabbed but returns immediately instead of polling the lock + (let ((needs-unlocking-p (gensym)) + (lock-var (gensym))) + `(let* ((,lock-var ,lock) + (ccl::*grabbed-io-buffer-locks* (cons ,lock-var ccl::*grabbed-io-buffer-locks*)) + (,needs-unlocking-p (needs-unlocking-p ,lock-var))) + (declare (dynamic-extent ccl::*grabbed-io-buffer-locks*)) + (when ,needs-unlocking-p + (,(if multiple-value-p 'multiple-value-prog1 'prog1) + (progn , at body) + (ccl::%release-io-buffer-lock ,lock-var))))))) + (labels ((needs-unlocking-p (lock) + (declare (type ccl::lock lock)) + ;; crucial - clears bogus lock.value as in grab-io-buffer-lock-out-of-line: + (ccl::%io-buffer-lock-really-grabbed-p lock) + (ccl:store-conditional lock nil ccl:*current-process*)) + (input-available (stream) + "similar to stream-listen on buffered-input-stream-mixin but without waiting for lock" + (let ((io-buffer (ccl::stream-io-buffer stream))) + (or (not (eql 0 (ccl::io-buffer-incount io-buffer))) + (ccl::io-buffer-untyi-char io-buffer) + (locally (declare (optimize (speed 3) (safety 0))) + (when-io-buffer-lock-grabbed ((ccl::io-buffer-lock io-buffer)) + (funcall (ccl::io-buffer-listen-function io-buffer) stream io-buffer)))))) + (ready-sockets (sockets) + (dolist (sock sockets result) + (when (input-available (socket-stream sock)) + (push sock result))))) + (with-mapped-conditions () + (ccl:process-wait-with-timeout + "socket input" + (when timeout (truncate (* timeout 60))) + #'ready-sockets + (wait-list-waiters wait-list))) + (nreverse result)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +#| Test for wait-for-input +(let* ((sock1 (usocket:socket-connect "in-progress.com" 80)) + (sock2 (usocket:socket-connect "common-lisp.net" 80)) + (sockets (list sock1 sock2))) + (dolist (sock sockets) + (format (usocket:socket-stream sock) + "GET / HTTP/1.0~A~A~A~A" + #\Return #\Linefeed #\Return #\Linefeed) + (force-output (usocket:socket-stream sock))) + (wait-for-input sockets :timeout 5000)) +|# + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #| TEST (from test-usocket.lisp) Modified: usocket/trunk/doc/backends.txt ============================================================================== --- usocket/trunk/doc/backends.txt (original) +++ usocket/trunk/doc/backends.txt Mon Jan 4 02:16:10 2010 @@ -15,6 +15,7 @@ - get-hosts-by-name [ optional ] - get-host-by-address [ optional ] + - wait-for-input-internal (new in 0.4.x) Methods: Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Mon Jan 4 02:16:10 2010 @@ -24,8 +24,10 @@ :depends-on ("package")) (:file "condition" :depends-on ("usocket")) + (:module "vendor" + :components (#+mcl (:file "kqueue"))) (:module "backend" - :depends-on ("condition") + :depends-on ("condition" "vendor") :components (#+clisp (:file "clisp") #+cmu (:file "cmucl") #+scl (:file "scl") Added: usocket/trunk/vendor/kqueue.lisp ============================================================================== --- (empty file) +++ usocket/trunk/vendor/kqueue.lisp Mon Jan 4 02:16:10 2010 @@ -0,0 +1 @@ +;;;-*-Mode: LISP; Package: CCL -*- ;; ;; KQUEUE.LISP ;; ;; KQUEUE - BSD kernel event notification mechanism support for Common LISP. ;; Copyright (C) 2007 Terje Norderhaug ;; Released under LGPL - see . ;; Alternative licensing available upon request. ;; ;; DISCLAIMER: The user of this module should understand that executing code is a potentially hazardous ;; activity, and that many dangers and obstacles, marked or unmarked, may exist within this code. ;; As a condition of your use of the module, you assume all risk of personal injury, death, or property ;; loss, and all other bad things that may happen, even if caused by negligence, ignorance or stupidity. ;; The author is is no way responsible, and besides, does not have "deep pockets" nor any spare change. ;; ;; Version: 0.20 alpha (July 26, 2009) - subject to major revisions, so consider yourself warned. ;; Tested with Macintosh Common LISP 5.1 and 5.2, but is intended to be platform and system independent in the future. ;; ;; Email feedback and improvements to . ;; Updated versions will be available from . ;; ;; RELATED IMPLEMENTATIONS ;; There is another kevent.lisp for other platforms by Risto Laakso (merge?). ;; Also a Scheme kevent.ss by Jose Antonio Ortega. ;; ;; SEE ALSO: ;; http://people.freebsd.org/~jlemon/papers/kqueue.pdf ;; http://developer.apple.com/samplecode/FileNotification/index.html ;; The Man page for kqueue() or kevent(). ;; PyKQueue - Python OO interface to KQueue. ;; LibEvent - an event notification library in C by Niels Provos. ;; Liboop - another abstract library in C on top of kevent or other kernel notification. #| HISTORY: 2007-Oct-18 terje version 0.1 released on the Info-MCL mailing list. 2008-Aug-21 terje load-framework-bundle is not needed under MCL 5.2 2008-Aug-21 terje rename get-addr to lookup-function-in-bundle (only for pre MCL 5.2) 2009-Jul-19 terje uses kevent-error condition and strerror. 2009-Jul-24 terje reports errors unless nil-if-not-found in lookup-function-in-bundle. 2009-Jul-24 terje kevent :variant for C's intptr_t type for 64bit (and osx 10.5) compatibility. 2009-Jul-25 terje 64bit support, dynamically determined for PPC. Kudos to Glen Foy for helping out. 2009-Jul-25 terje make-kevent function. |# #| IMPLEMENTATION NOTES: kevents are copied into and from the kernel, so the records don't have to be kept in the app! kevents does not work in OSX before 10.3. *kevent-record* has to be explcitly set to :kevent64 to work on 64bit intel macs. Consider using sysctlbyname() to test for 64bit, combining hw.cpu64bit_capable, hw.optional.x86_64 and hw.optional.64bitops |# (in-package :ccl) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #-ccl-5.2 ; has been added to MCL 5.2 (defmethod load-framework-bundle ((framework-name string) &key (load-executable t)) ;; FRAMWORK CALL FUNCTIONALITY FROM BSD.LISP ;; (C) 2003 Brendan Burns ;; Released under LGPL. (with-cfstrs ((framework framework-name)) (let ((err 0) (baseURL nil) (bundleURL nil) (result nil)) (rlet ((folder :fsref)) ;; Find the folder holding the bundle (setf err (#_FSFindFolder #$kOnAppropriateDisk #$kFrameworksFolderType t folder)) ;; if everything's cool, make a URL for it (when (zerop err) (setf baseURL (#_CFURLCreateFromFSRef (%null-ptr) folder)) (if (%null-ptr-p baseURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, make a URL for the bundle (when (zerop err) (setf bundleURL (#_CFURLCreateCopyAppendingPathComponent (%null-ptr) baseURL framework nil)) (if (%null-ptr-p bundleURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, load it (when (zerop err) (setf result (#_CFBundleCreate (%null-ptr) bundleURL)) (if (%null-ptr-p result) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, and the user wants it loaded, load it (when (and load-executable (zerop err)) (if (not (#_CFBundleLoadExecutable result)) (setf err #$coreFoundationUnknownErr))) ;; if there's an error, but we've got a pointer, free it and clear result (when (and (not (zerop err)) (not (%null-ptr-p result))) (#_CFRelease result) (setf result nil)) ;; free the URLs if there non-null (when (not (%null-ptr-p bundleURL)) (#_CFRelease bundleURL)) (when (not (%null-ptr-p baseURL)) (#_CFRelease baseURL)) ;; return pointer + error value (values result err))))) #+ignore (defun get-addr (bundle name) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle name))) (rlet ((buf :long)) (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))) #-ccl-5.2 (defun lookup-function-in-bundle (name bundle &optional nil-if-not-found) (with-cfstrs ((str name)) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle str))) (if (%null-ptr-p addr) (unless nil-if-not-found (error "Couldn't resolve address of foreign function ~s" name)) (rlet ((buf :long)) ;; mcl 5.2 uses %fixnum-from-macptr here (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Convenient way to declare BSD system calls #+ignore (defparameter *system-bundle* #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle)) (defmacro declare-bundle-ff (name name-string &rest arglist &aux (fn (gensym (format nil "ff_~A_" (string name))))) ;; Is there an existing define-trap like macro for this? or could one be modified for use with bundles? `(progn (defloadvar ,fn (let* ((bundle #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle))) (lookup-function-in-bundle ,name-string bundle))) ,(let ((args (do ((arglist arglist (cddr arglist)) (result)) ((not (cdr arglist)) (nreverse result)) (push (second arglist) result)))) `(defun ,name ,args (ppc-ff-call ,fn , at arglist))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (declare-bundle-ff %system-kqueue "kqueue" :signed-fullword) ;; returns a file descriptor no! (defun system-kqueue () (let ((kq (%system-kqueue))) (if (= kq -1) (ecase (%system-errno) (12 (error "The kernel failed to allocate enough memory for the kernel queue")) ; ENOMEM (24 (error "The per-process descriptor table is full")) ; EMFILE (23 (error "The system file table is full"))) ; ENFILE kq))) (declare-bundle-ff %system-kevent "kevent" :unsigned-fullword kq :address ke :unsigned-fullword nke :address ko :unsigned-fullword nko :address timeout :signed-fullword) (declare-bundle-ff %system-open "open" :address name :unsigned-fullword mode :unsigned-fullword arg :signed-fullword) (declare-bundle-ff %system-close "close" :unsigned-fullword fd :signed-fullword) (declare-bundle-ff %system-errno* "__error" :signed-fullword) (declare-bundle-ff %system-strerror "strerror" :signed-fullword errno :address) (defun %system-errno () (%get-fixnum (%int-to-ptr (%system-errno*)))) ; (%system-errno) (defconstant $O-EVTONLY #x8000) ; (defconstant $O-NONBLOCK #x800 "Non blocking mode") (defun system-open (posix-namestring) "Low level open function, as in C, returns an fd number" (with-cstrs ((name posix-namestring)) (%system-open name $O-EVTONLY 0))) (defun system-close (fd) (%system-close fd)) (defrecord timespec (sec :unsigned-long) (usec :unsigned-long)) (defVar *kevent-record* nil) (def-ccl-pointers determine-64bit-kevents () (setf *kevent-record* (if (ccl::gestalt #$gestaltPowerPCProcessorFeatures #+ccl-5.2 #$gestaltPowerPCHas64BitSupport #-ccl-5.2 6) :kevent32 :kevent64))) (defrecord :kevent32 (ident :unsigned-long) ; uintptr_t (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (data :long) ; intptr_t (udata :pointer)) (defrecord :kevent64 (:variant ; uintptr_t ((ident64 :uint64)) ((ident :unsigned-long))) (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (:variant ; intptr_t ((data64 :sint64)) ((data :long))) (:variant ; RMCL :pointer is 32bit ((udata64 :uint64)) ((udata :pointer)))) (defun make-kevent (&key (ident 0) (filter 0) (flags 0) (fflags 0) (data 0) (udata *null-ptr*)) (ecase *kevent-record* (:kevent64 (make-record kevent64 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)) (:kevent32 (make-record kevent32 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)))) (defun kevent-rref (ke field) (ecase *kevent-record* (:kevent32 (ecase field (:ident (rref ke :kevent32.ident)) (:filter (rref ke :kevent32.filter)) (:flags (rref ke :kevent32.flags)) (:fflags (rref ke :kevent32.fflags)) (:data (rref ke :kevent32.data)) (:udata (rref ke :kevent32.udata)))) (:kevent64 (ecase field (:ident (rref ke :kevent64.ident)) (:filter (rref ke :kevent64.filter)) (:flags (rref ke :kevent64.flags)) (:fflags (rref ke :kevent64.fflags)) (:data (rref ke :kevent64.data)) (:udata (rref ke :kevent64.udata)))))) (defun kevent-filter (ke) (kevent-rref ke :filter)) (defun kevent-flags (ke) (kevent-rref ke :flags)) (defun kevent-data (ke) (kevent-rref ke :data)) ;; FILTER TYPES: (defconstant $kevent-read-filter -1 "Data available to read") (defconstant $kevent-write-filter -2 "Writing is possible") (defconstant $kevent-aio-filter -3 "AIO system call has been made") (defconstant $kevent-vnode-filter -4 "Event occured on a file descriptor") (defconstant $kevent-proc-filter -5 "Process performed one or more of the requested events") (defconstant $kevent-signal-filter -6 "Attempted to deliver a signal to a process") (defconstant $kevent-timer-filter -7 "Establishes an arbitrary timer") (defconstant $kevent-netdev-filter -8 "Event occured on a network device") (defconstant $kevent-filesystem-filter -9) ; FLAGS: (defconstant $kevent-add #x01) (defconstant $kevent-delete #x02) (defconstant $kevent-enable #x04) (defconstant $kevent-disable #x08) (defconstant $kevent-oneshot #x10) (defconstant $kevent-clear #x20) (defconstant $kevent-error #x4000) (defconstant $kevent-eof #x8000 "EV_EOF") ;; FFLAGS: (defconstant $kevent-file-delete #x01 "The file was unlinked from the file system") (defconstant $kevent-file-write #x02 "A write occurred on the file") (defconstant $kevent-file-extend #x04 "The file was extended") (defconstant $kevent-file-attrib #x08 "The file had its attributes changed") (defconstant $kevent-file-link #x10 "The link count on the file changed") (defconstant $kevent-file-rename #x20 "The file was renamed") (defconstant $kevent-file-revoke #x40 "Access to the file was revoked or the file system was unmounted") (defconstant $kevent-file-all (logior $kevent-file-delete $kevent-file-write $kevent-file-extend $kevent-file-attrib $kevent-file-link $kevent-file-rename $kevent-file-revoke)) (defconstant $kevent-net-linkup #x01 "Link is up") (defconstant $kevent-net-linkdown #x02 "Link is down") (defconstant $kevent-net-linkinvalid #x04 "Link state is invalid") (defconstant $kevent-net-added #x08 "IP adress added") (defconstant $kevent-net-deleted #x10 "IP adress deleted") (define-condition kevent-error (simple-error) ((errno :initform NIL :initarg :errno) (ko :initform nil :type (or null kevent) :initarg :ko) (syserr :initform (%system-errno))) (:report (lambda (c s) (with-slots (errno ko syserr) c (format s "kevent system call error ~A [~A]" errno syserr) (when errno (format s "(~A)" (%get-cstring (%system-strerror errno)))) (when ko (format s " for ") (let ((*standard-output* s)) (print-record ko *kevent-record*))))))) (defun %kevent (kq &optional ke ko (timeout 0)) (check-type kq integer) (rlet ((&timeout :timespec :sec timeout :usec 1)) (let ((num (with-timer ;; does not seem to make a difference... (%system-kevent kq (or ke (%null-ptr))(if ke 1 0)(or ko (%null-ptr))(if ko 1 0) &timeout)))) ; "If an error occurs while processing an element of the changelist and there ; is enough room in the eventlist, then the event will be placed in the eventlist with ; EV_ERROR set in flags and the system error in data." (when (and ko (plusp (logand $kevent-error (kevent-flags ko)))) (error 'kevent-error :errno (kevent-data ko) :ko ko)) ; "Otherwise, -1 will be returned, and errno will be set to indicate the error condition." (when (= num -1) ;; hack - opentransport provides the constants for the errors documented for the call (case (%system-errno) (0 (error "kevent system call failed with an unspecified error")) ;; should not happen! (13 (error "The process does not have permission to register a filter")) (14 (error "There was an error reading or writing the kevent structure")) ; EFAULT (9 (error "The specified descriptor is invalid")) ; EBADF (4 (error "A signal was delivered before the timeout expired and before any events were placed on the kqueue for return.")) ; EINTR (22 (error "The specified time limit or filter is invalid")) ; EINVAL (2 (error "The event could not be found to be modified or deleted")) ; ENOENT (12 (error "No memory was available to register the event")) ; ENOMEM (78 (error "The specified process to attach to does not exist"))) ; ESRCH ;; shouldn't get here... (errchk (%system-errno)) (error "error ~A" (%system-errno))) (unless (zerop num) (values ko num))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CLOS INTERFACE (defclass kqueue () ((kq :initform (system-kqueue) :documentation "file descriptor referencing the kqueue") (fds :initform NIL)) ;; ## better if kept on top level, perhaps as a hash table... (:documentation "A kernal event notification channel")) (defmethod initialize-instance :after ((q kqueue) &rest rest) (declare (ignore rest)) (terminate-when-unreachable q 'kqueue-close)) (defmethod kqueue-close ((q kqueue)) (with-slots (kq fds) q (when (or kq fds) ;; allow repeated close (system-close kq) (setf fds NIL) (setf kq NIL)))) (defmethod kqueue-poll ((q kqueue)) "Polls a kqueue for kevents" ;; may not have to be cleared, but just in case: (flet ((kqueue-poll2 (ko) (let ((result (with-slots (kq) q (without-interrupts (%kevent kq NIL ko))))) (when result (let ((type (kevent-filter result))) (ecase type (0 (values)) (#.$kevent-read-filter (values :read (kevent-rref result :ident) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-write-filter :write) (#.$kevent-aio-filter :aio) (#.$kevent-vnode-filter (values :vnode (cdr (assoc (kevent-rref result :ident) (slot-value q 'fds))) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-filesystem-filter :filesystem))))))) (ecase *kevent-record* (:kevent64 (rlet ((ko :kevent64 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko))) (:kevent32 (rlet ((ko :kevent32 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko)))))) (defmethod kqueue-subscribe ((q kqueue) &key ident filter (flags 0) (fflags 0) (data 0) (udata (%null-ptr))) (let ((ke (make-kevent :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata))) (with-slots (kq) q (without-interrupts (%kevent kq ke))))) (defmethod kqueue-vnode-subscribe ((q kqueue) pathname) "Makes the queue report an event when there is a change to a directory or file" (let* ((namestring (posix-namestring (full-pathname pathname))) (fd (system-open namestring))) (with-slots (fds) q (push (cons fd pathname) fds)) (kqueue-subscribe q :ident fd :filter $kevent-vnode-filter :flags (logior $kevent-add $kevent-clear) :fflags $kevent-file-all) namestring)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+test (defun kevent-d (pathname &optional (*standard-output* (fred))) "Report changes to a file or directory" (loop with kqueue = (make-instance 'kqueue) with sub = (kqueue-vnode-subscribe kqueue pathname) for i from 1 to 60 for result = (multiple-value-list (kqueue-poll kqueue)) unless (equal result '(NIL)) do (progn (format T "~A~%" result) (force-output)) ; do (process-allow-schedule) do (sleep 1) finally (write-line "Done") )) #| ; Report changes to this file in a fred window (save this document to see what happens): (process-run-function "kevent-d" #'kevent-d *loading-file-source-file* (fred)) ; Reports files added or removed from the directory of this file: (process-run-function "kevent-d" #'kevent-d (make-pathname :directory (pathname-directory *loading-file-source-file*)) (fred)) |# \ No newline at end of file From ctian at common-lisp.net Mon Jan 4 07:20:24 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Mon, 04 Jan 2010 02:20:24 -0500 Subject: [usocket-cvs] r509 - usocket/trunk/backend Message-ID: Author: ctian Date: Mon Jan 4 02:20:24 2010 New Revision: 509 Log: OpenMCL bugfix: socket-connect scales the timeout value incorrectly, thanks to James Anderson Modified: usocket/trunk/backend/openmcl.lisp Modified: usocket/trunk/backend/openmcl.lisp ============================================================================== --- usocket/trunk/backend/openmcl.lisp (original) +++ usocket/trunk/backend/openmcl.lisp Mon Jan 4 02:20:24 2010 @@ -85,8 +85,7 @@ :format (to-format element-type) :deadline deadline :nodelay nodelay - :connect-timeout (and timeout - (* timeout internal-time-units-per-second))))) + :connect-timeout timeout))) (openmcl-socket:socket-connect mcl-sock) (make-stream-socket :stream mcl-sock :socket mcl-sock)))) From ctian at common-lisp.net Mon Jan 4 07:49:40 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Mon, 04 Jan 2010 02:49:40 -0500 Subject: [usocket-cvs] r510 - in usocket/trunk: . backend test Message-ID: Author: ctian Date: Mon Jan 4 02:49:39 2010 New Revision: 510 Log: MCL and usocket-test fixes from James Anderson Added: usocket/trunk/usocket-test.asd Removed: usocket/trunk/test/usocket-test.asd Modified: usocket/trunk/backend/mcl.lisp usocket/trunk/test/test-usocket.lisp usocket/trunk/usocket.asd Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Mon Jan 4 02:49:39 2010 @@ -9,7 +9,9 @@ (defun handle-condition (condition &optional socket) ; incomplete, needs to handle additional conditions (flet ((raise-error (&optional socket-condition) - (error (or socket-condition 'unknown-error) :socket socket :real-error condition))) + (if socket-condition + (error socket-condition :socket socket) + (error 'unknown-error :socket socket :real-error condition)))) (typecase condition (ccl:host-stopped-responding (raise-error 'host-down-error)) @@ -20,24 +22,25 @@ (ccl:connection-timed-out (raise-error 'timeout-error)) (ccl:opentransport-protocol-error - (raise-error ''protocol-not-supported-error)) + (raise-error 'protocol-not-supported-error)) (otherwise (raise-error))))) (defun socket-connect (host port &key (element-type 'character) timeout deadline nodelay local-host local-port) - (let* ((socket - (make-instance 'active-socket - :remote-host (when host (host-to-hostname host)) - :remote-port port - :local-host (when local-host (host-to-hostname local-host)) - :local-port local-port - :deadline deadline - :nodelay nodelay - :connect-timeout (and timeout (round (* timeout 60))) - :element-type element-type)) - (stream (socket-open-stream socket))) - (make-stream-socket :socket socket :stream stream))) + (with-mapped-conditions () + (let* ((socket + (make-instance 'active-socket + :remote-host (when host (host-to-hostname host)) + :remote-port port + :local-host (when local-host (host-to-hostname local-host)) + :local-port local-port + :deadline deadline + :nodelay nodelay + :connect-timeout (and timeout (round (* timeout 60))) + :element-type element-type)) + (stream (socket-open-stream socket))) + (make-stream-socket :socket socket :stream stream)))) (defun socket-listen (host port &key reuseaddress @@ -45,16 +48,18 @@ (backlog 5) (element-type 'character)) (declare (ignore reuseaddress reuse-address-supplied-p)) - (let ((socket (make-instance 'passive-socket - :local-port port - :local-host host - :reuse-address reuse-address - :backlog backlog))) + (let ((socket (with-mapped-conditions () + (make-instance 'passive-socket + :local-port port + :local-host host + :reuse-address reuse-address + :backlog backlog)))) (make-stream-server-socket socket :element-type element-type))) (defmethod socket-accept ((usocket stream-server-usocket) &key element-type) (let* ((socket (socket usocket)) - (stream (socket-accept socket :element-type element-type))) + (stream (with-mapped-conditions (usocket) + (socket-accept socket :element-type element-type)))) (make-stream-socket :socket socket :stream stream))) (defmethod socket-close ((usocket usocket)) @@ -93,6 +98,17 @@ (defmethod get-peer-port ((usocket stream-usocket)) (remote-port (socket usocket))) + +(defun %setup-wait-list (wait-list) + (declare (ignore wait-list))) + +(defun %add-waiter (wait-list waiter) + (declare (ignore wait-list waiter))) + +(defun %remove-waiter (wait-list waiter) + (declare (ignore wait-list waiter))) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; BASIC MCL SOCKET IMPLEMENTATION Modified: usocket/trunk/test/test-usocket.lisp ============================================================================== --- usocket/trunk/test/test-usocket.lisp (original) +++ usocket/trunk/test/test-usocket.lisp Mon Jan 4 02:49:39 2010 @@ -7,12 +7,15 @@ ;; The parameters below may need adjustments to match the system ;; the tests are run on. -(defparameter +non-existing-host+ "192.168.1.1") +(defparameter +non-existing-host+ "192.168.1.199") (defparameter +unused-local-port+ 15213) (defparameter *soc1* (usocket::make-stream-socket :socket :my-socket :stream :my-stream)) (eval-when (:compile-toplevel :load-toplevel :execute) - (defparameter +common-lisp-net+ #(80 68 86 115))) ;; common-lisp.net IP + (defparameter +local-ip+ #(192 168 1 25)) + (defparameter +common-lisp-net+ + #+ignore #(80 68 86 115) ;; common-lisp.net IP (not valid as of 2010-01-03 + (first (usocket::get-hosts-by-name "common-lisp.net")))) (defmacro with-caught-conditions ((expect throw) &body body) `(catch 'caught-error @@ -48,29 +51,29 @@ (deftest socket-no-connect.1 (with-caught-conditions ('usocket:socket-error nil) - (usocket:socket-connect "127.0.0.0" +unused-local-port+) + (usocket:socket-connect "127.0.0.0" +unused-local-port+ :timeout 0) t) nil) (deftest socket-no-connect.2 (with-caught-conditions ('usocket:socket-error nil) - (usocket:socket-connect #(127 0 0 0) +unused-local-port+) + (usocket:socket-connect #(127 0 0 0) +unused-local-port+ :timeout 0) t) nil) (deftest socket-no-connect.3 (with-caught-conditions ('usocket:socket-error nil) - (usocket:socket-connect 2130706432 +unused-local-port+) ;; == #(127 0 0 0) + (usocket:socket-connect 2130706432 +unused-local-port+ :timeout 0) ;; == #(127 0 0 0) t) nil) (deftest socket-failure.1 - (with-caught-conditions (#-(or cmu lispworks armedbear openmcl) + (with-caught-conditions (#-(or cmu lispworks armedbear openmcl mcl) 'usocket:network-unreachable-error #+(or cmu lispworks armedbear) 'usocket:unknown-error - #+openmcl + #+(or openmcl mcl) 'usocket:timeout-error nil) - (usocket:socket-connect 2130706432 +unused-local-port+) ;; == #(127 0 0 0) + (usocket:socket-connect 2130706432 +unused-local-port+ :timeout 0) ;; == #(127 0 0 0) :unreach) nil) (deftest socket-failure.2 @@ -78,12 +81,12 @@ 'usocket:unknown-error #+cmu 'usocket:network-unreachable-error - #+openmcl + #+(or openmcl mcl) 'usocket:timeout-error - #-(or lispworks armedbear cmu openmcl) + #-(or lispworks armedbear cmu openmcl mcl) 'usocket:host-unreachable-error nil) - (usocket:socket-connect +non-existing-host+ 80) ;; 80 = just a port + (usocket:socket-connect +non-existing-host+ 80 :timeout 0) ;; 80 = just a port :unreach) nil) @@ -94,21 +97,21 @@ (with-caught-conditions (nil nil) (let ((sock (usocket:socket-connect "common-lisp.net" 80))) (unwind-protect - (typep sock 'usocket:usocket) + (when (typep sock 'usocket:usocket) t) (usocket:socket-close sock)))) t) (deftest socket-connect.2 (with-caught-conditions (nil nil) (let ((sock (usocket:socket-connect +common-lisp-net+ 80))) (unwind-protect - (typep sock 'usocket:usocket) + (when (typep sock 'usocket:usocket) t) (usocket:socket-close sock)))) t) (deftest socket-connect.3 (with-caught-conditions (nil nil) (let ((sock (usocket:socket-connect (usocket::host-byte-order +common-lisp-net+) 80))) (unwind-protect - (typep sock 'usocket:usocket) + (when (typep sock 'usocket:usocket) t) (usocket:socket-close sock)))) t) @@ -119,13 +122,13 @@ (unwind-protect (progn (format (usocket:socket-stream sock) - "GET / HTTP/1.0~A~A~A~A" - #\Return #\Newline #\Return #\Newline) + "GET / HTTP/1.0~c~c~c~c" + #\Return #\linefeed #\Return #\linefeed) (force-output (usocket:socket-stream sock)) (read-line (usocket:socket-stream sock))) (usocket:socket-close sock)))) - #+clisp "HTTP/1.1 200 OK" - #-clisp #.(format nil "HTTP/1.1 200 OK~A" #\Return) nil) + #+(or mcl clisp) "HTTP/1.1 200 OK" + #-(or clisp mcl) #.(format nil "HTTP/1.1 200 OK~A" #\Return) nil) (deftest socket-name.1 (with-caught-conditions (nil nil) @@ -154,8 +157,10 @@ (unwind-protect (usocket::get-local-address sock) (usocket:socket-close sock)))) - #(192 168 1 65)) + #.+local-ip+) (defun run-usocket-tests () (do-tests)) + +;;; (usoct::run-usocket-tests ) \ No newline at end of file Added: usocket/trunk/usocket-test.asd ============================================================================== --- (empty file) +++ usocket/trunk/usocket-test.asd Mon Jan 4 02:49:39 2010 @@ -0,0 +1,26 @@ +;;;; -*- Mode: Lisp -*- +;;;; $Id: usocket-test.asd 46 2006-02-06 20:50:07Z ehuelsmann $ +;;;; $URL: svn+ssh://common-lisp.net/project/usocket/svn/usocket/trunk/test/usocket-test.asd $ + +;;;; See the LICENSE file for licensing information. + +(in-package :cl-user) + +(unless (find-package ':usocket-system) + (make-package ':usocket-system + :use '(:cl :asdf))) + +(in-package :usocket-system) + +(defsystem usocket-test + :name "usocket test" + :author "Erik Enge" + :version "0.1.0" + :licence "MIT" + :description "Tests for usocket" + :depends-on (:usocket + :rt) + :components ((:module "test" + :components ((:file "package") + (:file "test-usocket" + :depends-on ("package")))))) Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Mon Jan 4 02:49:39 2010 @@ -1,4 +1,4 @@ - +;;;; -*- Mode: Lisp -*- ;;;; $Id$ ;;;; $URL$ @@ -11,13 +11,17 @@ (in-package #:usocket-system) +(pushnew :split-sequence-deprecated *features*) + (defsystem usocket :name "usocket" :author "Erik Enge & Erik Huelsmann" - :version "0.5.0-dev" + :version "0.5.0" :licence "MIT" :description "Universal socket library for Common Lisp" - :depends-on (:split-sequence + :depends-on (;; :split-sequence + ;; use the splie-sequence from cl-utilities + :cl-utilities #+sbcl :sb-bsd-sockets) :components ((:file "package") (:file "usocket" @@ -25,15 +29,15 @@ (:file "condition" :depends-on ("usocket")) (:module "vendor" - :components (#+mcl (:file "kqueue"))) + :components (#+mcl (:file "kqueue"))) (:module "backend" - :depends-on ("condition" "vendor") - :components (#+clisp (:file "clisp") - #+cmu (:file "cmucl") - #+scl (:file "scl") - #+(or sbcl ecl) (:file "sbcl") - #+lispworks (:file "lispworks") - #+mcl (:file "mcl") - #+openmcl (:file "openmcl") - #+allegro (:file "allegro") - #+armedbear (:file "armedbear"))))) + :depends-on ("condition" "vendor") + :components (#+clisp (:file "clisp") + #+cmu (:file "cmucl") + #+scl (:file "scl") + #+(or sbcl ecl) (:file "sbcl") + #+lispworks (:file "lispworks") + #+mcl (:file "mcl") + #+openmcl (:file "openmcl") + #+allegro (:file "allegro") + #+armedbear (:file "armedbear"))))) From ctian at common-lisp.net Mon Jan 4 08:06:22 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Mon, 04 Jan 2010 03:06:22 -0500 Subject: [usocket-cvs] r511 - in usocket/trunk: . backend doc notes test vendor Message-ID: Author: ctian Date: Mon Jan 4 03:06:20 2010 New Revision: 511 Log: Update ignore patterns Modified: usocket/trunk/ (props changed) usocket/trunk/backend/ (props changed) usocket/trunk/doc/ (props changed) usocket/trunk/notes/ (props changed) usocket/trunk/test/ (props changed) usocket/trunk/vendor/ (props changed) From ctian at common-lisp.net Wed Jan 6 01:23:51 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Tue, 05 Jan 2010 20:23:51 -0500 Subject: [usocket-cvs] r513 - usocket/trunk/backend Message-ID: Author: ctian Date: Tue Jan 5 20:23:50 2010 New Revision: 513 Log: Include MCL Issue 28. Modified: usocket/trunk/backend/mcl.lisp Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Tue Jan 5 20:23:50 2010 @@ -6,35 +6,45 @@ (eval-when (:compile-toplevel :load-toplevel :execute) (require :opentransport)) +;; MCL Issue 28: Passive TCP streams should be able to listen to the loopback interface +;; see http://code.google.com/p/mcl/issues/detail?id=28 for details + +(defparameter *passive-interface-address* NIL + "Address to use for passive connections - optionally bind to loopback address while opening a tcp stream") + +(advise local-interface-ip-address + (or *passive-interface-address* (:do-it)) + :when :around :name 'override-local-interface-ip-address) + ;; MCL Issue 29: Passive TCP connections on OS assigned ports ;; see http://code.google.com/p/mcl/issues/detail?id=29 for details -(ccl:advise ot-conn-tcp-passive-connect - (destructuring-bind (conn port &optional (allow-reuse t)) arglist - (declare (ignore allow-reuse)) - (if (eql port #$kOTAnyInetAddress) - ;; Avoids registering a proxy for port 0 but instead registers one for the true port: - (multiple-value-bind (proxy result) - (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL - (result (:do-it)) ;; pushes onto *opentransport-class-proxies* - (proxy (prog1 - (pop *opentransport-class-proxies*) - (assert (not *opentransport-class-proxies*)))) - (context (cdr proxy)) - (tmpconn (make-ot-conn :context context - :endpoint (pref context :ot-context.ref))) - (localaddress (ot-conn-tcp-get-addresses tmpconn))) - (declare (dynamic-extent tmpconn)) - ;; replace original set in body of function - (setf (ot-conn-local-address conn) localaddress) - (values - (cons localaddress context) - result)) - ;; need to be outside local binding of *opentransport-class-proxies* - (without-interrupts - (push proxy *opentransport-class-proxies*)) - result) - (:do-it))) - :when :around :name 'ot-conn-tcp-passive-connect-any-address) +(advise ot-conn-tcp-passive-connect + (destructuring-bind (conn port &optional (allow-reuse t)) arglist + (declare (ignore allow-reuse)) + (if (eql port #$kOTAnyInetAddress) + ;; Avoids registering a proxy for port 0 but instead registers one for the true port: + (multiple-value-bind (proxy result) + (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL + (result (:do-it)) ;; pushes onto *opentransport-class-proxies* + (proxy (prog1 + (pop *opentransport-class-proxies*) + (assert (not *opentransport-class-proxies*)))) + (context (cdr proxy)) + (tmpconn (make-ot-conn :context context + :endpoint (pref context :ot-context.ref))) + (localaddress (ot-conn-tcp-get-addresses tmpconn))) + (declare (dynamic-extent tmpconn)) + ;; replace original set in body of function + (setf (ot-conn-local-address conn) localaddress) + (values + (cons localaddress context) + result)) + ;; need to be outside local binding of *opentransport-class-proxies* + (without-interrupts + (push proxy *opentransport-class-proxies*)) + result) + (:do-it))) + :when :around :name 'ot-conn-tcp-passive-connect-any-address) (in-package :usocket) From ctian at common-lisp.net Thu Jan 7 07:28:40 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Thu, 07 Jan 2010 02:28:40 -0500 Subject: [usocket-cvs] r514 - usocket/trunk/backend Message-ID: Author: ctian Date: Thu Jan 7 02:28:38 2010 New Revision: 514 Log: Patch from Terje Norderhaug: an upgrade to the usocket MCL backend that allows a socket server to be shared between multiple processes. It adds a lock so only one process at a time polls for an established connection for the socket. Modified: usocket/trunk/backend/mcl.lisp Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Thu Jan 7 02:28:38 2010 @@ -177,8 +177,9 @@ (defclass passive-socket (socket) ((streams :accessor socket-streams :type list :initform NIL - :documentation "Circular list of streams with first element the next to open") - (reuse-address :reader reuse-address :initarg :reuse-address))) + :documentation "Circular list of streams with first element the next to open") + (reuse-address :reader reuse-address :initarg :reuse-address) + (lock :reader socket-lock :initform (ccl:make-lock "Socket")))) (defmethod initialize-instance :after ((socket passive-socket) &key backlog) (loop repeat backlog @@ -191,20 +192,18 @@ #'ccl::stream-local-port (car (socket-streams socket))) (error "timeout"))))) -(defmethod socket-accept ((socket passive-socket) &key element-type) - (flet ((connection-established-p (stream) - (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil)) - (let ((state (ccl::opentransport-stream-connection-state stream))) - (not (eq :unbnd state)))))) +(defmethod socket-accept ((socket passive-socket) &key element-type &aux (lock (socket-lock socket))) + (flet ((connection-established-p (stream) + (ccl::with-io-buffer-locked ((ccl::stream-io-buffer stream nil)) + (let ((state (ccl::opentransport-stream-connection-state stream))) + (not (eq :unbnd state)))))) (with-mapped-conditions () - (let* ((new (socket-open-listener socket element-type)) - (connection (car (socket-streams socket)))) - (assert connection) - (rplaca (socket-streams socket) new) - (setf (socket-streams socket) - (cdr (socket-streams socket))) - (ccl::process-wait "Socket Accept" #'connection-established-p connection) ; expensive polling... - connection)))) + (ccl:with-lock-grabbed (lock nil "Socket Lock") + (let ((connection (shiftf (car (socket-streams socket)) + (socket-open-listener socket element-type)))) + (pop (socket-streams socket)) + (ccl:process-wait "Accepting" #'connection-established-p connection) + connection))))) (defmethod socket-close ((socket passive-socket)) (loop From ctian at common-lisp.net Thu Jan 7 23:47:12 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Thu, 07 Jan 2010 18:47:12 -0500 Subject: [usocket-cvs] r516 - in usocket/branches/0.4.x: . backend Message-ID: Author: ctian Date: Thu Jan 7 18:47:11 2010 New Revision: 516 Log: merge bugfix from trunk (r496-504) Modified: usocket/branches/0.4.x/backend/openmcl.lisp usocket/branches/0.4.x/backend/sbcl.lisp usocket/branches/0.4.x/condition.lisp Modified: usocket/branches/0.4.x/backend/openmcl.lisp ============================================================================== --- usocket/branches/0.4.x/backend/openmcl.lisp (original) +++ usocket/branches/0.4.x/backend/openmcl.lisp Thu Jan 7 18:47:11 2010 @@ -62,9 +62,9 @@ (raise-error-from-id (openmcl-socket:socket-error-identifier condition) socket condition)) (ccl:input-timeout - (error 'timeout-error :socket socket :real-error condition)) + (error 'timeout-error :socket socket)) (ccl:communication-deadline-expired - (error 'deadline-error :socket socket :real-error condition)) + (error 'deadline-timeout-error :socket socket)) (ccl::socket-creation-error #| ugh! |# (raise-error-from-id (ccl::socket-creation-error-identifier condition) socket condition)))) @@ -123,10 +123,14 @@ (close (socket usocket)))) (defmethod get-local-address ((usocket usocket)) - (hbo-to-vector-quad (openmcl-socket:local-host (socket usocket)))) + (let ((address (openmcl-socket:local-host (socket usocket)))) + (when address + (hbo-to-vector-quad address)))) (defmethod get-peer-address ((usocket stream-usocket)) - (hbo-to-vector-quad (openmcl-socket:remote-host (socket usocket)))) + (let ((address (openmcl-socket:remote-host (socket usocket)))) + (when address + (hbo-to-vector-quad address)))) (defmethod get-local-port ((usocket usocket)) (openmcl-socket:local-port (socket usocket))) Modified: usocket/branches/0.4.x/backend/sbcl.lisp ============================================================================== --- usocket/branches/0.4.x/backend/sbcl.lisp (original) +++ usocket/branches/0.4.x/backend/sbcl.lisp Thu Jan 7 18:47:11 2010 @@ -50,7 +50,8 @@ "#include ") (ffi:clines - "#include " + #+:msvc "#include " + #-:msvc "#include " "#include ") #+:prefixed-api @@ -174,6 +175,8 @@ . socket-type-not-supported-error) (sb-bsd-sockets:network-unreachable-error . network-unreachable-error) (sb-bsd-sockets:operation-timeout-error . timeout-error) + #-ecl + (sb-sys:io-timeout . timeout-error) (sb-bsd-sockets:socket-error . ,#'map-socket-error) ;; Nameservice errors: mapped to unknown-error Modified: usocket/branches/0.4.x/condition.lisp ============================================================================== --- usocket/branches/0.4.x/condition.lisp (original) +++ usocket/branches/0.4.x/condition.lisp Thu Jan 7 18:47:11 2010 @@ -111,6 +111,7 @@ host-unreachable-error shutdown-error timeout-error + deadline-timeout-error invalid-socket-error invalid-socket-stream-error) (socket-error)) @@ -183,7 +184,7 @@ ((49 99) . address-not-available-error) ((9) . bad-file-descriptor-error) ((61 111) . connection-refused-error) - ((64 131) . connection-reset-error) + ((54 104) . connection-reset-error) ((53 103) . connection-aborted-error) ((22) . invalid-argument-error) ((55 105) . no-buffers-error) From ctian at common-lisp.net Thu Jan 7 23:49:04 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Thu, 07 Jan 2010 18:49:04 -0500 Subject: [usocket-cvs] r517 - usocket/branches/0.4.x/backend Message-ID: Author: ctian Date: Thu Jan 7 18:49:04 2010 New Revision: 517 Log: merge from trunk (r509) Modified: usocket/branches/0.4.x/backend/openmcl.lisp Modified: usocket/branches/0.4.x/backend/openmcl.lisp ============================================================================== --- usocket/branches/0.4.x/backend/openmcl.lisp (original) +++ usocket/branches/0.4.x/backend/openmcl.lisp Thu Jan 7 18:49:04 2010 @@ -85,8 +85,7 @@ :format (to-format element-type) :deadline deadline :nodelay nodelay - :connect-timeout (and timeout - (* timeout internal-time-units-per-second))))) + :connect-timeout timeout))) (openmcl-socket:socket-connect mcl-sock) (make-stream-socket :stream mcl-sock :socket mcl-sock)))) From ctian at common-lisp.net Wed Jan 13 07:01:22 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Wed, 13 Jan 2010 02:01:22 -0500 Subject: [usocket-cvs] r518 - in usocket/trunk: . vendor Message-ID: Author: ctian Date: Wed Jan 13 02:01:21 2010 New Revision: 518 Log: Remove dependency on split-sequence/cl-utilities, add as vendor code. Added: usocket/trunk/vendor/split-sequence.lisp (contents, props changed) Modified: usocket/trunk/usocket.asd usocket/trunk/usocket.lisp Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Wed Jan 13 02:01:21 2010 @@ -11,25 +11,20 @@ (in-package #:usocket-system) -(pushnew :split-sequence-deprecated *features*) - (defsystem usocket :name "usocket" :author "Erik Enge & Erik Huelsmann" :version "0.5.0" :licence "MIT" :description "Universal socket library for Common Lisp" - :depends-on (;; :split-sequence - ;; use the splie-sequence from cl-utilities - :cl-utilities - #+sbcl :sb-bsd-sockets) + :depends-on (#+sbcl :sb-bsd-sockets) :components ((:file "package") - (:file "usocket" :depends-on ("package")) - (:file "condition" :depends-on ("usocket")) (:module "vendor" - :components (#+mcl (:file "kqueue"))) - (:module "backend" - :depends-on ("condition" "vendor") + :components ((:file "split-sequence") + #+mcl (:file "kqueue"))) + (:file "usocket" :depends-on ("package" "vendor")) + (:file "condition" :depends-on ("usocket")) + (:module "backend" :depends-on ("usocket" "condition") :components (#+clisp (:file "clisp") #+cmu (:file "cmucl") #+scl (:file "scl") Modified: usocket/trunk/usocket.lisp ============================================================================== --- usocket/trunk/usocket.lisp (original) +++ usocket/trunk/usocket.lisp Wed Jan 13 02:01:21 2010 @@ -399,14 +399,14 @@ (aref vector 3))) (defun dotted-quad-to-vector-quad (string) - (let ((list (list-of-strings-to-integers (split-sequence:split-sequence #\. string)))) + (let ((list (list-of-strings-to-integers (split-sequence #\. string)))) (vector (first list) (second list) (third list) (fourth list)))) (defgeneric host-byte-order (address)) (defmethod host-byte-order ((string string)) "Convert a string, such as 192.168.1.1, to host-byte-order, such as 3232235777." - (let ((list (list-of-strings-to-integers (split-sequence:split-sequence #\. string)))) + (let ((list (list-of-strings-to-integers (split-sequence #\. string)))) (+ (* (first list) 256 256 256) (* (second list) 256 256) (* (third list) 256) (fourth list)))) Added: usocket/trunk/vendor/split-sequence.lisp ============================================================================== --- (empty file) +++ usocket/trunk/vendor/split-sequence.lisp Wed Jan 13 02:01:21 2010 @@ -0,0 +1,245 @@ +;;;; SPLIT-SEQUENCE +;;; +;;; This code was based on Arthur Lemmens' in +;;; ; +;;; +;;; changes include: +;;; +;;; * altering the behaviour of the :from-end keyword argument to +;;; return the subsequences in original order, for consistency with +;;; CL:REMOVE, CL:SUBSTITUTE et al. (:from-end being non-NIL only +;;; affects the answer if :count is less than the number of +;;; subsequences, by analogy with the above-referenced functions). +;;; +;;; * changing the :maximum keyword argument to :count, by analogy +;;; with CL:REMOVE, CL:SUBSTITUTE, and so on. +;;; +;;; * naming the function SPLIT-SEQUENCE rather than PARTITION rather +;;; than SPLIT. +;;; +;;; * adding SPLIT-SEQUENCE-IF and SPLIT-SEQUENCE-IF-NOT. +;;; +;;; * The second return value is now an index rather than a copy of a +;;; portion of the sequence; this index is the `right' one to feed to +;;; CL:SUBSEQ for continued processing. + +;;; There's a certain amount of code duplication here, which is kept +;;; to illustrate the relationship between the SPLIT-SEQUENCE +;;; functions and the CL:POSITION functions. + +;;; Examples: +;;; +;;; * (split-sequence #\; "a;;b;c") +;;; -> ("a" "" "b" "c"), 6 +;;; +;;; * (split-sequence #\; "a;;b;c" :from-end t) +;;; -> ("a" "" "b" "c"), 0 +;;; +;;; * (split-sequence #\; "a;;b;c" :from-end t :count 1) +;;; -> ("c"), 4 +;;; +;;; * (split-sequence #\; "a;;b;c" :remove-empty-subseqs t) +;;; -> ("a" "b" "c"), 6 +;;; +;;; * (split-sequence-if (lambda (x) (member x '(#\a #\b))) "abracadabra") +;;; -> ("" "" "r" "c" "d" "" "r" ""), 11 +;;; +;;; * (split-sequence-if-not (lambda (x) (member x '(#\a #\b))) "abracadabra") +;;; -> ("ab" "a" "a" "ab" "a"), 11 +;;; +;;; * (split-sequence #\; ";oo;bar;ba;" :start 1 :end 9) +;;; -> ("oo" "bar" "b"), 9 + +#+ignore ; comment by usocket +(defpackage "SPLIT-SEQUENCE" + (:use "CL") + (:nicknames "PARTITION") + (:export "SPLIT-SEQUENCE" "SPLIT-SEQUENCE-IF" "SPLIT-SEQUENCE-IF-NOT" + "PARTITION" "PARTITION-IF" "PARTITION-IF-NOT")) + +(in-package :usocket #+ignore "SPLIT-SEQUENCE") + +(defun split-sequence (delimiter seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (test nil test-supplied) (test-not nil test-not-supplied) (key nil key-supplied)) + "Return a list of subsequences in seq delimited by delimiter. + +If :remove-empty-subseqs is NIL, empty subsequences will be included +in the result; otherwise they will be discarded. All other keywords +work analogously to those for CL:SUBSTITUTE. In particular, the +behaviour of :from-end is possibly different from other versions of +this function; :from-end values of NIL and T are equivalent unless +:count is supplied. The second return value is an index suitable as an +argument to CL:SUBSEQ into the sequence indicating where processing +stopped." + (let ((len (length seq)) + (other-keys (nconc (when test-supplied + (list :test test)) + (when test-not-supplied + (list :test-not test-not)) + (when key-supplied + (list :key key))))) + (unless end (setq end len)) + (if from-end + (loop for right = end then left + for left = (max (or (apply #'position delimiter seq + :end right + :from-end t + other-keys) + -1) + (1- start)) + unless (and (= right (1+ left)) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values (nreverse subseqs) right) + else + collect (subseq seq (1+ left) right) into subseqs + and sum 1 into nr-elts + until (< left start) + finally (return (values (nreverse subseqs) (1+ left)))) + (loop for left = start then (+ right 1) + for right = (min (or (apply #'position delimiter seq + :start left + other-keys) + len) + end) + unless (and (= right left) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values subseqs left) + else + collect (subseq seq left right) into subseqs + and sum 1 into nr-elts + until (>= right end) + finally (return (values subseqs right)))))) + +(defun split-sequence-if (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied)) + "Return a list of subsequences in seq delimited by items satisfying +predicate. + +If :remove-empty-subseqs is NIL, empty subsequences will be included +in the result; otherwise they will be discarded. All other keywords +work analogously to those for CL:SUBSTITUTE-IF. In particular, the +behaviour of :from-end is possibly different from other versions of +this function; :from-end values of NIL and T are equivalent unless +:count is supplied. The second return value is an index suitable as an +argument to CL:SUBSEQ into the sequence indicating where processing +stopped." + (let ((len (length seq)) + (other-keys (when key-supplied + (list :key key)))) + (unless end (setq end len)) + (if from-end + (loop for right = end then left + for left = (max (or (apply #'position-if predicate seq + :end right + :from-end t + other-keys) + -1) + (1- start)) + unless (and (= right (1+ left)) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values (nreverse subseqs) right) + else + collect (subseq seq (1+ left) right) into subseqs + and sum 1 into nr-elts + until (< left start) + finally (return (values (nreverse subseqs) (1+ left)))) + (loop for left = start then (+ right 1) + for right = (min (or (apply #'position-if predicate seq + :start left + other-keys) + len) + end) + unless (and (= right left) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values subseqs left) + else + collect (subseq seq left right) into subseqs + and sum 1 into nr-elts + until (>= right end) + finally (return (values subseqs right)))))) + +(defun split-sequence-if-not (predicate seq &key (count nil) (remove-empty-subseqs nil) (from-end nil) (start 0) (end nil) (key nil key-supplied)) + "Return a list of subsequences in seq delimited by items satisfying +(CL:COMPLEMENT predicate). + +If :remove-empty-subseqs is NIL, empty subsequences will be included +in the result; otherwise they will be discarded. All other keywords +work analogously to those for CL:SUBSTITUTE-IF-NOT. In particular, +the behaviour of :from-end is possibly different from other versions +of this function; :from-end values of NIL and T are equivalent unless +:count is supplied. The second return value is an index suitable as an +argument to CL:SUBSEQ into the sequence indicating where processing +stopped." + (let ((len (length seq)) + (other-keys (when key-supplied + (list :key key)))) + (unless end (setq end len)) + (if from-end + (loop for right = end then left + for left = (max (or (apply #'position-if-not predicate seq + :end right + :from-end t + other-keys) + -1) + (1- start)) + unless (and (= right (1+ left)) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values (nreverse subseqs) right) + else + collect (subseq seq (1+ left) right) into subseqs + and sum 1 into nr-elts + until (< left start) + finally (return (values (nreverse subseqs) (1+ left)))) + (loop for left = start then (+ right 1) + for right = (min (or (apply #'position-if-not predicate seq + :start left + other-keys) + len) + end) + unless (and (= right left) + remove-empty-subseqs) ; empty subseq we don't want + if (and count (>= nr-elts count)) + ;; We can't take any more. Return now. + return (values subseqs left) + else + collect (subseq seq left right) into subseqs + and sum 1 into nr-elts + until (>= right end) + finally (return (values subseqs right)))))) + +;;; clean deprecation + +(defun partition (&rest args) + (apply #'split-sequence args)) + +(defun partition-if (&rest args) + (apply #'split-sequence-if args)) + +(defun partition-if-not (&rest args) + (apply #'split-sequence-if-not args)) + +(define-compiler-macro partition (&whole form &rest args) + (declare (ignore args)) + (warn "PARTITION is deprecated; use SPLIT-SEQUENCE instead.") + form) + +(define-compiler-macro partition-if (&whole form &rest args) + (declare (ignore args)) + (warn "PARTITION-IF is deprecated; use SPLIT-SEQUENCE-IF instead.") + form) + +(define-compiler-macro partition-if-not (&whole form &rest args) + (declare (ignore args)) + (warn "PARTITION-IF-NOT is deprecated; use SPLIT-SEQUENCE-IF-NOT instead") + form) + +#+ignore ; comment by usocket +(pushnew :split-sequence *features*) From ctian at common-lisp.net Wed Jan 13 09:48:06 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Wed, 13 Jan 2010 04:48:06 -0500 Subject: [usocket-cvs] r519 - usocket/trunk Message-ID: Author: ctian Date: Wed Jan 13 04:48:05 2010 New Revision: 519 Log: ASDF dependency fixes Modified: usocket/trunk/usocket.asd Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Wed Jan 13 04:48:05 2010 @@ -19,12 +19,12 @@ :description "Universal socket library for Common Lisp" :depends-on (#+sbcl :sb-bsd-sockets) :components ((:file "package") - (:module "vendor" + (:module "vendor" :depends-on ("package") :components ((:file "split-sequence") #+mcl (:file "kqueue"))) - (:file "usocket" :depends-on ("package" "vendor")) + (:file "usocket" :depends-on ("vendor")) (:file "condition" :depends-on ("usocket")) - (:module "backend" :depends-on ("usocket" "condition") + (:module "backend" :depends-on ("condition") :components (#+clisp (:file "clisp") #+cmu (:file "cmucl") #+scl (:file "scl") From ctian at common-lisp.net Wed Jan 13 09:51:08 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Wed, 13 Jan 2010 04:51:08 -0500 Subject: [usocket-cvs] r520 - usocket/trunk/backend Message-ID: Author: ctian Date: Wed Jan 13 04:51:07 2010 New Revision: 520 Log: Patch from R. Matthew Emerson: report nameserver errors in the socket-creation-error condition object. Modified: usocket/trunk/backend/openmcl.lisp Modified: usocket/trunk/backend/openmcl.lisp ============================================================================== --- usocket/trunk/backend/openmcl.lisp (original) +++ usocket/trunk/backend/openmcl.lisp Wed Jan 13 04:51:07 2010 @@ -25,6 +25,10 @@ (:shutdown . shutdown-error) (:access-denied . operation-not-permitted-error))) +(defparameter +openmcl-nameserver-error-map+ + '((:no-recovery . ns-no-recovery-error) + (:try-again . ns-try-again-condition) + (:host-not-found . ns-host-not-found-error))) ;; we need something which the openmcl implementors 'forgot' to do: ;; wait for more than one socket-or-fd @@ -66,8 +70,12 @@ (ccl:communication-deadline-expired (error 'deadline-timeout-error :socket socket)) (ccl::socket-creation-error #| ugh! |# - (raise-error-from-id (ccl::socket-creation-error-identifier condition) - socket condition)))) + (let* ((condition-id (ccl::socket-creation-error-identifier condition)) + (nameserver-error (cdr (assoc condition-id + +openmcl-nameserver-error-map+)))) + (if nameserver-error + (error nameserver-error :host-or-ip nil) + (raise-error-from-id condition-id socket condition)))))) (defun to-format (element-type) (if (subtypep element-type 'character) From ctian at common-lisp.net Mon Jan 4 10:22:53 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Mon, 04 Jan 2010 10:22:53 -0000 Subject: [usocket-cvs] r512 - in usocket/trunk: backend vendor Message-ID: Author: ctian Date: Mon Jan 4 05:22:52 2010 New Revision: 512 Log: Include MCL Issue 29, and slightly change kqueue.lisp to make it compiles on MCL. Modified: usocket/trunk/backend/mcl.lisp usocket/trunk/vendor/kqueue.lisp Modified: usocket/trunk/backend/mcl.lisp ============================================================================== --- usocket/trunk/backend/mcl.lisp (original) +++ usocket/trunk/backend/mcl.lisp Mon Jan 4 05:22:52 2010 @@ -1,11 +1,43 @@ ;; MCL backend for USOCKET 0.4.1 ;; Terje Norderhaug , January 1, 2009 -(in-package :usocket) +(in-package :ccl) (eval-when (:compile-toplevel :load-toplevel :execute) (require :opentransport)) +;; MCL Issue 29: Passive TCP connections on OS assigned ports +;; see http://code.google.com/p/mcl/issues/detail?id=29 for details +(ccl:advise ot-conn-tcp-passive-connect + (destructuring-bind (conn port &optional (allow-reuse t)) arglist + (declare (ignore allow-reuse)) + (if (eql port #$kOTAnyInetAddress) + ;; Avoids registering a proxy for port 0 but instead registers one for the true port: + (multiple-value-bind (proxy result) + (let* ((*opentransport-class-proxies* NIL) ; makes ot-find-proxy return NIL + (result (:do-it)) ;; pushes onto *opentransport-class-proxies* + (proxy (prog1 + (pop *opentransport-class-proxies*) + (assert (not *opentransport-class-proxies*)))) + (context (cdr proxy)) + (tmpconn (make-ot-conn :context context + :endpoint (pref context :ot-context.ref))) + (localaddress (ot-conn-tcp-get-addresses tmpconn))) + (declare (dynamic-extent tmpconn)) + ;; replace original set in body of function + (setf (ot-conn-local-address conn) localaddress) + (values + (cons localaddress context) + result)) + ;; need to be outside local binding of *opentransport-class-proxies* + (without-interrupts + (push proxy *opentransport-class-proxies*)) + result) + (:do-it))) + :when :around :name 'ot-conn-tcp-passive-connect-any-address) + +(in-package :usocket) + (defun handle-condition (condition &optional socket) ; incomplete, needs to handle additional conditions (flet ((raise-error (&optional socket-condition) Modified: usocket/trunk/vendor/kqueue.lisp ============================================================================== --- usocket/trunk/vendor/kqueue.lisp (original) +++ usocket/trunk/vendor/kqueue.lisp Mon Jan 4 05:22:52 2010 @@ -1 +1 @@ -;;;-*-Mode: LISP; Package: CCL -*- ;; ;; KQUEUE.LISP ;; ;; KQUEUE - BSD kernel event notification mechanism support for Common LISP. ;; Copyright (C) 2007 Terje Norderhaug ;; Released under LGPL - see . ;; Alternative licensing available upon request. ;; ;; DISCLAIMER: The user of this module should understand that executing code is a potentially hazardous ;; activity, and that many dangers and obstacles, marked or unmarked, may exist within this code. ;; As a condition of your use of the module, you assume all risk of personal injury, death, or property ;; loss, and all other bad things that may happen, even if caused by negligence, ignorance or stupidity. ;; The author is is no way responsible, and besides, does not have "deep pockets" nor any spare change. ;; ;; Version: 0.20 alpha (July 26, 2009) - subject to major revisions, so consider yourself warned. ;; Tested with Macintosh Common LISP 5.1 and 5.2, but is intended to be platform and system independent in the future. ;; ;; Email feedback and improvements to . ;; Updated versions will be available from . ;; ;; RELATED IMPLEMENTATIONS ;; There is another kevent.lisp for other platforms by Risto Laakso (merge?). ;; Also a Scheme kevent.ss by Jose Antonio Ortega. ;; ;; SEE ALSO: ;; http://people.freebsd.org/~jlemon/papers/kqueue.pdf ;; http://developer.apple.com/samplecode/FileNotification/index.html ;; The Man page for kqueue() or kevent(). ;; PyKQueue - Python OO interface to KQueue. ;; LibEvent - an event notification library in C by Niels Provos. ;; Liboop - another abstract library in C on top of kevent or other kernel notification. #| HISTORY: 2007-Oct-18 terje version 0.1 released on the Info-MCL mailing list. 2008-Aug-21 terje load-framework-bundle is not needed under MCL 5.2 2008-Aug-21 terje rename get-addr to lookup-function-in-bundle (only for pre MCL 5.2) 2009-Jul-19 terje uses kevent-error condition and strerror. 2009-Jul-24 terje reports errors unless nil-if-not-found in lookup-function-in-bundle. 2009-Jul-24 terje kevent :variant for C's intptr_t type for 64bit (and osx 10.5) compatibility. 2009-Jul-25 terje 64bit support, dynamically determined for PPC. Kudos to Glen Foy for helping out. 2009-Jul-25 terje make-kevent function. |# #| IMPLEMENTATION NOTES: kevents are copied into and from the kernel, so the records don't have to be kept in the app! kevents does not work in OSX before 10.3. *kevent-record* has to be explcitly set to :kevent64 to work on 64bit intel macs. Consider using sysctlbyname() to test for 64bit, combining hw.cpu64bit_capable, hw.optional.x86_64 and hw.optional.64bitops |# (in-package :ccl) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #-ccl-5.2 ; has been added to MCL 5.2 (defmethod load-framework-bundle ((framework-name string) &key (load-executable t)) ;; FRAMWORK CALL FUNCTIONALITY FROM BSD.LISP ;; (C) 2003 Brendan Burns ;; Released under LGPL. (with-cfstrs ((framework framework-name)) (let ((err 0) (baseURL nil) (bundleURL nil) (result nil)) (rlet ((folder :fsref)) ;; Find the folder holding the bundle (setf err (#_FSFindFolder #$kOnAppropriateDisk #$kFrameworksFolderType t folder)) ;; if everything's cool, make a URL for it (when (zerop err) (setf baseURL (#_CFURLCreateFromFSRef (%null-ptr) folder)) (if (%null-ptr-p baseURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, make a URL for the bundle (when (zerop err) (setf bundleURL (#_CFURLCreateCopyAppendingPathComponent (%null-ptr) baseURL framework nil)) (if (%null-ptr-p bundleURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, load it (when (zerop err) (setf result (#_CFBundleCreate (%null-ptr) bundleURL)) (if (%null-ptr-p result) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, and the user wants it loaded, load it (when (and load-executable (zerop err)) (if (not (#_CFBundleLoadExecutable result)) (setf err #$coreFoundationUnknownErr))) ;; if there's an error, but we've got a pointer, free it and clear result (when (and (not (zerop err)) (not (%null-ptr-p result))) (#_CFRelease result) (setf result nil)) ;; free the URLs if there non-null (when (not (%null-ptr-p bundleURL)) (#_CFRelease bundleURL)) (when (not (%null-ptr-p baseURL)) (#_CFRelease baseURL)) ;; return pointer + error value (values result err))))) #+ignore (defun get-addr (bundle name) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle name))) (rlet ((buf :long)) (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))) #-ccl-5.2 (defun lookup-function-in-bundle (name bundle &optional nil-if-not-found) (with-cfstrs ((str name)) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle str))) (if (%null-ptr-p addr) (unless nil-if-not-found (error "Couldn't resolve address of foreign function ~s" name)) (rlet ((buf :long)) ;; mcl 5.2 uses %fixnum-from-macptr here (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Convenient way to declare BSD system calls #+ignore (defparameter *system-bundle* #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle)) (defmacro declare-bundle-ff (name name-string &rest arglist &aux (fn (gensym (format nil "ff_~A_" (string name))))) ;; Is there an existing define-trap like macro for this? or could one be modified for use with bundles? `(progn (defloadvar ,fn (let* ((bundle #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle))) (lookup-function-in-bundle ,name-string bundle))) ,(let ((args (do ((arglist arglist (cddr arglist)) (result)) ((not (cdr arglist)) (nreverse result)) (push (second arglist) result)))) `(defun ,name ,args (ppc-ff-call ,fn , at arglist))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (declare-bundle-ff %system-kqueue "kqueue" :signed-fullword) ;; returns a file descriptor no! (defun system-kqueue () (let ((kq (%system-kqueue))) (if (= kq -1) (ecase (%system-errno) (12 (error "The kernel failed to allocate enough memory for the kernel queue")) ; ENOMEM (24 (error "The per-process descriptor table is full")) ; EMFILE (23 (error "The system file table is full"))) ; ENFILE kq))) (declare-bundle-ff %system-kevent "kevent" :unsigned-fullword kq :address ke :unsigned-fullword nke :address ko :unsigned-fullword nko :address timeout :signed-fullword) (declare-bundle-ff %system-open "open" :address name :unsigned-fullword mode :unsigned-fullword arg :signed-fullword) (declare-bundle-ff %system-close "close" :unsigned-fullword fd :signed-fullword) (declare-bundle-ff %system-errno* "__error" :signed-fullword) (declare-bundle-ff %system-strerror "strerror" :signed-fullword errno :address) (defun %system-errno () (%get-fixnum (%int-to-ptr (%system-errno*)))) ; (%system-errno) (defconstant $O-EVTONLY #x8000) ; (defconstant $O-NONBLOCK #x800 "Non blocking mode") (defun system-open (posix-namestring) "Low level open function, as in C, returns an fd number" (with-cstrs ((name posix-namestring)) (%system-open name $O-EVTONLY 0))) (defun system-close (fd) (%system-close fd)) (defrecord timespec (sec :unsigned-long) (usec :unsigned-long)) (defVar *kevent-record* nil) (def-ccl-pointers determine-64bit-kevents () (setf *kevent-record* (if (ccl::gestalt #$gestaltPowerPCProcessorFeatures #+ccl-5.2 #$gestaltPowerPCHas64BitSupport #-ccl-5.2 6) :kevent32 :kevent64))) (defrecord :kevent32 (ident :unsigned-long) ; uintptr_t (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (data :long) ; intptr_t (udata :pointer)) (defrecord :kevent64 (:variant ; uintptr_t ((ident64 :uint64)) ((ident :unsigned-long))) (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (:variant ; intptr_t ((data64 :sint64)) ((data :long))) (:variant ; RMCL :pointer is 32bit ((udata64 :uint64)) ((udata :pointer)))) (defun make-kevent (&key (ident 0) (filter 0) (flags 0) (fflags 0) (data 0) (udata *null-ptr*)) (ecase *kevent-record* (:kevent64 (make-record kevent64 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)) (:kevent32 (make-record kevent32 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)))) (defun kevent-rref (ke field) (ecase *kevent-record* (:kevent32 (ecase field (:ident (rref ke :kevent32.ident)) (:filter (rref ke :kevent32.filter)) (:flags (rref ke :kevent32.flags)) (:fflags (rref ke :kevent32.fflags)) (:data (rref ke :kevent32.data)) (:udata (rref ke :kevent32.udata)))) (:kevent64 (ecase field (:ident (rref ke :kevent64.ident)) (:filter (rref ke :kevent64.filter)) (:flags (rref ke :kevent64.flags)) (:fflags (rref ke :kevent64.fflags)) (:data (rref ke :kevent64.data)) (:udata (rref ke :kevent64.udata)))))) (defun kevent-filter (ke) (kevent-rref ke :filter)) (defun kevent-flags (ke) (kevent-rref ke :flags)) (defun kevent-data (ke) (kevent-rref ke :data)) ;; FILTER TYPES: (defconstant $kevent-read-filter -1 "Data available to read") (defconstant $kevent-write-filter -2 "Writing is possible") (defconstant $kevent-aio-filter -3 "AIO system call has been made") (defconstant $kevent-vnode-filter -4 "Event occured on a file descriptor") (defconstant $kevent-proc-filter -5 "Process performed one or more of the requested events") (defconstant $kevent-signal-filter -6 "Attempted to deliver a signal to a process") (defconstant $kevent-timer-filter -7 "Establishes an arbitrary timer") (defconstant $kevent-netdev-filter -8 "Event occured on a network device") (defconstant $kevent-filesystem-filter -9) ; FLAGS: (defconstant $kevent-add #x01) (defconstant $kevent-delete #x02) (defconstant $kevent-enable #x04) (defconstant $kevent-disable #x08) (defconstant $kevent-oneshot #x10) (defconstant $kevent-clear #x20) (defconstant $kevent-error #x4000) (defconstant $kevent-eof #x8000 "EV_EOF") ;; FFLAGS: (defconstant $kevent-file-delete #x01 "The file was unlinked from the file system") (defconstant $kevent-file-write #x02 "A write occurred on the file") (defconstant $kevent-file-extend #x04 "The file was extended") (defconstant $kevent-file-attrib #x08 "The file had its attributes changed") (defconstant $kevent-file-link #x10 "The link count on the file changed") (defconstant $kevent-file-rename #x20 "The file was renamed") (defconstant $kevent-file-revoke #x40 "Access to the file was revoked or the file system was unmounted") (defconstant $kevent-file-all (logior $kevent-file-delete $kevent-file-write $kevent-file-extend $kevent-file-attrib $kevent-file-link $kevent-file-rename $kevent-file-revoke)) (defconstant $kevent-net-linkup #x01 "Link is up") (defconstant $kevent-net-linkdown #x02 "Link is down") (defconstant $kevent-net-linkinvalid #x04 "Link state is invalid") (defconstant $kevent-net-added #x08 "IP adress added") (defconstant $kevent-net-deleted #x10 "IP adress deleted") (define-condition kevent-error (simple-error) ((errno :initform NIL :initarg :errno) (ko :initform nil :type (or null kevent) :initarg :ko) (syserr :initform (%system-errno))) (:report (lambda (c s) (with-slots (errno ko syserr) c (format s "kevent system call error ~A [~A]" errno syserr) (when errno (format s "(~A)" (%get-cstring (%system-strerror errno)))) (when ko (format s " for ") (let ((*standard-output* s)) (print-record ko *kevent-record*))))))) (defun %kevent (kq &optional ke ko (timeout 0)) (check-type kq integer) (rlet ((&timeout :timespec :sec timeout :usec 1)) (let ((num (with-timer ;; does not seem to make a difference... (%system-kevent kq (or ke (%null-ptr))(if ke 1 0)(or ko (%null-ptr))(if ko 1 0) &timeout)))) ; "If an error occurs while processing an element of the changelist and there ; is enough room in the eventlist, then the event will be placed in the eventlist with ; EV_ERROR set in flags and the system error in data." (when (and ko (plusp (logand $kevent-error (kevent-flags ko)))) (error 'kevent-error :errno (kevent-data ko) :ko ko)) ; "Otherwise, -1 will be returned, and errno will be set to indicate the error condition." (when (= num -1) ;; hack - opentransport provides the constants for the errors documented for the call (case (%system-errno) (0 (error "kevent system call failed with an unspecified error")) ;; should not happen! (13 (error "The process does not have permission to register a filter")) (14 (error "There was an error reading or writing the kevent structure")) ; EFAULT (9 (error "The specified descriptor is invalid")) ; EBADF (4 (error "A signal was delivered before the timeout expired and before any events were placed on the kqueue for return.")) ; EINTR (22 (error "The specified time limit or filter is invalid")) ; EINVAL (2 (error "The event could not be found to be modified or deleted")) ; ENOENT (12 (error "No memory was available to register the event")) ; ENOMEM (78 (error "The specified process to attach to does not exist"))) ; ESRCH ;; shouldn't get here... (errchk (%system-errno)) (error "error ~A" (%system-errno))) (unless (zerop num) (values ko num))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CLOS INTERFACE (defclass kqueue () ((kq :initform (system-kqueue) :documentation "file descriptor referencing the kqueue") (fds :initform NIL)) ;; ## better if kept on top level, perhaps as a hash table... (:documentation "A kernal event notification channel")) (defmethod initialize-instance :after ((q kqueue) &rest rest) (declare (ignore rest)) (terminate-when-unreachable q 'kqueue-close)) (defmethod kqueue-close ((q kqueue)) (with-slots (kq fds) q (when (or kq fds) ;; allow repeated close (system-close kq) (setf fds NIL) (setf kq NIL)))) (defmethod kqueue-poll ((q kqueue)) "Polls a kqueue for kevents" ;; may not have to be cleared, but just in case: (flet ((kqueue-poll2 (ko) (let ((result (with-slots (kq) q (without-interrupts (%kevent kq NIL ko))))) (when result (let ((type (kevent-filter result))) (ecase type (0 (values)) (#.$kevent-read-filter (values :read (kevent-rref result :ident) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-write-filter :write) (#.$kevent-aio-filter :aio) (#.$kevent-vnode-filter (values :vnode (cdr (assoc (kevent-rref result :ident) (slot-value q 'fds))) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-filesystem-filter :filesystem))))))) (ecase *kevent-record* (:kevent64 (rlet ((ko :kevent64 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko))) (:kevent32 (rlet ((ko :kevent32 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko)))))) (defmethod kqueue-subscribe ((q kqueue) &key ident filter (flags 0) (fflags 0) (data 0) (udata (%null-ptr))) (let ((ke (make-kevent :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata))) (with-slots (kq) q (without-interrupts (%kevent kq ke))))) (defmethod kqueue-vnode-subscribe ((q kqueue) pathname) "Makes the queue report an event when there is a change to a directory or file" (let* ((namestring (posix-namestring (full-pathname pathname))) (fd (system-open namestring))) (with-slots (fds) q (push (cons fd pathname) fds)) (kqueue-subscribe q :ident fd :filter $kevent-vnode-filter :flags (logior $kevent-add $kevent-clear) :fflags $kevent-file-all) namestring)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+test (defun kevent-d (pathname &optional (*standard-output* (fred))) "Report changes to a file or directory" (loop with kqueue = (make-instance 'kqueue) with sub = (kqueue-vnode-subscribe kqueue pathname) for i from 1 to 60 for result = (multiple-value-list (kqueue-poll kqueue)) unless (equal result '(NIL)) do (progn (format T "~A~%" result) (force-output)) ; do (process-allow-schedule) do (sleep 1) finally (write-line "Done") )) #| ; Report changes to this file in a fred window (save this document to see what happens): (process-run-function "kevent-d" #'kevent-d *loading-file-source-file* (fred)) ; Reports files added or removed from the directory of this file: (process-run-function "kevent-d" #'kevent-d (make-pathname :directory (pathname-directory *loading-file-source-file*)) (fred)) |# \ No newline at end of file +;;;-*-Mode: LISP; Package: CCL -*- ;; ;; KQUEUE.LISP ;; ;; KQUEUE - BSD kernel event notification mechanism support for Common LISP. ;; Copyright (C) 2007 Terje Norderhaug ;; Released under LGPL - see . ;; Alternative licensing available upon request. ;; ;; DISCLAIMER: The user of this module should understand that executing code is a potentially hazardous ;; activity, and that many dangers and obstacles, marked or unmarked, may exist within this code. ;; As a condition of your use of the module, you assume all risk of personal injury, death, or property ;; loss, and all other bad things that may happen, even if caused by negligence, ignorance or stupidity. ;; The author is is no way responsible, and besides, does not have "deep pockets" nor any spare change. ;; ;; Version: 0.20 alpha (July 26, 2009) - subject to major revisions, so consider yourself warned. ;; Tested with Macintosh Common LISP 5.1 and 5.2, but is intended to be platform and system independent in the future. ;; ;; Email feedback and improvements to . ;; Updated versions will be available from . ;; ;; RELATED IMPLEMENTATIONS ;; There is another kevent.lisp for other platforms by Risto Laakso (merge?). ;; Also a Scheme kevent.ss by Jose Antonio Ortega. ;; ;; SEE ALSO: ;; http://people.freebsd.org/~jlemon/papers/kqueue.pdf ;; http://developer.apple.com/samplecode/FileNotification/index.html ;; The Man page for kqueue() or kevent(). ;; PyKQueue - Python OO interface to KQueue. ;; LibEvent - an event notification library in C by Niels Provos. ;; Liboop - another abstract library in C on top of kevent or other kernel notification. #| HISTORY: 2007-Oct-18 terje version 0.1 released on the Info-MCL mailing list. 2008-Aug-21 terje load-framework-bundle is not needed under MCL 5.2 2008-Aug-21 terje rename get-addr to lookup-function-in-bundle (only for pre MCL 5.2) 2009-Jul-19 terje uses kevent-error condition and strerror. 2009-Jul-24 terje reports errors unless nil-if-not-found in lookup-function-in-bundle. 2009-Jul-24 terje kevent :variant for C's intptr_t type for 64bit (and osx 10.5) compatibility. 2009-Jul-25 terje 64bit support, dynamically determined for PPC. Kudos to Glen Foy for helping out. 2009-Jul-25 terje make-kevent function. |# #| IMPLEMENTATION NOTES: kevents are copied into and from the kernel, so the records don't have to be kept in the app! kevents does not work in OSX before 10.3. *kevent-record* has to be explcitly set to :kevent64 to work on 64bit intel macs. Consider using sysctlbyname() to test for 64bit, combining hw.cpu64bit_capable, hw.optional.x86_64 and hw.optional.64bitops |# (in-package :ccl) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #-ccl-5.2 ; has been added to MCL 5.2 (defmethod load-framework-bundle ((framework-name string) &key (load-executable t)) ;; FRAMWORK CALL FUNCTIONALITY FROM BSD.LISP ;; (C) 2003 Brendan Burns ;; Released under LGPL. (with-cfstrs ((framework framework-name)) (let ((err 0) (baseURL nil) (bundleURL nil) (result nil)) (rlet ((folder :fsref)) ;; Find the folder holding the bundle (setf err (#_FSFindFolder #$kOnAppropriateDisk #$kFrameworksFolderType t folder)) ;; if everything's cool, make a URL for it (when (zerop err) (setf baseURL (#_CFURLCreateFromFSRef (%null-ptr) folder)) (if (%null-ptr-p baseURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, make a URL for the bundle (when (zerop err) (setf bundleURL (#_CFURLCreateCopyAppendingPathComponent (%null-ptr) baseURL framework nil)) (if (%null-ptr-p bundleURL) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, load it (when (zerop err) (setf result (#_CFBundleCreate (%null-ptr) bundleURL)) (if (%null-ptr-p result) (setf err #$coreFoundationUnknownErr))) ;; if everything's cool, and the user wants it loaded, load it (when (and load-executable (zerop err)) (if (not (#_CFBundleLoadExecutable result)) (setf err #$coreFoundationUnknownErr))) ;; if there's an error, but we've got a pointer, free it and clear result (when (and (not (zerop err)) (not (%null-ptr-p result))) (#_CFRelease result) (setf result nil)) ;; free the URLs if there non-null (when (not (%null-ptr-p bundleURL)) (#_CFRelease bundleURL)) (when (not (%null-ptr-p baseURL)) (#_CFRelease baseURL)) ;; return pointer + error value (values result err))))) #+ignore (defun get-addr (bundle name) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle name))) (rlet ((buf :long)) (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))) #-ccl-5.2 (defun lookup-function-in-bundle (name bundle &optional nil-if-not-found) (with-cfstrs ((str name)) (let* ((addr (#_CFBundleGetFunctionPointerForName bundle str))) (if (%null-ptr-p addr) (unless nil-if-not-found (error "Couldn't resolve address of foreign function ~s" name)) (rlet ((buf :long)) ;; mcl 5.2 uses %fixnum-from-macptr here (setf (%get-ptr buf) addr) (ash (%get-signed-long buf) -2)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Convenient way to declare BSD system calls #+ignore (defparameter *system-bundle* #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle)) (defmacro declare-bundle-ff (name name-string &rest arglist &aux (fn (gensym (format nil "ff_~A_" (string name))))) ;; Is there an existing define-trap like macro for this? or could one be modified for use with bundles? `(progn (defloadvar ,fn (let* ((bundle #+ccl-5.2 (get-bundle-for-framework-name "System.framework") #-ccl-5.2 (let ((bundle (load-framework-bundle "System.framework"))) (terminate-when-unreachable bundle (lambda (b)(#_CFRelease b))) bundle))) (lookup-function-in-bundle ,name-string bundle))) ,(let ((args (do ((arglist arglist (cddr arglist)) (result)) ((not (cdr arglist)) (nreverse result)) (push (second arglist) result)))) `(defun ,name ,args (ppc-ff-call ,fn , at arglist))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (declare-bundle-ff %system-kqueue "kqueue" :signed-fullword) ;; returns a file descriptor no! (defun system-kqueue () (let ((kq (%system-kqueue))) (if (= kq -1) (ecase (%system-errno) (12 (error "The kernel failed to allocate enough memory for the kernel queue")) ; ENOMEM (24 (error "The per-process descriptor table is full")) ; EMFILE (23 (error "The system file table is full"))) ; ENFILE kq))) (declare-bundle-ff %system-kevent "kevent" :unsigned-fullword kq :address ke :unsigned-fullword nke :address ko :unsigned-fullword nko :address timeout :signed-fullword) (declare-bundle-ff %system-open "open" :address name :unsigned-fullword mode :unsigned-fullword arg :signed-fullword) (declare-bundle-ff %system-close "close" :unsigned-fullword fd :signed-fullword) (declare-bundle-ff %system-errno* "__error" :signed-fullword) (declare-bundle-ff %system-strerror "strerror" :signed-fullword errno :address) (defun %system-errno () (%get-fixnum (%int-to-ptr (%system-errno*)))) ; (%system-errno) (defconstant $O-EVTONLY #x8000) ; (defconstant $O-NONBLOCK #x800 "Non blocking mode") (defun system-open (posix-namestring) "Low level open function, as in C, returns an fd number" (with-cstrs ((name posix-namestring)) (%system-open name $O-EVTONLY 0))) (defun system-close (fd) (%system-close fd)) (defrecord timespec (sec :unsigned-long) (usec :unsigned-long)) (defVar *kevent-record* nil) (def-ccl-pointers determine-64bit-kevents () (setf *kevent-record* (if (ccl::gestalt #$gestaltPowerPCProcessorFeatures #+ccl-5.2 #$gestaltPowerPCHas64BitSupport #-ccl-5.2 6) :kevent32 :kevent64))) (defrecord :kevent32 (ident :unsigned-long) ; uintptr_t (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (data :long) ; intptr_t (udata :pointer)) (defrecord :kevent64 (:variant ; uintptr_t ((ident64 :uint64)) ((ident :unsigned-long))) (filter :short) (flags :unsigned-short) (fflags :unsigned-long) (:variant ; intptr_t ((data64 :sint64)) ((data :long))) (:variant ; RMCL :pointer is 32bit ((udata64 :uint64)) ((udata :pointer)))) (defun make-kevent (&key (ident 0) (filter 0) (flags 0) (fflags 0) (data 0) (udata *null-ptr*)) (ecase *kevent-record* (:kevent64 (make-record kevent64 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)) (:kevent32 (make-record kevent32 :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata)))) (defun kevent-rref (ke field) (ecase *kevent-record* (:kevent32 (ecase field (:ident (rref ke :kevent32.ident)) (:filter (rref ke :kevent32.filter)) (:flags (rref ke :kevent32.flags)) (:fflags (rref ke :kevent32.fflags)) (:data (rref ke :kevent32.data)) (:udata (rref ke :kevent32.udata)))) (:kevent64 (ecase field (:ident (rref ke :kevent64.ident)) (:filter (rref ke :kevent64.filter)) (:flags (rref ke :kevent64.flags)) (:fflags (rref ke :kevent64.fflags)) (:data (rref ke :kevent64.data)) (:udata (rref ke :kevent64.udata)))))) (defun kevent-filter (ke) (kevent-rref ke :filter)) (defun kevent-flags (ke) (kevent-rref ke :flags)) (defun kevent-data (ke) (kevent-rref ke :data)) ;; FILTER TYPES: (eval-when (:compile-toplevel :load-toplevel :execute) ; added by binghe (defconstant $kevent-read-filter -1 "Data available to read") (defconstant $kevent-write-filter -2 "Writing is possible") (defconstant $kevent-aio-filter -3 "AIO system call has been made") (defconstant $kevent-vnode-filter -4 "Event occured on a file descriptor") (defconstant $kevent-proc-filter -5 "Process performed one or more of the requested events") (defconstant $kevent-signal-filter -6 "Attempted to deliver a signal to a process") (defconstant $kevent-timer-filter -7 "Establishes an arbitrary timer") (defconstant $kevent-netdev-filter -8 "Event occured on a network device") (defconstant $kevent-filesystem-filter -9) ) ; eval-when ; FLAGS: (defconstant $kevent-add #x01) (defconstant $kevent-delete #x02) (defconstant $kevent-enable #x04) (defconstant $kevent-disable #x08) (defconstant $kevent-oneshot #x10) (defconstant $kevent-clear #x20) (defconstant $kevent-error #x4000) (defconstant $kevent-eof #x8000 "EV_EOF") ;; FFLAGS: (defconstant $kevent-file-delete #x01 "The file was unlinked from the file system") (defconstant $kevent-file-write #x02 "A write occurred on the file") (defconstant $kevent-file-extend #x04 "The file was extended") (defconstant $kevent-file-attrib #x08 "The file had its attributes changed") (defconstant $kevent-file-link #x10 "The link count on the file changed") (defconstant $kevent-file-rename #x20 "The file was renamed") (defconstant $kevent-file-revoke #x40 "Access to the file was revoked or the file system was unmounted") (defconstant $kevent-file-all (logior $kevent-file-delete $kevent-file-write $kevent-file-extend $kevent-file-attrib $kevent-file-link $kevent-file-rename $kevent-file-revoke)) (defconstant $kevent-net-linkup #x01 "Link is up") (defconstant $kevent-net-linkdown #x02 "Link is down") (defconstant $kevent-net-linkinvalid #x04 "Link state is invalid") (defconstant $kevent-net-added #x08 "IP adress added") (defconstant $kevent-net-deleted #x10 "IP adress deleted") (define-condition kevent-error (simple-error) ((errno :initform NIL :initarg :errno) (ko :initform nil :type (or null kevent) :initarg :ko) (syserr :initform (%system-errno))) (:report (lambda (c s) (with-slots (errno ko syserr) c (format s "kevent system call error ~A [~A]" errno syserr) (when errno (format s "(~A)" (%get-cstring (%system-strerror errno)))) (when ko (format s " for ") (let ((*standard-output* s)) (print-record ko *kevent-record*))))))) (defun %kevent (kq &optional ke ko (timeout 0)) (check-type kq integer) (rlet ((&timeout :timespec :sec timeout :usec 1)) (let ((num (with-timer ;; does not seem to make a difference... (%system-kevent kq (or ke (%null-ptr))(if ke 1 0)(or ko (%null-ptr))(if ko 1 0) &timeout)))) ; "If an error occurs while processing an element of the changelist and there ; is enough room in the eventlist, then the event will be placed in the eventlist with ; EV_ERROR set in flags and the system error in data." (when (and ko (plusp (logand $kevent-error (kevent-flags ko)))) (error 'kevent-error :errno (kevent-data ko) :ko ko)) ; "Otherwise, -1 will be returned, and errno will be set to indicate the error condition." (when (= num -1) ;; hack - opentransport provides the constants for the errors documented for the call (case (%system-errno) (0 (error "kevent system call failed with an unspecified error")) ;; should not happen! (13 (error "The process does not have permission to register a filter")) (14 (error "There was an error reading or writing the kevent structure")) ; EFAULT (9 (error "The specified descriptor is invalid")) ; EBADF (4 (error "A signal was delivered before the timeout expired and before any events were placed on the kqueue for return.")) ; EINTR (22 (error "The specified time limit or filter is invalid")) ; EINVAL (2 (error "The event could not be found to be modified or deleted")) ; ENOENT (12 (error "No memory was available to register the event")) ; ENOMEM (78 (error "The specified process to attach to does not exist"))) ; ESRCH ;; shouldn't get here... (errchk (%system-errno)) (error "error ~A" (%system-errno))) (unless (zerop num) (values ko num))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CLOS INTERFACE (defclass kqueue () ((kq :initform (system-kqueue) :documentation "file descriptor referencing the kqueue") (fds :initform NIL)) ;; ## better if kept on top level, perhaps as a hash table... (:documentation "A kernal event notification channel")) (defmethod initialize-instance :after ((q kqueue) &rest rest) (declare (ignore rest)) (terminate-when-unreachable q 'kqueue-close)) (defmethod kqueue-close ((q kqueue)) (with-slots (kq fds) q (when (or kq fds) ;; allow repeated close (system-close kq) (setf fds NIL) (setf kq NIL)))) (defmethod kqueue-poll ((q kqueue)) "Polls a kqueue for kevents" ;; may not have to be cleared, but just in case: (flet ((kqueue-poll2 (ko) (let ((result (with-slots (kq) q (without-interrupts (%kevent kq NIL ko))))) (when result (let ((type (kevent-filter result))) (ecase type (0 (values)) (#.$kevent-read-filter (values :read (kevent-rref result :ident) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-write-filter :write) (#.$kevent-aio-filter :aio) (#.$kevent-vnode-filter (values :vnode (cdr (assoc (kevent-rref result :ident) (slot-value q 'fds))) (kevent-rref result :flags) (kevent-rref result :fflags) (kevent-rref result :data) (kevent-rref result :udata))) (#.$kevent-filesystem-filter :filesystem))))))) (ecase *kevent-record* (:kevent64 (rlet ((ko :kevent64 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko))) (:kevent32 (rlet ((ko :kevent32 :ident 0 :filter 0 :flags 0 :fflags 0 :data 0 :udata (%null-ptr))) (kqueue-poll2 ko)))))) (defmethod kqueue-subscribe ((q kqueue) &key ident filter (flags 0) (fflags 0) (data 0) (udata (%null-ptr))) (let ((ke (make-kevent :ident ident :filter filter :flags flags :fflags fflags :data data :udata udata))) (with-slots (kq) q (without-interrupts (%kevent kq ke))))) (defmethod kqueue-vnode-subscribe ((q kqueue) pathname) "Makes the queue report an event when there is a change to a directory or file" (let* ((namestring (posix-namestring (full-pathname pathname))) (fd (system-open namestring))) (with-slots (fds) q (push (cons fd pathname) fds)) (kqueue-subscribe q :ident fd :filter $kevent-vnode-filter :flags (logior $kevent-add $kevent-clear) :fflags $kevent-file-all) namestring)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+test (defun kevent-d (pathname &optional (*standard-output* (fred))) "Report changes to a file or directory" (loop with kqueue = (make-instance 'kqueue) with sub = (kqueue-vnode-subscribe kqueue pathname) for i from 1 to 60 for result = (multiple-value-list (kqueue-poll kqueue)) unless (equal result '(NIL)) do (progn (format T "~A~%" result) (force-output)) ; do (process-allow-schedule) do (sleep 1) finally (write-line "Done") )) #| ; Report changes to this file in a fred window (save this document to see what happens): (process-run-function "kevent-d" #'kevent-d *loading-file-source-file* (fred)) ; Reports files added or removed from the directory of this file: (process-run-function "kevent-d" #'kevent-d (make-pathname :directory (pathname-directory *loading-file-source-file*)) (fred)) |# \ No newline at end of file From ctian at common-lisp.net Thu Jan 7 18:26:07 2010 From: ctian at common-lisp.net (Chun Tian (binghe)) Date: Thu, 07 Jan 2010 18:26:07 -0000 Subject: [usocket-cvs] r515 - in usocket/trunk: . backend Message-ID: Author: ctian Date: Thu Jan 7 13:26:06 2010 New Revision: 515 Log: Branch experimental-udp merged into trunk. Added: usocket/trunk/server.lisp - copied unchanged from r514, /usocket/branches/experimental-udp/server.lisp Modified: usocket/trunk/backend/allegro.lisp usocket/trunk/backend/armedbear.lisp usocket/trunk/backend/clisp.lisp usocket/trunk/backend/cmucl.lisp usocket/trunk/backend/lispworks.lisp usocket/trunk/backend/openmcl.lisp usocket/trunk/backend/sbcl.lisp usocket/trunk/backend/scl.lisp usocket/trunk/package.lisp usocket/trunk/usocket.asd usocket/trunk/usocket.lisp Modified: usocket/trunk/backend/allegro.lisp ============================================================================== --- usocket/trunk/backend/allegro.lisp (original) +++ usocket/trunk/backend/allegro.lisp Thu Jan 7 13:26:06 2010 @@ -49,7 +49,7 @@ :text :binary)) -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay t) ;; nodelay == t is the ACL default local-host local-port) @@ -58,20 +58,39 @@ (let ((socket)) (setf socket - (labels ((make-socket () - (socket:make-socket :remote-host (host-to-hostname host) - :remote-port port - :local-host (when local-host - (host-to-hostname local-host)) - :local-port local-port - :format (to-format element-type) - :nodelay nodelay))) - (with-mapped-conditions (socket) - (if timeout - (mp:with-timeout (timeout nil) - (make-socket)) - (make-socket))))) - (make-stream-socket :socket socket :stream socket))) + (with-mapped-conditions (socket) + (ecase protocol + (:stream + (labels ((make-socket () + (socket:make-socket :remote-host (host-to-hostname host) + :remote-port port + :local-host (when local-host + (host-to-hostname local-host)) + :local-port local-port + :format (to-format element-type) + :nodelay nodelay))) + (if timeout + (mp:with-timeout (timeout nil) + (make-socket)) + (make-socket)))) + (:datagram + (apply #'socket:make-socket + (nconc (list :type protocol + :address-family :internet + :local-host (when local-host + (host-to-hostname local-host)) + :local-port local-port + :format (to-format element-type)) + (if (and host port) + (list :connect :active + :remote-host (host-to-hostname host) + :remote-port port) + (list :connect :passive)))))))) + (ecase protocol + (:stream + (make-stream-socket :socket socket :stream socket)) + (:datagram + (make-datagram-socket socket))))) ;; One socket close method is sufficient, ;; because socket-streams are also sockets. @@ -130,6 +149,15 @@ (values (get-peer-address usocket) (get-peer-port usocket))) +(defmethod socket-send ((socket datagram-usocket) buffer length &key host port) + (with-mapped-conditions (socket) + (let ((s (socket socket))) + (socket:send-to s buffer length :remote-host host :remote-port port)))) + +(defmethod socket-receive ((socket datagram-usocket) buffer length &key) + (with-mapped-conditions (socket) + (let ((s (socket socket))) + (socket:receive-from s length :buffer buffer :extract t)))) (defun get-host-by-address (address) (with-mapped-conditions () Modified: usocket/trunk/backend/armedbear.lisp ============================================================================== --- usocket/trunk/backend/armedbear.lisp (original) +++ usocket/trunk/backend/armedbear.lisp Thu Jan 7 13:26:06 2010 @@ -6,7 +6,7 @@ (in-package :usocket) -;;;;; Proposed contribution to the JAVA package +;;; Proposed contribution to the JAVA package (defpackage :jdi (:use :cl) @@ -186,24 +186,36 @@ (typecase condition (error (error 'unknown-error :socket socket :real-error condition)))) -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay nil nodelay-specified) local-host local-port) (when deadline (unsupported 'deadline 'socket-connect)) - (when local-host (unimplemented 'local-host 'socket-connect)) - (when local-port (unimplemented 'local-port 'socket-connect)) (let ((usock)) (with-mapped-conditions (usock) - (let* ((sock-addr (jdi:jcoerce - (jdi:do-jnew-call "java.net.InetSocketAddress" - (host-to-hostname host) - (jdi:jcoerce port :int)) - "java.net.SocketAddress")) - (jchan (jdi:do-jstatic-call "java.nio.channels.SocketChannel" - "open" sock-addr)) + (let* ((sock-addr (when (and host port) + (jdi:jcoerce + (jdi:do-jnew-call "java.net.InetSocketAddress" + (host-to-hostname host) + (jdi:jcoerce port :int)) + "java.net.SocketAddress"))) + (local-addr (when (or local-host local-port) + (jdi:jcoerce + (jdi:do-jnew-call "java.net.InetSocketAddress" + (host-to-hostname (or host *wildcard-host*)) + (jdi:jcoerce (or port *auto-port*) :int)) + "java.net.SocketAddress"))) + (jchan (jdi:do-jstatic-call (ecase protocol + (:stream "java.nio.channels.SocketChannel") + (:datagram "java.nio.channels.DatagramChannel")) + "open")) (sock (jdi:do-jmethod-call jchan "socket"))) - (when nodelay-specified + ;; TODO: Fix it + (when (or local-host local-port) + (jdi:do-jmethod-call sock "bind" local-addr)) + (when (and host port) + (jdi:do-jmethod-call jchan "connect" sock-addr)) + (when (and (eq protocol 'stream) nodelay-specified) (jdi:do-jmethod-call sock "setTcpNoDelay" (if nodelay (java:make-immediate-object t :boolean) @@ -212,10 +224,14 @@ (jdi:do-jmethod-call sock "setSoTimeout" (truncate (* 1000 timeout)))) (setf usock - (make-stream-socket - :socket jchan - :stream (ext:get-socket-stream (jdi:jop-deref sock) - :element-type element-type))))))) + (ecase protocol + (:stream + (make-stream-socket + :socket jchan + :stream (ext:get-socket-stream (jdi:jop-deref sock) + :element-type element-type))) + (:datagram + (make-datagram-socket jchan)))))))) (defun socket-listen (host port &key reuseaddress @@ -447,4 +463,29 @@ w)) (defun %remove-waiter (wl w) - (remhash (socket w) (wait-list-%wait wl))) \ No newline at end of file + (remhash (socket w) (wait-list-%wait wl))) + +;; +;; UDP support +;; + +(defmethod socket-send ((socket datagram-usocket) buffer length &key host port) + (let ((jchan (socket socket))) + (let ((srcs (jdi:jcoerce buffer "java.nio.ByteBuffer")) + (offset (jdi:jcoerce 0 :int)) + (length (jdi:jcoerce length :int))) + (if (and host port) + (let ((target (jdi:jcoerce + (jdi:do-jnew-call "java.net.InetSocketAddress" + (host-to-hostname host) + (jdi:jcoerce port :int)) + "java.net.SocketAddress"))) + ;; how to use "length" argument here? --binghe, 2009/12/12 + (jdi:do-jmethod-call jchan "send" buffer target)) + (jdi:do-jmethod-call jchan "write" srcs offset length))))) + +(defmethod socket-receive ((socket datagram-usocket) buffer length &key) + (let ((jchan (socket socket))) + (multiple-value-bind (buffer size host port) + 0 + (values buffer size host port)))) Modified: usocket/trunk/backend/clisp.lisp ============================================================================== --- usocket/trunk/backend/clisp.lisp (original) +++ usocket/trunk/backend/clisp.lisp Thu Jan 7 13:26:06 2010 @@ -55,7 +55,7 @@ (error usock-err :socket socket) (signal usock-err :socket socket))))))) -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay t nodelay-specified) local-host local-port) (declare (ignore nodelay)) Modified: usocket/trunk/backend/cmucl.lisp ============================================================================== --- usocket/trunk/backend/cmucl.lisp (original) +++ usocket/trunk/backend/cmucl.lisp Thu Jan 7 13:26:06 2010 @@ -50,7 +50,7 @@ :socket socket :condition condition)))) -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay t nodelay-specified) (local-host nil local-host-p) (local-port nil local-port-p) @@ -65,25 +65,53 @@ (when (and local-port-p (not local-bind-p)) (unsupported 'local-port 'socket-connect :minimum "Snapshot 2008-08 (19E)")) - (let* ((socket)) - (setf socket - (let ((args (list (host-to-hbo host) port :stream))) - (when (and local-bind-p (or local-host-p local-port-p)) - (nconc args (list :local-host (when local-host - (host-to-hbo local-host)) - :local-port local-port))) - (with-mapped-conditions (socket) - (apply #'ext:connect-to-inet-socket args)))) - (if socket - (let* ((stream (sys:make-fd-stream socket :input t :output t - :element-type element-type - :buffering :full)) - ;;###FIXME the above line probably needs an :external-format - (usocket (make-stream-socket :socket socket - :stream stream))) - usocket) - (let ((err (unix:unix-errno))) - (when err (cmucl-map-socket-error err)))))) + (let ((socket)) + (ecase protocol + (:stream + (setf socket + (let ((args (list (host-to-hbo host) port protocol))) + (when (and local-bind-p (or local-host-p local-port-p)) + (nconc args (list :local-host (when local-host + (host-to-hbo local-host)) + :local-port local-port))) + (with-mapped-conditions (socket) + (apply #'ext:connect-to-inet-socket args)))) + (if socket + (let* ((stream (sys:make-fd-stream socket :input t :output t + :element-type element-type + :buffering :full)) + ;;###FIXME the above line probably needs an :external-format + (usocket (make-stream-socket :socket socket + :stream stream))) + usocket) + (let ((err (unix:unix-errno))) + (when err (cmucl-map-socket-error err))))) + (:datagram + (setf socket + (if (and host port) + (let ((args (list (host-to-hbo host) port protocol))) + (when (and local-bind-p (or local-host-p local-port-p)) + (nconc args (list :local-host (when local-host + (host-to-hbo local-host)) + :local-port local-port))) + (with-mapped-conditions (socket) + (apply #'ext:connect-to-inet-socket args))) + (if (or local-host-p local-port-p) + (with-mapped-conditions (socket) + (apply #'ext:create-inet-listener + (nconc (list (or local-port 0) protocol) + (when (and local-host-p + (ip/= local-host *wildcard-host*)) + (list :host (host-to-hbo local-host)))))) + (with-mapped-conditions (socket) + (ext:create-inet-socket protocol))))) + (if socket + (let ((usocket (make-datagram-socket socket))) + (ext:finalize usocket #'(lambda () (when (%open-p usocket) + (ext:close-socket socket)))) + usocket) + (let ((err (unix:unix-errno))) + (when err (cmucl-map-socket-error err)))))))) (defun socket-listen (host port &key reuseaddress @@ -128,6 +156,24 @@ (with-mapped-conditions (usocket) (ext:close-socket (socket usocket)))) +(defmethod socket-close :after ((socket datagram-usocket)) + (setf (%open-p socket) nil)) + +(defmethod socket-send ((usocket datagram-usocket) buffer length &key host port) + (with-mapped-conditions (usocket) + (ext:inet-sendto (socket usocket) buffer length (if host (host-to-hbo host)) port))) + +(defmethod socket-receive ((usocket datagram-usocket) buffer length &key) + (let ((real-buffer (or buffer + (make-array length :element-type '(unsigned-byte 8)))) + (real-length (or length + (length buffer)))) + (multiple-value-bind (nbytes remote-host remote-port) + (with-mapped-conditions (usocket) + (ext:inet-recvfrom (socket usocket) real-buffer real-length)) + (when (plusp nbytes) + (values real-buffer nbytes remote-host remote-port))))) + (defmethod get-local-name ((usocket usocket)) (multiple-value-bind (address port) @@ -216,5 +262,5 @@ (when (unix:fd-isset (socket x) rfds) (setf (state x) :READ))) (progn - ;;###FIXME generate an error, except for EINTR + ;;###FIXME generate an error, except for EINTR ))))))) Modified: usocket/trunk/backend/lispworks.lisp ============================================================================== --- usocket/trunk/backend/lispworks.lisp (original) +++ usocket/trunk/backend/lispworks.lisp Thu Jan 7 13:26:06 2010 @@ -89,15 +89,172 @@ (declare (ignore host port err-msg)) (raise-usock-err errno socket condition))))) -(defun socket-connect (host port &key (element-type 'base-char) +(defconstant *socket_sock_dgram* 2 + "Connectionless, unreliable datagrams of fixed maximum length.") + +(defconstant *sockopt_so_rcvtimeo* + #+(not linux) #x1006 + #+linux 20 + "Socket receive timeout") + +(fli:define-c-struct timeval + (tv-sec :long) + (tv-usec :long)) + +;;; ssize_t +;;; recvfrom(int socket, void *restrict buffer, size_t length, int flags, +;;; struct sockaddr *restrict address, socklen_t *restrict address_len); +(fli:define-foreign-function (%recvfrom "recvfrom" :source) + ((socket :int) + (buffer (:pointer (:unsigned :byte))) + (length :int) + (flags :int) + (address (:pointer (:struct comm::sockaddr))) + (address-len (:pointer :int))) + :result-type :int + #+win32 :module + #+win32 "ws2_32") + +;;; ssize_t +;;; sendto(int socket, const void *buffer, size_t length, int flags, +;;; const struct sockaddr *dest_addr, socklen_t dest_len); +(fli:define-foreign-function (%sendto "sendto" :source) + ((socket :int) + (buffer (:pointer (:unsigned :byte))) + (length :int) + (flags :int) + (address (:pointer (:struct comm::sockaddr))) + (address-len :int)) + :result-type :int + #+win32 :module + #+win32 "ws2_32") + +#-win32 +(defun set-socket-receive-timeout (socket-fd seconds) + "Set socket option: RCVTIMEO, argument seconds can be a float number" + (declare (type integer socket-fd) + (type number seconds)) + (multiple-value-bind (sec usec) (truncate seconds) + (fli:with-dynamic-foreign-objects ((timeout (:struct timeval))) + (fli:with-foreign-slots (tv-sec tv-usec) timeout + (setf tv-sec sec + tv-usec (truncate (* 1000000 usec))) + (if (zerop (comm::setsockopt socket-fd + comm::*sockopt_sol_socket* + *sockopt_so_rcvtimeo* + (fli:copy-pointer timeout + :type '(:pointer :void)) + (fli:size-of '(:struct timeval)))) + seconds))))) + +#+win32 +(defun set-socket-receive-timeout (socket-fd seconds) + "Set socket option: RCVTIMEO, argument seconds can be a float number. + On win32, you must bind the socket before use this function." + (declare (type integer socket-fd) + (type number seconds)) + (fli:with-dynamic-foreign-objects ((timeout :int)) + (setf (fli:dereference timeout) + (truncate (* 1000 seconds))) + (if (zerop (comm::setsockopt socket-fd + comm::*sockopt_sol_socket* + *sockopt_so_rcvtimeo* + (fli:copy-pointer timeout + :type '(:pointer :char)) + (fli:size-of :int))) + seconds))) + +#-win32 +(defmethod get-socket-receive-timeout (socket-fd) + "Get socket option: RCVTIMEO, return value is a float number" + (declare (type integer socket-fd)) + (fli:with-dynamic-foreign-objects ((timeout (:struct timeval)) + (len :int)) + (comm::getsockopt socket-fd + comm::*sockopt_sol_socket* + *sockopt_so_rcvtimeo* + (fli:copy-pointer timeout + :type '(:pointer :void)) + len) + (fli:with-foreign-slots (tv-sec tv-usec) timeout + (float (+ tv-sec (/ tv-usec 1000000)))))) + +#+win32 +(defmethod get-socket-receive-timeout (socket-fd) + "Get socket option: RCVTIMEO, return value is a float number" + (declare (type integer socket-fd)) + (fli:with-dynamic-foreign-objects ((timeout :int) + (len :int)) + (comm::getsockopt socket-fd + comm::*sockopt_sol_socket* + *sockopt_so_rcvtimeo* + (fli:copy-pointer timeout + :type '(:pointer :void)) + len) + (float (/ (fli:dereference timeout) 1000)))) + +(defun open-udp-socket (&key local-address local-port read-timeout) + "Open a unconnected UDP socket. + For binding on address ANY(*), just not set LOCAL-ADDRESS (NIL), + for binding on random free unused port, set LOCAL-PORT to 0." + (let ((socket-fd (comm::socket comm::*socket_af_inet* *socket_sock_dgram* comm::*socket_pf_unspec*))) + (if socket-fd + (progn + (when read-timeout (set-socket-receive-timeout socket-fd read-timeout)) + (if local-port + (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in))) + (comm::initialize-sockaddr_in client-addr comm::*socket_af_inet* + local-address local-port "udp") + (if (comm::bind socket-fd + (fli:copy-pointer client-addr :type '(:struct comm::sockaddr)) + (fli:pointer-element-size client-addr)) + ;; success, return socket fd + socket-fd + (progn + (comm::close-socket socket-fd) + (error "cannot bind")))) + socket-fd)) + (error "cannot create socket")))) + +(defun connect-to-udp-server (hostname service + &key local-address local-port read-timeout) + "Something like CONNECT-TO-TCP-SERVER" + (let ((socket-fd (open-udp-socket :local-address local-address + :local-port local-port + :read-timeout read-timeout))) + (if socket-fd + (fli:with-dynamic-foreign-objects ((server-addr (:struct comm::sockaddr_in))) + ;; connect to remote address/port + (comm::initialize-sockaddr_in server-addr comm::*socket_af_inet* hostname service "udp") + (if (comm::connect socket-fd + (fli:copy-pointer server-addr :type '(:struct comm::sockaddr)) + (fli:pointer-element-size server-addr)) + ;; success, return socket fd + socket-fd + ;; fail, close socket and return nil + (progn + (comm::close-socket socket-fd) + (error "cannot connect")))) + (error "cannot create socket")))) + +;; Register a special free action for closing datagram usocket when being GCed +(defun usocket-special-free-action (object) + (when (and (typep object 'datagram-usocket) + (%open-p object)) + (socket-close object))) + +(eval-when (:load-toplevel :execute) + (hcl:add-special-free-action 'usocket-special-free-action)) + +(defun socket-connect (host port &key (protocol :stream) (element-type 'base-char) timeout deadline (nodelay t nodelay-specified) - local-host local-port) + local-host (local-port #+win32 *auto-port* #-win32 nil)) (declare (ignorable nodelay)) ;; What's the meaning of this keyword? (when deadline (unimplemented 'deadline 'socket-connect)) - + #+(and lispworks4 (not lispworks4.4)) ; < 4.4.5 (when timeout (unsupported 'timeout 'socket-connect :minimum "LispWorks 4.4.5")) @@ -112,26 +269,39 @@ (when local-port (unsupported 'local-port 'socket-connect :minimum "LispWorks 5.0")) - (let ((hostname (host-to-hostname host)) - (stream)) - (setf stream - (with-mapped-conditions () - (comm:open-tcp-stream hostname port - :element-type element-type - #-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5 - #-(and lispworks4 (not lispworks4.4)) - :timeout timeout - #-lispworks4 #-lispworks4 - #-lispworks4 #-lispworks4 - :local-address (when local-host (host-to-hostname local-host)) - :local-port local-port - #-(or lispworks4 lispworks5.0) ; >= 5.1 - #-(or lispworks4 lispworks5.0) - :nodelay nodelay))) - (if stream - (make-stream-socket :socket (comm:socket-stream-socket stream) - :stream stream) - (error 'unknown-error)))) + (ecase protocol + (:stream + (let ((hostname (host-to-hostname host)) + (stream)) + (setf stream + (with-mapped-conditions () + (comm:open-tcp-stream hostname port + :element-type element-type + #-(and lispworks4 (not lispworks4.4)) ; >= 4.4.5 + #-(and lispworks4 (not lispworks4.4)) + :timeout timeout + #-lispworks4 #-lispworks4 + #-lispworks4 #-lispworks4 + :local-address (when local-host (host-to-hostname local-host)) + :local-port local-port + #-(or lispworks4 lispworks5.0) ; >= 5.1 + #-(or lispworks4 lispworks5.0) + :nodelay nodelay))) + (if stream + (make-stream-socket :socket (comm:socket-stream-socket stream) + :stream stream) + (error 'unknown-error)))) + (:datagram + (let ((usocket (make-datagram-socket + (if (and host port) + (connect-to-udp-server host port + :local-address local-host + :local-port local-port) + (open-udp-socket :local-address local-host + :local-port local-port)) + :connected-p t))) + (hcl:flag-special-free-action usocket) + usocket)))) (defun socket-listen (host port &key reuseaddress @@ -180,6 +350,107 @@ (with-mapped-conditions (usocket) (comm::close-socket (socket usocket)))) +(defmethod socket-close :after ((socket datagram-usocket)) + "Additional socket-close method for datagram-usocket" + (setf (%open-p socket) nil)) + +(defvar *message-send-buffer* + (make-array +max-datagram-packet-size+ + :element-type '(unsigned-byte 8) + :allocation :static)) + +(defvar *message-send-lock* (mp:make-lock)) + +(defun send-message (socket-fd buffer &optional (length (length buffer)) host service) + "Send message to a socket, using sendto()/send()" + (declare (type integer socket-fd) + (type sequence buffer)) + (let ((message *message-send-buffer*)) + (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)) + (len :int + #-(or lispworks3 lispworks4 lispworks5.0) + :initial-element + (fli:size-of '(:struct comm::sockaddr_in)))) + (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte)) + (mp:with-lock (*message-send-lock*) + (replace message buffer :end2 length) + (if (and host service) + (progn + (comm::initialize-sockaddr_in client-addr comm::*socket_af_inet* host service "udp") + (%sendto socket-fd ptr (min length +max-datagram-packet-size+) 0 + (fli:copy-pointer client-addr :type '(:struct comm::sockaddr)) + (fli:dereference len))) + (comm::%send socket-fd ptr (min length +max-datagram-packet-size+) 0))))))) + +(defmethod socket-send ((socket datagram-usocket) buffer length &key host port) + (let ((s (socket socket))) + (send-message s buffer length (host-to-hbo host) port))) + +(defvar *message-receive-buffer* + (make-array +max-datagram-packet-size+ + :element-type '(unsigned-byte 8) + :allocation :static)) + +(defvar *message-receive-lock* (mp:make-lock)) + +(defun receive-message (socket-fd &optional buffer (length (length buffer)) + &key read-timeout (max-buffer-size +max-datagram-packet-size+)) + "Receive message from socket, read-timeout is a float number in seconds. + + This function will return 4 values: + 1. receive buffer + 2. number of receive bytes + 3. remote address + 4. remote port" + (declare (type integer socket-fd) + (type sequence buffer)) + (let ((message *message-receive-buffer*) + old-timeout) + (fli:with-dynamic-foreign-objects ((client-addr (:struct comm::sockaddr_in)) + (len :int + #-(or lispworks3 lispworks4 lispworks5.0) + :initial-element + (fli:size-of '(:struct comm::sockaddr_in)))) + (fli:with-dynamic-lisp-array-pointer (ptr message :type '(:unsigned :byte)) + ;; setup new read timeout + (when read-timeout + (setf old-timeout (get-socket-receive-timeout socket-fd)) + (set-socket-receive-timeout socket-fd read-timeout)) + (mp:with-lock (*message-receive-lock*) + (let ((n (%recvfrom socket-fd ptr max-buffer-size 0 + (fli:copy-pointer client-addr :type '(:struct comm::sockaddr)) + len))) + ;; restore old read timeout + (when (and read-timeout (/= old-timeout read-timeout)) + (set-socket-receive-timeout socket-fd old-timeout)) + (if (plusp n) + (values (if buffer + (replace buffer message + :end1 (min length max-buffer-size) + :end2 (min n max-buffer-size)) + (subseq message 0 (min n max-buffer-size))) + (min n max-buffer-size) + (comm::ntohl (fli:foreign-slot-value + (fli:foreign-slot-value client-addr + 'comm::sin_addr + :object-type '(:struct comm::sockaddr_in) + :type '(:struct comm::in_addr) + :copy-foreign-object nil) + 'comm::s_addr + :object-type '(:struct comm::in_addr))) + (comm::ntohs (fli:foreign-slot-value client-addr + 'comm::sin_port + :object-type '(:struct comm::sockaddr_in) + :type '(:unsigned :short) + :copy-foreign-object nil))) + (values nil n 0 0)))))))) + +(defmethod socket-receive ((socket datagram-usocket) buffer length &key) + (let ((s (socket socket))) + (multiple-value-bind (buffer size host port) + (receive-message s buffer length) + (values buffer size host port)))) + (defmethod get-local-name ((usocket usocket)) (multiple-value-bind (address port) Modified: usocket/trunk/backend/openmcl.lisp ============================================================================== --- usocket/trunk/backend/openmcl.lisp (original) +++ usocket/trunk/backend/openmcl.lisp Thu Jan 7 13:26:06 2010 @@ -74,20 +74,35 @@ :text :binary)) -(defun socket-connect (host port &key (element-type 'character) timeout deadline nodelay +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) + timeout deadline nodelay local-host local-port) (with-mapped-conditions () - (let ((mcl-sock - (openmcl-socket:make-socket :remote-host (host-to-hostname host) - :remote-port port - :local-host (when local-host (host-to-hostname local-host)) - :local-port local-port - :format (to-format element-type) - :deadline deadline - :nodelay nodelay - :connect-timeout timeout))) - (openmcl-socket:socket-connect mcl-sock) - (make-stream-socket :stream mcl-sock :socket mcl-sock)))) + (ecase protocol + (:stream + (let ((mcl-sock + (openmcl-socket:make-socket :remote-host (host-to-hostname host) + :remote-port port + :local-host (when local-host (host-to-hostname local-host)) + :local-port local-port + :format (to-format element-type) + :deadline deadline + :nodelay nodelay + :connect-timeout timeout))) + (openmcl-socket:socket-connect mcl-sock) + (make-stream-socket :stream mcl-sock :socket mcl-sock))) + (:datagram + (let ((mcl-sock + (openmcl-socket:make-socket :address-family :internet + :type :datagram + :local-host (when local-host (host-to-hostname local-host)) + :local-port local-port + :format :binary))) + (when (and host port) + (ccl::inet-connect (ccl::socket-device mcl-sock) + (ccl::host-as-inet-host host) + (ccl::port-as-inet-port port "udp"))) + (make-datagram-socket mcl-sock)))))) (defun socket-listen (host port &key reuseaddress @@ -121,6 +136,16 @@ (with-mapped-conditions (usocket) (close (socket usocket)))) +(defmethod socket-send ((usocket datagram-usocket) buffer length &key host port) + (with-mapped-conditions (usocket) + (openmcl-socket:send-to (socket usocket) buffer length + :remote-host (host-to-hbo host) + :remote-port port))) + +(defmethod socket-receive ((usocket datagram-usocket) buffer length &key) + (with-mapped-conditions (usocket) + (openmcl-socket:receive-from (socket usocket) length :buffer buffer))) + (defmethod get-local-address ((usocket usocket)) (let ((address (openmcl-socket:local-host (socket usocket)))) (when address Modified: usocket/trunk/backend/sbcl.lisp ============================================================================== --- usocket/trunk/backend/sbcl.lisp (original) +++ usocket/trunk/backend/sbcl.lisp Thu Jan 7 13:26:06 2010 @@ -203,8 +203,7 @@ (if usock-cond (signal usock-cond :socket socket)))))) - -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay t nodelay-specified) local-host local-port &aux @@ -221,29 +220,43 @@ (unsupported 'nodelay 'socket-connect)) (let ((socket (make-instance 'sb-bsd-sockets:inet-socket - :type :stream :protocol :tcp))) + :type protocol + :protocol (case protocol + (:stream :tcp) + (:datagram :udp))))) (handler-case - (let* ((stream - (sb-bsd-sockets:socket-make-stream socket - :input t - :output t - :buffering :full - :element-type element-type)) - ;;###FIXME: The above line probably needs an :external-format - (usocket (make-stream-socket :stream stream :socket socket)) - (ip (host-to-vector-quad host))) - ;; binghe: use SOCKOPT-TCP-NODELAY as internal symbol - ;; to pass compilation on ECL without it. - (when (and nodelay-specified sockopt-tcp-nodelay-p) - (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) nodelay)) - (when (or local-host local-port) - (sb-bsd-sockets:socket-bind socket - (host-to-vector-quad - (or local-host *wildcard-host*)) - (or local-port *auto-port*))) - (with-mapped-conditions (usocket) - (sb-bsd-sockets:socket-connect socket ip port)) - usocket) + (ecase protocol + (:stream + (let* ((stream + (sb-bsd-sockets:socket-make-stream socket + :input t + :output t + :buffering :full + :element-type element-type)) + ;;###FIXME: The above line probably needs an :external-format + (usocket (make-stream-socket :stream stream :socket socket)) + (ip (host-to-vector-quad host))) + ;; binghe: use SOCKOPT-TCP-NODELAY as internal symbol + ;; to pass compilation on ECL without it. + (when (and nodelay-specified sockopt-tcp-nodelay-p) + (setf (sb-bsd-sockets::sockopt-tcp-nodelay socket) nodelay)) + (when (or local-host local-port) + (sb-bsd-sockets:socket-bind socket + (host-to-vector-quad + (or local-host *wildcard-host*)) + (or local-port *auto-port*))) + (with-mapped-conditions (usocket) + (sb-bsd-sockets:socket-connect socket ip port)) + usocket)) + (:datagram + (when (or local-host local-port) + (sb-bsd-sockets:socket-bind socket + (host-to-vector-quad + (or local-host *wildcard-host*)) + (or local-port *auto-port*))) + (when (and host port) + (sb-bsd-sockets:socket-connect socket (host-to-vector-quad host) port)) + (make-datagram-socket socket))) (t (c) ;; Make sure we don't leak filedescriptors (sb-bsd-sockets:socket-close socket) @@ -295,6 +308,18 @@ (with-mapped-conditions (usocket) (close (socket-stream usocket)))) +(defmethod socket-send ((socket datagram-usocket) buffer length &key host port) + (with-mapped-conditions (socket) + (let* ((s (socket socket)) + (dest (if (and host port) (list (host-to-vector-quad host) port) nil))) + (sb-bsd-sockets:socket-send s buffer length :address dest)))) + +(defmethod socket-receive ((socket datagram-usocket) buffer length + &key (element-type '(unsigned-byte 8))) + (with-mapped-conditions (socket) + (let ((s (socket socket))) + (sb-bsd-sockets:socket-receive s buffer length :element-type element-type)))) + (defmethod get-local-name ((usocket usocket)) (sb-bsd-sockets:socket-name (socket usocket))) Modified: usocket/trunk/backend/scl.lisp ============================================================================== --- usocket/trunk/backend/scl.lisp (original) +++ usocket/trunk/backend/scl.lisp Thu Jan 7 13:26:06 2010 @@ -28,7 +28,7 @@ :socket socket :condition condition)))) -(defun socket-connect (host port &key (element-type 'character) +(defun socket-connect (host port &key (protocol :stream) (element-type 'character) timeout deadline (nodelay t nodelay-specified) (local-host nil local-host-p) (local-port nil local-port-p) @@ -43,17 +43,50 @@ (when (and local-port-p (not patch-udp-p)) (unsupported 'local-port 'socket-connect :minimum "1.3.9")) - (let* ((socket (let ((args (list (host-to-hbo host) port :kind :stream))) + (let ((socket)) + (ecase protocol + (:stream + (setf socket (let ((args (list (host-to-hbo host) port :kind protocol))) + (when (and patch-udp-p (or local-host-p local-port-p)) + (nconc args (list :local-host (when local-host + (host-to-hbo local-host)) + :local-port local-port))) + (with-mapped-conditions (socket) + (apply #'ext:connect-to-inet-socket args)))) + (let ((stream (sys:make-fd-stream socket :input t :output t + :element-type element-type + :buffering :full))) + (make-stream-socket :socket socket :stream stream))) + (:datagram + (when (not patch-udp-p) + (error 'unsupported + :feature '(protocol :datagram) + :context 'socket-connect + :minumum "1.3.9")) + (setf socket + (if (and host port) + (let ((args (list (host-to-hbo host) port :kind protocol))) (when (and patch-udp-p (or local-host-p local-port-p)) (nconc args (list :local-host (when local-host (host-to-hbo local-host)) :local-port local-port))) - (with-mapped-conditions () - (apply #'ext:connect-to-inet-socket args)))) - (stream (sys:make-fd-stream socket :input t :output t - :element-type element-type - :buffering :full))) - (make-stream-socket :socket socket :stream stream))) + (with-mapped-conditions (socket) + (apply #'ext:connect-to-inet-socket args))) + (if (or local-host-p local-port-p) + (with-mapped-conditions () + (ext:create-inet-listener (or local-port 0) + protocol + :host (when local-host + (if (ip= local-host *wildcard-host*) + 0 + (host-to-hbo local-host))))) + (with-mapped-conditions () + (ext:create-inet-socket protocol))))) + (let ((usocket (make-datagram-socket socket))) + (ext:finalize usocket #'(lambda () + (when (%open-p usocket) + (ext:close-socket socket)))) + usocket))))) (defun socket-listen (host port &key reuseaddress @@ -99,6 +132,30 @@ (with-mapped-conditions (usocket) (close (socket-stream usocket)))) +(defmethod socket-close :after ((socket datagram-usocket)) + (setf (%open-p socket) nil)) + +(defmethod socket-send ((socket datagram-usocket) buffer length &key host port) + (let ((s (socket socket)) + (host (if host (host-to-hbo host)))) + (multiple-value-bind (result errno) + (ext:inet-socket-send-to s buffer length + :remote-host host :remote-port port) + (or result + (scl-map-socket-error errno :socket socket))))) + +(defmethod socket-receive ((socket datagram-usocket) buffer length &key) + (let ((s (socket socket))) + (let ((real-buffer (or buffer + (make-array length :element-type '(unsigned-byte 8)))) + (real-length (or length + (length buffer)))) + (multiple-value-bind (result errno remote-host remote-port) + (ext:inet-socket-receive-from s real-buffer real-length) + (if result + (values real-buffer result remote-host remote-port) + (scl-map-socket-error errno :socket socket)))))) + (defmethod get-local-name ((usocket usocket)) (multiple-value-bind (address port) (with-mapped-conditions (usocket) Modified: usocket/trunk/package.lisp ============================================================================== --- usocket/trunk/package.lisp (original) +++ usocket/trunk/package.lisp Thu Jan 7 13:26:06 2010 @@ -3,14 +3,20 @@ ;;;; See the LICENSE file for licensing information. -#+lispworks (cl:require "comm") +(in-package :usocket-system) -(cl:eval-when (:execute :load-toplevel :compile-toplevel) - (cl:defpackage :usocket - (:use :cl) - (:export #:*wildcard-host* +#+lispworks +(eval-when (:compile-toplevel :load-toplevel :execute) + (require "comm")) + +(defpackage :usocket + (:use :common-lisp) + (:export #:*wildcard-host* #:*auto-port* + #:*remote-host* ; special variables (udp) + #:*remote-port* + #:socket-connect ; socket constructors and methods #:socket-listen #:socket-accept @@ -22,6 +28,10 @@ #:get-local-name #:get-peer-name + #:socket-send ; udp function (send) + #:socket-receive ; udp function (receive) + #:socket-server ; udp server + #:wait-for-input ; waiting for input-ready state (select() like) #:make-wait-list #:add-waiter @@ -65,9 +75,8 @@ #:ns-unknown-condition #:unknown-error #:ns-unknown-error + #:socket-warning ; warnings (udp) #:insufficient-implementation ; conditions regarding usocket support level #:unsupported - #:unimplemented - ))) - + #:unimplemented)) Modified: usocket/trunk/usocket.asd ============================================================================== --- usocket/trunk/usocket.asd (original) +++ usocket/trunk/usocket.asd Thu Jan 7 13:26:06 2010 @@ -24,10 +24,8 @@ :cl-utilities #+sbcl :sb-bsd-sockets) :components ((:file "package") - (:file "usocket" - :depends-on ("package")) - (:file "condition" - :depends-on ("usocket")) + (:file "usocket" :depends-on ("package")) + (:file "condition" :depends-on ("usocket")) (:module "vendor" :components (#+mcl (:file "kqueue"))) (:module "backend" @@ -40,4 +38,5 @@ #+mcl (:file "mcl") #+openmcl (:file "openmcl") #+allegro (:file "allegro") - #+armedbear (:file "armedbear"))))) + #+armedbear (:file "armedbear"))) + (:file "server" :depends-on ("backend")))) Modified: usocket/trunk/usocket.lisp ============================================================================== --- usocket/trunk/usocket.lisp (original) +++ usocket/trunk/usocket.lisp Thu Jan 7 13:26:06 2010 @@ -11,6 +11,8 @@ (defparameter *auto-port* 0 "Port number to pass when an auto-assigned port number is wanted.") +(defconstant +max-datagram-packet-size+ 65536) + (defclass usocket () ((socket :initarg :socket @@ -83,9 +85,16 @@ be initiated from remote sockets.")) (defclass datagram-usocket (usocket) - ((connected-p :initarg :connected-p :accessor connected-p)) -;; ###FIXME: documentation to be added. - (:documentation "")) + ((connected-p :type boolean + :accessor connected-p + :initarg :connected-p) + #+(or cmu scl lispworks) + (%open-p :type boolean + :accessor %open-p + :initform t + :documentation "Flag to indicate if usocket is open, +for GC on implementions operate on raw socket fd.")) + (:documentation "UDP (inet-datagram) socket")) (defun usocket-p (socket) (typep socket 'usocket)) @@ -151,6 +160,14 @@ (defgeneric socket-close (usocket) (:documentation "Close a previously opened `usocket'.")) +(defgeneric socket-send (usocket buffer length &key host port) + (:documentation "Send packets through a previously opend `usocket'.")) + +(defgeneric socket-receive (usocket buffer length &key) + (:documentation "Receive packets from a previously opend `usocket'. + +Returns 4 values: (values buffer size host port)")) + (defgeneric get-local-address (socket) (:documentation "Returns the IP address of the socket."))