Bug 223629 - [JSC] Functor for WeakGCMap::ensureValue must not invoke GC
Summary: [JSC] Functor for WeakGCMap::ensureValue must not invoke GC
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Yusuke Suzuki
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-03-23 01:42 PDT by Yusuke Suzuki
Modified: 2021-03-24 14:13 PDT (History)
12 users (show)

See Also:


Attachments
Patch (5.86 KB, patch)
2021-03-23 01:45 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (7.93 KB, patch)
2021-03-23 01:53 PDT, Yusuke Suzuki
mark.lam: review+
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
Patch (11.13 KB, patch)
2021-03-23 11:49 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Yusuke Suzuki 2021-03-23 01:42:15 PDT
[JSC] Functor for WeakGCMap::ensureValue must not invoke GC
Comment 1 Yusuke Suzuki 2021-03-23 01:45:43 PDT
Created attachment 423997 [details]
Patch
Comment 2 Yusuke Suzuki 2021-03-23 01:45:46 PDT
<rdar://problem/75619217>
Comment 3 Yusuke Suzuki 2021-03-23 01:48:17 PDT
Comment on attachment 423997 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=423997&action=review

> Source/JavaScriptCore/runtime/WeakGCMap.h:73
> +        // If functor invokes GC, GC can prune WeakGCMap, and manipulate HashMap while we are touching it in ensure function.
> +        // The functor must not invoke GC.
> +        DisallowGC disallowGC;

I do not want to put DeferGC here since we would like to be aware about the scope of DeferGC explicitly. Deferring GC is not generally good. We would like to invoke GC responsively.
If we use DeferGC, we should make DeferGC scope small, and we should be aware of that.
Comment 4 Yusuke Suzuki 2021-03-23 01:53:22 PDT
Created attachment 423998 [details]
Patch
Comment 5 Mark Lam 2021-03-23 08:06:49 PDT
Comment on attachment 423998 [details]
Patch

