Add clamp-timeout to base package.
[iolib/alendvai.git] / io.multiplex / poll.lisp
blob0b4a752f20fcb0a65407fdbcc8ba8a1594af7f23
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
2 ;;;
3 ;;; --- poll(2) multiplexer implementation.
4 ;;;
6 (in-package :io.multiplex)
8 (defconstant +poll-priority+ 2)
10 (define-multiplexer poll-multiplexer +poll-priority+ (multiplexer)
11 ((fd-set :initform (allocate-pollfd-set) :accessor fd-set-of)
12 (fd-set-size :initform 5 :accessor fd-set-size-of)
13 (fd-count :initform 0 :accessor fd-count-of)))
15 (defun allocate-pollfd-set (&optional (count 5))
16 (let ((fds (foreign-alloc 'nix::pollfd :count count)))
17 (nix:bzero fds (* nix::size-of-pollfd count))
18 (values fds)))
20 (defmethod print-object ((mux poll-multiplexer) stream)
21 (print-unreadable-object (mux stream :type nil :identity nil)
22 (format stream "poll(2) multiplexer")))
24 (defmethod close-multiplexer progn ((mux poll-multiplexer))
25 (foreign-free (fd-set-of mux))
26 (setf (fd-set-of mux) nil))
28 (defvar *pollfd-table* (make-hash-table :test #'eql))
30 (defun calc-pollfd-flags (readp writep)
31 (let ((flags 0))
32 (when readp (setf flags (logior nix:pollin nix:pollrdhup nix:pollpri)))
33 (when writep (setf flags (logior flags nix:pollout nix:pollhup)))
34 (values flags)))
36 (defun set-pollfd-entry (fd-set index fd readp writep)
37 (with-foreign-slots ((nix::fd nix::events nix::revents)
38 (mem-aref fd-set 'nix::pollfd index)
39 nix::pollfd)
40 (setf nix::fd fd
41 nix::revents 0
42 nix::events (calc-pollfd-flags readp writep))))
44 (defun extend-pollfd-set (fd-set size)
45 (let* ((new-size (+ size 5))
46 (new-fd-set (foreign-alloc 'nix::pollfd :count new-size)))
47 (nix:memcpy new-fd-set fd-set (* size nix::size-of-pollfd))
48 (foreign-free fd-set)
49 (values new-fd-set new-size)))
51 (defmethod monitor-fd ((mux poll-multiplexer) fd-entry)
52 (let ((fd (fd-entry-fd fd-entry))
53 (readp (fd-entry-read-event fd-entry))
54 (writep (fd-entry-write-event fd-entry)))
55 (with-accessors ((fd-set fd-set-of) (size fd-set-size-of)
56 (count fd-count-of)) mux
57 (when (= count size)
58 (setf (values fd-set size) (extend-pollfd-set fd-set size)))
59 (set-pollfd-entry fd-set count fd readp writep)
60 (setf (gethash fd *pollfd-table*) count)
61 (incf count))))
63 (defmethod update-fd ((mux poll-multiplexer) fd-entry event-type edge-change)
64 (declare (ignore event-type edge-change))
65 (let* ((fd (fd-entry-fd fd-entry))
66 (pos (gethash fd *pollfd-table*))
67 (readp (fd-entry-read-event fd-entry))
68 (writep (fd-entry-write-event fd-entry)))
69 (with-accessors ((fd-set fd-set-of)) mux
70 (set-pollfd-entry fd-set pos fd readp writep))))
72 (defun shrink-pollfd-set (fd-set count size pos)
73 (let* ((new-size (if (> 5 (- size count)) (- size 5) size))
74 (new-fd-set (foreign-alloc 'nix::pollfd :count new-size)))
75 (when (plusp pos)
76 (nix:memcpy new-fd-set fd-set (* pos nix::size-of-pollfd)))
77 (when (< pos count)
78 (nix:memcpy new-fd-set fd-set (* (- count pos) nix::size-of-pollfd)))
79 (foreign-free fd-set)
80 (values new-fd-set new-size)))
82 (defmethod unmonitor-fd ((mux poll-multiplexer) fd-entry)
83 (let* ((fd (fd-entry-fd fd-entry))
84 (pos (gethash fd *pollfd-table*)))
85 (with-accessors ((fd-set fd-set-of) (size fd-set-size-of)
86 (count fd-count-of)) mux
87 (setf (values fd-set size) (shrink-pollfd-set fd-set (1- count) size pos))
88 (remhash fd *pollfd-table*)
89 (decf count))))
91 (defmethod harvest-events ((mux poll-multiplexer) timeout)
92 (with-accessors ((fd-set fd-set-of) (size fd-set-size-of)
93 (count fd-count-of)) mux
94 ;; if there are no fds set and timeout is NULL
95 ;; poll() blocks forever
96 (when (and (zerop count)
97 (null timeout))
98 (warn "Non fds to monitor and no timeout set !")
99 (return* nil))
100 ;; FIXME: when does poll() return EBADF ?
101 (nix:repeat-upon-condition-decreasing-timeout
102 ((nix:eintr) tmp-timeout timeout)
103 (nix:poll fd-set count (timeout->milisec tmp-timeout)))
104 (harvest-pollfd-events fd-set count)))
106 (defun harvest-pollfd-events (fd-set count)
107 (macrolet ((pollfd-slot (name index)
108 `(foreign-slot-value (mem-aref fd-set 'nix::pollfd ,index)
109 'nix::pollfd ,name)))
110 (loop :for i :below count
111 :for event := ()
112 :for fd := (pollfd-slot 'nix::fd i)
113 :for revents := (pollfd-slot 'nix::revents i)
114 :do (flags-case revents
115 ((nix:pollout nix:pollhup) (push :write event))
116 ((nix:pollin nix:pollrdhup nix:pollpri) (push :read event))
117 ((nix:pollerr nix:pollnval) (push :error event)))
118 :when event :collect (list fd event))))