How to consume an HTTP API in a non-blocking way?

I’m developing my first VCV rack module vcvrack-mienophone which is meant to:

  • capture pictures from a webcam
  • trade the pictures for emotion parameters using Azure Face API
  • map emotion parameters onto module outputs.

The current state of the module: It sends a local image to HTTP API using curl.c and maps the result set to 8 module outputs. Shameless green early development state.

The issue I face is that the API requests are synchronous, blocking the whole rack while curl is waiting for the HTTP response.

Could you please share your ideas how to implement this asynchonous? Is there an API in VCV rack core I could use? I am quite a C/C++ beginner.

1 Like

There are only two ways. Find a non blocking http client that you can poll. Or make a worker thread to do all the work. Threads are pretty easy to get wrong…

For multithreading where you are sharing the same variable across multiple threads, you will probably find mutex and locks very useful to avoid race conditions.

You cannot acquire a lock in the audio process call, or you will cause a dropout.

1 Like

oh right that makes sense, i’m just used to multithreading in a different context.

This library may be worth trying, the http async client looks quite straightforward

I know, me 2. I didn’t even believe the pops and dropouts would happen until I saw it in action. We don’t tend to do this kind of real time programming outside of music,

std::async should work great with cURL. Just be sure to wait() on your tasks/futures in your ~Module destructor, so the tasks are guaranteed to be able to access your Module resources if the user quits Rack or deletes your module while an HTTP request is resolving.

that’s pretty cool. Is there any way to get the result from the future without doing a blocking wait? I can’t tell if std::future::wait_until() has a guaranteed non-blocking form.

Not really (in C++11), but you can just ignore the return value of std::async() and modify your state inside the async function instead of use the future object.

1 Like

I suppose if you don’t need the future value, using std::thread would be basically the same thing. You’d just call join() in ~Module().

Some kind of function that returns bool indicating if something got done with an anonymous function as a parameter would be good for many lock-free algorithms. Quite often the process thread would not mind a lack of sample-accurate process completion.