r=me.  Please make sure that the EWS failures are not real.  FYI, I've started a re-run.
Comment 6 Darin Adler 2021-03-23 09:14:10 PDT
Instead of deferring GC we can switch to a double hashing algorithm (separate get and add), or we can make code that conditionally rehashes if the hash table is modified during the ensure functor.
Comment 7 Sam Weinig 2021-03-23 09:22:41 PDT
It's too bad we can't reuse any of the logic in WeakMapImpl for this, which already should have to deal with this issue.
Comment 8 Sam Weinig 2021-03-23 09:25:52 PDT
(In reply to Sam Weinig from comment #7)
> It's too bad we can't reuse any of the logic in WeakMapImpl for this, which
> already should have to deal with this issue.

There is very similar code to this in

JSDOMGlobalObject::createCrossOriginFunction/JSDOMGlobalObject::createCrossOriginGetterSetter. 

We should make sure that case is also fixed and potentially add a test if possible.
Comment 9 Sam Weinig 2021-03-23 09:46:35 PDT
(In reply to Yusuke Suzuki from comment #3)
> Comment on attachment 423997 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=423997&action=review
> 
> > Source/JavaScriptCore/runtime/WeakGCMap.h:73
> > +        // If functor invokes GC, GC can prune WeakGCMap, and manipulate HashMap while we are touching it in ensure function.
> > +        // The functor must not invoke GC.
> > +        DisallowGC disallowGC;
> 
> I do not want to put DeferGC here since we would like to be aware about the
> scope of DeferGC explicitly. Deferring GC is not generally good. We would
> like to invoke GC responsively.
> If we use DeferGC, we should make DeferGC scope small, and we should be
> aware of that.

Would it work to instead of using DeferGC for this, we added a bit to WeakGCMap that makes pruneStaleEntries() a no-op while it is set? And then a function like ensureValue would set that bit while it was running?
Comment 10 Mark Lam 2021-03-23 09:53:25 PDT
(In reply to Sam Weinig from comment #9)
> (In reply to Yusuke Suzuki from comment #3)
> > Comment on attachment 423997 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=423997&action=review
> > 
> > > Source/JavaScriptCore/runtime/WeakGCMap.h:73
> > > +        // If functor invokes GC, GC can prune WeakGCMap, and manipulate HashMap while we are touching it in ensure function.
> > > +        // The functor must not invoke GC.
> > > +        DisallowGC disallowGC;
> > 
> > I do not want to put DeferGC here since we would like to be aware about the
> > scope of DeferGC explicitly. Deferring GC is not generally good. We would
> > like to invoke GC responsively.
> > If we use DeferGC, we should make DeferGC scope small, and we should be
> > aware of that.
> 
> Would it work to instead of using DeferGC for this, we added a bit to
> WeakGCMap that makes pruneStaleEntries() a no-op while it is set? And then a
> function like ensureValue would set that bit while it was running?

if GC can run, and the setter is not reachable by GC, and we don't prune the pointer, wouldn't simply skipping pruning means we'll get a dangling pointer because GC can sweep that setter away later?
Comment 11 Mark Lam 2021-03-23 09:55:35 PDT
(In reply to Mark Lam from comment #10)
> if GC can run, and the setter is not reachable by GC, and we don't prune the
> pointer, wouldn't simply skipping pruning means we'll get a dangling pointer
> because GC can sweep that setter away later?

Nevermind, I misunderstood pruning.  The pointer is already null.
Comment 12 Yusuke Suzuki 2021-03-23 11:44:44 PDT
(In reply to Darin Adler from comment #6)
> Instead of deferring GC we can switch to a double hashing algorithm
> (separate get and add), or we can make code that conditionally rehashes if
> the hash table is modified during the ensure functor.

I think the current DeferGC thing is the best way since,

1. DeferGC does not defer GC forever. When destroying DeferGC scope, we invoke GC if GC request happened during DeferGC scope. So this DeferGC only defers one function allocation.
2. While we can just defer GC for one allocation, we can avoid double hashing, and we can avoid making hash table more complicated.
Comment 13 Yusuke Suzuki 2021-03-23 11:46:34 PDT
(In reply to Sam Weinig from comment #9)
> (In reply to Yusuke Suzuki from comment #3)
> > Comment on attachment 423997 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=423997&action=review
> > 
> > > Source/JavaScriptCore/runtime/WeakGCMap.h:73
> > > +        // If functor invokes GC, GC can prune WeakGCMap, and manipulate HashMap while we are touching it in ensure function.
> > > +        // The functor must not invoke GC.
> > > +        DisallowGC disallowGC;
> > 
> > I do not want to put DeferGC here since we would like to be aware about the
> > scope of DeferGC explicitly. Deferring GC is not generally good. We would
> > like to invoke GC responsively.
> > If we use DeferGC, we should make DeferGC scope small, and we should be
> > aware of that.
> 
> Would it work to instead of using DeferGC for this, we added a bit to
> WeakGCMap that makes pruneStaleEntries() a no-op while it is set? And then a
> function like ensureValue would set that bit while it was running?

I think DeferGC is better since this makes collecting dead Weak handles & shrinking later.
When comparing (1) not collecting dead Weak handles until the next Full GC and (2) defer GC for one function allocation, (2) is better.
Comment 14 Yusuke Suzuki 2021-03-23 11:47:53 PDT
(In reply to Sam Weinig from comment #8)
> (In reply to Sam Weinig from comment #7)
> > It's too bad we can't reuse any of the logic in WeakMapImpl for this, which
> > already should have to deal with this issue.
> 
> There is very similar code to this in
> 
> JSDOMGlobalObject::createCrossOriginFunction/JSDOMGlobalObject::
> createCrossOriginGetterSetter. 
> 
> We should make sure that case is also fixed and potentially add a test if
> possible.

I think the existing test is failing because of DisallowGC, and this ensures that we are not invoking GC which incurs WeakGCMap modification. So crashing tests are guaranteeing this.
Comment 15 Yusuke Suzuki 2021-03-23 11:49:16 PDT
Created attachment 424043 [details]
Patch
Comment 16 Yusuke Suzuki 2021-03-23 16:36:44 PDT
Committed r274911 (235681@main): <https://commits.webkit.org/235681@main>
Comment 17 Darin Adler 2021-03-23 16:54:41 PDT
Why are we deferring *both* at all call sites of ensureValue *and* inside ensureValue? It seems like we are holding up our pants using both belts and suspenders. There is probably a good reason, but I can’t figure it out from reading the comments.
Comment 18 Yusuke Suzuki 2021-03-24 02:16:08 PDT
(In reply to Darin Adler from comment #17)
> Why are we deferring *both* at all call sites of ensureValue *and* inside
> ensureValue? It seems like we are holding up our pants using both belts and
> suspenders. There is probably a good reason, but I can’t figure it out from
> reading the comments.

No, inside ensureValue we are using DisallowGC, not DeferGC.
Comment 19 Darin Adler 2021-03-24 10:29:46 PDT
(In reply to Yusuke Suzuki from comment #18)
> No, inside ensureValue we are using DisallowGC, not DeferGC.

I see. Let me check my understanding: callers must not GC-allocate in the function they pass to ensureValue, unless they discover they must, in which case they can relax the restriction by doing DeferGC.
Comment 20 Yusuke Suzuki 2021-03-24 14:13:49 PDT
(In reply to Darin Adler from comment #19)
> (In reply to Yusuke Suzuki from comment #18)
> > No, inside ensureValue we are using DisallowGC, not DeferGC.
> 
> I see. Let me check my understanding: callers must not GC-allocate in the
> function they pass to ensureValue, unless they discover they must, in which
> case they can relax the restriction by doing DeferGC.

Right. This enforces the caller to be aware that they are deferring GC while running this code.