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
Member

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
Member Author

ferjm commented May 24, 2018

@Manishearth @sdroege WDYT of this approach?

@ferjm ferjm force-pushed the ferjm:no.appsrc.needdata branch from 58425f8 to 4534150 May 24, 2018
Copy link
Member

Manishearth left a comment

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();

This comment has been minimized.

@Manishearth

Manishearth May 24, 2018

Member

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() {

This comment has been minimized.

@Manishearth

Manishearth May 24, 2018

Member

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

This comment has been minimized.

@ferjm

ferjm May 24, 2018

Author Member

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

Manishearth commented May 24, 2018

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
Member 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

@ferjm
Copy link
Member 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.

@ferjm ferjm force-pushed the ferjm:no.appsrc.needdata branch from 76bdcc8 to 8ae1b5b May 25, 2018
@Manishearth
Copy link
Member

Manishearth commented May 25, 2018

@ferjm
Copy link
Member 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
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
Member 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
@ferjm
Copy link
Member Author

ferjm commented May 25, 2018

@Manishearth
Copy link
Member

Manishearth commented May 26, 2018

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 force-pushed the ferjm:no.appsrc.needdata branch from ec2efca to 9d4616e May 26, 2018
@ferjm ferjm merged commit e504202 into servo:master May 26, 2018
@ferjm ferjm deleted the ferjm:no.appsrc.needdata branch May 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

3 participants
You can’t perform that action at this time.