(in-package #:candle) (defvar *mutex* (sb-thread:make-mutex)) (defvar *waitq* (sb-thread:make-waitqueue)) (defvar *job-system* nil "*JOB-SYSTEM* VALUE TYPE: A keyword INITIAL VALUE: NIL DESCRIPTION: The job system the server is running under. When implementing a job system, the generics PROCESS-JOB-IN-SYSTEM and SHUTDOWN-SYSTEM must be implemented. Is set by the command line when the server is started.") (defgeneric process-job-in-system (job-system job) (:documentation "PROCESS-JOB-IN-SYSTEM JOB-SYSTEM JOB => SUCCESS, LOG ARGUMENTS AND VALUES: JOB-SYSTEM: a keyword representing an installed system JOB: A job to be run SUCCESS: A boolean, whether the job was successful LOG: The log from the job DESCRIPTION: Runs a job in the specified system. The code will be checked out, so any defining system should copy the code from the job's project's code dir to wherever it will run, and then ran `candle run` inside that directory, capturing the output.")) (defgeneric shutdown-system (job-system) (:documentation "SHUTDOWN-SYSTEM JOB-SYSTEM => UNUSED ARGUMENTS AND VALUES: JOB-SYSTEM: a keyword representing an installed system UNUSED: the result is unused DESCRIPTION: Shuts down the specified system. Sometimes this will be an empty method, as the system requires no special shutdown instructions. This is run when the candle server is shutdown for the job system that's specified.")) (defun start-processor-thread () (log:info "Starting processor in ~(~A~) mode" *job-system*) (let* ((active t) (processor-thread (sb-thread:make-thread (lambda () (loop :while active :do (let ((job (find :queued *all-job* :key #'job-status))) (if job (process-job job) ; We just wait here until the processor is released, which is usually done ; when a project is refreshed. (sb-thread:with-mutex (*mutex*) (sb-thread:condition-wait *waitq* *mutex*)))))) :name "Processor"))) (push (lambda () (log:info "Shutting down processor thread") (setf active nil) (awaken-processor-thread) (sb-thread:join-thread processor-thread) (shutdown-system *job-system*)) sb-ext:*exit-hooks*))) (defun awaken-processor-thread () (sb-thread:with-mutex (*mutex*) (sb-thread:condition-broadcast *waitq*))) (defun process-job (job) (set-job-status job :in-progress) (git (job-project job) "checkout" (job-sha job)) (if (not (probe-file (format nil "~A.candle" (project-dir (job-project job))))) (set-job-status job :no-candle-file) (multiple-value-bind (result log) (process-job-in-system *job-system* job) (set-job-status job (if result :succeeded :failed)) (set-job-log job log))))