Add clamp-timeout to base package.
[iolib/alendvai.git] / io.multiplex / fd-wait.lisp
blob7c88245f6facb93845c90f0baa7e4dd7c5646452
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; indent-tabs-mode: nil -*-
2 ;;;
3 ;;; --- Wait for events on single FDs.
4 ;;;
6 (in-package :io.multiplex)
8 ;;; FIXME: Until a way to autodetect platform features is implemented
9 #+(or darwin freebsd)
10 (define-constant pollrdhup 0)
12 (define-condition poll-error (error)
13 ((fd :initarg :fd :reader poll-error-fd)
14 (identifier :initarg :identifier :initform "Unknown error"
15 :reader poll-error-identifier))
16 (:report (lambda (condition stream)
17 (format stream "Error caught while polling file descriptor ~A: ~A"
18 (poll-error-fd condition)
19 (poll-error-identifier condition))))
20 (:documentation
21 "Signaled when an error occurs while polling for I/O readiness
22 of a file descriptor."))
24 (define-condition poll-timeout (condition)
25 ((fd :initarg :fd :reader poll-timeout-fd)
26 (event-type :initarg :event-type :reader poll-timeout-event-type))
27 (:report (lambda (condition stream)
28 (format stream "Timeout occurred while polling file descriptor ~A for event ~S"
29 (poll-timeout-fd condition)
30 (poll-timeout-event-type condition))))
31 (:documentation
32 "Signaled when a timeout occurs while polling for I/O readiness
33 of a file descriptor."))
35 (defun compute-poll-flags (type)
36 (ecase type
37 (:input (logior pollin pollrdhup pollpri))
38 (:output (logior pollout))
39 (:io (logior pollin pollrdhup pollpri pollout))))
41 (defun process-poll-revents (revents fd)
42 (let ((readp nil) (writep nil))
43 (flags-case revents
44 ((pollin pollrdhup pollpri)
45 (setf readp t))
46 ((pollout pollhup) (setf writep t))
47 ((pollerr) (error 'poll-error :fd fd))
48 ((pollnval) (error 'poll-error :fd fd
49 :identifier "Invalid file descriptor")))
50 (values readp writep)))
52 (defun wait-until-fd-ready (file-descriptor event-type &optional timeout errorp)
53 "Poll file descriptor `FILE-DESCRIPTOR' for I/O readiness.
54 `EVENT-TYPE' must be either :INPUT, :OUTPUT or :IO.
55 `TIMEOUT' must be either a non-negative integer measured in seconds,
56 or `NIL' meaning no timeout at all. If `ERRORP' is not NIL and a timeout
57 occurs, then a condition of type `POLL-TIMEOUT' is signaled.
58 Returns two boolean values indicating readability and writeability of `FILE-DESCRIPTOR'."
59 (flet ((poll-error (unix-err)
60 (error 'poll-error :fd file-descriptor
61 :identifier (osicat-sys:system-error-identifier unix-err))))
62 (with-foreign-object (pollfd 'pollfd)
63 (bzero pollfd size-of-pollfd)
64 (with-foreign-slots ((fd events revents) pollfd pollfd)
65 (setf fd file-descriptor
66 events (compute-poll-flags event-type))
67 (handler-case
68 (let ((ret (nix:repeat-upon-condition-decreasing-timeout
69 ((nix:eintr) remaining-time timeout)
70 (poll pollfd 1 (timeout->milisec remaining-time)))))
71 (when (zerop ret)
72 (if errorp
73 (error 'poll-timeout :fd file-descriptor :event-type event-type)
74 (return* (values nil nil)))))
75 (nix:posix-error (err) (poll-error err)))
76 (process-poll-revents revents file-descriptor)))))
78 (defun fd-ready-p (fd &optional (event-type :input))
79 "Tests file-descriptor `FD' for I/O readiness.
80 `EVENT-TYPE' must be either :INPUT, :OUTPUT or :IO ."
81 (multiple-value-bind (readp writep)
82 (wait-until-fd-ready fd event-type 0)
83 (ecase event-type
84 (:input readp)
85 (:output writep)
86 (:io (or readp writep)))))
88 (defun fd-readablep (fd)
89 (nth-value 0 (wait-until-fd-ready fd :input 0)))
91 (defun fd-writablep (fd)
92 (nth-value 1 (wait-until-fd-ready fd :output 0)))