microsoft / cpprestsdk

The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
Other
7.96k stars 1.65k forks source link

wait() on request hangs / deadlocks #1694

Open stelro opened 2 years ago

stelro commented 2 years ago

Hello, I have the following code embedded in my library, which is built for Windows with VS2015 And after running some tests, I have noticed that there is a deadlock that is happening spuriously (probably 2-3 times on 50 runs).

The strange part is that this deadlock occurs only if I run multiple requests in the same applications (e.g multiple requests to different endpoints).

For example:

requestA requestB <- deadlock

or

requestA requestB requestC <- deadlock or requestB deadlocks, but never request A

bool send_request(request_method method,
                  const std::string& apiName,
                  const web::json::value& query_body,
                  response_data_container& response_data) {

    int status_code = 200;
    uri_builder builder(apiName));

    bool status = true;

    http_request request(get_method(method));
    request.set_body(query_body);
    request.set_request_uri(builder.to_string());
    // Add some request headers

    client_->request(request)
        .then([&status, &status_code](const http_response& response) {
            // continue when the response is available

            if (response.status_code() != status_codes::OK) {
                status_code = response.status_code();

                // LOG some error

                // return an empty JSON value and set the status to false
                status = false;
                return pplx::task<json::value>();
            }

            status_code = response.status_code();
            return response.extract_json();

        }).then([&response_data, &status, &status_code](
                    const pplx::task<json::value> &previous_response) {

                // continue when the JSON value is available
                try {
                    if (previous_response.get().size() == 0) {
                        LOG("JSON content of reply is empty");
                        return status;
                    }

                    response_data = previous_response.get();
                    return status;

                } catch (http_exception &e) {

                    const int error_code = e.error_code().value();
                    LOG("Error getting response: (%d) %s", error_code, e.what());

                    // Map http error code to status_code here..

                    status = false;
                }

                return status;
            }).wait();  <-- deadlocks here

    return status;
}

I can't figure out what I am doing wrong here? I know that .get() is block API but I am calling it in continuation. maybe it is the problem?

stelro commented 2 years ago

I want also to add here, that this behaviour is seen only on Windows 10, but when I am trying to run these tests on Windows server 2016, I can't see or get any deadlock.

elnoir commented 2 years ago

I'm having the exact same issue, with Visual Studio 2019 and Windows 10. I've mixing cppwinrt's thread handling with cpprest. All of the cpprest object where created on the same thread, so that shouldn't be an issue. In my case when there is an ongoing request and the second thread calls the client.request(request).get() function the both threads will be locked up, and never return. As a workaround I switched the http request handling to the crp and kept the json parsing from the cpprest-sdk.

barcharcraz commented 2 years ago

This looks like it's probably the "waiting on completions inside of other tasks" thing. How many tasks are active (or waiting) when the deadlock happens

stelro commented 2 years ago

So I have retried again with the simplest code path as possible. I have only one task that is running. That is, just sending a simple request to the server, and doing nothing else, not even extracting JSON response.

And I am still hitting the same issue. From my debug session that looks related to some kind of condition variable inside Windows, or PPL library that is cpprestdk/windows is using for tasking.

Here are few screenshots:

callstack1 callstack2

As you can see, the condition is waiting, but I don't know what is waiting for? I have a single threaded application with a single task.

Thank you,