1.0.42.14: socket-connect thread safety, version 2
authorNikodemus Siivola <nikodemus@random-state.net>
Wed, 1 Sep 2010 14:14:55 +0000 (14:14 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Wed, 1 Sep 2010 14:14:55 +0000 (14:14 +0000)
 * Code from 1.0.40.7 to use getprotobyname_r.

 * Use grovel-features.sh to check for its and getprotobynumber_r's
   existence, and add feature :os-provides-getprotoby-r when so.

   If they do not exist, use a lock instead.

NEWS
contrib/sb-bsd-sockets/constants.lisp
contrib/sb-bsd-sockets/inet.lisp
tools-for-build/grovel-features.sh
tools-for-build/os-provides-getprotoby-r-test.c [new file with mode: 0644]
version.lisp-expr

diff --git a/NEWS b/NEWS
index d8c19e0..7590b59 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ changes relative to sbcl-1.0.42
     &co.
   * enhancement: ASDF has been updated to version 2.004. (lp#605260, thanks to
     Faré Rideau)
+  * bug fix: SB-BSD-SOCKETS:SOCKET-CONNECT was not thread safe. (lp#505497,
+    thanks to Andrew Golding)
   * bug fix: DOTIMES accepted literal non-integer reals. (lp#619393, thanks to
     Roman Marynchak)
   * bug fix: WRITE-TO-STRING compiler macro binding special variable names,
index 3cc35a4..efd65ba 100644 (file)
@@ -74,6 +74,7 @@
  (:integer EOPNOTSUPP "EOPNOTSUPP")
  (:integer EPERM "EPERM")
  (:integer EPROTONOSUPPORT "EPROTONOSUPPORT")
+ (:integer ERANGE "ERANGE")
  (:integer ESOCKTNOSUPPORT "ESOCKTNOSUPPORT")
  (:integer ENETUNREACH "ENETUNREACH")
  (:integer ENOTCONN "ENOTCONN")
                        (integer proto "int" "p_proto")))
  (:function getprotobyname ("getprotobyname" (* protoent)
                                              (name c-string)))
+
+;; getprotobyname_r is a thread-safe reentrant version of getprotobyname
+ #+os-provides-getprotoby-r
+ (:function getprotobyname-r ("getprotobyname_r" int
+                                                 (name c-string)
+                                                 (result_buf (* protoent))
+                                                 (buffer (* char))
+                                                 (buffer-len size-t)
+                                                 #-solaris
+                                                 (result (* (* protoent)))))
+
+
  (:function getprotobynumber ("getprotobynumber" (* protoent)
                                                  (proto int)))
+ ;;ditto, save for the getprotobynumber part
+ #+os-provides-getprotoby-r
+ (:function getprotobynumber-r ("getprotobynumber_r" int
+                                                 (proto int)
+                                                 (result_buf (* protoent))
+                                                 (buffer (* char))
+                                                 (buffer-len size-t)
+                                                 #-solaris
+                                                 (result (* (* protoent)))))
+
  (:integer inaddr-any "INADDR_ANY")
  (:structure in-addr ("struct in_addr"
                       ((array (unsigned 8)) addr "u_int32_t" "s_addr")))
index 869b80f..351251f 100644 (file)
@@ -64,17 +64,76 @@ Examples:
              (format s "Protocol not found: ~a" (prin1-to-string
                                                  (unknown-protocol-name c))))))
 
