Version 0.5.4
[iolib.git] / io-multiplex / select.lisp
blob358e4dc6bd71b4e13d85670abb5286b9d40abeea
1 ;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp -*-
3 ;; Copyright (C) 2006, 2007 Stelian Ionescu
4 ;;
5 ;; This code is free software; you can redistribute it and/or
6 ;; modify it under the terms of the version 2.1 of
7 ;; the GNU Lesser General Public License as published by
8 ;; the Free Software Foundation, as clarified by the
9 ;; preamble found here:
10 ;; http://opensource.franz.com/preamble.html
12 ;; This program is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU Lesser General
18 ;; Public License along with this library; if not, write to the
19 ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 ;; Boston, MA 02110-1301, USA
22 (in-package :io.multiplex)
24 (defconstant +select-priority+ 3)
26 (define-multiplexer select-multiplexer +select-priority+ (multiplexer)
27 ((max-fd :initform 0
28 :accessor max-fd-of)
29 (read-fd-set :initform (allocate-fd-set)
30 :reader read-fd-set-of)
31 (write-fd-set :initform (allocate-fd-set)
32 :reader write-fd-set-of)
33 (except-fd-set :initform (allocate-fd-set)
34 :reader except-fd-set-of))
35 (:default-initargs :fd-limit (1- et:fd-setsize)))
37 (defun allocate-fd-set ()
38 (et:fd-zero (foreign-alloc 'et:fd-set)))
40 (defmethod print-object ((mux select-multiplexer) stream)
41 (print-unreadable-object (mux stream :type nil :identity nil)
42 (format stream "select(2) multiplexer")))
44 (defmethod close-multiplexer progn ((mux select-multiplexer))
45 (foreign-free (read-fd-set-of mux))
46 (foreign-free (write-fd-set-of mux))
47 (foreign-free (except-fd-set-of mux))
48 (dolist (slot '(max-fd read-fd-set write-fd-set except-fd-set))
49 (setf (slot-value mux slot) nil)))
51 (defun find-max-fd (fd-set end)
52 (loop :for i :downfrom end :to 0
53 :do (when (et:fd-isset i fd-set)
54 (return-from find-max-fd i)))
55 ;; this means no fd <= end is set
56 -1)
58 (defun recalc-fd-masks (mux fd read write)
59 (with-accessors ((rs read-fd-set-of) (ws write-fd-set-of)
60 (es except-fd-set-of) (max-fd max-fd-of)) mux
61 (cond (read
62 (et:fd-set fd rs)
63 (et:fd-set fd es))
65 (et:fd-clr fd rs)
66 (et:fd-clr fd es)))
67 (if write
68 (et:fd-set fd ws)
69 (et:fd-clr fd ws))
70 (setf max-fd (max (find-max-fd rs fd)
71 (find-max-fd ws fd)))
72 t))
74 (defmethod monitor-fd ((mux select-multiplexer) fd-entry)
75 (recalc-fd-masks mux (fd-entry-fd fd-entry)
76 (not (queue-empty-p (fd-entry-read-events fd-entry)))
77 (not (queue-empty-p (fd-entry-write-events fd-entry)))))
79 (defmethod update-fd ((mux select-multiplexer) fd-entry)
80 (recalc-fd-masks mux (fd-entry-fd fd-entry)
81 (not (queue-empty-p (fd-entry-read-events fd-entry)))
82 (not (queue-empty-p (fd-entry-write-events fd-entry)))))
84 (defmethod unmonitor-fd ((mux select-multiplexer) fd-entry)
85 (recalc-fd-masks mux (fd-entry-fd fd-entry) nil nil))
87 (defmethod harvest-events ((mux select-multiplexer) timeout)
88 (with-accessors ((rs read-fd-set-of) (ws write-fd-set-of)
89 (es except-fd-set-of) (max-fd max-fd-of)) mux
90 ;; if there are no fds set and timeout is NULL
91 ;; select() blocks forever
92 (when (and (minusp max-fd)
93 (null timeout))
94 (warn "Non fds to monitor and no timeout set !")
95 (return-from harvest-events nil))
96 (with-foreign-objects ((read-fds 'et:fd-set)
97 (write-fds 'et:fd-set)
98 (except-fds 'et:fd-set))
99 (et:copy-fd-set rs read-fds)
100 (et:copy-fd-set ws write-fds)
101 (et:copy-fd-set es except-fds)
102 (handler-case
103 (with-foreign-object (tv 'et:timeval)
104 (et:repeat-upon-condition-decreasing-timeout ((et:eintr)
105 tmp-timeout timeout)
106 (when tmp-timeout
107 (timeout->timeval tmp-timeout tv))
108 (et:select (1+ max-fd)
109 read-fds
110 write-fds
111 except-fds
112 (if tmp-timeout tv (null-pointer)))))
113 (et:ebadf ()
114 (return-from harvest-events
115 (harvest-select-fd-errors rs ws max-fd))))
116 (harvest-select-events max-fd read-fds write-fds except-fds))))
118 (defun harvest-select-events (max-fd read-fds write-fds except-fds)
119 (loop :for fd :upto max-fd
120 :for event := () :then ()
121 :when (or (et:fd-isset fd read-fds)
122 (et:fd-isset fd except-fds)) :do (push :read event)
123 :when (et:fd-isset fd write-fds) :do (push :write event)
124 :when event :collect (list fd event)))
126 ;; FIXME: I don't know whether on all *nix systems select()
127 ;; returns EBADF only when a given FD present in some fd-set
128 ;; is closed(as the POSIX docs say) or if some other kinds of
129 ;; errors are reported too(as the Linux manpages seem to suggest)
130 (defun fd-error-p (fd)
131 (not (et:fd-open-p fd)))
133 (defun harvest-select-fd-errors (read-fds write-fds max-fd)
134 (loop :for fd :upto max-fd
135 :when (and (or (et:fd-isset fd read-fds)
136 (et:fd-isset fd write-fds))
137 (fd-error-p fd))
138 :collect (cons fd :error)))