source: branches/home/psmith/restructure/src/io/async-fd.lisp @ 68

Last change on this file since 68 was 68, checked in by psmith, 18 years ago

large packet server side OK.

File size: 8.1 KB
Line 
1#|
2Copyright (c) 2006 Risto Laakso
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions
7are met:
81. Redistributions of source code must retain the above copyright
9notice, this list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright
11notice, this list of conditions and the following disclaimer in the
12documentation and/or other materials provided with the distribution.
133. The name of the author may not be used to endorse or promote products
14derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26|#
27(in-package :nio)
28
29(declaim (optimize (debug 3) (speed 3) (space 0)))
30
31(defclass async-fd ()
32  ((write-fd :initarg :write-fd
33             :accessor write-fd)
34   (read-fd :initarg :read-fd
35            :accessor read-fd)
36   (foreign-read-buffer :initform (byte-buffer 1024)
37                        :accessor foreign-read-buffer)
38   (foreign-write-buffer :initform (byte-buffer 1024)
39                         :accessor foreign-write-buffer)
40   (read-ready :initform nil
41                 :accessor read-ready
42                 :documentation "Have we been notified as read ready and not received EAGAIN from %read?")
43   (write-ready :initform nil
44                  :accessor write-ready
45                  :documentation "Have we been notified as write ready and not received EAGAIN from %write?")
46   (close-pending :initform nil)
47   (socket :initarg :socket
48           :accessor socket)))
49
50
51(defmethod print-object ((async-fd async-fd) stream)
52  (with-slots (socket read-fd write-fd) async-fd
53    (format stream "#<ASYNC-FD :socket ~D :read-fd ~D :write-fd ~D.>"
54            socket read-fd write-fd)))
55
56;;Implement this in concrete SM for read
57(defgeneric process-read (async-fd))
58
59;;Implement this in concrete SM for read
60(defgeneric process-write (async-fd))
61
62;;SM factory
63(defun create-state-machine(sm-type read-fd write-fd socket)
64  (let ((sm (make-instance sm-type :read-fd read-fd :write-fd write-fd :socket socket)))
65    (format-log t "async-fd:create-state-machine - Created ~S~%" sm)
66    (nio-buffer:clear (foreign-read-buffer sm))
67    (nio-buffer:clear (foreign-write-buffer sm))
68    sm))
69
70;;override this in concrete SM for close
71(defmethod process-close((async-fd async-fd)reason)())
72
73
74(defmethod close-sm((async-fd async-fd))
75  :documentation "Mark the socket for close to write"
76#+nio-debug  (format t "(mark for)close called with ~A~%" async-fd)
77  (with-slots (close-pending) async-fd
78      (setf close-pending t)))
79
80(define-condition read-error (error) ())
81
82;;  "Read more data from STATE-MACHINE."
83(defun read-more (state-machine)
84  (with-slots (foreign-read-buffer read-fd) state-machine
85#+nio-debug  (format t "read-more called with ~A~%" state-machine)
86#+nio-debug      (format t "read-more - calling read() into ~A~%" foreign-read-buffer) 
87    (let ((new-bytes (%read read-fd (buffer-pointer foreign-read-buffer) (remaining foreign-read-buffer))))
88#+nio-debug      (format t "read-more : Read ~A bytes into ~A~%" new-bytes foreign-read-buffer)
89      (cond
90       ((< new-bytes 0)
91        (progn
92;;TODO if ret is -1 and errno is EAGAIN save state and wait for notification
93          (format t "read-error - Errno: ~A~%" (get-errno))
94          (error 'read-error)))
95       ((= new-bytes 0)
96        nil);;(throw 'end-of-file nil)
97       (t
98          ;;Update buffer position
99          (inc-position foreign-read-buffer new-bytes)
100          (setf (read-ready state-machine) nil))))))
101
102(defun close-async-fd (async-fd)
103  "Close ASYNC-FD's fd after everything has been written from write-queue."
104#+nio-debug    (format t "close-async-fd called with :async-fd ~A~%" async-fd)
105  (with-slots (read-fd write-fd foreign-read-buffer foreign-write-buffer) async-fd
106    (nio-buffer:flip foreign-write-buffer)
107#+nio-debug    (format t "close-async-fd foreign-write-buffer ~A~%" foreign-write-buffer)
108    (assert (eql (remaining foreign-write-buffer) 0))
109      ;; if write-queue is emtpy, close now
110       (close-fd read-fd)
111       (free-buffer foreign-read-buffer)
112       (free-buffer foreign-write-buffer)
113       (unless (= read-fd write-fd) (close-fd write-fd))))
114
115
116(define-condition read-error (error) ())
117
118(defun write-more (async-fd)
119  "Write data from ASYNC-FD's write bytebuffer"
120  (format-log t "async-fd:write-more - called with ~A~%" async-fd)
121  (with-slots (write-fd foreign-write-buffer close-pending) async-fd
122#+nio-debug  (format t "async-fd:write-more - foreign-write-buffer b4 flip ~A~%" foreign-write-buffer)
123    (nio-buffer:flip foreign-write-buffer)
124#+nio-debug (format t "async-fd:write-more -foreign-write-buffer after flip ~A~%" foreign-write-buffer)
125    (let ((now-written 0))
126      (do ((total-written 0))
127          ((or (eql now-written -1) (eql (remaining foreign-write-buffer) 0)) total-written)
128        (progn
129          (setf now-written (%write write-fd (buffer-buf foreign-write-buffer) (remaining foreign-write-buffer)))
130          (when (not (eql now-written -1))
131            (inc-position foreign-write-buffer now-written)
132            (incf total-written now-written)))
133#+nio-debug (format t "async-fd:write-more - after write :foreign-write-buffer ~A :now-written ~A :total-written ~A ~%" foreign-write-buffer now-written total-written))
134      (if (eql now-written -1)
135          ;;Deal with failure
136        (let ((err (get-errno)))
137          (format t "write-more - write returned -1 :errno ~A~%" err)
138          (unless (eql err 11) ;; eagain - failed to write whole buffer need to wait for next notify
139            (perror)
140            (let ((err-cond (make-instance 'write-error :error err)))
141              (close err-cond)
142              (error err-cond))))
143        ;;update buffers
144        (if (eql (remaining foreign-write-buffer) 0)
145            (clear foreign-write-buffer)
146            (error 'not-implemented-yet))))
147
148#+nio-debug    (format t "write buffer after write :~A~%" foreign-write-buffer)
149      (when (eql (remaining foreign-write-buffer) 0)
150        (when close-pending (close-async-fd async-fd)))))
151
152
153(defconstant +MAX-BUFFER-SIZE-BYTES+ (* 1024 1024))
154
155
156
157;(let ((buffer (foreign-read-buffer async-fd)))
158;          (if (>= (length buffer) size)
159;              t
160;              (let ((new-buffer (byte-buffer size)))
161;                (copy-buffer buffer new-buffer)
162;                (free-buffer buffer)
163;                (setf (foreign-read-buffer async-fd) new-buffer)))))
164
165
166(defmacro realloc-buffer(async-fd accessor size)
167  `(let ((buffer (,accessor ,async-fd)))
168     (if (>= (buffer-capacity buffer) size)
169         t
170         (let ((new-buffer (byte-buffer ,size)))
171           (copy-buffer buffer new-buffer)
172           (free-buffer buffer)
173           (setf (,accessor ,async-fd) new-buffer)))))
174
175
176
177;TODO actually deal with buffer allocation failure
178(defmethod recommend-buffer-size((async-fd async-fd) mode size)
179  (if (> size +MAX-BUFFER-SIZE-BYTES+) nil
180      (ecase mode
181        (:read (realloc-buffer async-fd foreign-read-buffer size))
182        (:write (realloc-buffer async-fd foreign-write-buffer size)))))
183   
184
185
186(defun force-close-async-fd (async-fd)
187  "Drop ASYNC-FD's write-queue and close it."
188  (free-buffer (slot-value async-fd 'foreign-write-buffer))
189  (close-async-fd async-fd))
190
191
192(defun add-async-fd (event-queue async-fd mode)
193  (ecase mode
194    (:read-write (add-fd event-queue (slot-value async-fd 'write-fd) :read-write))))
195
196
197(defun remove-async-fd (event-queue async-fd mode)
198  (ecase mode
199    (:read (remove-fd event-queue (slot-value async-fd 'read-fd) :read))
200    (:write (remove-fd event-queue (slot-value async-fd 'write-fd) :write))
201    (:read-write (remove-fd event-queue (slot-value async-fd 'write-fd) :read-write))))
202
203
204(defun async-fd-read-fd (async-fd)
205  (slot-value async-fd 'read-fd))
206
207(defun async-fd-write-fd (async-fd)
208  (slot-value async-fd 'write-fd))
209
Note: See TracBrowser for help on using the repository browser.