On 15/04/07, Luis Oliveira <[EMAIL PROTECTED]> wrote:
"Faré" <[EMAIL PROTECTED]> writes:
Since Stelian looks busy and that GSoC means there will be activity on
IOLib, here's my current patch to IOLib, it contains:
* character output for socket streams
* character peek and unread for socket streams
* support for EINTR in iomux event selection
* support for retrying with a timeout in general
Thanks!  You forgot to attach the patch though.
Hear hear! I am sure you are not stopped by such trifles.

[ François-René ÐVB Rideau | Reflection&Cybernethics | http://fare.tunes.org ]
http://Bastiat.org/ - debunking economic sophisms since 1845.
diff --git a/io-multiplex/common.lisp b/io-multiplex/common.lisp
index 4acaab4..e1d166a 100644
--- a/io-multiplex/common.lisp
+++ b/io-multiplex/common.lisp
@@ -271,7 +271,15 @@
   (with-accessors ((mux mux-of) (fds fds-of)
                    (timeouts timeouts-of)) event-base
     (let ((deletion-list ())
-          (fd-events (harvest-events mux timeout)))
+          (fd-events (block nil
+                       (repeat-with-timeout
+                        timeout
+                        #'(lambda (timeout)
+                            (handler-case
+                                (return (harvest-events mux timeout))
+                              (et:unix-error-intr () nil)))
+                        #'(lambda ()
+                            (return-from dispatch-fd-events-once nil))))))
       (dolist (ev fd-events)
         (destructuring-bind (fd ev-types) ev
           (let ((fd-entry (fd-entry-of event-base fd)))
@@ -517,6 +525,10 @@
 (iolib-utils:define-constant et::pollrdhup 0)
 
 (defun wait-until-fd-ready (fd event-type &optional timeout)
+  ;; FIXME: this conses badly for its return value
+  ;; solution: (1) use a fixnum bitmap, just like C
+  ;; (2) if we really want to expose only lists of keyword as the API,
+  ;; cache a bitmap-indexed vector of all the combinations (sharing tails)
   (flet ((choose-poll-flags (type)
            (ecase type
              (:read (logior et:pollin et::pollrdhup et:pollpri))
diff --git a/io-multiplex/defpackage.lisp b/io-multiplex/defpackage.lisp
index 6f068fc..3878a2e 100644
--- a/io-multiplex/defpackage.lisp
+++ b/io-multiplex/defpackage.lisp
@@ -35,4 +35,6 @@
    #:add-fd #:add-timeout
    #:remove-event #:event-dispatch
 
+   #:repeat-with-timeout
+
    #:wait-until-fd-ready #:fd-ready-p))
diff --git a/io-multiplex/time.lisp b/io-multiplex/time.lisp
index f87ffa9..6689655 100644
--- a/io-multiplex/time.lisp
+++ b/io-multiplex/time.lisp
@@ -63,3 +63,14 @@
           (min t1 t2)
           t1)
       t2))
+
+
+(defun repeat-with-timeout (timeout thunk
+                                   &optional
+                                   (timeout-thunk (constantly :timeout)))
+  (loop with deadline = (if timeout (+ (gettime) timeout) 0) do
+        (funcall thunk timeout)
+        (when timeout
+          (setf timeout (- deadline (gettime)))
+          (unless (> timeout 0)
+            (return (funcall timeout-thunk))))))
diff --git a/io.encodings/external-format.lisp b/io.encodings/external-format.lisp
index e274cc7..955ffed 100644
--- a/io.encodings/external-format.lisp
+++ b/io.encodings/external-format.lisp
@@ -537,14 +537,17 @@
   `(funcall (ef-char-to-octets ,ef) ,input ,output ,error-fn
             ,chars-left))
 
-(defun string-to-octets (string &key (start 0) end
+(defun string-to-octets (string &key start end
                          (external-format :default)
                          adjust-factor)
   (declare (type string string)
-           (type buffer-index start)
+           (type (or null buffer-index) start)
            (type (or null buffer-index) end)
            (type (or null real) adjust-factor)
            (optimize (speed 3) (space 0) (safety 0) (debug 0)))
+
+  (setf start (or start 0)
+        end (or end (length string)))
   (let* ((ef (find-external-format external-format))
          (buffer (make-array (1+ (length string))
                              :element-type 'octet
@@ -554,8 +557,7 @@
          (pos -1) oldpos)
     (setf adjust-factor (if (and adjust-factor (<= 1 adjust-factor 4))
                             adjust-factor
-                            (ef-octet-size ef))
-          end (or end (length string)))
+                            (ef-octet-size ef)))
     (tagbody
        (flet ((input ()
                 (prog1 (char string ptr) (incf ptr)))
diff --git a/sockets/gray-stream-methods.lisp b/sockets/gray-stream-methods.lisp
index 9d9f248..ed5fe23 100644
--- a/sockets/gray-stream-methods.lisp
+++ b/sockets/gray-stream-methods.lisp
@@ -92,13 +92,16 @@
     (iobuf-reset ob)
     nil))
 
-;; (defmethod stream-finish-output ((stream active-socket))
-;;   (with-slots ((ob output-buffer)) stream
-;;     nil))
+(defmethod stream-finish-output ((stream active-socket))
+  (with-slots ((ob output-buffer) fd) stream
+    (flush-obuf ob fd)
+    nil))
 
-;; (defmethod stream-force-output ((stream active-socket))
-;;   (with-slots ((ob output-buffer)) stream
-;;     nil))
+(defmethod stream-force-output ((stream active-socket))
+  ;; FIXME: add non-blocking version of this?
+  ;; and/or re-write the flush code in a non-blocking variant,
+  ;; and have the finish-output synchronize on the result.
+  (stream-finish-output stream))
 
 ;; (defmethod stream-read-sequence ((stream active-socket) seq
 ;;                                  &optional start end)
@@ -110,8 +113,13 @@
 ;;                 ;;
 ;;;;;;;;;;;;;;;;;;;;;
 
-(defun fill-iobuf (buf fd &optional timeout)
-  (iomux:wait-until-fd-ready fd :read timeout)
+(defun fill-ibuf (buf fd &optional timeout)
+  (when timeout
+    (let ((status
+           (iomux:wait-until-fd-ready fd :read timeout)))
+      (unless (member :read status)
+        ;; FIXME signal something better
+        (return-from fill-ibuf :timeout))))
   (let ((num (et:read fd (cffi:inc-pointer (iobuf-data buf)
                                            (iobuf-start buf))
                       (- (iobuf-size buf)
@@ -133,7 +141,7 @@
                 (return (values #\Newline 1))))
         (:dos (when (= char-code (char-code #\Return))
                 (when (and (= (iobuf-length ib) 1)
-                           (eq (fill-iobuf ib fd) :eof))
+                           (eq (fill-ibuf ib fd) :eof))
                   (incf (iobuf-start ib))
                   (return (values #\Return 1)))
                 (when (= (bref ib (1+ start-off))
@@ -151,7 +159,7 @@
           (ret nil))
       (flet ((fill-buf-or-eof ()
                ;; FIXME - what if we can't refill, in the middle of a wide-char??
-               (setf ret (fill-iobuf ib fd))
+               (setf ret (fill-ibuf ib fd))
                (when (eq ret :eof)
                  (return-from stream-read-char :eof))))
         (cond ((zerop (iobuf-length ib))
@@ -215,7 +223,7 @@
         (when (< 0 (iobuf-end-space-length ib) 4)
           (iobuf-copy-data-to-start ib))
         (when (and (iomux:fd-ready-p fd :read)
-                   (eql :eof (fill-iobuf ib fd)))
+                   (eql :eof (fill-ibuf ib fd)))
           (setf eof t))
         (when (zerop (iobuf-length ib))
           (return (if eof :eof nil)))
@@ -245,6 +253,8 @@
         (char str 0)))))
 
 (defun %stream-unread-char (stream)
+  ;; unreading anything but the latest character is wrong,
+  ;; but checking is not mandated by the standard
   (declare (type active-socket stream))
   (with-slots ((ib input-buffer) (unread-index ibuf-unread-index)) stream
     (symbol-macrolet ((start (iobuf-start ib)))
@@ -256,6 +266,12 @@
   nil)
 
 (defmethod stream-unread-char ((stream active-socket) character)
+  #+super-anal-checks
+  (progn
+    (%stream-unread-char stream)
+    (unless (ignore-errors (eql (stream-read-char stream) character))
+      (error "Trying to unread wrong character ~S" character)))
+  #-super-anal-checks
   (declare (ignore character))
   (%stream-unread-char stream))
 
@@ -324,11 +340,73 @@
          (ioenc::char-to-octets ef #'input #'output #'error-fn (- end ptr)))
       (exit))))
 
+(defun flush-obuf (buf fd &optional timeout)
+  (flet ((write-once ()
+           (let* ((num (et:write
+                        fd
+                        (cffi:inc-pointer (iobuf-data buf)
+                                          (iobuf-start buf))
+                        (iobuf-length buf))))
+             (if (zerop num)
+               nil
+               (progn (incf (iobuf-start buf) num) t))))
+         (emptyp ()
+           (let ((x (= (iobuf-end buf) (iobuf-start buf))))
+             (when x
+               (iobuf-reset buf)
+               t))))
+    (if (emptyp)
+      (values t nil)
+      (block nil
+        (iomux:repeat-with-timeout
+         timeout
+         #'(lambda (timeout)
+             (let ((status (iomux:wait-until-fd-ready fd :write timeout)))
+               (unless (member :write status)
+                 ;; FIXME signal something better -- maybe analyze the status
+                 (return (values nil :timeout)))
+               (unless (write-once)
+                 (return (values nil :fail)))
+               (when (emptyp)
+                 (return t))))
+         #'(lambda ()
+             (return (values nil :timeout))))))))
+
+(defgeneric %stream-write-octets (stream octets &optional start end))
+
 (defmethod %stream-write-octets ((stream active-socket) octets
                                  &optional start end)
-  (error "NOT IMPLEMENTED YET"))
+  ;; FIXME: when calling write-sequence with a simple-array of octets
+  ;; do required I/O directly, not through a buffer
+  (check-type octets (simple-array ub8 (*)))
+  (let ((max (length octets)))
+    (if start
+      (check-type start (integer 0 *))
+      (setf start 0))
+    (if end
+      (progn
+        (check-type end (integer 0 *))
+        (assert (<= end max)))
+      (setf end max)))
+  (with-slots ((buf output-buffer) fd) stream
+    (loop while (< start end) do
+          (let ((len (min (- end start) (iobuf-end-space-length buf))))
+          (setf *print-readably* nil)
+            ;; FIXME: optimize this BLT
+            (loop for i from start
+                  for j from (iobuf-end buf)
+                  repeat len do
+                  (setf (bref buf j) (aref octets i))
+                  )
+            (incf (iobuf-end buf) len)
+            (incf start len)
+            (when (= (iobuf-end buf) (iobuf-size buf))
+            (or (flush-obuf buf fd)
+                ;; FIXME: better error handling
+                (error "Failed to write octets")))))))
 
 (defmethod stream-write-char ((stream active-socket) character)
+  ;; FIXME: avoid consing a string here. At worst, declare it dynamic-extent
   (stream-write-string stream (make-string 1 :initial-element character)))
 
 ;; (defmethod stream-advance-to-column ((stream active-socket)
@@ -348,10 +426,15 @@
 (defmethod stream-write-string ((stream active-socket)
                                 (string string)
                                 &optional start end)
+  ;; FIXME: have the ef do i/o directly into the existing buffer,
+  ;; don't do double buffering of I/O
   (%stream-write-octets
    stream
    (ioenc:string-to-octets string :start start :end end
                            :external-format (slot-value stream 'external-format))))
+
+;; FIXME: isn't there a generic stream-write-sequence???
+
 
 ;;;;;;;;;;;;;;;;;;
 ;;              ;;
@@ -363,7 +446,7 @@
   (with-slots ((fd fd) (ib input-buffer)
                (pos istream-pos)) stream
     (flet ((fill-buf-or-eof ()
-             (when (eq :eof (fill-iobuf ib fd))
+             (when (eq :eof (fill-ibuf ib fd))
                (return-from stream-read-byte :eof))))
       (when (zerop (iobuf-length ib))
         (iobuf-reset ib)
_______________________________________________
iolib-devel mailing list
[email protected]
http://common-lisp.net/cgi-bin/mailman/listinfo/iolib-devel

Reply via email to