Re: propUtils#testProp/2: not synchronously callable

Thomas Leonard <[email protected]>
Newsgroups gmane.comp.lang.e.general
Message-ID <[email protected]>
2009/12/29 Thomas Leonard <[email protected]>:
[...]
> 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.