VST3 with database does not work

I scanned the VST3 plugin with pluginval and it gave me an error. As soon as I commented out the database everything worked. Is it not possible to link a VST3 plugin with a database?


Starting test: pluginval / Scan for known types: E:\EQ\Builds\VisualStudio2019\x64\Debug\VST3\EQ.vst3…
Num types found: 0
!!! Test 1 failed: No types found
FAILED!! 1 test failed, out of a total of 1

Finished validating: E:\EQ\Builds\VisualStudio2019\x64\Debug\VST3\EQ.vst3
*** FAILED: 1 TESTS

What’s a “database”? you mean some online access?

You probably need to make sure you’re doing such operations in a way that isn’t “blocking” the regular operation of the plugin. For example if you have a long wait on the constructor, the host will think your plugin has failed scanning.

That would be the code:

int qstate;

int SQLQuery(std::string ID)
{
    MYSQL* conn;
    MYSQL_ROW row;
    MYSQL_RES* res;
    char* key;
    conn = mysql_init(0);

    mysql_real_connect(conn, "localhost", "EQ", "Kennwort0", "eq", 3306, NULL, 0);

    if (conn) {

        std::ostringstream str;
        str << "SELECT * FROM users WHERE UserID='" << ID <<"';";
        std::string sql = str.str();

        qstate = mysql_query(conn, sql.c_str());
        if (!qstate) {
            res = mysql_store_result(conn);
            if (res->row_count == 0) {

                return 0;
            }
            while (row = mysql_fetch_row(res)) {
                if (row[0] == ID) {
                    if (atoi(row[7]) == 1) {
                        return 1;
                    }
                    else {
                        return 0;
                    }
                }
            }
        }
        else {
            return 0;
        }
    }
    else {
        return 0;
    }
}

