Skip to content

Instantly share code, notes, and snippets.

@lrodorigo
Last active July 12, 2021 17:44
Show Gist options
  • Save lrodorigo/6934bc025a81b7695e697b0c1ef97f86 to your computer and use it in GitHub Desktop.
Save lrodorigo/6934bc025a81b7695e697b0c1ef97f86 to your computer and use it in GitHub Desktop.
#include <speechapi_cxx.h>
#include <iostream>
#include <unistd.h>
int main() {
using namespace Microsoft::CognitiveServices::Speech;
using namespace Microsoft::CognitiveServices::Speech::Audio;
auto config = SpeechConfig::FromSubscription("<<the-key>>", "eastus");
// Creates a speech recognizer using microphone as audio input. The default language is "en-us".
auto recognizer = SpeechRecognizer::FromConfig(config, "it-IT");
auto audio_config = AudioConfig::FromMicrophoneInput("default"); // Or an alternative input
auto keyword_recognizer = KeywordRecognizer::FromConfig(audio_config);
keyword_recognizer->Recognized += [&](const KeywordRecognitionEventArgs &event) {
std::cout<<"Keyword recognized"<<std::endl;
};
auto keyword_model = KeywordRecognitionModel::FromFile("./my-model.table");
std::cout<<"Invoking RecognizeOnceAsync"<<std::endl;
keyword_recognizer->RecognizeOnceAsync(keyword_model);
//auto res = keyword_recognizer->RecognizeOnceAsync(keyword_model); // if the result is used the method is actually async
// this line is never reached if the result of RecognizeOnceAsync is not used (due to the future destruction)
std::cout<<"Waiting for keyword..."<<std::endl;
sleep(60);
}
@trrwilson
Copy link

Hello! Travis from the SDK team here. We've had a good talk about what's going on with this. RecognizeOnceAsync is using std::async to generate the std::future you get, and that's what originates the "blocks if you don't capture" behavior:

https://en.cppreference.com/w/cpp/thread/async

If the std::future obtained from std::async is not moved from or bound to a reference, the destructor of the std::future will block at the end of the full expression until the asynchronous operation completes, essentially making code such as the following synchronous:

std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f()
std::async(std::launch::async, []{ g(); }); // does not start until f() completes

(note that the destructors of std::futures obtained by means other than a call to std::async never block)

You can see this in action in the speechapi_cxx_recognition_async_recognizer.h header file from the SDK.

This came as a bit of a surprise to me and I'll following up to see how we can improve the documentation, but it's expected behavior (inherited from std::async) that the function is only "async" as long as the std::future is bound.

@lrodorigo
Copy link
Author

Pretty clear.

I think that it would be a good idea to force the user to bound the future, by taking the shared-ptr as an output parameter, instead of returning it by-value.

Of course it is a breaking change...

@trrwilson
Copy link

The breaking change bit is definitely the challenge!

When we can move the SDK to C++17, the [[nodiscard]] attribute is another fantastic way to improve this. One way or another, this will certainly be improved when we get the opportunity.

In the meantime, we can at least add clarity to the documentation. If I could ask: is there a place that would've been most helpful to you to have an extra warning about the behavior? Contextual IntelliSense, online ref docs, conceptual docs, any or none of the above?

@lrodorigo
Copy link
Author

lrodorigo commented Jul 12, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment