[racket] instantiating multiple sandboxes with gui's

From: Greg Hendershott (greghendershott at gmail.com)
Date: Sat Apr 5 14:15:06 EDT 2014

Oh wait. There could be per-thread "resources". Such as thread cells?
That's why it really ought to be a fresh thread?

On Sat, Apr 5, 2014 at 1:58 PM, Greg Hendershott
<greghendershott at gmail.com> 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.
>
> 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.