paint(juce::Graphics& g)


    ...
    ...
    int valueSQLQuery = SQLQuery(ID);
    if (valueSQLQuery == 0 || ID.length() != 13 || ID == "0") {
    
        g.setColour(juce::Colour::fromRGB(juce::uint8(11101110), juce::uint8(11101110), juce::uint8(11101110)));
        g.setFont(juce::Font(20.f));

        g.drawFittedText("SerialNuber:", 0, 265, bounds.getWidth(), 20, juce::Justification::centred,1, 0.f);
        g.drawFittedText("-", getWidth() * 0.5 - 90 , 300, 35, 20, juce::Justification::centred, 0.f);
        g.drawFittedText("-", getWidth() * 0.5 - 5, 300, 35, 20, juce::Justification::centred, 0.f);
        g.drawFittedText("-", getWidth() * 0.5 + 80, 300, 35, 20, juce::Justification::centred, 0.f);
    ...
    ...

I need this query

Usually network connections like that are better spawned from a side thread to not block the Audio or GUI threads. Take a look at juce::Thread, juce::ThreadPool, etc to see how can that be done in a way that’s not blocking.

1 Like

But when I use threads like here the program does not open. I believe that the program only executes the thread first but nothing else. Threads should run in the background

int qstate;

int SQLQuery(std::string ID)
{
    MYSQL* conn;
    MYSQL_ROW row;
    MYSQL_RES* res;
    char* key;
    conn = mysql_init(0);

    mysql_real_connect(conn, "localhost", "EQ", "Kennwort0", "eq", 3306, NULL, 0);

    if (conn) {

        std::ostringstream str;
        str << "SELECT * FROM users WHERE UserID='" << ID <<"';";
        std::string sql = str.str();

        qstate = mysql_query(conn, sql.c_str());
        if (!qstate) {
            res = mysql_store_result(conn);
            if (res->row_count == 0) {

                return 0;
            }
            while (row = mysql_fetch_row(res)) {
                if (row[0] == ID) {
                    if (atoi(row[7]) == 1) {
                        return 1;
                    }
                    else {
                        return 0;
                    }
                }
            }
        }
        else {
            return 0;
        }
    }
    else {
        return 0;
    }
}

std::promise<int> p;
    auto f = p.get_future();
    std::thread SQLQuery(SQLQuery, ID);
    SQLQuery.join();
    int valueSQLQuery = f.get();

    if (valueSQLQuery == 0 || ID.length() != 13 || ID == "0") {
    ...
    ...
    }

When you do join() on the std::thread the current thread (where you create/start the thread) will block anyway, rendering the thread useless for “background” operations. (join() waits for the thread to finish its execution.)

I also don’t see where you are initialising the value of the future, I think that will throw a C++ exception if you call get() when there actually isn’t a value assigned to the future.

I don’t get an error. However, even if I remove join() nothing changes, nothing loads.

Removing the join() call isn’t correct either. Then the local std::thread object is destroyed without joining, which is also an error. It wouldn’t also make sense in the context of your current code, you would have to somehow wait for the thread to finish its execution before you proceed to the

int valueSQLQuery = f.get();
if (valueSQLQuery == 0 || ID.length() != 13 || ID == "0")

and following code.

I am a bit confused what is currently happening with the plugin? You might want to run pluginval under the debugger to see if there’s some error in the plugin that is so severe pluginval can’t even report it. Or pluginval can’t even load the plugin at all now? Maybe it has a plugin “blacklist” and your plugin has been put into that. If that is the case, you should remove the plugin from the blacklist.

what is a blacklist and how can i remove it?

It is also possible that pluginval cannot load the plugin

so i have debugged my code. Therad is terminated at SQLQuery.join (). But nothing is loaded anymore

Not sure if helpful for you, but my open source plugin calls a URL at startup to see if there are any news like e.g. an update notification for the user to display. This is done via a std::thread being created on processor startup, which is detached so that the plugin construction can continue. The result is referenced by a std::future that can be queried later by the editor to check if it should display a message. The implementation looks like this

Usage in the processor implementation:

Note: This was written real quickly, I think it could be done even nicer via std::async – but it might maybe serve as a starting point for you to figure out how to launch a background thread doing all the database stuff while not blocking the execution of other threads. Especially since it can be seen that you access the data during paint, you definitely want to make sure that your database access is only done once and then paint only accesses pre-fetched data that was stored to some member variable. paint can be called any time something happens to the GUI (e.g. hovering over it with the mouse might be enough) and for sure you don’t want each mouse move to trigger a database access :wink:

3 Likes

I don’t quite understand the code, but thanks anyway, I’ll find out what the std :: async function is

If you have specific questions about the code feel free to ask them! A few general things that might help you to follow the code:

std::thread is a class wrapping a thread. The constructor of it takes some kind of function object that it should execute on the new thread and optionally some arguments to pass to that function. A very explicit way of doing this might look like that

// the code we want to run on the new thread, wrapped in a free function
void backgroundThreadWork()
{
    std::cout << "Hello printed from background thread" << std::endl;
}

// A function to be called from our main code to launch that thread
void launchThread()
{
    // We create a thread instance that launches the function above
    std::thread backgroundThread (backgroundThreadWork);

    // Since we don't want to wait on the thread, we call detach. This will return immediately, so this function doesn't block execution.
    // The thread will continue running and do its work, but we loose explicit control about it at this point.
    backgroundThread.detach();
    
}

In my code above, I did some simplifications. First of all, I wrapped the code to be executed into a lambda instead of a function, which works as well. To me, it’s a bit more straightforward to declare
the code to be executed right above the thread instead of putting it in an extra function. The example above, done with a lambda would look like

// A function to be called from our main code to launch that thread
void launchThread()
{
    auto backgroundThreadWork = [] ()
    {
        std::cout << "Hello printed from background thread" << std::endl;
    }

    // We create a thread instance that launches the lambda declared above
    std::thread backgroundThread (backgroundThreadWork);

    // Since we don't want to wait on the thread, we call detach. This will return immediately, so this function doesn't block execution.
    // The thread will continue running and do its work, but we loose explicit control about it at this point.
    backgroundThread.detach();
    
}

Now we can squeeze it all together a bit. Instead of assigning the lambda to the backgroundThreadWork variable first and passing it to the thread, we can just put the lambda declaration right into the thread constructor

// A function to be called from our main code to launch that thread
void launchThread()
{
    // We create a thread instance that launches the lambda declared in the constructor
    std::thread backgroundThread ([] ()
    {
        std::cout << "Hello printed from background thread" << std::endl;
    });

    // Since we don't want to wait on the thread, we call detach. This will return immediately, so this function doesn't block execution.
    // The thread will continue running and do its work, but we loose explicit control about it at this point.
    backgroundThread.detach();
}

Last but not least, we can even avoid creating the named backgroundThread variable – instead we can call detach on the newly created thread in place.

// A function to be called from our main code to launch that thread
void launchThread()
{
    // We create a thread instance that launches the lambda declared in the constructor and detach it immediately
    std::thread backgroundThread ([] ()
    {
        std::cout << "Hello printed from background thread" << std::endl;
    }).detach();
}

Now, in our case we need to get something back from the thread when it’s done. This is done via the std::promise passed to the thread, which is some storage to write the result to. To access the promise from our main thread, the std::future object is used, you basically can ask the future if the promised result is ready.

std::async nicely wraps the thread construction, the promise and the future into a simple call, you put a function/lambda into it and it returns a std::future wrapping whatever you returns from that function. Internally it will launch the thread for you, creates the promise etc. So you likely want to implement a function that does your sql query, returns a struct with everything that you need and keep a std::future wrapping that struct as member. That member can be assigned by std::async.

1 Like

But what is the difference between a lambda and a normal function?

I have now accessed the function with std::async and have saved the value in a variable with a .get(). It works, but the same error occurs with pluginval

    auto future = std::async(std::launch::async, &SQLQuery, ID);
    int valueSQLQuery = future.get();

    if (valueSQLQuery == 0 || ID.length() != 13 || ID == "0") {
    ...
    }

A detailed answer to this question would likely be better located in a new forum topic. A few key points: C++ defines the concept of callable types, this means not only functions can be invoked by calling them with arguments, but also types. A lambda is basically a callable type, defined in place. If you don’t pass any arguments to its capture list, technically it won’t be any different from a traditional C-style free function.However, it won’t be visible outside the scope where you declared it, so it can be a technique to hide implementation details.

Lamdas become extremely powerful when binding variables to them via their capture list, this can solve tasks that cannot be solved in a straightforward way with functions. For now, I’d recommend you to focus on the async topic and have a read on lambdas after that – for now it should be enough to get a rough idea that they are something like a function that can be declared inside another function :wink:

Well, this approach won’t help you. The cppreference page on std::future::get tells us

The get method waits until the future has a valid result and (depending on which template is used) retrieves it. It effectively calls wait() in order to wait for the result.

So what you are doing here is simply waiting in your calling thread until the async thread has done it’s work. This has no benefit over executing the work in your calling thread directly. Instead you want to do something different:

  • Launch your async operation as the first action in the processor constructor and assign the future to a member variable of your processor. Don’t wait on the result after that.
  • In the editor constructor, spin up a timer that periodically checks the future member of the processor to see if the result is there. Keep a flag in the editor that indicates if the data is ready. When the data is ready, set that flag to true and call repaint to make the editor show the desired data. In paint, just check the flag, if it’s false display a message that you are still waiting for some data, if it’s true, display the actual data received.

This makes sure that your sql query which takes some time does not block the execution of any other code. Instead you just react to the result once it is there – this is one of the core concepts of async code execution.

I don’t have the time to write you some example code right now, but you’ll find some bits of that in the example I linked you above. In my example there is one difference, which is that I’m waiting for the result at the end of the editor constructor instead of launching a timer, but the timer approach is better if your query can take longer.

Hope that gives you enough hints to figure out the missing bits yourself :slight_smile:

1 Like