[racket] instantiating multiple sandboxes with gui's

From: Greg Hendershott (greghendershott at gmail.com)
Date: Sat Apr 5 13:58:41 EDT 2014

> re grokking extra thread: No no, that's not what I'm saying. I'm saying that
> if you don't create the extra thread, then the custodian's protection is
> circumvented. The thread you get started on is not under the auspices of the
> custodian you created, so that thread doesn't get shutdown when you shutdown
> the custodian. This means that the user's program won't be completely reset
> unless you set up the extra thread.

Right but the REPL uses the default thread to run the user's program.
I don't need or want _that_ thread to be shutdown. I do want any
_additional_ threads (and ports etc.) to be shutdown. I thought
setting current-custodian means, from that point on, these things will
be under the custodian. So for instance:

(define t0 (current-thread)) ;; from `main`
(define user-cust (make-custodian (current-custodian))
(current-custodian user-cust)
;; resources
(define t1 (thread ...))
(define es (make-eventspace ...))
(define ip (open-input-port ...))
(custodian-shutdown-all user-cust)
;; ^ releases t1, es, ip -- but _not_ t0

Assuming that last comment is correct (?), that seems fine. Everything
about the user program has been released, except for REPL's main
thread that was simply running the user program for awhile. So that's
fine. No?

On Sat, Apr 5, 2014 at 10:02 AM, Robby Findler
<robby at eecs.northwestern.edu> wrote:
> re grokking extra thread: No no, that's not what I'm saying. I'm saying that
> if you don't create the extra thread, then the custodian's protection is
> circumvented. The thread you get started on is not under the auspices of the
> custodian you created, so that thread doesn't get shutdown when you shutdown
> the custodian. This means that the user's program won't be completely reset
> unless you set up the extra thread.
>
> As for the question of whether or not to create an eventspace (to use its
> thread) or to call 'thread' directly, I think that you're going to have to
> rely on someone external to tell you that. I guess it would be possible look
> at the program's require graph and see if it requires racket/gui/base, but
> that is work the user might wish you didn't do and it is foiled by some
> programs that will dynamic-require it. Probably the best thing is to have an
> emacs-level variable that determines if you create that first thread via an
> eventspace or not.
>
> 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
>> >> >
>> >
>> >
>
>

Posted on the users mailing list.