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.