public
Created

Racket custodians and namespaces for rebootable racket programs

  • Download Gist
README.md
Markdown

Demonstrates the use of Racket's custodians and namespaces to create enough isolation to "reboot" programs without restarting the whole of Racket, including possibly recompiling and reloading code.

To experiment with this:

$ racket ~/src/racket-experiments$ racket
Welcome to Racket v5.3.1.4.
-> (require "reloader.rkt")
-> (load-and-foo)
Starting worker...
test-mod: "foo"
other-mod: "foo"
other-mod: "foo"
other-mod: "foo"
other-mod: "foo"
Terminating worker...
-> 

Subsequent runs of (load-and-foo) will reload the code from scratch. If you've edited one of test-mod.rkt or other-mod.rkt in the meantime, the changes will show up.

Note that other-mod.rkt runs a loop "forever", but that reloader.rkt terminates all threads in the nested custodian.

other-mod.rkt
Racket
1 2 3 4 5 6 7 8
#lang racket/base
 
(require "test-mod.rkt")
 
(let loop ()
(printf "other-mod: ~v\n" (foo))
(sleep 1)
(loop))
reloader.rkt
Racket
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#lang racket/base
 
(define (load-and-foo)
(define sub-custodian (make-custodian))
(printf "Starting worker...\n")
(parameterize ((current-custodian sub-custodian))
(thread (lambda ()
(parameterize ((current-namespace (make-base-namespace)))
(dynamic-require "other-mod.rkt" #f)))))
(sleep 3.5)
(printf "Terminating worker...\n")
(custodian-shutdown-all sub-custodian))
 
(provide load-and-foo)
test-mod.rkt
Racket
1 2 3 4 5 6 7
#lang racket/base
 
(provide foo)
 
(define (foo) "foo")
 
(printf "test-mod: ~v\n" (foo))

Hi,

This is wonderful.
I have one problem when test-mod.rkt requires racket/gui instead of racket/base:

Terminating worker...
Starting worker...
cannot instantiate `racket/gui/base' a second time in the same process

By any chance, would you know how to solve this issue?

Laurent

I may have found a workaround, but I'm not really sure it works as it should:

(require racket/gui/base)
(...)

(define-namespace-anchor this-namespace-anchor)
(define this-namespace (namespace-anchor->namespace this-namespace-anchor))
(...)
      (parameterize ([current-custodian sub-custodian]
                     [current-namespace (make-base-namespace)])
        (namespace-attach-module this-namespace 'racket/gui/base)
        (dynamic-require (...))
        (...)

This way, racket/gui/base is instanciated only once, and reused for every dynamic-require,
but then maybe some other things are not properly shut down with the custodian?

Laurent

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.