#lang syndicate ;; Crude steps toward reloadable Syndicate modules (provide (except-out (struct-out reload-pending) reload-pending) (rename-out [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 syndicate/supervise) (require/activate syndicate/drivers/filesystem) (struct reload-pending (filename) #:prefab) ;; ASSERTION (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 ...))])) (define-syntax-rule (spawn-reloader module-path) (spawn-reloader* 'module-path)) (define (spawn-reloader* module-path) (match (module-path->path-string module-path) [#f #f] [pathstr (supervise #:name (list 'reloader-supervisor pathstr) (spawn #:name 'reloader (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) (react (field [obstacles-cleared? #f]) (define/query-value obstacles-exist? #f (observe (reload-pending pathstr)) #t #:on-add (log-syndicate/reload-info "waiting to reload ~v" pathstr) #:on-remove (obstacles-cleared? #t)) (assert #:when (obstacles-exist?) (reload-pending pathstr)) (on-start (flush!) (obstacles-cleared? (not (obstacles-exist?)))) (stop-when (rising-edge (obstacles-cleared?)) (flush!) ;; Wait one turn for effects of newly-cleared obstacles (log-syndicate/reload-info "(re)loading ~v" pathstr) (dynamic-rerequire module-path) (schedule-actions! ((dynamic-require `(submod ,module-path syndicate-main) 'activate!))) (reloading? #f))))) (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)))))