I’m scared of the JUCE windowing system (Windows only?)


#1

Hi Julian!

There is a big problem when running one of my JUCE applications. The most frightful part of the problem is that the problem IS UNSTEADY! I’ve not been being able even to determine any reasons that might cause the problem. It happens at times by no sensible reasons. Sometimes I have to restart my application 10 times sequentially to reproduce the problem; at times it happens in 20 consecutive launches or so. The number of launches, before the problem occurs, is unpredictable. It resembles “The Russian roulette” game.

The problem is:

Sometimes, when the application is being launched, one of my two CPU cores becomes 100% busy until I close the application. In that condition, the application runs normally, but a single thread consumes 100% of one of a CPU core. When I kill that thread (I use “Sysinternals Process Explorer” for that) the application continues running smoothly and normally until the end, but when closed it stays in memory calmly until I kill it forcedly. I did not manage to debug the application because I couldn’t reproduce the problem in the debug mode. Seldom, this problem hangs deadly my computer – everything becomes frozen even the mouse pointer. So, only powering my computer off is able to help. First, I thought I’ve written something wrong in my code that might cause the problem but I’ve discovered that I did not start any thread within my code. Moreover, when that thread killed, the application runs normally and full-function, so all my functions are functional.
This application differs from my other small applications in two things: it shows a splash screen and it plays sounds. This happens under Windows XP SP2. The application is compiled under MinGW (GCC 4.3.0).
All I can do I can provide two screenshots from “Process Explorer” of the problem when I’ve managed to catch it.

On the screenshot below (selected line), the value of 49.24 means 50% of a CPU core time is consumed by that thread (1 core + 1 core = 50% + 50% = 100% of a whole CPU).

here is the calling stack of the thread

Can anyone help me to get rid of the problem?


#2

Never had this problem and I use a dual core. In the mean time, I don’t use mingw for Juce but VisualStudio.

Anyway, this is how I’d debug it.
Change your linker option to get a map file (with gcc it’s -Wl,-Map,foo.map)
Then, on the “sucking” thread stack, use the map file content to map back to the method name that stall the CPU (you should also deal with every other thread, in case it’s a resource contention).

Then, you’ve the code, it’s simply looking at every last function of every thread to figure out why whatever stalling thread is stalling.

I hope it helps.


#3

That sounds like a good plan.

I also use dual-cores and haven’t seen this, though I admit it’s the sort of thing that you could easily miss.

It could be something like an audio driver thread getting into a spin, so that might explain why only you’re seeing it. Most of the juce threads have sensible names assigned to them - is there a way to get your proc explorer to show the thread names? If so you could at least figure out if it’s a thread in the app or one that the OS or a driver is creating.


#4

Thanks you X-Ryl669 and Jules.

I’ll give it a try!


#5

Well, it seems that the juce::AudioSourcePlayer::audioDeviceIOCallback(float const**, int, float**, int, int) function causes the problem… (thanks to Map file)

I want Julian to check my piece of code. Now, I think I do something wrong, but it’s working somehow:

[code] {
_deviceNames = AudioDeviceManager().getAvailableAudioDeviceNames();
}

int nrOfDevices = _deviceNames.size();
for(int idx = 0; idx < nrOfDevices; idx++)
{
	_audioDeviceManagers.add(new AudioDeviceManager);
	_audioSourcePlayers.add(new AudioSourcePlayer);
	_mixerAudioSources.add(new MixerAudioSource);

	_audioDeviceManagers[idx]->initialise(0, 2, 0, true);
	_audioDeviceManagers[idx]->setAudioDevice(_deviceNames[idx], 960, 48000, 0, 0, true);

	_audioSourcePlayers[idx]->setSource(_mixerAudioSources[idx]);
	_audioDeviceManagers[idx]->setAudioCallback(_audioSourcePlayers[idx]);

	_deviceMenu.addItem(idx + 1, _deviceNames[idx], true, false, _deviceColours[idx % 8]);
}

[/code]

Well, I try to create a separate AudioDeviceManager for each sound card I have. Is it a correct code to write?


#6

If you’ve got more than one ASIO device, then that’s really not a good idea. ASIO is only designed to open one device at a time…


#7

Well, I know about ASIO device.

If my code is correct then something causes problems in the juce::AudioSourcePlayer::audioDeviceIOCallback(float const**, int, float**, int, int) function.


#8

Well, I hope I’ve found where the problem hides.

The point is that the bool DSoundInternalOutChannel::service() function does not deal with the DSERR_BUFFERLOST
error. You should call IDirectSoundBuffer_Restore to restore the buffer to continue writing to it. I’ve discovered that even if my application starts normally the problem may occur later on after some situations that might cause buffer lost.

I’m not 100% sure but I’m sure that you should correct the function code to deal with the mentioned error.

P.S. consider the const String DSoundInternalOutChannel::open() function too, because there two Locks of a buffer too.


#9

…furthermore

I’d lain when I’ve said that my application was functining OK. After thorough testing I’ve discovered that one of a AudioManager instance always was corrupted when the problem occured. I’ll explain what is going on:

Windows always returns 3 DirectSound device names for 2 sound cards:

  1. Default device
  2. Sound card #1
  3. Sound card #2

So, “1. Default device” refers to “2. Sound card #1” by default (until I change it in the Windows audio settings panel).

So, when the problem occur, AudioManager successfully creates only “1. Default device” and “3. Sound card #2”. The “2. Sound card #1” (that refers to already opened “1. Default device”) becomes corrupted. It does not play a sound. When I kill the hungry thread then playing to this AudioManager device leads to hang of the application.

That’s the problem. Sometimes it creates them all successfully, but sometimes it doesn’t.


#10

Ah, I see. Good old windows. Ok, I’ll need to try that myself and see if I can get it to fail gracefully.


#11

You may do the following to provoke the problem easily:

  1. Run an audio application (you may start playing a sound if you wish).
  2. While running, change the screen resolution temporarily or run a screen saver that’s changes your screen resolution.
  3. That’s it.

P.S. Not all sound cards can be fooled by this trick. The build-in ones can be.


#12

Why would the screen res have anything to do with the audio drivers??


#13

:smiley: well… nothing :wink:

I was thinknig your way. That’s why I’ve thought that this problem was pertaining to the JUCE windowing system. But later on I’ve discovered that Windows can lose DirectSound audio buffers on ANY hardware interruption (no matter, video interruption or some other hardware one). Microsoft says that programer must not know why a buffer was lost but must deal with that error by restoring it. Microsoft even cannot explain and name the exact conditions that lead to the buffer lost error. They only can insist on restoring a buffer in those situations.

MSDN says: [quote]“Memory for a sound buffer can be lost in certain situations: for example, when buffers are located in sound card memory and another application gains control of the hardware resources. Loss can also occur when an application with the write-primary cooperative level moves to the foreground; in this case, DirectSound makes all other sound buffers lost so that the foreground application can write directly to the primary buffer.”[/quote]

I say… only Windows knows when buffers will be lost :lol:

That’s the today’s Windows truth. :frowning: