Another round of IO.MULTIPLEX cleanup, API changes.
[iolib.git] / io.multiplex / event-loop.lisp
blobf6e24a0b48b7903d8cd01c0e71f5a5c4aa2aed68
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
2 ;;;
3 ;;; --- Main event loop.
4 ;;;
6 (in-package :io.multiplex)
9 ;;;-------------------------------------------------------------------------
10 ;;; Classes and Types
11 ;;;-------------------------------------------------------------------------
13 (defclass event-base ()
14 ((mux :reader mux-of)
15 (fds :initform (make-hash-table :test 'eql)
16 :reader fds-of)
17 (timers :initform (make-priority-queue :key #'%timer-expire-time)
18 :reader timers-of)
19 (fd-timers :initform (make-priority-queue :key #'%timer-expire-time)
20 :reader fd-timers-of)
21 (expired-events :initform nil
22 :accessor expired-events-of)
23 (exit :initform nil
24 :accessor exit-p)
25 (exit-when-empty :initarg :exit-when-empty
26 :accessor exit-when-empty-p))
27 (:default-initargs :mux *default-multiplexer*
28 :exit-when-empty nil))
31 ;;;-------------------------------------------------------------------------
32 ;;; PRINT-OBJECT
33 ;;;-------------------------------------------------------------------------
35 (defmethod print-object ((base event-base) stream)
36 (print-unreadable-object (base stream :type nil :identity t)
37 (if (fds-of base)
38 (format stream "event base, ~A FDs monitored, using: ~A"
39 (hash-table-count (fds-of base)) (mux-of base))
40 (format stream "event base, closed"))))
43 ;;;-------------------------------------------------------------------------
44 ;;; Generic functions
45 ;;;-------------------------------------------------------------------------
47 (defgeneric set-io-handler (event-base fd event-type function &key timeout one-shot))
49 (defgeneric set-error-handler (event-base fd function))
51 (defgeneric add-timer (event-base function timeout &key one-shot))
53 (defgeneric remove-fd-handlers (event-base fd &key read write error))
55 (defgeneric remove-timer (event-base timer))
57 (defgeneric event-dispatch (event-base &key one-shot timeout min-step max-step))
59 (defgeneric exit-event-loop (event-base &key delay))
61 (defgeneric event-base-empty-p (event-base))
64 ;;;-------------------------------------------------------------------------
65 ;;; Constructors
66 ;;;-------------------------------------------------------------------------
68 (defmethod initialize-instance :after
69 ((base event-base) &key mux)
70 (setf (slot-value base 'mux) (make-instance mux)))
73 ;;;-------------------------------------------------------------------------
74 ;;; CLOSE
75 ;;;-------------------------------------------------------------------------
77 ;;; KLUDGE: CLOSE is for streams. --luis
78 ;;;
79 ;;; Also, we might want to close FDs here. Or have a version/argument
80 ;;; that handles that. Or... add finalizers to the fd streams.
81 (defmethod close ((event-base event-base) &key abort)
82 (declare (ignore abort))
83 (close-multiplexer (mux-of event-base))
84 (dolist (slot '(mux fds timers fd-timers expired-events))
85 (setf (slot-value event-base slot) nil))
86 (values event-base))
89 ;;;-------------------------------------------------------------------------
90 ;;; Helper macros
91 ;;;-------------------------------------------------------------------------
93 (defmacro with-event-base ((var &rest initargs) &body body)
94 "Binds VAR to a new EVENT-BASE, instantiated with INITARGS,
95 within the extent of BODY. Closes VAR."
96 `(let ((,var (make-instance 'event-base ,@initargs)))
97 (unwind-protect
98 (locally ,@body)
99 (when ,var (close ,var)))))
102 ;;;-------------------------------------------------------------------------
103 ;;; Utilities
104 ;;;-------------------------------------------------------------------------
106 (defun fd-entry-of (event-base fd)
107 (gethash fd (fds-of event-base)))
109 (defun (setf fd-entry-of) (fd-entry event-base fd)
110 (setf (gethash fd (fds-of event-base)) fd-entry))
112 (defmethod exit-event-loop ((event-base event-base) &key (delay 0))
113 (add-timer event-base
114 (lambda () (setf (exit-p event-base) t))
115 delay :one-shot t))
117 (defmethod event-base-empty-p ((event-base event-base))
118 (and (zerop (hash-table-count (fds-of event-base)))
119 (priority-queue-empty-p (timers-of event-base))))
122 ;;;-------------------------------------------------------------------------
123 ;;; SET-IO-HANDLER
124 ;;;-------------------------------------------------------------------------
126 (defmethod set-io-handler :before
127 ((event-base event-base) fd event-type function &key timeout one-shot)
128 (declare (ignore timeout))
129 (check-type fd unsigned-byte)
130 (check-type event-type fd-event-type)
131 (check-type function function-designator)
132 ;; FIXME: check the type of the timeout
133 (check-type one-shot boolean)
134 (when (fd-monitored-p event-base fd event-type)
135 (error "FD ~A is already monitored for event ~A" fd event-type)))
137 (defun fd-monitored-p (event-base fd event-type)
138 (let ((entry (fd-entry-of event-base fd)))
139 (and entry (fd-entry-handler entry event-type))))
141 (defmethod set-io-handler
142 ((event-base event-base) fd event-type function &key timeout one-shot)
143 (let ((current-fd-entry (fd-entry-of event-base fd))
144 (event (make-fd-handler fd event-type function one-shot)))
145 (cond
146 (current-fd-entry
147 (%set-io-handler event-base fd event current-fd-entry timeout)
148 (update-fd (mux-of event-base) current-fd-entry event-type :add))
150 (let ((new-fd-entry (make-fd-entry fd)))
151 (%set-io-handler event-base fd event new-fd-entry timeout)
152 (monitor-fd (mux-of event-base) new-fd-entry))))
153 (values event)))
155 (defun %set-io-handler (event-base fd event fd-entry timeout)
156 (when timeout
157 (%set-io-handler-timer event-base event timeout))
158 (setf (fd-entry-handler fd-entry (fd-handler-type event)) event)
159 (setf (fd-entry-of event-base fd) fd-entry)
160 (values event))
162 (defun %set-io-handler-timer (event-base event timeout)
163 (let ((timer (make-timer (lambda () (expire-event event-base event))
164 timeout)))
165 (setf (fd-handler-timer event) timer)
166 (schedule-timer (fd-timers-of event-base) timer)))
168 (defun expire-event (event-base event)
169 (push event (expired-events-of event-base)))
172 ;;;-------------------------------------------------------------------------
173 ;;; SET-ERROR-HANDLER
174 ;;;-------------------------------------------------------------------------
176 (defmethod set-error-handler :before
177 ((event-base event-base) fd function)
178 (check-type fd unsigned-byte)
179 (check-type function function-designator)
180 (unless (fd-entry-of event-base fd)
181 (error "FD ~A is not being monitored" fd))
182 (when (fd-has-error-handler-p event-base fd)
183 (error "FD ~A already has an error handler" fd)))
185 (defun fd-has-error-handler-p (event-base fd)
186 (let ((entry (fd-entry-of event-base fd)))
187 (and entry (fd-entry-error-callback entry))))
189 (defmethod set-error-handler
190 ((event-base event-base) fd function)
191 (let ((fd-entry (fd-entry-of event-base fd)))
192 (setf (fd-entry-error-callback fd-entry) function)))
195 ;;;-------------------------------------------------------------------------
196 ;;; ADD-TIMER
197 ;;;-------------------------------------------------------------------------
199 (defmethod add-timer :before
200 ((event-base event-base) function timeout &key one-shot)
201 (declare (ignore timeout))
202 (check-type function function-designator)
203 ;; FIXME: check the type of the timeout
204 (check-type one-shot boolean))
206 (defmethod add-timer
207 ((event-base event-base) function timeout &key one-shot)
208 (schedule-timer (timers-of event-base)
209 (make-timer function timeout :one-shot one-shot)))
212 ;;;-------------------------------------------------------------------------
213 ;;; REMOVE-FD-HANDLERS and REMOVE-TIMER
214 ;;;-------------------------------------------------------------------------
216 (defmethod remove-fd-handlers
217 ((event-base event-base) fd &key read write error)
218 (unless (or read write error)
219 (setf read t write t error t))
220 (let ((entry (fd-entry-of event-base fd)))
221 (cond
222 (entry
223 (%remove-fd-handlers event-base fd entry read write error)
224 (assert (null (fd-entry-of event-base fd))))
226 (error "Trying to remove a non-monitored FD.")))))
228 (defun %remove-fd-handlers (event-base fd entry read write error)
229 (let ((rev (fd-entry-read-handler entry))
230 (wev (fd-entry-write-handler entry)))
231 (when (and rev read)
232 (%remove-io-handler event-base fd entry rev))
233 (when (and wev write)
234 (%remove-io-handler event-base fd entry wev))
235 (when error
236 (setf (fd-entry-error-callback entry) nil))))
238 (defun %remove-io-handler (event-base fd fd-entry event)
239 (let ((event-type (fd-handler-type event)))
240 (setf (fd-entry-handler fd-entry event-type) nil)
241 (when-let (timer (fd-handler-timer event))
242 (unschedule-timer (fd-timers-of event-base) timer))
243 (cond
244 ((fd-entry-empty-p fd-entry)
245 (%remove-fd-entry event-base fd)
246 (unmonitor-fd (mux-of event-base) fd-entry))
248 (update-fd (mux-of event-base) fd-entry event-type :del)))))
250 (defun %remove-fd-entry (event-base fd)
251 (remhash fd (fds-of event-base)))
253 (defmethod remove-timer :before
254 ((event-base event-base) timer)
255 (check-type timer timer))
257 (defmethod remove-timer ((event-base event-base) timer)
258 (unschedule-timer (timers-of event-base) timer)
259 (values event-base))
262 ;;;-------------------------------------------------------------------------
263 ;;; EVENT-DISPATCH
264 ;;;-------------------------------------------------------------------------
266 (defvar *minimum-event-loop-step* 0.5d0)
267 (defvar *maximum-event-loop-step* 1.0d0)
269 (defmethod event-dispatch :before
270 ((event-base event-base) &key timeout one-shot min-step max-step)
271 (declare (ignore one-shot min-step max-step))
272 (setf (exit-p event-base) nil)
273 (when timeout
274 (exit-event-loop event-base :delay timeout)))
276 (defmethod event-dispatch ((event-base event-base) &key one-shot timeout
277 (min-step *minimum-event-loop-step*)
278 (max-step *maximum-event-loop-step*))
279 (declare (ignore timeout))
280 (with-accessors ((mux mux-of) (fds fds-of) (exit-p exit-p)
281 (exit-when-empty exit-when-empty-p)
282 (timers timers-of) (fd-timers fd-timers-of)
283 (expired-events expired-events-of))
284 event-base
285 (flet ((poll-timeout ()
286 (clamp-timeout (min-timeout (time-to-next-timer timers)
287 (time-to-next-timer fd-timers))
288 min-step max-step)))
289 (do ((deletion-list () ())
290 (eventsp nil nil)
291 (poll-timeout (poll-timeout) (poll-timeout))
292 (now (osicat-sys:get-monotonic-time)
293 (osicat-sys:get-monotonic-time)))
294 ((or exit-p (and exit-when-empty (event-base-empty-p event-base))))
295 (setf expired-events nil)
296 (setf (values eventsp deletion-list)
297 (dispatch-fd-events-once event-base poll-timeout now))
298 (%remove-handlers event-base deletion-list)
299 (when (expire-pending-timers fd-timers now) (setf eventsp t))
300 (dispatch-fd-timeouts expired-events)
301 (when (expire-pending-timers timers now) (setf eventsp t))
302 (when (and eventsp one-shot) (setf exit-p t))))))
304 (defun %remove-handlers (event-base event-list)
305 (loop :for ev :in event-list
306 :for fd := (fd-handler-fd ev)
307 :for fd-entry := (fd-entry-of event-base fd)
308 :do (%remove-io-handler event-base fd fd-entry ev)))
310 ;;; Waits for events and dispatches them. Returns T if some events
311 ;;; have been received, NIL otherwise.
312 (defun dispatch-fd-events-once (event-base timeout now)
313 (loop
314 :with fd-events := (harvest-events (mux-of event-base) timeout)
315 :for ev :in fd-events
316 :for dlist := (%handle-one-fd event-base ev now nil)
317 :then (%handle-one-fd event-base ev now dlist)
318 :finally
319 (priority-queue-reorder (fd-timers-of event-base))
320 (return (values (consp fd-events) dlist))))
322 (defun %handle-one-fd (event-base event now deletion-list)
323 (destructuring-bind (fd ev-types) event
324 (let* ((readp nil) (writep nil)
325 (fd-entry (fd-entry-of event-base fd))
326 (errorp (and fd-entry (member :error ev-types))))
327 (cond
328 (fd-entry
329 (when (member :read ev-types)
330 (setf readp (%dispatch-event fd-entry :read
331 (if errorp :error nil) now)))
332 (when (member :write ev-types)
333 (setf writep (%dispatch-event fd-entry :write
334 (if errorp :error nil) now)))
335 (when errorp
336 (%dispatch-event fd-entry :error :error now)
337 (setf readp t writep t))
338 (when readp (push (fd-entry-read-handler fd-entry) deletion-list))
339 (when writep (push (fd-entry-write-handler fd-entry) deletion-list)))
341 (error "Got spurious event for non-monitored FD: ~A" fd)))
342 (values deletion-list))))
344 (defun %dispatch-event (fd-entry event-type errorp now)
345 (let ((ev (fd-entry-handler fd-entry event-type)))
346 (funcall (fd-handler-callback ev)
347 (fd-entry-fd fd-entry)
348 event-type
349 (if errorp :error nil))
350 (when-let (timer (fd-handler-timer ev))
351 (reschedule-timer-relative-to-now timer now))
352 (fd-handler-one-shot-p ev)))
354 (defun dispatch-fd-timeouts (events)
355 (dolist (ev events)
356 (funcall (fd-handler-callback ev)
357 (fd-handler-fd ev)
358 (fd-handler-type ev)
359 :timeout)))