Block execution over a variable (TTS Use-Case), other than log statements (spooky)
Asked Answered
D

1

0

I wish to have a Boolean be true before executing a block of code, and I want it to actually block the thread it is on. The use-case is simple enough. I have a use-case for TTS in my app, and i wish to have a sequential execution, where a TTS speaks, then something happens and it speaks again and so on, but the thing is that this is entirely time-sensitive. I need to start a timer as soon as the TTS finishes talking, but it turns out the speak(...) method is non-blocking (apparently), and so the moment it speaks, the control shifts to the next line.

To handle this, I got hold of a variable isSpeaking implemented using the deprecated OnUtteranceCompletedListener, and that seems to work just fine, as always.

However, it is a SUPER-PECULIAR execution that I see in my implementation. The variable, as I mentioned earlier tracks the speech just fine, but when I apply my loop like so,

while(speaking){
  continue
}

IT BLOCKS UNTIL SPEECH OCCURS, AND THEN THE CONTROL JUST VANISHES!!!,

I put a log statement right after this loop, and once the condition becomes false, i.e., the loop breaks, it never logs that part of the code. In fact, it does nothing at all afterwards. It is like the execution is "lost", somewhere after that. The app does not even freeze. I use Jetpack-Compose so all the infinite-repeatable animations work just fine, but the app doesn't do anything afterwards.

But, here's the thing -- if i replace my continue with just a Log statement, it magically works. EVERYTHING WORKS, ABSOLUTELY NO UNUSUAL OBSERVATIONS UPON THE ADDITION OF ONE LOG STATMENT.

I remember I observed this as a child, but was not in touch with the overflow community back in the day, and so I never posted that. I never made a TTS-using app afterwards so I couldn't share anything.

What I am saying is, this works:

while(speaking){
 Log.i("WHY?", "speaking")
}

This hurts, since I can't use Log calls in my production softwares, the official docs strictly prohibit it. Help!

Dasya answered 17/5, 2022 at 2:5 Comment(1)
I'll shortly post a compose implementation alongside to better visualise the effect.Dasya
A
1

You're mishandling a multithreading condition and that's going to be a problem. The loop you're using is waiting for a variable to change on another thread. First off, that only works if the variable is volatile. If not the interpreter doesn't know it needs to reload from memory and this loop will never end. Secondly, what you're doing is called busy waiting and is a BAD idea for battery issues (and if done on the UI thread can cause your app to become non responsive or even be killed by a watchdog timer). Instead, if this is not the UI thread you should be using a signaling mechanism like a semaphore. That would allow you to wait, without idling the CPU, until the signal is given. If this is the UI thread, you shouldn't do it at all. Instead you should have the speech listener post a message to a Handler to the UI thread, and perform whatever is after the loop in response to that message.

Atmo answered 17/5, 2022 at 2:42 Comment(6)
D'you happen to have any resourceful links where I can explore all this? Also, as mentioned above, I use Jetpack-Compose in my projects so coroutines are in use all over the place. I use the I/O dispatcher to run this, which as you may know, spawns its own thread pool, so this work is definitely happening off the UI thread.Dasya
Ok, that's good. In that case, you can use a singaling mechnism. Or just run the following code in the speech recognizer to begin with.Atmo
javatpoint.com/volatile-keyword-in-java That's a description of the volatile keyword and an explanation of why its used.Atmo
My main focus is the unexplained deal with the fact that "With/Without Volatility, it works if a log statement is added, and absolutely doesn't when just continue or an empty while loop is used". I can agree on the fact that volatility matters, and that could have indeed been the factor causing a seemingly infinite loop, but WHY, without any changes to the nature of variable, adding a single Log call makes the loop work as expected? That's the unexplained bit I'd like to have clarified, sir. ThanksDasya
without volatile, the interpreter gets to choose whether or not to load it from memory. It won't if it doesn't need to, it will if it does. The log statement caused something in the interpreter to decide it needed to load it from memory again. Most likely it flushed the CPUs registers with the function call, or the ABI it use didn't require the function call to restore the register it used to hold that value. But there's nothing there you can rely on, another Java interpreter, or even another version of this one could make a different choice.Atmo
Wow... Sorta horrifying but... I'll accept it as the correct answer.Dasya

© 2022 - 2024 — McMap. All rights reserved.