Handle shutdown gracefully
[candle] / src / main / processor.lisp
1 (in-package #:candle)
2
3 (defvar *mutex* (sb-thread:make-mutex))
4 (defvar *waitq* (sb-thread:make-waitqueue))
5
6 (defvar *job-system*)
7 (defgeneric process-job-in-system (job-system job))
8 (defgeneric shutdown-system (job-system))
9
10 (defun start-processor-thread ()
11  (format t "Starting processor in ~(~A~) mode~%" *job-system*)
12  (let*
13   ((active t)
14    (processor-thread
15     (sb-thread:make-thread
16      (lambda ()
17       (loop
18        :while active
19        :do
20        (let
21         ((job (find :queued *all-job* :key #'job-status)))
22         (if job
23          (process-job job)
24          ; We just wait here until the processor is released, which is usually done
25          ; when a project is refreshed.
26          (sb-thread:with-mutex (*mutex*)
27           (sb-thread:condition-wait *waitq* *mutex*))))))
28      :name "Processor")))
29   (push
30    (lambda ()
31     (format t "Shutting down processor thread~%")
32     (setf active nil)
33     (awaken-processor-thread)
34     (sb-thread:join-thread processor-thread)
35     (shutdown-system *job-system*))
36    sb-ext:*exit-hooks*)))
37
38 (defun awaken-processor-thread ()
39  (sb-thread:with-mutex (*mutex*)
40   (sb-thread:condition-broadcast *waitq*)))
41
42 (defun process-job (job)
43  (set-job-status job :in-progress)
44  (git (job-project job) "checkout" (job-sha job))
45  (if (not (probe-file (format nil "~A.candle" (project-dir (job-project job)))))
46   (set-job-status job :no-candle-file)
47   (multiple-value-bind (result log) (process-job-in-system *job-system* job)
48    (set-job-status job (if result :succeeded :failed))
49    (set-job-log job log))))