Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix audio render thread thread safety issues #29

Merged
merged 4 commits into from
May 26, 2018

Conversation

ferjm
Copy link
Contributor

@ferjm ferjm commented May 24, 2018

…d tick

This is a different approach for #27.

There is one issue here that can be noticeable running the play example. It is supposed to play 2 seconds of two sine waves with different frequencies. With this patch the first sine wave steals most of the time of the second sine wave. I believe it is caused by the difference between the JS clock and the (currently not existent) WebAudio clock (there is a nice blog post about this). If that's the case this should be fixed by using the scheduling feature of AudioScheduledSourceNodes in the play example once #21 is done.

@ferjm
Copy link
Contributor Author

ferjm commented May 24, 2018

@Manishearth @sdroege WDYT of this approach?

Copy link
Member

@Manishearth Manishearth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach. The lack of blocking in the event loop is a bit concerning though.


fn push_data(&self, mut chunk: Chunk) -> Result<(), ()> {
let sample_rate = self.sample_rate.get() as u64;
let ref audio_info = self.audio_info.borrow().clone().unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need to clone here. Call .as_ref().unwrap()


// Process a render quantum if the sink can handle
// more data.
if !self.sink.has_enough_data() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a slight problem here; this loop will eat up CPU instead of blocking because there's nothing to block on.

Copy link
Contributor Author

@ferjm ferjm May 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, isn't this (not blocking) what's supposed to happen for the rendering thread? In any case, we could use appsrc's block property here instead of the custom has_enough_data check.

@Manishearth
Copy link
Member

Now we have the problem that the event loop won't process events when the buffer is full 😄

Which is ... okay, I guess?

@ferjm
Copy link
Contributor Author

ferjm commented May 24, 2018

The other option may be to use the previous has_enough_data and sleep the thread for a few ms if the buffer is full to avoid 100% CPU usage.

@Manishearth
Copy link
Member

Manishearth commented May 24, 2018 via email

@ferjm
Copy link
Contributor Author

ferjm commented May 25, 2018

Until we have performance tests or we can manually test with more nodes or real WebAudio apps, it's hard to make an informed decision here, but I think the has_enough_data + sleep(5ms), while it's not very elegant, it's a good middle ground solution. We have no messaging overhead, the render thread can continue processing control thread messages and the blocking situations are controlled and for a known and configurable amount time.

@Manishearth
Copy link
Member

Manishearth commented May 25, 2018 via email

@ferjm
Copy link
Contributor Author

ferjm commented May 25, 2018

Actually, I want to try yet another approach, but I am not sure if it'll work.

I think we can have an extra thread where we wait for appsrc callbacks. We can share the control <-> render thread channel with this thread. If the appsrc buffer is full, we will block on the control <-> render thread channel reception. When an appsrc.need_data callback is triggered, a message will be sent through this channel to unblock and switch to the previous non-blocking event loop.

@sdroege
Copy link
Sponsor Contributor

sdroege commented May 25, 2018

but I think the has_enough_data + sleep(5ms)

I didn't review anything in detail here yet (later!) but a) sleeping 5ms will drift slowly if done just like that as it does not consider the amount of time taken for the actual code, b) you'll sleep according to the system clock and not according to the audio clock, so you either produce too much or too little data over a longer time (the audio clock will run at different speed).

@ferjm
Copy link
Contributor Author

ferjm commented May 25, 2018

I just pushed the commit with the approach I mentioned above. We no longer sleep the thread and we switch between a non-blocking and a blocking event loop depending on the audio sink data needs

@ferjm ferjm changed the title Do not wait for appsrc::need_data, push data on every rendering threa… Fix audio render thread thread safety issues May 25, 2018
@ferjm ferjm requested a review from Manishearth May 25, 2018 16:30
@ferjm
Copy link
Contributor Author

ferjm commented May 25, 2018

r? @Manishearth

@Manishearth
Copy link
Member

Ah, yeah, this new approach is closer to #28 in the sense that the app src still can request stuff through the queue, but backpressure is better dealt with. I like it.

@ferjm ferjm merged commit e504202 into servo:master May 26, 2018
@ferjm ferjm deleted the no.appsrc.needdata branch May 26, 2018 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants