Bug 135358

Summary: Create a more generic way for VMEntryScope to notify those interested that it will be destroyed
Product: WebKit Reporter: Saam Barati <saam>
Component: JavaScriptCoreAssignee: Saam Barati <saam>
Status: RESOLVED FIXED    
Severity: Normal CC: commit-queue, fpizlo, ggaren, mark.lam
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 135627    
Bug Blocks:    
Attachments:
Description Flags
patch
none
patch
ggaren: review-, ggaren: commit-queue-
patch
ggaren: review-
patch
none
patch
none
patch
darin: review+
patch none

Description Saam Barati 2014-07-28 15:12:30 PDT
Currently, when VMEntryScope is destroyed, and it has a flag set that the VM needs recompilation, it calls Debugger::recompileAllJSFunctions.
This is tailored specifically for the Debugger and its purposes of recompiling all functions once the VM stops executing code. 
This patch will substitute this one recompilation flag with a list of callbacks that can all be notified when the VMEntryScope dies. 
This creates a more generalized interface to being notified when VMEntryScope dies.
Comment 1 Saam Barati 2014-07-28 16:09:08 PDT
Created attachment 235630 [details]
patch 

Currently, when VMEntryScope is destroyed, and it has a flag set that the VM
needs recompilation, it calls Debugger::recompileAllJSFunctions. This is
tailored specifically for the Debugger and its purposes of recompiling all
functions once the VM stops executing code (specifically, to ensure that there are 
no functions on the stack that will be clobbered by recompiling them). This patch 
will substitute this one recompilation flag with a list of callbacks that are all 
notified when the VMEntryScope dies. This creates a more generalized interface to 
being notified when the VM stops executing code via the event of VMEntryScope
dying.
Comment 2 WebKit Commit Bot 2014-07-28 16:11:36 PDT
Attachment 235630 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/debugger/Debugger.cpp:338:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
ERROR: Source/JavaScriptCore/debugger/Debugger.cpp:341:  One line control clauses should not use braces.  [whitespace/braces] [4]
Total errors found: 2 in 4 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 3 Saam Barati 2014-07-28 16:16:57 PDT
Created attachment 235635 [details]
patch 

Same patch as above, but with WebKit style compliant changes.
Comment 4 Geoffrey Garen 2014-07-28 16:27:34 PDT
Comment on attachment 235635 [details]
patch 

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

> Source/JavaScriptCore/debugger/Debugger.cpp:338
> +        VMEntryScope::EntryScopeDidPopListener callback = [this] (VM* vm, JSGlobalObject* globalObject) 

This is probably a good place to use "auto".

> Source/JavaScriptCore/debugger/Debugger.cpp:341
> +            if (globalObject->debugger() && globalObject->debugger() == this)
> +                this->recompileAllJSFunctions(vm);

It feels weird to use 'this' here, since we can't guarantee its lifetime. I think it would be better to use globalObject->debugger() instead of 'this'.
Comment 5 Mark Lam 2014-07-28 16:28:40 PDT
Comment on attachment 235630 [details]
patch 

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

> Source/JavaScriptCore/ChangeLog:10
> +        tailored specifically for the Debugger and its purposes of recompiling all

typos in "its purposes of recompiling"?  Can it be phrased better?

> Source/JavaScriptCore/ChangeLog:14
> +        notified when the VMEntryScope dies. This creates a more generalized interface to 

... when the *outermost* VMEntryScope *exits* ...

> Source/JavaScriptCore/debugger/Debugger.cpp:341
> +            if (globalObject->debugger() && globalObject->debugger() == this) {
> +                this->recompileAllJSFunctions(vm);
> +            }

no need for { }

> Source/JavaScriptCore/runtime/VMEntryScope.cpp:71
> +        callback(&m_vm, m_globalObject);

nit: why not us a VM& instead of a VM*?

> Source/JavaScriptCore/runtime/VMEntryScope.h:47
> +    void addEntryScopeDidPopListener(EntryScopeDidPopListener lambda);

The "lambda" is not needed.  Please remove.
Comment 6 Saam Barati 2014-07-28 17:12:52 PDT
Created attachment 235648 [details]
patch 

Took into account the changes that both Mark and Geoff recommended.
Comment 7 Geoffrey Garen 2014-07-29 11:44:15 PDT
Comment on attachment 235648 [details]
patch 

I think you re-uploaded the old patch.
Comment 8 Mark Lam 2014-07-29 12:00:24 PDT
Comment on attachment 235648 [details]
patch 

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

> Source/JavaScriptCore/ChangeLog:14
> +        to being notified when the VM stops executing code via the event of the outermost 

"to being" ==> "for being"

> Source/JavaScriptCore/debugger/Debugger.cpp:345
> +        auto listener = [this] (VM* vm, JSGlobalObject* globalObject) 
> +        {
> +            Debugger* debugger = globalObject->debugger();
> +            if (debugger && debugger == this)
> +                debugger->recompileAllJSFunctions(vm);
> +        };
> +
> +        vm->entryScope->addEntryScopeDidPopListener(listener);

