2021-05-27 09:28:10 +00:00
|
|
|
#lang racket/base
|
2021-06-04 14:20:14 +00:00
|
|
|
;;; SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
;;; SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
2021-05-27 09:28:10 +00:00
|
|
|
|
|
|
|
(provide engine?
|
2021-06-13 05:55:50 +00:00
|
|
|
engine-id
|
|
|
|
engine-name
|
|
|
|
engine-running?
|
|
|
|
engine-custodian
|
2021-05-27 09:28:10 +00:00
|
|
|
engine-thread
|
|
|
|
engine-inhabitant-count
|
|
|
|
make-engine
|
|
|
|
adjust-inhabitant-count!
|
2021-06-08 07:24:25 +00:00
|
|
|
queue-task!
|
2021-06-13 05:55:50 +00:00
|
|
|
engine-register!
|
|
|
|
engine-deregister!
|
|
|
|
engine-shutdown!
|
2021-06-08 07:24:25 +00:00
|
|
|
*dead-engine*)
|
2021-05-27 09:28:10 +00:00
|
|
|
|
2021-05-28 08:33:02 +00:00
|
|
|
(require racket/match)
|
2021-05-27 09:28:10 +00:00
|
|
|
(require (only-in racket/exn exn->string))
|
|
|
|
(require "support/counter.rkt")
|
|
|
|
|
2021-06-08 13:57:25 +00:00
|
|
|
(define-logger syndicate/engine)
|
2021-05-27 09:28:10 +00:00
|
|
|
|
2021-06-13 05:55:50 +00:00
|
|
|
(struct engine (id name custodian thread [running? #:mutable] actors [inhabitant-count #:mutable])
|
2021-05-27 09:28:10 +00:00
|
|
|
#:methods gen:custom-write
|
|
|
|
[(define (write-proc e port mode)
|
2021-06-13 05:55:50 +00:00
|
|
|
(fprintf port "#<engine:~a:~a>" (engine-id e) (engine-name e)))])
|
2021-05-27 09:28:10 +00:00
|
|
|
|
|
|
|
(define generate-engine-id (make-counter))
|
|
|
|
|
2021-06-13 05:55:50 +00:00
|
|
|
(define (make-engine initial-inhabitant-count name termination-handler)
|
|
|
|
(define custodian (make-custodian))
|
2021-05-27 09:28:10 +00:00
|
|
|
(define e (engine (generate-engine-id)
|
2021-06-13 05:55:50 +00:00
|
|
|
name
|
|
|
|
custodian
|
|
|
|
(parameterize ((current-custodian custodian))
|
|
|
|
(thread (lambda ()
|
|
|
|
(thread-receive) ;; delay boot until we're ready
|
|
|
|
(log-syndicate/engine-debug "~a starting" e)
|
|
|
|
(with-handlers ([exn? (handle-unexpected-task-runner-termination e)])
|
|
|
|
(let loop ((termination-handler termination-handler))
|
|
|
|
(log-syndicate/engine-debug
|
|
|
|
"~a task count: ~a" e (engine-inhabitant-count e))
|
|
|
|
(if (positive? (engine-inhabitant-count e))
|
|
|
|
;; We have some non-daemon users so just block
|
|
|
|
(begin ((thread-receive))
|
|
|
|
(loop termination-handler))
|
|
|
|
;; No non-daemon users, so keep running until there's no more work
|
|
|
|
(match (thread-try-receive)
|
|
|
|
[#f ;; No work, no non-daemons, we're done.
|
|
|
|
(termination-handler loop)]
|
|
|
|
[thunk
|
|
|
|
(thunk)
|
|
|
|
(loop termination-handler)])))
|
|
|
|
(log-syndicate/engine-debug "~a stopping" e)
|
|
|
|
(custodian-shutdown-all custodian)))))
|
|
|
|
#t
|
|
|
|
(make-hash)
|
2021-05-27 09:28:10 +00:00
|
|
|
initial-inhabitant-count))
|
|
|
|
(thread-send (engine-thread e) 'boot)
|
|
|
|
e)
|
|
|
|
|
|
|
|
(define (adjust-inhabitant-count! e delta)
|
|
|
|
(queue-task! e (lambda ()
|
|
|
|
(set-engine-inhabitant-count! e (+ (engine-inhabitant-count e) delta)))))
|
|
|
|
|
2021-06-13 05:55:50 +00:00
|
|
|
(define (engine-register! e ac)
|
|
|
|
(when (not (eq? (current-thread) (engine-thread e)))
|
|
|
|
(error 'engine-register! "Called from wrong thread"))
|
|
|
|
(hash-set! (engine-actors e) ac #t))
|
|
|
|
|
|
|
|
(define (engine-deregister! e ac)
|
|
|
|
(when (not (eq? (current-thread) (engine-thread e)))
|
|
|
|
(error 'engine-deregister! "Called from wrong thread"))
|
|
|
|
(hash-remove! (engine-actors e) ac))
|
|
|
|
|
|
|
|
(define (engine-shutdown! e)
|
|
|
|
(log-syndicate/engine-debug "~a shutdown" e)
|
|
|
|
(set-engine-running?! e #f)
|
|
|
|
(define actors (hash-keys (engine-actors e)))
|
|
|
|
(hash-clear! (engine-actors e))
|
|
|
|
actors)
|
|
|
|
|
2021-05-27 09:28:10 +00:00
|
|
|
(define ((handle-unexpected-task-runner-termination e) exn)
|
2021-06-08 13:57:25 +00:00
|
|
|
(log-syndicate/engine-error "~a terminated unexpectedly!\n~a" e (exn->string exn))
|
2021-05-27 09:28:10 +00:00
|
|
|
(exit 1))
|
|
|
|
|
|
|
|
(define (queue-task! e thunk)
|
2021-06-08 14:09:59 +00:00
|
|
|
(thread-send (engine-thread e)
|
|
|
|
thunk
|
|
|
|
(lambda ()
|
|
|
|
(log-syndicate/engine-warning "Attempt to enqueue task for dead engine ~v" e))))
|
2021-06-08 07:24:25 +00:00
|
|
|
|
2021-06-13 05:55:50 +00:00
|
|
|
(define *dead-engine* (make-engine 0 'dead-engine void))
|