[e-lang] propUtils#testProp/2: not synchronously callable

Thomas Leonard talex5 at gmail.com
Wed Dec 30 06:16:27 PST 2009


2009/12/29 Thomas Leonard <talex5 at gmail.com>:
[...]
> After adding some more information to the error message, it seems that
> __matchBind is sometimes only a promise. The error message can be made
> easily reproducible by adding a delay to the top of
> elang/expand/__matchBind.emaker
[...]
> I thought the problem might be in LazyEvalSlot, which is used to get
> the value of __matchBind when needed. A comment in the code says:
>
>            synchronized (myLock) {
>                // If it's asked for while it's doing its own evaluation, it
>                // returns a promise for what it'll evaluate to.
>                myOptValue = promise[0];
>                myOptScope = null;
>                myOptSource = null;
>            }
>
> However, making the whole method synchronised didn't help. I also
> thought it might be something to do with ImportLoader, which does a
> similar trick, but I can't see why either of these would be shared
> between threads; ScopeSetup seems to make a new ImportLoader in
> privileged/7. Why are they "synchronized"?

To answer my own question: lazy slots and import loaders are shared
between threads when using Vat.seed(rec). Its comment says:

     * This is safe only when all the mutable
     * state transitively reachable from <tt>rec</tt> is no longer reachable
     * from any other vat (typically, not from the current vat -- the vat of
     * origin), or that any possibly shared mutable state is managed in a
     * conventionally thread-safe manner.

eLauncherAuthor.emaker does this:

                def parseFunc :rcvr := parserVat.seed(fn{
                    def input := if (fname =~ `-` || fname.startsWith("-.")) {
                        auths["metain"]
                    } else {
                        def <file> := auths["file__uriGetter"]
                        <file>[fname].textReader()
                    }

                    def optPromptOut := if (isInteractive) {
                        auths["metaout"]
                    } else {
                        null
                    }

                    def lineFeeder := auths["makeFileFeeder"](fname,
                                                              input,
                                                              optPromptOut)

                    makeParseFunc(lineFeeder, props, auths["metaerr"])
                })

I don't understand how this is supposed to work. The function uses the
start thread's scope to look up __matchBind (for the =~), which isn't
safe because it will cause this to resolve to a promise briefly if the
start thread tries to load it too (using their shared LazyEvalSlot).

However, even if this were fixed, the code is still using a number of
objects from the start thread, including some passed as arguments. It
seems unlikely that these are all thread-safe (given that importing
any E code or using many things in safeScope  is unsafe).

Perhaps it should use seedVatAuthor instead, and hard-code the
required objects (makeFileFeeder, makeELexer, etc) rather than taking
them as inputs?

Could someone explain how this is intended to work? I'm hoping to make
a package of the current svn version of E, but I need a version that
compiles and runs reliably first. I'm thinking of just doing the
parsing and repl in the start thread, as a workaround to avoid these
issues; is that sensible?

Thanks,


-- 
Dr Thomas Leonard		ROX desktop / Zero Install
GPG: 9242 9807 C985 3C07 44A6  8B9A AE07 8280 59A5 3CC1


More information about the e-lang mailing list