+#+(and sb-thread (not os-provides-getprotoby-r))
+;; Since getprotobyname is not thread-safe, we need a lock.
+(sb-ext:defglobal **getprotoby-lock** (sb-thread:make-mutex :name "getprotoby lock"))
+
 ;;; getprotobyname only works in the internet domain, which is why this
 ;;; is here
 (defun get-protocol-by-name (name)      ;exported
-  "Returns the network protocol number associated with the string NAME,
-using getprotobyname(2) which typically looks in NIS or /etc/protocols"
-  ;; for extra brownie points, could return canonical protocol name
-  ;; and aliases as extra values
-  (let ((ent (sockint::getprotobyname name)))
-    (if (sb-alien::null-alien ent)
-        (error 'unknown-protocol :name name))
-    (sockint::protoent-proto ent)))
+
+  ;; Brownie Points.  Hopefully there's one person out there using
+  ;; RSPF sockets and SBCL who will appreciate the extra info
+  (labels ((protoent-to-values (protoent)
+             (values
+              (sockint::protoent-proto protoent)
+              (sockint::protoent-name protoent)
+              (let ((index 0))
+                (loop
+                   for alias = (sb-alien:deref (sockint::protoent-aliases protoent) index)
+                   while (not (sb-alien:null-alien alias))
+                   do (incf index)
+                   collect (sb-alien::c-string-to-string (sb-alien:alien-sap alias)
+                                                         (sb-impl::default-external-format)
+                                                         'character))))))
+    #+(and sb-thread os-provides-getprotoby-r)
+    (let ((buffer-length 1024)
+          (max-buffer 10000))
+      (declare (type fixnum buffer-length)
+               (type fixnum max-buffer))
+      (loop
+         (sb-alien:with-alien ((result-buf (* sockint::protoent)
+                                           (sb-alien:make-alien sockint::protoent))
+                               (buffer (* char )
+                                       (sb-alien:make-alien sb-alien:char buffer-length))
+                               #-solaris
+                               (result (* (* sockint::protoent))
+                                       (sb-alien:make-alien (* sockint::protoent))))
+
+           (let ((res (sockint::getprotobyname-r name
+                                                 result-buf
+                                                 buffer
+                                                 buffer-length
+                                                 #-solaris
+                                                 result)))
+             (if (eql res 0)
+                 (progn
+                   #-solaris
+                   (when (sb-alien::null-alien (sb-alien:deref result 0))
+                     (error 'unknown-protocol :name name))
+                   (return-from get-protocol-by-name
+                     (protoent-to-values result-buf)))
+                 (let ((errno (sb-unix::get-errno)))
+                   (if (eql errno  sockint::erange)
+                       (progn
+                         (incf buffer-length 1024)
+                         (if (> buffer-length max-buffer)
+                             (error "Exceeded max-buffer of ~d" max-buffer)))
+                       (error "Unexpected errno ~d" errno))))))))
+    #-(and sb-thread os-provides-getprotoby-r)
+    (tagbody
+       (flet ((get-it ()
+                (let ((ent (sockint::getprotobyname name)))
+                  (if (sb-alien::null-alien ent)
+                      (go :error)
+                      (return-from get-protocol-by-name (protoent-to-values ent))))))
+         #+sb-thread
+         (sb-thread::with-system-mutex (**getprotoby-lock**)
+           (get-it))
+         #-sb-thread
+         (get-it))
+     :error
+       (error 'unknown-protocol :name name))))
 
 ;;; our protocol provides make-sockaddr-for, size-of-sockaddr,
 ;;; bits-of-sockaddr
index 8bc5a2c..d889caa 100644 (file)
@@ -28,3 +28,5 @@ featurep os-provides-putwc
 featurep os-provides-blksize-t
 
 featurep os-provides-suseconds-t
+
+featurep os-provides-getprotoby-r
diff --git a/tools-for-build/os-provides-getprotoby-r-test.c b/tools-for-build/os-provides-getprotoby-r-test.c
new file mode 100644 (file)
index 0000000..b09f0e4
--- /dev/null
@@ -0,0 +1,16 @@
+/* test to build and run so that we know if we have getprotobyname_r
+ * and getprotobynumber_r */
+
+#include <netdb.h>
+
+#define BUFSIZE 1024
+
+int main ()
+{
+    struct protoent result_buf;
+    struct protoent *result;
+    char buf[BUFSIZE];
+    getprotobyname_r("", &result_buf, buf, BUFSIZE, &result);
+    getprotobynumber_r("", &result_buf, buf, BUFSIZE, &result);
+    return 104;
+}
index 7d66b8f..e552d0a 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.42.13"
+"1.0.42.14"