Вызов serial_port::cancel вызывает ошибку проверки во время выполнения #0 в Visual Studio 2010 SP1.

Это фатальная ошибка, исправить ее невозможно, а в релизных сборках появляется неприятное окно сообщения. Это происходит, когда я вызываю serial_port::cancel, а Boost пытается выдать какое-то исключение. Точная ошибка:

Ошибка проверки во время выполнения № 0 — значение ESP не было должным образом сохранено при вызове функции. Обычно это результат вызова функции, объявленной с одним соглашением о вызовах, с указателем на функцию, объявленным с другим соглашением о вызовах.

И похоже, это происходит в throw_error.ipp внутри метода do_throw_error. Вот небольшая примерная программа, которая воспроизводит это:

#include <boost/asio/serial_port.hpp>
#include <boost/asio/read.hpp>
#include <boost/thread.hpp>
#include <iostream>

using namespace std;

BYTE g_pBuffer[128];

void ReadCompletionHandler( boost::system::error_code ec, std::size_t bytesTransferred ){
    if( !ec ){
        cout << "Read " << bytesTransferred << " bytes successfully" << endl;
    }else if( ec == boost::asio::error::operation_aborted ){
        cout << "Aborted async_read_some, " << bytesTransferred << " bytes transferred" << endl;
    }else{
        cout << "Read finished with errors: " << ec.message() << endl;
    }
}

void ReaderThread( boost::asio::serial_port* pSerialPort ){
    cout << "[ReaderThread] Started reading" << endl;
    boost::asio::async_read( *pSerialPort, boost::asio::buffer( g_pBuffer, 5 ), ReadCompletionHandler );
    cout << "[ReaderThread] Done!" << endl;
}

void main(){
    //Tell cout not to buffer its output so output better reflects multithreaded execution
    std::cout.setf(std::ios::unitbuf);
    boost::asio::io_service ioService;
    boost::asio::serial_port serialPort( ioService );
    string portName = "COM1";
    serialPort.open( portName );
    if( !serialPort.is_open() ){
        cout << "Failed to open " << portName << endl;
        return;
    }
    cout << "Launching reader thread and io_service" << endl;
    boost::thread readerThread( boost::bind( ReaderThread, &serialPort ) );
    ioService.run();
    cout << "Sleeping..." << endl;
    ::Sleep( 5000 );
    cout << "Woke up, cancelling pending reads" << endl;
    serialPort.cancel();
    cout << "Waiting for reader thread to finish..." << endl;
    readerThread.join();
    cout << "Done!" << endl;
    cin.get();
}

Может быть, я делаю что-то глупое. Что вы думаете?


person dario_ramos    schedule 15.03.2012    source источник
comment
Насколько мне известно, для обработчиков завершения требуется соглашение о вызовах _stdcall.   -  person Martin James    schedule 15.03.2012
comment
Добавление __stdcall к определению ReadCompletionHandler не решило проблему. Если я изменю соглашение о вызовах проекта на __stdcall, он прекратит сборку. Таким образом, кажется, что Boost был создан с использованием соглашения __cdecl. Должен ли я пересобрать Boost, чтобы он использовал __stdcall?   -  person dario_ramos    schedule 15.03.2012


Ответы (1)


В Windows XP serial_port::cancel вызывает CancelIo, который отменяет ожидающий ввод-вывод для вызывающего потока. Таким образом, решение отправляет вызов потоку, в котором выполняется io_service::run:

ioService.post( [&]{serialPort.cancel();} );

Большое спасибо ребятам из списка пользователей boost (Игорю Р. и Ричарду Роулендсу). В любом случае, я считаю, что Boost должен проверять, что вызывающий поток является владельцем последовательного порта, я имею в виду, что отмена - это не операция, которую вы будете вызывать интенсивно.

person dario_ramos    schedule 15.03.2012