I just thought of an issue.  This code registers the listener, but you don't have any code to unregister it when the debugger is detached.  Please fix.
Comment 9 Saam Barati 2014-07-29 12:22:16 PDT
(In reply to comment #7)
> (From update of attachment 235648 [details])
> I think you re-uploaded the old patch.

No, this was the updated patch. But I think I took your point yesterday as being that we should call this method on globalObject->debugger and still compare that to 'this', but maybe, you meant that we shouldn't compare to 'this' at all?

So:
Debugger* debugger = globalObject->debugger();
> +            if (debugger && debugger == this)
> +                debugger->recompileAllJSFunctions(vm);

==> becomes ==>

> +            if (debugger)
> +                debugger->recompileAllJSFunctions(vm);


Is that what you meant?
Comment 10 Saam Barati 2014-07-29 12:22:53 PDT
(In reply to comment #8)
> (From update of attachment 235648 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=235648&action=review
> 
> > Source/JavaScriptCore/ChangeLog:14
> > +        to being notified when the VM stops executing code via the event of the outermost 
> 
> "to being" ==> "for being"
> 
> > Source/JavaScriptCore/debugger/Debugger.cpp:345
> > +        auto listener = [this] (VM* vm, JSGlobalObject* globalObject) 
> > +        {
> > +            Debugger* debugger = globalObject->debugger();
> > +            if (debugger && debugger == this)
> > +                debugger->recompileAllJSFunctions(vm);
> > +        };
> > +
> > +        vm->entryScope->addEntryScopeDidPopListener(listener);
> 
> I just thought of an issue.  This code registers the listener, but you don't have any code to unregister it when the debugger is detached.  Please fix.

I'll create an interface for this.
Comment 11 Geoffrey Garen 2014-07-29 15:10:19 PDT
> So:
> Debugger* debugger = globalObject->debugger();
> > +            if (debugger && debugger == this)
> > +                debugger->recompileAllJSFunctions(vm);
> 
> ==> becomes ==>
> 
> > +            if (debugger)
> > +                debugger->recompileAllJSFunctions(vm);

That's right.

It's not good to use 'this' unless we can guarantee that the object pointed to by 'this' has not been deleted.

It's not good to use a potentially deleted pointer -- even if you never dereference it. If the object pointed to by 'this' is deleted, and a new object is allocated in its place, then our "== this" comparison means something totally different, and probably wrong.
Comment 12 Saam Barati 2014-07-29 16:18:57 PDT
Created attachment 235711 [details]
patch 

More review considerations taken into account. Ultimately, I decided not to provide an interface for unregistering listeners.

If such an interface becomes required, it can be added in the future.
Comment 13 WebKit Commit Bot 2014-07-29 16:21:19 PDT
Attachment 235711 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/runtime/VMEntryScope.h:32:  Alphabetical sorting problem.  [build/include_order] [4]
Total errors found: 1 in 4 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 14 Saam Barati 2014-07-29 16:27:23 PDT
Created attachment 235714 [details]
patch 

Fixed out of alphabetical order include.
Comment 15 Geoffrey Garen 2014-08-04 14:58:14 PDT
Comment on attachment 235714 [details]
patch 

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

r=me

> Source/JavaScriptCore/debugger/Debugger.cpp:342
> +            Debugger* debugger = globalObject->debugger();
> +            if (debugger)
> +                debugger->recompileAllJSFunctions(vm);

It's nice to write this as a single line. That way, it's lexically impossible to use the NULL pointer:

if (Debugger* debugger = globalObject->debugger()) { ... }
Comment 16 WebKit Commit Bot 2014-08-04 15:05:01 PDT
Comment on attachment 235714 [details]
patch 

Clearing flags on attachment: 235714

Committed r172009: <http://trac.webkit.org/changeset/172009>
Comment 17 WebKit Commit Bot 2014-08-04 15:05:05 PDT
All reviewed patches have been landed.  Closing bug.
Comment 18 WebKit Commit Bot 2014-08-05 16:01:09 PDT
Re-opened since this is blocked by bug 135627
Comment 19 Saam Barati 2014-08-05 18:18:37 PDT
Created attachment 236068 [details]
patch

Same patch as before, but with Geoff's recommendation taken into account.
Comment 20 Darin Adler 2014-08-05 18:42:54 PDT
Comment on attachment 236068 [details]
patch

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

> Source/JavaScriptCore/runtime/VMEntryScope.cpp:59
> +    m_allEntryScopeDidPopListeners.set(key, listener);

Probably should be add instead of set; set is a slower version that knows how to override values already in the map, and I think the intent here is not to do that.

Also, I think we should assert that they key isn’t already in there.

> Source/JavaScriptCore/runtime/VMEntryScope.cpp:74
> +    auto iter = m_allEntryScopeDidPopListeners.begin();
> +    auto end = m_allEntryScopeDidPopListeners.end();
> +    for ( ; iter != end; ++iter) {
> +        EntryScopeDidPopListener listener = iter->value;
> +        listener(&m_vm, m_globalObject);
>      }

The typical way of writing this kind of thing in new WebKit code is:

    for (auto& listener : m_allEntryScopeDidPopListeners.values())
        listener(&m_vm, m_globalObject);

I think that’s easier to read than the 6-line version.

> Source/JavaScriptCore/runtime/VMEntryScope.h:46
> +    typedef std::function<void (VM*, JSGlobalObject*)> EntryScopeDidPopListener;

Argument should be VM& instead of VM*.

> Source/JavaScriptCore/runtime/VMEntryScope.h:52
> +    HashMap<void*, EntryScopeDidPopListener> m_allEntryScopeDidPopListeners;

Is void* really the best type for these keys?

Why does this need to be a map? Can’t it just be a Vector? Do we ever remove or overwrite any of these?
Comment 21 Saam Barati 2014-08-05 19:41:57 PDT
(In reply to comment #20)
> (From update of attachment 236068 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=236068&action=review
> 
> > Source/JavaScriptCore/runtime/VMEntryScope.cpp:59
> > +    m_allEntryScopeDidPopListeners.set(key, listener);
> 
> Probably should be add instead of set; set is a slower version that knows how to override values already in the map, and I think the intent here is not to do that.
> 
> Also, I think we should assert that they key isn’t already in there.
> 
The intention here is to override the current listener if there is one. Those interested should be able to register listeners as many times as needed without having to worry handling duplicate callbacks themselves. This use case is currently unused because the only users of this API are various WebInspector Objects that subclass Debugger, who only register for this callback once, when they themselves attach to their first global object and request that all JS functions are recompiled. But I have another patch in the works that uses this feature to re-register callbacks (https://bugs.webkit.org/show_bug.cgi?id=135423). That being said, this method is named badly. I think it should be changed from "addEntryScopeDidPopListener" to "setEntryScopeDidPopListener" to better indicate its behavior.

> > Source/JavaScriptCore/runtime/VMEntryScope.cpp:74
> > +    auto iter = m_allEntryScopeDidPopListeners.begin();
> > +    auto end = m_allEntryScopeDidPopListeners.end();
> > +    for ( ; iter != end; ++iter) {
> > +        EntryScopeDidPopListener listener = iter->value;
> > +        listener(&m_vm, m_globalObject);
> >      }
> 
> The typical way of writing this kind of thing in new WebKit code is:
> 
>     for (auto& listener : m_allEntryScopeDidPopListeners.values())
>         listener(&m_vm, m_globalObject);
> 
> I think that’s easier to read than the 6-line version.
> 
I agree, this is much nicer.

> > Source/JavaScriptCore/runtime/VMEntryScope.h:46
> > +    typedef std::function<void (VM*, JSGlobalObject*)> EntryScopeDidPopListener;
> 
> Argument should be VM& instead of VM*.
Okay.

> 
> > Source/JavaScriptCore/runtime/VMEntryScope.h:52
> > +    HashMap<void*, EntryScopeDidPopListener> m_allEntryScopeDidPopListeners;
> 
> Is void* really the best type for these keys?
The intention of this API is for the Objects that are interested in receiving this notification to pass themselves in as a pointer to uniquely identify themselves. So I think void* is the best way to describe that any object might request this notification and that object uses itself as its identification. How would you recommend describing this behavior?

> 
> Why does this need to be a map? Can’t it just be a Vector? Do we ever remove or overwrite any of these?
For the reason stated above that we do overwrite old values on conflict (which will be utilized the the above patch I mentioned) and do uniquing based on pointers being passed in.
Comment 22 Saam Barati 2014-08-07 17:17:54 PDT
Created attachment 236245 [details]
patch

Above comments taken into consideration.
Comment 23 WebKit Commit Bot 2014-08-07 18:55:32 PDT
Comment on attachment 236245 [details]
patch

Rejecting attachment 236245 [details] from review queue.

sbarati@apple.com does not have reviewer permissions according to http://trac.webkit.org/browser/trunk/Tools/Scripts/webkitpy/common/config/contributors.json.

- If you do not have reviewer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.

- If you have reviewer rights please correct the error in Tools/Scripts/webkitpy/common/config/contributors.json by adding yourself to the file (no review needed).  The commit-queue restarts itself every 2 hours.  After restart the commit-queue will correctly respect your reviewer rights.
Comment 24 WebKit Commit Bot 2014-08-07 19:29:40 PDT
Comment on attachment 236245 [details]
patch

Clearing flags on attachment: 236245

Committed r172324: <http://trac.webkit.org/changeset/172324>
Comment 25 WebKit Commit Bot 2014-08-07 19:29:45 PDT
All reviewed patches have been landed.  Closing bug.