One more low-level question (where it seems like implementation details
matter to embedders):

Based on what you've said, an object may or may not be "black" on
allocation. It seems, though, that I need to do different things depending
on the color. If it is allocated black, then I need to arrange to trace the
object myself to register all its outgoing references. If it is white, then
I need to call RegisterExternalReference(), and V8 will trace it later. If
it's gray, I don't need to do anything.

But if I don't know the color, it seems I need to *both* call
RegisterExternalReference() and initiate my own trace to cover all bases.
But in the case that it's not already-black, this will result in a
redundant trace later on.

Is it best to live with this potential redundant trace, or is there some
way I can detect the color after allocation?

-Kenton

On Wed, Jan 30, 2019 at 11:35 AM Kenton Varda <ken...@cloudflare.com> wrote:

> Hi Michael,
>
> Thanks, I think I now see the problem: I had assumed that newly-allocated
> objects were always marked immediately on allocation, since they are
> clearly reachable at that time. But, now that I think about it, I suppose
> that would make it hard to quickly collect short-lived objects.
>
> I think realistically the current embedding API requires an understanding
> of implementation details to use correctly. Since there is no written
> guide, the only way I've been able to understand how the pieces fit
> together is by understanding implementation details. In particular I think
> the concept of write barriers and when to use them is not very intuitive to
> those who haven't studied garbage collection. Once I get this sorted out I
> may try to write a beginner's guide from that perspective.
>
> ------
>
> The TracedGlobal API looks like a good addition to make embedders' lives
> easier, but it might turn out it doesn't quite fit with the model we've
> settled on in our code. Specifically, we want to allocate JS wrapper
> objects for a C++ object lazily, on the first time the object is directly
> referenced from JS. This makes it tricky to use V8 handles to link C++
> objects together, because there may not be a JS object yet for the handle
> to point to. Instead, we reference the C++ object directly, and the C++
> object holds a handle to its own JS wrapper which is allocated lazily. If
> each reference from another C++ object required its own TracedHandle, then
> we'd have to keep a reverse mapping of all the references pointing to an
> object so that we could initialize all of the TracedHandles when the
> wrapper is lazily allocated.
>
> A further problem is that the parent object may itself not have any
> wrapper allocated, but may have references from C++ which we considered to
> be strong references. Consider e.g. the case of a Request object containing
> a Headers object. Imagine the Headers object has a JS wrapper but the
> Request object does not, and is only referenced from C++. In this case, the
> reference from Request -> Headers will never be traced, and so we need to
> treat the Headers wrapper as having a strong reference. Later on, a wrapper
> may be allocated for the Request. Once that happens, then we only need a
> strong reference on the Request wrapper itself, and we can rely on tracing
> to find the Headers wrapper.
>
> So, the problem here is that over the lifetime of a reference between two
> C++ objects, the reference's nature can change between being C++-only (no
> V8 handle), being a strong V8 handle, and being a traced V8 handle. So it
> doesn't seem like we can simply drop in a TracedGlobal here, unfortunately.
>
> That said, if you would prefer that we move towards using weak handles
> strictly for registering the destructor callback, and strictly use
> TracedGlobal for traced handles, then I think we can work with that by
> having a scheme where each object potentially holds three different handles
> to its own wrapper:
> - A weak independent handle, to get the weak callback.
> - A strong handle, when any non-traced references exist from C++, to keep
> the wrapper alive.
> - A TracedGlobal, when any traced references exist from C++, used to
> implement tracing.
>
> Let me know if you think it would be a good idea for me to implement that
> instead of relying on one handle and doing SetWeak()/ClearWeak().
>
> ------
>
> Also, another technical question clarifying something you said: If an
> object is first discovered and marked during "final pause" (e.g. because it
> has a strong persistent handle at that point which was never seen before),
> it still gets traced, right? I guess that means that the "final pause"
> could end up arbitrarily long, if some deep object tree managed to slip by
> the tracer until then?
>
> Thanks,
> -Kenton
>
> On Wed, Jan 30, 2019 at 2:50 AM Michael Lippautz <mlippa...@chromium.org>
> wrote:
>
>> On Mon, Jan 28, 2019 at 11:12 PM 'Kenton Varda' via v8-users <
>> v8-users@googlegroups.com> wrote:
>>
>>> I'm still working on this off-and-on. The issue is not as urgent as it
>>> sounds because in the case that a wrapper object is collected prematurely,
>>> we simply remake it as needed. Still, this could cause issues if scripts
>>> add JS properties on native objects and expect them to stay there, but the
>>> issues seems to be affecting only a handful of specific scripts on our
>>> platform and none of them happen to do that... Still, we do need to fix the
>>> issue for future scripts.
>>>
>>>
>> We are currently reworking APIs around the use of EmbedderHeapTracer and
>> how it is used with V8 handles. In 7.4 we introduced a new type
>> (TracedGlobal) that is tied to the use case of tracing through the embedder
>> heap. This type is treated as root for scavenges unless explicitly opted in
>> into treating it as non-root.
>>
>> We advise against switching handles from strong to weak and vice versa as
>> it is hard to follow what's going on exactly.
>>
>>
>>> On Wed, Jan 16, 2019 at 2:23 AM Michael Lippautz <mlippa...@chromium.org>
>>> wrote:
>>>
>>>> - The ClearWeak part should work if there's a final GC pause
>>>> interrupting whatever is done with C++ stack.
>>>>
>>>
>>> Sorry, I'm a GC noob. What happens in the "final GC pause", exactly?
>>> What does it mean for it to "interrupt whatever is done with the C++ stack"?
>>>
>>
>> Final pause is when the garbage collector finalizes the current cycle.
>> The stack is re-scanned if needed. Handles are also re-scanned. So, if some
>> handles is made strong by called ClearWeak() it will be considered as
>> strong root during this phase.
>>
>> "interrupt whatever is done in C++" was referring to the embedder
>> situation where there's the V8 garbage collection is usually triggered when
>> there's a native C++ stack. All of V8's objects are held through Local or
>> some sort of Persistent handle when this happens.
>>
>>
>>>
>>> Does ClearWeak() implicitly mark the object, if tracing is already
>>> in-progress? Or is that what happens in the final GC pause? Does that
>>> object get traced?
>>>
>>
>> No to all of it. As mentioned above, it will be discovered as strong root
>> during root scanning.
>>
>>
>>>
>>> Looking at the code, it seems like ClearWeak() does not mark the object.
>>> So I guess I should probably RegisterExternalReference() after ClearWeak()?
>>> Normally a strong reference would be a root and so would be marked at the
>>> beginning of the trace cycle, but if ClearWeak() happens mid-cycle it seems
>>> like there's an issue.
>>>
>>
>> ClearWeak makes the handle strong. It should be discovered during root
>> scanning.
>>
>>
>>>
>>> However, this doesn't seem to fit the pattern of the problems I'm seeing
>>> in production.
>>>
>>>
>>>> - The SetWeak after being done may or may not require a manual
>>>> registering call, depending on how EmbedderHeapTracer is implemented.
>>>>
>>>
>>>> In Blink we implemented regular traced garbage collection for the
>>>> EmbedderHeapTracer. This means that the object containing such an
>>>> interesting references may have been processed by the EmbedderHeapTracer
>>>> already. If the reference is then just marked as weak with SetWeak() the GC
>>>> misses out on it as it never sees the containing object again. Blink emits
>>>> a RegisterExternalReference() call for such objects. The general concept
>>>> for solving mutation in the graph while a garbage collector is running is
>>>> called (write barrier).
>>>>
>>>
>>> Not sure I follow. RegisterExternalReference() is only meaningful on
>>> objects that are currently white, right? But before SetWeak(), the handle
>>> was strong, making it a root. Roots should have been marked gray at the
>>> start of the tracer cycle?
>>>
>>>
>> This is an implementation detail, but yeah, V8 marks the full root set,
>> including the handles, at the beginning of incremental marking. If the
>> handle was strong at this point the object is transitioning through the
>> marking phase. V8 also re-scans roots before finalizing the current cycle.
>>
>> The embedder should not reason about object colors as they are an
>> implementation detail.
>>
>>
>>> In my case, I think the objects are frequently newly-allocated at the
>>> time SetWeak() is called. But a newly-allocated object should be marked
>>> black at allocation, right?
>>>
>>>
>> Depends and is an implementation detail. It is not safe to assume that
>> new objects are allocated black during GC. (It does not hold for new space
>> for various optimization reasons.)
>>
>> So, if you want to use tracing and create a new reference of that sort
>> using SetWeak() you also need to register the object using
>> RegisterExternalReference. (In future, just use TracedGlobal and pass it to
>> EmbedderHeapTracer::RegisterEmbedderReference.)
>>
>>
>>> Thanks again for the help! Hopefully we'll be able to open source this
>>> glue library so others can reuse this work...
>>>
>>>
>> https://chromium-review.googlesource.com/c/v8/v8/+/1425523 could be
>> interesting then. The idea is to separate out the tracing use case so that
>> there's no mix up between regular strong and weak handles anymore.
>>
>> -Michael
>>
>> --
>> --
>> v8-users mailing list
>> v8-users@googlegroups.com
>> http://groups.google.com/group/v8-users
>> ---
>> You received this message because you are subscribed to a topic in the
>> Google Groups "v8-users" group.
>> To unsubscribe from this topic, visit
>> https://groups.google.com/d/topic/v8-users/EeIPnAmNa4g/unsubscribe.
>> To unsubscribe from this group and all its topics, send an email to
>> v8-users+unsubscr...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>

-- 
-- 
v8-users mailing list
v8-users@googlegroups.com
http://groups.google.com/group/v8-users
--- 
You received this message because you are subscribed to the Google Groups 
"v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to v8-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to