Bug 223226 - Add assertions to guard against heap allocations on the audio thread
Summary: Add assertions to guard against heap allocations on the audio thread
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Audio (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Dumez
URL:
Keywords: InRadar
Depends on: 223227 223228 223230 223272 223444 223445 223449 223452 223461 223466 223477 223549
Blocks: 223640
  Show dependency treegraph
 
Reported: 2021-03-15 17:20 PDT by Chris Dumez
Modified: 2021-04-05 09:35 PDT (History)
16 users (show)

See Also:


Attachments
WIP Patch (6.52 KB, patch)
2021-03-15 17:23 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (7.22 KB, patch)
2021-03-15 18:57 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
WIP Patch (7.21 KB, patch)
2021-03-15 19:30 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
WIP Patch (10.18 KB, patch)
2021-03-17 16:49 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
WIP Patch (10.86 KB, patch)
2021-03-18 11:54 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (12.47 KB, patch)
2021-03-18 13:57 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (15.76 KB, patch)
2021-03-18 16:59 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (17.37 KB, patch)
2021-03-19 08:10 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (18.26 KB, patch)
2021-03-19 13:04 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (21.14 KB, patch)
2021-03-22 13:11 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (21.82 KB, patch)
2021-03-22 13:34 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (24.26 KB, patch)
2021-03-22 14:14 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (23.23 KB, patch)
2021-03-22 15:29 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumez 2021-03-15 17:20:01 PDT
Avoid heap allocations on the audio rendering thread, for performance reasons.
Comment 1 Chris Dumez 2021-03-15 17:23:54 PDT
Created attachment 423269 [details]
WIP Patch
Comment 2 Yusuke Suzuki 2021-03-15 17:40:23 PDT
Comment on attachment 423269 [details]
WIP Patch

How about,
1. Adding a `setForbidMallocUseInCurrentThread(bool)`
2. Use ForbidMallocUse scoped variable which set and reset the configuration.
such an utility can be useful for the other cases.
Comment 3 Yusuke Suzuki 2021-03-15 17:41:23 PDT
(In reply to Yusuke Suzuki from comment #2)
> Comment on attachment 423269 [details]
> WIP Patch
> 
> How about,
> 1. Adding a `setForbidMallocUseInCurrentThread(bool)`
> 2. Use ForbidMallocUse scoped variable which set and reset the configuration.
> such an utility can be useful for the other cases.

BTW, in JSC, we have DisallowScope<T> and some implementations like, DisallowGC.
Comment 4 Chris Dumez 2021-03-15 18:09:55 PDT
(In reply to Yusuke Suzuki from comment #2)
> Comment on attachment 423269 [details]
> WIP Patch
> 
> How about,
> 1. Adding a `setForbidMallocUseInCurrentThread(bool)`
> 2. Use ForbidMallocUse scoped variable which set and reset the configuration.
> such an utility can be useful for the other cases.

As it turns out, it is useful for WebAudio to turn it off too. I'll make the changes you suggest.
Comment 5 Chris Dumez 2021-03-15 18:57:52 PDT
Created attachment 423279 [details]
WIP Patch
Comment 6 Chris Dumez 2021-03-15 19:30:49 PDT
Created attachment 423285 [details]
WIP Patch
Comment 7 Chris Dumez 2021-03-17 16:49:47 PDT
Created attachment 423540 [details]
WIP Patch
Comment 8 Chris Dumez 2021-03-18 11:54:45 PDT
Created attachment 423630 [details]
WIP Patch
Comment 9 Chris Dumez 2021-03-18 13:57:02 PDT
Created attachment 423648 [details]
WIP Patch
Comment 10 Chris Dumez 2021-03-18 16:59:56 PDT
Created attachment 423672 [details]
Patch
Comment 11 Chris Dumez 2021-03-19 08:10:24 PDT
Created attachment 423733 [details]
Patch
Comment 12 Chris Dumez 2021-03-19 13:04:17 PDT
Created attachment 423765 [details]
Patch
Comment 13 Chris Dumez 2021-03-22 13:11:43 PDT
Created attachment 423923 [details]
Patch
Comment 14 Chris Dumez 2021-03-22 13:34:17 PDT
Created attachment 423932 [details]
Patch
Comment 15 Chris Dumez 2021-03-22 14:14:36 PDT
Created attachment 423937 [details]
Patch
Comment 16 Chris Dumez 2021-03-22 15:29:33 PDT
Created attachment 423948 [details]
Patch
Comment 17 Darin Adler 2021-03-22 15:53:19 PDT
Comment on attachment 423948 [details]
Patch

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

What’s the overall strategy here? Why do we forbid memory allocation and exactly where? When we are done, can there be any remaining exceptions, or do we have to do this 100% to get the result we are looking for?

> Source/WTF/wtf/FastMalloc.cpp:58
> +thread_local static unsigned forbidMallocUseScopeCount;
> +thread_local static unsigned disableMallocRestrictionScopeCount;

Wow, I have never heard of the C++ thread_local keyword until today! For use outside assertions: How does it compare in efficiency to the other kinds of per-thread stuff?

> Source/WTF/wtf/FastMalloc.h:132
> +    ~ForbidMallocUseForCurrentThreadScope() { }

Why not use = default here too? Or omit explicit constructor and destructors entirely?

> Source/WTF/wtf/FastMalloc.h:148
> +    ~DisableMallocRestrictionsForCurrentThreadScope() { }

Why not use = default here too? Or omit explicit constructor and destructors entirely?

> Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp:90
> +        ForbidMallocUseForCurrentThreadScope forbidMallocUse;

I don’t understand why this is scoped to only cover this part of the rendering and doesn’t cover pre-render and post-render. Should that be obvious?
Comment 18 Chris Dumez 2021-03-22 16:24:18 PDT
(In reply to Darin Adler from comment #17)
> Comment on attachment 423948 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=423948&action=review
> 
> What’s the overall strategy here? Why do we forbid memory allocation and
> exactly where? When we are done, can there be any remaining exceptions, or
> do we have to do this 100% to get the result we are looking for?

Basically, the audio thread should avoid waiting on a lock and doing a heap allocation may require grabbing a lock, which would negatively impact audio performance.
As a result, we want to get heap allocations as close to 0 as possible on the audio thread. I don't think we can realistically get to 0 though so I think some exceptions will remain.
However, the assertions will help make sure we avoid introducing new heap allocations without realizing the impact on performance. If someone does introduce such a heap allocation in the
future, they will hit the assertion and will need to either get rid of the heap allocation (ideally) OR add a DisableMallocRestrictionScope to allow it (because no other choice). Because
of the DisableMallocRestrictionScope, it will be obvious when reviewing that we are consciously introducing a new heap allocation on the audio thread.
 
> > Source/WTF/wtf/FastMalloc.cpp:58
> > +thread_local static unsigned forbidMallocUseScopeCount;
> > +thread_local static unsigned disableMallocRestrictionScopeCount;
> 
> Wow, I have never heard of the C++ thread_local keyword until today! For use
> outside assertions: How does it compare in efficiency to the other kinds of
> per-thread stuff?

I did not compare the performance with other kinds of per-thread stuff. I honestly don't have much experience with per-thread stuff. I initially tried to add these counts to WTF::Thread but calling Thread::current() can do a heap allocation and I would end up in an infinite loop :)

> 
> > Source/WTF/wtf/FastMalloc.h:132
> > +    ~ForbidMallocUseForCurrentThreadScope() { }
> 
> Why not use = default here too? Or omit explicit constructor and destructors
> entirely?
> 
> > Source/WTF/wtf/FastMalloc.h:148
> > +    ~DisableMallocRestrictionsForCurrentThreadScope() { }
> 
> Why not use = default here too? Or omit explicit constructor and destructors
> entirely?

Ok, so this is intentional or the compiler complains about unused scope variables. I did not want to have to UNUSED_PARAM() the scope variables.

> > Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp:90
> > +        ForbidMallocUseForCurrentThreadScope forbidMallocUse;
> 
> I don’t understand why this is scoped to only cover this part of the
> rendering and doesn’t cover pre-render and post-render. Should that be
> obvious?

I tried to explain in the changelog but basically, the idea is to expand to cover pre-render / post-render in the future. However, those have quite a few heap allocations and I have not had time to go through those yet.
I figure I should land the assertions ASAP for what's already done and we can work to expand the scope later on.
Comment 19 Chris Dumez 2021-03-22 16:28:00 PDT
(In reply to Chris Dumez from comment #18)
> (In reply to Darin Adler from comment #17)
> > Comment on attachment 423948 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=423948&action=review
> > 
> > What’s the overall strategy here? Why do we forbid memory allocation and
> > exactly where? When we are done, can there be any remaining exceptions, or
> > do we have to do this 100% to get the result we are looking for?
> 
> Basically, the audio thread should avoid waiting on a lock and doing a heap
> allocation may require grabbing a lock, which would negatively impact audio
> performance.
> As a result, we want to get heap allocations as close to 0 as possible on
> the audio thread. I don't think we can realistically get to 0 though so I
> think some exceptions will remain.
> However, the assertions will help make sure we avoid introducing new heap
> allocations without realizing the impact on performance. If someone does
> introduce such a heap allocation in the
> future, they will hit the assertion and will need to either get rid of the
> heap allocation (ideally) OR add a DisableMallocRestrictionScope to allow it
> (because no other choice). Because
> of the DisableMallocRestrictionScope, it will be obvious when reviewing that
> we are consciously introducing a new heap allocation on the audio thread.
>  
> > > Source/WTF/wtf/FastMalloc.cpp:58
> > > +thread_local static unsigned forbidMallocUseScopeCount;
> > > +thread_local static unsigned disableMallocRestrictionScopeCount;
> > 
> > Wow, I have never heard of the C++ thread_local keyword until today! For use
> > outside assertions: How does it compare in efficiency to the other kinds of
> > per-thread stuff?
> 
> I did not compare the performance with other kinds of per-thread stuff. I
> honestly don't have much experience with per-thread stuff. I initially tried
> to add these counts to WTF::Thread but calling Thread::current() can do a
> heap allocation and I would end up in an infinite loop :)
> 
> > 
> > > Source/WTF/wtf/FastMalloc.h:132
> > > +    ~ForbidMallocUseForCurrentThreadScope() { }
> > 
> > Why not use = default here too? Or omit explicit constructor and destructors
> > entirely?
> > 
> > > Source/WTF/wtf/FastMalloc.h:148
> > > +    ~DisableMallocRestrictionsForCurrentThreadScope() { }
> > 
> > Why not use = default here too? Or omit explicit constructor and destructors
> > entirely?
> 
> Ok, so this is intentional or the compiler complains about unused scope
> variables. I did not want to have to UNUSED_PARAM() the scope variables.

I am curious if there is a better way to deal with those compiler warnings in release?
Comment 20 Radar WebKit Bug Importer 2021-03-22 17:20:17 PDT
<rdar://problem/75716380>
Comment 21 Chris Dumez 2021-03-23 08:33:43 PDT
Comment on attachment 423948 [details]
Patch

Clearing flags on attachment: 423948

Committed r274871 (235653@main): <https://commits.webkit.org/235653@main>
Comment 22 Chris Dumez 2021-03-23 08:33:46 PDT
All reviewed patches have been landed.  Closing bug.