[racket] instantiating multiple sandboxes with gui's
Yes, that aspect of it -- attaching the racket/gui/base module, if
any, from the old namespace to the new one -- I figured out fairly
fast and it has been working fine in my testing so far.
The tricky part is that I seem to need a hypothetical
"custodian-shutdown-all-except-the-root-main-eventspace-in-queue.rkt",
or, a "please-revive-the-main-eventspace".
Either that, or, I need to insinuate myself into the load process so I
can notice that racket/gui/base is about to be required for the first
time, setup a separate custodian and require it msyelf, then set
_another_ custodian for the user module. That way I could do a
custodian-shutdown-all with the latter custodian, leaving the main
eventspace untouched. I think.
Or I could just punt it onto the user with a "hey I'm going to run a
GUI" command or flag they need to use. Which might be the sane thing
to do. I'm just not quite ready to give up and settle for that.
On Sat, Apr 5, 2014 at 10:04 AM, Robby Findler
<robby at eecs.northwestern.edu> wrote:
> Oh, and I see a second issue that you're probably getting at: in the case
> you decide you are going to run GUI programs (via the emacs-level variable
> if you don't find a better way than my recommendation) you need to share
> '(lib "mred/mred.rkt") (or maybe it is okay to share racket/gui/base)
> between the original namespace that your code created and the user program
> you wish to run.
>
> This library is designed to support this form of sharing and is carefully
> implemented not to leak, no matter what nastiness the user program might try
> to do to it (most libraries do not provide such guarantees).
>
> Robby
>
>
> On Sat, Apr 5, 2014 at 8:25 AM, Greg Hendershott <greghendershott at gmail.com>
> wrote:
>>
>> 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
>> >> >
>> >
>> >
>
>