[racket] instantiating multiple sandboxes with gui's
Thank you Robby. I've spent some time thinking about your response,
trying to make use of it, and even looking at some
mred/private/common/wx code.
First, I don't grok the need for an extra thread. This is for an Emacs
mode REPL. It's quite similar to Racket's xrepl, which doesn't use an
extra thread. Although I could create one if necessary, I think it
would complicate things when I have a more basic issue regarding
`main-eventspace`, custodians, and how/when racket/gui/base gets
loaded:
As an Emacs mode (unlike DrRacket, which is a GUI that needs
racket/gui/base itself), I don't want to statically `require`
racket/gui/base, because I don't want it loaded until/unless it's
actually needed by a user program. (Otherwise, e.g. on OS X, running a
non-GUI program, you would get a weird windowless menu bar, and it
steals the focus from Emacs.) So the way that racket/gui/base _first_
gets required, is via the dynamic-require of the first user program
that happens to require it. As a result the `main-eventspace` state in
mred/private/wx/common/queue.rkt (IIUC) is associated with
`user-cust`, not `orig-cust`. As a result, custodian-shutdown-all
blows that away. And I don't know how to recreate it correctly.
Symptom: Subsequent runs of a user program like `(require plot) (plot
__)`, result in windows that sit there un-drawn and/or unresponsive to
mouse input.
Seems like it could be solved on either "end":
1. Somehow try to manage the load process so that the first-ever load
of racket/gui/base gets its stuff on orig-cust but the user's stuff on
user-cust.
2. Somehow re-initialize the main eventspace sufficiently.
Although 2 seems more plausible and less kludgy, I can't work out how
to do it either way.
Some code snippets that might be more understandable than prose:
;; (or/c #f path?) -> any
(define (run path-str)
(define-values (mod load-dir) (path-string->mod-path&load-dir path-str))
;; Use custodian to release resources.
;;
;; The problem here is that this seems to be "too thorough" -- it
;; boinks `main-eventspace` in mred/private/queue.rkt. Why? How
;; racket/gui/base first got loaded was at end of this function,
;; in the dynamic-require of whatever user module first happened to
;; use racket/gui/base. (We don't want to load racket/gui/base
;; in the Emacs mode until/unless it's actually needed). Therefore
;; `main-eventspace` is created on `user-cust`, and gets blown away
;; here. Ideally, instead `main-eventspace` would be managed by
;; `orig-cust`, and everything else about the user program would be
;; on `user-cust`. But how to do that??
(custodian-shutdown-all user-cust)
;; New custodian
(set! user-cust (make-custodian orig-cust))
(current-custodian user-cust)
;; Save the current namespace. Save whether it uses racket/gui/base.
(define orig-ns (current-namespace))
(define had-gui? (module-declared? 'racket/gui/base))
;; Fresh, clear racket/base namespace.
(current-namespace (make-base-namespace))
(when had-gui?
(displayln "had-gui? #t")
;; If racket/gui/base module was instantiated in orig-ns, attach
;; and require it into the new namespace, and _before_ requiring
;; the new module. Avoids "cannot instantiate `racket/gui/base' a
;; second time in the same process" problem.
(namespace-attach-module orig-ns 'racket/gui/base)
(namespace-require 'racket/gui/base)
;; Also need to create an eventspace. FIXME: Not working with
;; plot.rkt example. custodian-shutdown-all seems to have borked
;; the original main eventspace state, and our newly-created one
;; ain't good enough.
(define (gdr sym)
(dynamic-require 'racket/gui/base sym))
(define current-eventspace (gdr 'current-eventspace))
(define make-eventspace (gdr 'make-eventspace))
(current-eventspace (make-eventspace)))
;; Load the user module, if any
(when mod
(parameterize ([current-load-relative-directory load-dir])
(dynamic-require mod 0)
(current-namespace (module->namespace mod))))
(current-prompt-read (make-prompt-read mod)))
Example GUI user program:
#lang racket
(require plot math)
(plot-new-window? #t)
(plot (function sin (- pi) pi #:label "y = sin(x)"))
On Thu, Apr 3, 2014 at 5:49 PM, Robby Findler
<robby at eecs.northwestern.edu> wrote:
> I think the main missing thing is that the thread you're requiriing the
> user's program on should be under the control of the custodian. In other
> words, you'll want to create a new eventspace (under the new custodian) and
> queue a callback over to it to require the user's program.
>
> Robby
>
>
> On Thu, Apr 3, 2014 at 4:26 PM, Greg Hendershott <greghendershott at gmail.com>
> wrote:
>>
>> So the background for Spencer's question was a problem with my Emacs
>> racket-mode when using racket/gui/base.
>>
>> There's a long-ish bug report comment thread. If you want to read it,
>> at all, you might want to skip to the last few comments, here:
>>
>>
>> https://github.com/greghendershott/racket-mode/issues/26#issuecomment-39494285
>>
>> Or the TL;DR: I probably don't need racket/sandbox at all. I probably
>> just need a new namespace and a custodian -- in order to do a
>> DrRacket-style "reset the REPL to the source file".
>>
>> Matthew and Robby I know you're both incredibly busy, but if either of
>> you had a chance to look at this code, I'd be grateful. Although part
>> of me hopes you'll say "perfect!", the older/wiser part of me hopes
>> you'll point out X Y and Z problems I don't yet realize I have. It's
>> just these 30 lines of code here:
>>
>>
>> https://github.com/greghendershott/racket-mode/blob/experimental/sandbox.rkt#L30-L66
>>
>> On Sun, Mar 23, 2014 at 8:35 PM, Robby Findler
>> <robby at eecs.northwestern.edu> wrote:
>> > It is safe, however, to share the racket/gui/base that you get with the
>> > one
>> > in the sandbox.
>> >
>> > Robby
>> >
>> >
>> > On Sun, Mar 23, 2014 at 7:25 PM, Matthew Flatt <mflatt at cs.utah.edu>
>> > wrote:
>> >>
>> >> Yes, this is a limitation of `racket/gui/base`. On initialization, the
>> >> library must register in various non-composable ways with an underlying
>> >> GUI toolkit (callbacks, Objective-C classes, Win32 classes, etc.), and
>> >> so `racket/gui/base` cannot be instantiated multiple times.
>> >>
>> >> At Sun, 23 Mar 2014 20:12:43 -0400, Spencer Florence wrote:
>> >> > I'm attempting to launch multiple evaluators which require
>> >> > `racket/gui`,
>> >> > however I get the error:
>> >> >
>> >> > cannot instantiate `racket/gui/base` a second time in the same
>> >> > process
>> >> >
>> >> > Is `racket/gui/base` maintaining some kind of state thats escaping
>> >> > the
>> >> > sandbox?
>> >> >
>> >> >
>> >> >
>> >> >
>> >> >
>> >> > Example of the problem:
>> >> >
>> >> > #lang racket/base
>> >> > (require racket/sandbox)
>> >> >
>> >> > (call-with-trusted-sandbox-configuration
>> >> > (lambda ()
>> >> > (define (make)
>> >> > (make-evaluator 'racket/base
>> >> > #:requires '(racket/gui/base)))
>> >> >
>> >> > (make)
>> >> > (make)))
>> >> > ____________________
>> >> > Racket Users list:
>> >> > http://lists.racket-lang.org/users
>> >> ____________________
>> >> Racket Users list:
>> >> http://lists.racket-lang.org/users
>> >
>> >
>> >
>> > ____________________
>> > Racket Users list:
>> > http://lists.racket-lang.org/users
>> >
>
>