1 ;;;; -*- Mode: Lisp; indent-tabs-mode: nil -*-
3 ;;; --- kqueue(2) multiplexer implementation.
6 (in-package :iolib.multiplex
)
8 (defconstant +kqueue-priority
+ 1)
10 (define-multiplexer kqueue-multiplexer
+kqueue-priority
+ (multiplexer)
13 (defmethod print-object ((mux kqueue-multiplexer
) stream
)
14 (print-unreadable-object (mux stream
:type nil
:identity nil
)
15 (format stream
"kqueue(2) multiplexer")))
17 (defvar *kqueue-max-events
* 200)
19 (defmethod initialize-instance :after
((mux kqueue-multiplexer
) &key
)
20 (setf (slot-value mux
'fd
) (isys:kqueue
)))
22 (defun do-kqueue-event-request (kqueue-fd fd-entry filter request-type
)
23 (let ((fd (fd-entry-fd fd-entry
)))
24 (with-foreign-object (kev 'isys
:kevent
)
25 (isys:bzero kev isys
:size-of-kevent
)
26 (isys:ev-set kev fd filter request-type
0 0 (null-pointer))
27 (isys:kevent kqueue-fd
32 (defun calc-kqueue-monitor-filter (fd-entry)
33 (if (null (fd-entry-read-handler fd-entry
))
37 (defmethod monitor-fd ((mux kqueue-multiplexer
) fd-entry
)
38 (assert fd-entry
(fd-entry) "Must supply an FD-ENTRY!")
40 (do-kqueue-event-request (fd-of mux
) fd-entry
41 (calc-kqueue-monitor-filter fd-entry
)
44 (warn "FD ~A is invalid, cannot monitor it." (fd-entry-fd fd-entry
)))))
46 (defun calc-kqueue-update-filter-and-flags (event-type edge-change
)
50 (:add
(values isys
:evfilt-read isys
:ev-add
))
51 (:del
(values isys
:evfilt-read isys
:ev-delete
))))
54 (:add
(values isys
:evfilt-write isys
:ev-add
))
55 (:del
(values isys
:evfilt-write isys
:ev-delete
))))))
57 (defmethod update-fd ((mux kqueue-multiplexer
) fd-entry event-type edge-change
)
58 (assert fd-entry
(fd-entry) "Must supply an FD-ENTRY!")
60 (multiple-value-call #'do-kqueue-event-request
(fd-of mux
) fd-entry
61 (calc-kqueue-update-filter-and-flags event-type edge-change
))
63 (warn "FD ~A is invalid, cannot update its status."
64 (fd-entry-fd fd-entry
)))
66 (warn "FD ~A was not monitored, cannot update its status."
67 (fd-entry-fd fd-entry
)))))
69 (defun calc-kqueue-unmonitor-filter (fd-entry)
70 (if (null (fd-entry-read-handler fd-entry
))
74 (defmethod unmonitor-fd ((mux kqueue-multiplexer
) fd-entry
)
76 (do-kqueue-event-request (fd-of mux
) fd-entry
77 (calc-kqueue-unmonitor-filter fd-entry
)
80 (warn "FD ~A is invalid, cannot unmonitor it." (fd-entry-fd fd-entry
)))
82 (warn "FD ~A was not monitored, cannot unmonitor it."
83 (fd-entry-fd fd-entry
)))))
85 (defmethod harvest-events ((mux kqueue-multiplexer
) timeout
)
86 (with-foreign-objects ((events 'isys
:kevent
*kqueue-max-events
*)
88 (isys:bzero events
(* *kqueue-max-events
* isys
:size-of-kevent
))
90 (isys:repeat-upon-condition-decreasing-timeout
91 ((isys:eintr
) tmp-timeout timeout
)
93 (timeout->timespec tmp-timeout ts
))
95 (isys:kevent
(fd-of mux
) (null-pointer) 0
96 events
*kqueue-max-events
*
97 (if tmp-timeout ts
(null-pointer)))))
98 (macrolet ((kevent-slot (slot-name)
99 `(foreign-slot-value (mem-aref events
'isys
:kevent i
)
100 'isys
:kevent
',slot-name
)))
101 (loop for i below ready-fds
102 for fd
= (kevent-slot isys
:ident
)
103 for flags
= (kevent-slot isys
:flags
)
104 for filter
= (kevent-slot isys
:filter
)
105 for data
= (kevent-slot isys
:data
)
106 for kqueue-event
= (make-kqueue-event fd flags filter data
)
107 when kqueue-event collect kqueue-event
)))))
109 ;;; TODO: do something with DATA
110 (defun make-kqueue-event (fd flags filter data
)
111 (declare (ignore data
))
113 (switch (filter :test
#'=)
114 (isys:evfilt-write
(push :write event
))
115 (isys:evfilt-read
(push :read event
)))
117 ;; TODO: check what exactly EV_EOF means
118 ;; (ev-eof (pushnew :read event))
119 (isys:ev-error
(push :error event
)))