Ожидание с таймаутом на boost::asio::async_connect завершается неудачей (std::future::wait_for)

Я использую std::future с boost::asio::async_connect для отмены операции, когда происходит таймаут, как предложено здесь: /questions/48182842/sorcvtime-i-sorcvtimeo-ne-vliyayut-na-operatsii-boostasio/48182850#48182850

Тем не мение, std::future::wait_for() возвращается std::future_status::deferred немедленно, т. е. операция еще не началась. conn_result.get() позднее блокируется до тех пор, пока не возникнет ошибка соединения, поскольку удаленный хост не прослушивает. Я не хочу на это полагаться, потому что это может продолжаться очень долго, пока не будет выдана ошибка сокета.

Как правильно ждать фьючерсов, созданных boost::asio?

РЕДАКТИРОВАТЬ: SSCCE

#include <iostream>
#include <future>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>

using boost::asio::ip::tcp;

int main(int argc, char* argv[]) {
    boost::asio::io_service ioservice;
    boost::asio::io_service::work work(ioservice);

    std::thread t([&](){ioservice.run();});

    tcp::resolver resolver(ioservice);
    tcp::resolver::query query("127.0.0.1","27015"); // random unused adress

    tcp::socket socket(ioservice);

    std::future<tcp::resolver::iterator> conn_result = boost::asio::async_connect(socket,resolver.resolve(query),boost::asio::use_future);

    std::cout << "IO Service running: " << (!ioservice.stopped() ? "y":"n") << std::endl;
    auto status = conn_result.wait_for(std::chrono::milliseconds(500));
    if (status == std::future_status::timeout) {
        socket.cancel();
        std::cout << "Timeout" << std::endl;
        return 0;
    } else if(status == std::future_status::deferred) {
        std::cout << "Deferred" << std::endl;
    }
    // If the operation failed, then conn_result.get() will throw a
    // boost::system::system_error.
    try {
        conn_result.get();
    } catch(const boost::system::system_error& e) {
        std::cerr << e.what() << std::endl;
    }


    ioservice.stop();
    t.join();

    return 0;
}
  • Компилятор: MSVC2012
  • Повышение 1,58

1 ответ

Решение

Похоже, что это ошибка, как здесь ответил Стефан Лававей.

Я не смог найти оригинальную ошибку, но она исправлена ​​в "RTM-версии" (при условии VS2013).

На это влияет внутренний номер ошибки DevDiv # 255669 ": wait_for()/wait_until() не блокируйте ". К счастью, я получил исправление для этого от одного из наших разработчиков Concurrency Runtime, Hong Hong. С моей текущей сборкой VC11 это работает:

С моей текущей сборкой VC11 это работает:

C:\Temp>type meow.cpp
#include <stdio.h>
#include <chrono>
#include <future>
#include <thread>
#include <windows.h>
using namespace std;

long long counter() {
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return li.QuadPart;
}

long long frequency() {
    LARGE_INTEGER li;
    QueryPerformanceFrequency(&li);
    return li.QuadPart;
}

int main() {
    printf("%02d.%02d.%05d.%02d\n", _MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 100000, _MSC_BUILD);

    future<int> f = async(launch::async, []() -> int {
        this_thread::sleep_for(chrono::milliseconds(250));

        for (int i = 0; i < 5; ++i) {
            printf("Lambda: %d\n", i);
            this_thread::sleep_for(chrono::seconds(2));
        }

        puts("Lambda: Returning.");
        return 1729;
    });

    for (;;) {
        const auto fs = f.wait_for(chrono::seconds(0));

        if (fs == future_status::deferred) {
            puts("Main thread: future_status::deferred (shouldn't happen, we used launch::async)");
        } else if (fs == future_status::ready) {
            puts("Main thread: future_status::ready");
            break;
        } else if (fs == future_status::timeout) {
            puts("Main thread: future_status::timeout");
        } else {
            puts("Main thread: unknown future_status (UH OH)");
        }

        this_thread::sleep_for(chrono::milliseconds(500));
    }

    const long long start = counter();

    const int n = f.get();

    const long long finish = counter();

    printf("Main thread: f.get() took %f microseconds to return %d.\n",
        (finish - start) * 1000000.0 / frequency(), n);
}


C:\Temp>cl /EHsc /nologo /W4 /MTd meow.cpp
meow.cpp

C:\Temp>meow
17.00.50419.00
Main thread: future_status::timeout
Lambda: 0
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 1
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 2
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 3
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 4
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: Returning.
Main thread: future_status::ready
Main thread: f.get() took 2.303971 microseconds to return 1729.

Я вставил временный код, чтобы доказать, что когда wait_for() возвращается готовым, f.get() возвращается мгновенно без блокировки.

В основном, обходной путь должен заключаться в цикле, пока он сообщает об отложенном

Другие вопросы по тегам