Application with gtk and portaudio crashes


#1

Hi.

I’m learning Rust and I’m trying to write a simple real-time audio analyzer. I’m using gtk-rs for the UI and rust-portaudio to interface with the audio devices. Gtk runs in the main thread and a portaudio stream runs in another thread, and that thread computes the power of the signal and sends it via a Sender/Receiver to the main thread which displays the value using a “level bar”.

Unfortunately, very often when I start the application or when I use one of the UI controls, the application crashes with messages like

objc[1110]: Method cache corrupted. This may be a message to an invalid object, or a memory error somewhere else.

or

analyzer(1084,0x7fff76a89000) malloc: *** error for object 0x7ff373877a08: incorrect checksum for freed object - object was probably modified after being freed.

The latter message happens in any possible function (BOMStorageSizeOfBlock, __psynch_cvwait, _gtk_css_image_equal, etc.)

Independently they run fine, but together it’s a mess, and frankly one I had hoped Rust would help me avoid. Is there any way to get out of this, or to find which component is causing the problem?


#2

Can we see the code?


#3

Sure: http://pastebin.com/7x0GDq6t

I’ve added some comments, but don’t hesitate to ask more if needed. Also feel free to improve the code idiomatically, but note that this is just a starting point, so it’s not worth spending much time on improving that part.

I’ve checked running the gtk only code and running the audio code in a thread without gtk (just printing everything), and both work fine. The thread termination code is not the problem. Before I had a very crude method (call gtk::main_quit() from window.connect_on_delete) which didn’t use “ip_data”, and it had the same problem.

Platform: OSX 10.11.3, rust 1.6.

Dependencies:

portaudio = "0.6.2"
gtk = "0.0.6"
chrono = "0.2"

#4

To see if events_pending and main_iteration are the culprits, please try changing the event loop to this (add extern crate glib):

    gtk::idle_add(move || {
        if ip_data.lock().unwrap().terminate {
            gtk::main_quit();
            return glib::Continue(false)
        }
        // Receive and do stuff
        glib::Continue(true)
    });

    gtk::main();

#5

Thanks. Unfortunately, it doesn’t solve the problem. I get the same errors. I will keep this variant, though. It’s neater: it also allows me to avoid the ip_clone in the connect_on_delete function, since it can be set when gtk::main() returns.

Edit: here’s the updated version with idle_add and without extern crate gdk, which was missing from my dependencies: http://pastebin.com/urzLLbtS

Dependencies are now:

portaudio = "0.6.2"
gtk = "0.0.6"
chrono = "0.2"
glib = "^0"

#6

I don’t see any problems on the GTK side, you don’t seem to be doing anything unusual. The assertions also don’t seem to come from libglib or libgtk.

I can’t reproduce the problem because the portaudio thread doesn’t seem to ever send anything on my system.


#7

Indeed. When I take out the audio thread, there’s no problem at all.

It’s not important that it doesn’t seem to send anything, as long as the audio thread is running it crashes on my machine. Perhaps there’s almost no sound, so it just sends 0. If you make some noise like finger snapping near your microphone (assuming that’s the default input device), it might show some activity. You could add some println! statements to check if it does send/receive something. If that doesn’t work, and you want to see activity in the level bars, try sending a running counter % 100.

The crash happens often very briefly after the start of the application, and if they don’t come immediately, they can happen on clicking in the window. The assertions come from everywhere, so a plausible cause is that something is corrupting memory, possibly a thread problem between gtk and portaudio? Is there some memory allocation in both libraries that’s not thread safe?

What platform do you use?


#8

I’ve tested on Fedora 23.

[quote=“TGV, post:7, topic:4606”]
It’s not important that it doesn’t seem to send anything,
[/quote]It starves the main thread (which assumes there always is something to receive) so the application becomes completely unresponsive. I imagine it just sits there blocked on something without a chance to corrupt anything.

[quote=“TGV, post:7, topic:4606”]
Is there some memory allocation in both libraries that’s not thread safe?
[/quote]I can’t speak for portaudio. Although GTK+ is not thread safe it doesn’t care how many threads you have, its widgets aren’t Send or Sync and trying to create them in other threads will trigger an assertion.


#9

Hey!

I’m unsure about your issue specifically, but I looked through your pastebin post and noticed a couple things that might be worth checking.

It seems that you’re spawning a new thread to run a blocking PortAudio stream. Perhaps instead you could create a non-blocking stream and let portaudio kick off the new thread for you? Bencina, the author of PortAudio, recommends using the non-blocking stream where possible - he goes into a bit more detail on why here. You can find a rust-portaudio non-blocking stream example here. I’d be interested to hear whether this change fixes your issue, as the blocking stream tends to be a little less stable (which I imagine is partly because it’s used a lot less than the non-blocking stream).

Also, it seems like you’re planning on sharing data with the audio thread using a Mutex (i.e. the Arc<Mutex<IpData>>). Audio threads tend to be the epitome of real-time, and can easily glitch or underflow if they get stuck waiting for access to locks or other system resources or lose thread priority due to waiting, etc. Bencina has a great post on this called Real-time audio waits for nothing. I find rust’s std::sync::mpsc::channel to be a nice tool for non-blocking communication with the audio thread. (Edit: I doubt this is your problem btw, as both threads barely seem to touch the lock).

You might already be aware of all this, but just thought I’d mention them in case :slightly_smiling:


#10

Ah, yes, that would make a big change.


#11

The blocking thread doesn’t block. I’ve opened an issue for that on rust-portaudio. But it should be possible to run a separate thread, right?

I can try. I actually did that in the C version (which just runs fine).

I know, but it’s just a flag to politely stop the thread. Sending the data is already done via a channel. I’ll try the same mechanism for the non-blocking version. I’ll get back.

[quote]
You might already be aware of all this, but just thought I’d mention them in case [/quote]
Thanks, but I was indeed aware of it. The C version has an fft analysis in the callback, and that stays well within the 23ms or so it takes for the next callback. And as you noted, the rust version actually doesn’t almost do anything in the callback.


#12

I’ve rewritten the program to use a non-blocking stream, and that runs fine, also in release. Here’s the (rather rough) code: http://pastebin.com/xA4uFdnL

Bit weird that the other version crashed so hard.


#13

Good to hear you got it working! Will look into the blocking stream issues, thanks for the info.