<commit_before>(in-package #:laap)

(defclass timer ()
  ((fd :initarg :fd :reader fd)
   (direction :initarg :direction :reader direction)
   (callback :initarg :callback :reader callback)
   (closed :initform nil :accessor closed)))

(defmethod handle-error ((timer timer) error)
  (funcall (callback timer) error nil))

(defgeneric handle-event (timer)
  (:documentation "Handles an event for a file descriptor"))

(defclass timer-timer (timer)
  ((direction :initform +epollin+)))

(defmethod handle-event ((timer timer-timer))
  ;; We don't really need to read the timer fd, once we get
  ;; an event, it can only mean that it's ready.
  (funcall (callback timer) nil)
  (setf (closed timer) t)
  (c-close (fd timer)))

(defclass event-loop ()
  ((started :accessor started)
   (timers :accessor timers)
   (efd :accessor efd)))

(defmethod initialize-instance ((loop event-loop) &key)
  (setf (timers loop) (make-hash-table))
  (setf (started loop) nil))

(defun start (loop)
  ;; TODO: Don't hardcode 64.
  (let ((events (cffi:foreign-alloc '(:struct epoll-event) :count 64)))
    (unwind-protect
	 (let ((efd (epoll-create1 0)))
	   (when (= efd -1)
	     (error "epoll_create1"))
	   (setf (efd loop) efd)
	   (unwind-protect
		(progn
		  (maphash
		   (lambda (timerfd timer)
		     (add-event efd timerfd timer))
		   (timers loop))
		  (setf (started loop) t)
		  (main-loop loop efd events))
	     (c-close efd)))
      (cffi:foreign-free events))))

(defun main-loop (loop efd events)
  (loop
     (when (= (hash-table-count (timers loop)) 0)
       (setf (started loop) nil)
       (return-from main-loop))
     (loop for i from 0 to (1- (epoll-pwait efd events 64 -1 0))
	do (block continue
	     (let* ((event (cffi:mem-aref events '(:struct epoll-event) i))
		    (event-events (cffi:foreign-slot-value event '(:struct epoll-event) 'events))
		    (event-data (cffi:foreign-slot-value event '(:struct epoll-event) 'data))
		    (fd (cffi:foreign-slot-value event-data 'epoll-data-t 'fd))
		    (timer (gethash fd (timers loop))))
	      (when (or (> (logand event-events +epollerr+) 0)
			(> (logand event-events +epollhup+) 0))
		(return-from continue
		  (handle-error timer (make-condition 'error "epoll error"))))
	      (unwind-protect
		   (handle-event timer)
		(unless (closed timer)
		  (epoll-ctl efd +epoll-ctl-mod+ fd event))))))))
<commit_msg>Use epoll_wait while we're not handling signals.<commit_after>(in-package #:laap)

(defclass timer ()
  ((fd :initarg :fd :reader fd)
   (direction :initarg :direction :reader direction)
   (callback :initarg :callback :reader callback)
   (closed :initform nil :accessor closed)))

(defmethod handle-error ((timer timer) error)
  (funcall (callback timer) error nil))

(defgeneric handle-event (timer)
  (:documentation "Handles an event for a file descriptor"))

(defclass timer-timer (timer)
  ((direction :initform +epollin+)))

(defmethod handle-event ((timer timer-timer))
  ;; We don't really need to read the timer fd, once we get
  ;; an event, it can only mean that it's ready.
  (funcall (callback timer) nil)
  (setf (closed timer) t)
  (c-close (fd timer)))

(defclass event-loop ()
  ((started :accessor started)
   (timers :accessor timers)
   (efd :accessor efd)))

(defmethod initialize-instance ((loop event-loop) &key)
  (setf (timers loop) (make-hash-table))
  (setf (started loop) nil))

(defun start (loop)
  ;; TODO: Don't hardcode 64.
  (let ((events (cffi:foreign-alloc '(:struct epoll-event) :count 64)))
    (unwind-protect
	 (let ((efd (epoll-create1 0)))
	   (when (= efd -1)
	     (error "epoll_create1"))
	   (setf (efd loop) efd)
	   (unwind-protect
		(progn
		  (maphash
		   (lambda (timerfd timer)
		     (add-event efd timerfd timer))
		   (timers loop))
		  (setf (started loop) t)
		  (main-loop loop efd events))
	     (c-close efd)))
      (cffi:foreign-free events))))

(defun main-loop (loop efd events)
  (loop
     (when (= (hash-table-count (timers loop)) 0)
       (setf (started loop) nil)
       (return-from main-loop))
     (loop for i from 0 to (1- (epoll-wait efd events 64 -1))
	do (block continue
	     (let* ((event (cffi:mem-aref events '(:struct epoll-event) i))
		    (event-events (cffi:foreign-slot-value event '(:struct epoll-event) 'events))
		    (event-data (cffi:foreign-slot-value event '(:struct epoll-event) 'data))
		    (fd (cffi:foreign-slot-value event-data 'epoll-data-t 'fd))
		    (timer (gethash fd (timers loop))))
	      (when (or (> (logand event-events +epollerr+) 0)
			(> (logand event-events +epollhup+) 0))
		(return-from continue
		  (handle-error timer (make-condition 'error "epoll error"))))
	      (unwind-protect
		   (handle-event timer)
		(unless (closed timer)
		  (epoll-ctl efd +epoll-ctl-mod+ fd event))))))))

{'fraction_non_alphanumeric': 0.15188284518828452, 'fraction_numerical': 0.006903765690376569, 'mean_word_length': 3.4807872539831304, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1247782', 'n_tokens_mistral': 1811, 'n_tokens_neox': 1656, 'n_words': 513}