git.fiddlerwoaroof.com
Raw Blame History
(in-package :slacker)

(defclass queue-pair ()
  ((%work-queue :initarg :work-queue :reader work-queue :initform (make-instance 'chanl:unbounded-channel))
   (%result-queue :initarg :result-queue :reader result-queue :initform (make-instance 'chanl:unbounded-channel))))

(defclass event-pump ()
  ((%tick-pause :initform 0.01)
   (%running :accessor running :initform nil)
   (%finish-cb :accessor fwoar.event-loop:finish-cb :initform nil)
   (%ws-client :accessor ws-client :initarg :ws-client :initform nil)
   (%waiting-pings :accessor waiting-pings :initform 0)
   (%modules :accessor modules :initform (make-hash-table))
   (%client-factory :reader client-factory :initarg :client-factory :initform (error "must provide a client factory"))
   (%latest-id :accessor latest-id :initform 0)
   (%queue-pair :accessor queue-pair :initarg :queue-pair :initform (make-instance 'queue-pair))))

(defgeneric start-module (client module)
  (:documentation "start a module"))
(defgeneric stop-module (client module)
  (:documentation "stop a module"))

(defmethod result-queue ((obj event-pump))
  (result-queue (queue-pair obj)))

(defmethod work-queue ((obj event-pump))
  (work-queue (queue-pair obj)))

(defun clear-waiting-pings (event-pump)
  (setf (waiting-pings event-pump) 0))

(defmethod fwoar.event-loop:prepare-loop ((event-pump event-pump))
  (funcall (client-factory event-pump)
           event-pump))

(defmethod fwoar.event-loop:cleanup ((event-pump event-pump))
  (format t "~&cleaning up...~&")
  (setf (running event-pump) nil)
  (do-hash-table (_ v (modules event-pump))
    (declare (ignore _))
    (stop-module event-pump v))
  (when (ws-client event-pump)
    (wsd:close-connection (ws-client event-pump))))

(let ((last-ping nil))
  (defun maybe-ping (event-pump)
    (let ((current-time  (get-universal-time)))
      (when (or (null last-ping)
                (< 15 (- current-time last-ping)))
        (setf last-ping current-time)
        (websocket-driver:send-ping (ws-client event-pump))))))


(defun handle-work-queue (event-pump)
  (multiple-value-bind (message message-p)
      (chanl:recv (work-queue event-pump)
                  :blockp nil)
    (when message-p
      (funcall message
               event-pump))))

(defmethod fwoar.event-loop:tick ((task event-pump))
  (handle-work-queue task)
  (maybe-ping task)
  (sleep (slot-value task '%tick-pause)))

(defmethod attach-module ((event-pump event-pump) module &rest args &key)
  (setf (gethash (make-keyword module)
                 (modules event-pump))
        (apply #'make-instance
               module
               args)))

(defgeneric get-module (module event-pump)
  (:documentation "Get one of the activated modules")
  (:method (module (event-pump event-pump))
    (gethash (make-keyword module)
             (modules event-pump))))

(defun stop-slacker (event-pump)
  (funcall (fwoar.event-loop:finish-cb event-pump)))

(defun throttle-continue (num)
  (let ((continue-count 0)
        (now (local-time:now)))
    (lambda (&optional condition)
      (format t "~&~s ~s~%" now continue-count)
      (when (< continue-count num)
        (let ((new-now (local-time:now)))
          (if (< (* 1000 (local-time:timestamp-difference new-now now))
                 5)
              (incf continue-count)
              (setf now new-now
                    continue-count 0))
          (continue condition))))))

(defun test-event-pump ()
  (let ((the-event-pump (make-instance 'event-pump :client-factory 'identity)))
    (chanl:send (work-queue the-event-pump)
                (lambda (a)
                  (error "test")))
    (chanl:send (work-queue the-event-pump)
                (lambda (a)
                  (format t "This should happen ~s~%" a)))
    (bt:make-thread (lambda ()
                      (sleep 5)
                      (funcall (finish-cb the-event-pump))))
    (handler-bind ((serious-condition #'continue))
      (fwoar.event-loop:run-loop the-event-pump))))