2019-01-28 01:14:01 +00:00
|
|
|
#lang imperative-syndicate
|
|
|
|
;; Crude steps toward reloadable Syndicate modules
|
|
|
|
|
|
|
|
(provide (except-out (struct-out reload-pending) reload-pending)
|
|
|
|
(rename-out [reload-pending <reload-pending>])
|
|
|
|
(rename-out [make-reload-pending reload-pending])
|
|
|
|
|
|
|
|
stop-when-reloaded
|
|
|
|
spawn-reloader
|
|
|
|
spawn-reloader*
|
|
|
|
reloader-mixin
|
|
|
|
reloader-mixin*)
|
|
|
|
|
|
|
|
(define-logger syndicate/reload)
|
|
|
|
|
|
|
|
(require file/sha1)
|
|
|
|
(require (for-syntax racket/base))
|
|
|
|
(require racket/rerequire)
|
|
|
|
|
|
|
|
(require/activate imperative-syndicate/supervise)
|
|
|
|
(require/activate imperative-syndicate/drivers/filesystem)
|
|
|
|
|
|
|
|
(assertion-struct reloader (pathstr))
|
|
|
|
(assertion-struct reload-pending (filename))
|
|
|
|
|
|
|
|
(define-syntax (make-reload-pending stx)
|
|
|
|
(syntax-case stx ()
|
|
|
|
[(SELF)
|
|
|
|
(quasisyntax/loc stx
|
|
|
|
(reload-pending '#,(path->string (syntax-source #'SELF))))]))
|
|
|
|
|
|
|
|
(define-syntax (stop-when-reloaded stx)
|
|
|
|
(syntax-case stx ()
|
|
|
|
[(_ body ...)
|
|
|
|
(quasisyntax/loc stx
|
|
|
|
(stop-when (asserted (reload-pending '#,(path->string (syntax-source stx))))
|
|
|
|
body ...))]))
|
|
|
|
|
2019-01-30 16:40:12 +00:00
|
|
|
(define-syntax (spawn-reloader stx)
|
|
|
|
(syntax-case stx ()
|
|
|
|
[(_ module-path)
|
|
|
|
(quasisyntax/loc stx
|
|
|
|
(spawn-reloader* 'module-path '#,(path->string (syntax-source stx))))]))
|
2019-01-28 01:14:01 +00:00
|
|
|
|
2019-01-30 16:40:12 +00:00
|
|
|
(define (spawn-reloader* module-path loading-module-pathstr)
|
2019-01-28 01:14:01 +00:00
|
|
|
(match (module-path->path-string module-path)
|
|
|
|
[#f #f]
|
|
|
|
[pathstr
|
|
|
|
(supervise #:name (reloader pathstr)
|
2019-01-30 16:40:12 +00:00
|
|
|
(stop-when (asserted (reload-pending loading-module-pathstr)))
|
2019-01-28 01:14:01 +00:00
|
|
|
(reloader-mixin** module-path pathstr))
|
|
|
|
#t]))
|
|
|
|
|
|
|
|
(define-syntax-rule (reloader-mixin module-path)
|
|
|
|
(reloader-mixin* 'module-path))
|
|
|
|
|
|
|
|
(define (reloader-mixin* module-path)
|
|
|
|
(define pathstr (module-path->path-string module-path))
|
|
|
|
(when (not pathstr)
|
|
|
|
(error 'reloader-mixin "Cannot deduce source path from module-path ~v" module-path))
|
|
|
|
(reloader-mixin** module-path pathstr))
|
|
|
|
|
|
|
|
(define (module-path->path-string module-path)
|
|
|
|
(define mpi (module-path-index-join module-path #f))
|
|
|
|
(define rpath (module-path-index-resolve mpi))
|
|
|
|
(define path (let ((p (resolved-module-path-name rpath)))
|
|
|
|
(if (pair? p) (car p) p)))
|
|
|
|
(if (path? path)
|
|
|
|
(path->string path)
|
|
|
|
(begin (log-syndicate/reload-error "Could not process module-path ~v" module-path)
|
|
|
|
#f)))
|
|
|
|
|
|
|
|
(define (file->sha1 p)
|
|
|
|
(call-with-input-file p sha1))
|
|
|
|
|
|
|
|
(define (reloader-mixin** module-path pathstr)
|
|
|
|
(field [reloading? #f])
|
|
|
|
(define (reload!)
|
|
|
|
(when (not (reloading?))
|
|
|
|
(reloading? #t)
|
2019-01-30 16:40:12 +00:00
|
|
|
(react (field [obstacles-cleared? #f] [obstacles-existed? #f])
|
2019-01-28 01:14:01 +00:00
|
|
|
(define/query-value obstacles-exist? #f (observe (reload-pending pathstr)) #t
|
2019-01-30 16:40:12 +00:00
|
|
|
#:on-add (begin (log-syndicate/reload-info "waiting to reload ~v" pathstr)
|
|
|
|
(obstacles-existed? #t))
|
2019-01-28 01:14:01 +00:00
|
|
|
#:on-remove (obstacles-cleared? #t))
|
|
|
|
(assert #:when (obstacles-exist?) (reload-pending pathstr))
|
|
|
|
(on-start (flush!)
|
|
|
|
(obstacles-cleared? (not (obstacles-exist?))))
|
|
|
|
(stop-when-true (obstacles-cleared?)
|
|
|
|
(flush!) ;; Wait one turn for effects of newly-cleared obstacles
|
|
|
|
(log-syndicate/reload-info "(re)loading ~v" pathstr)
|
|
|
|
(dynamic-rerequire module-path)
|
2019-01-30 16:40:12 +00:00
|
|
|
(let ((force-reactivation? (obstacles-existed?)))
|
|
|
|
(when force-reactivation?
|
|
|
|
(log-syndicate/reload-info "forcing reactivation of ~v" pathstr))
|
|
|
|
(spawn* #:name module-path
|
|
|
|
((dynamic-require `(submod ,module-path syndicate-main)
|
|
|
|
(if force-reactivation? 'activate!* 'activate!)))))
|
2019-01-28 01:14:01 +00:00
|
|
|
(reloading? #f)))))
|
|
|
|
|
2019-01-30 16:40:12 +00:00
|
|
|
(on-start (log-syndicate/reload-debug "reloader ~v starting" pathstr))
|
|
|
|
(on-stop (log-syndicate/reload-debug "reloader ~v stopping" pathstr))
|
|
|
|
|
2019-01-28 01:14:01 +00:00
|
|
|
(field [previous-version 'unknown])
|
|
|
|
(define/query-value latest-version 'unknown (file-content pathstr file->sha1 $p) p)
|
|
|
|
(begin/dataflow
|
|
|
|
(when (and (not (eq? (latest-version) 'unknown))
|
|
|
|
(not (equal? (latest-version) (previous-version))))
|
|
|
|
(if (latest-version)
|
|
|
|
(reload!)
|
|
|
|
(log-syndicate/reload-warning "Module ~v does not exist" pathstr))
|
|
|
|
(previous-version (latest-version)))))
|
2019-03-20 22:36:13 +00:00
|
|
|
|
|
|
|
(module+ main
|
|
|
|
(require racket/cmdline)
|
|
|
|
(extend-ground-boot! (lambda ()
|
|
|
|
(define module-path (command-line #:args (module-path) module-path))
|
|
|
|
(spawn-reloader* module-path "<main>"))))
|