iolib
iolib copied to clipboard
Add eventfd(2) syscall for linux
Hi,
This patch exposes eventfd(2).
For reference I also wrote the following read/write functions to interact with the file descriptor returned by the call, but these are not in the patch:
(defun efd-read% (ptr fd)
(let ((ret (isys:read fd ptr (cffi:foreign-type-size :uint64))))
(assert (= ret (cffi:foreign-type-size :uint64)))
(cffi:mem-ref ptr :uint64)))
(defun efd-write% (ptr fd value)
(check-type value (unsigned-byte 64))
(setf (cffi:mem-ref ptr :uint64) value)
(prog1 T
(let ((ret (isys:write fd ptr (cffi:foreign-type-size :uint64))))
(assert (= ret (cffi:foreign-type-size :uint64))))))
(defun efd-read (fd)
(cffi:with-foreign-object (ptr :uint64 1)
(efd-read% ptr fd)))
(defun efd-write (fd value)
(check-type value (unsigned-byte 64))
(cffi:with-foreign-object (ptr :uint64 1)
(efd-write% ptr fd value)))
If for example we define the following macro:
(defmacro with-eventfd
((&key get set efd ((:ptr ptr%)) (initial 0) (flags 0)) &body body)
(with-gensyms (new)
(let* ((efd (or efd (gensym "EFD")))
(ptr (or ptr% (gensym "PTR")))
(get@ (and get `((,get () (efd-read% ,ptr ,efd)))))
(set@ (and set `((,set (,new) (efd-write% ,ptr ,efd ,new)))))
(core `(let ((,efd (iolib/syscalls:eventfd ,initial ,flags)))
(unwind-protect (flet (,@get@ ,@set@) ,@body)
(isys:close ,efd)))))
(if ptr%
core
`(cffi:with-foreign-object (,ptr :uint64 1) ,core)))))
Then here is a usage example of the syscal to have an interruptible event loop:
(with-event-base (base)
(with-eventfd (:efd wakeupfd :get read-wfd :set write-wfd)
(set-io-handler base wakeupfd :read
(lambda (args)
(declare (ignore args))
(read-wfd)))
;; ... others may call (write-wfd 1) to wakeup the event loop
(event-dispatch base)))
Without eventfd(2), the typical way of doing this is with a pipe.
Context: https://stackoverflow.com/questions/79724520/signal-handling-in-multi-threaded-scenario