Поток получения последовательного порта не ведет себя так, как ожидалось - C ++ - PullRequest
1 голос
/ 09 декабря 2010

Мое приложение использует отдельный поток для асинхронной обработки полученных последовательных данных.ПК попадает в обработчик получения, как и ожидалось, но оттуда все идет странно.

Это моя функция потока:

// Create event for OVERLAPPED structure.
s_ov.hEvent = ::CreateEvent(
    NULL,                           // No security
    TRUE,                           // Create a manual-reset event object
    FALSE,                          // Initial state is non-signaled
    NULL                            // No name specified
    );

// Load event handles.
pHandles[0] = s_hSerialPortRxThreadExitEvent;

while ( bContinue )
{
    if ( !::WaitCommEvent( s_hSerialPort, &dwEventMask, &s_ov ) )
    {
        if ( ::GetLastError() != ERROR_IO_PENDING )
        {
            TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
            return ::GetLastError();
        }
    }

    pHandles[1] = s_ov.hEvent;

    dwObjectWaitState = ::WaitForMultipleObjects( 2, pHandles, FALSE, INFINITE );

    switch ( dwObjectWaitState )
    {
    case WAIT_ABANDONED:
        TRACE(_T("SerialPortRxThreadFn : Owner thread terminated prematurely.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_ARENA_TRASHED, __WFILE__, __LINE__);
        return ERROR_ARENA_TRASHED;
        break;

    case WAIT_TIMEOUT:
        TRACE(_T("SerialPortRxThreadFn : The timeout is set to INFINITE; there should be no timeout.  State is nonsignaled.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), WAIT_TIMEOUT, __WFILE__, __LINE__);
        return WAIT_TIMEOUT;
        break;

    case WAIT_FAILED:
        TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
        return ::GetLastError();
        break;

    case WAIT_OBJECT_0:             // thread exit event signalled
        bContinue = FALSE;

        if ( !::ResetEvent( pHandles[0] ) )
        {
            TRACE(_T("SerialPortRxThreadFn  : Failed to reset the serial port thread exit event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
            return ::GetLastError();
        }
        break;

    case WAIT_OBJECT_0 + 1:         // OVERLAPPED structure event signalled
        // Read data from serial port.
        if ( !::ReadFile( s_hSerialPort, pBuf, RX_BUF_SIZE, &dwWritten, &s_ov ) ) // <- Set breakpoint here
        {
            TRACE(_T("SerialPortRxThreadFn : Call to ReadFile filed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
            return ::GetLastError();
        }

        // Discontinue thread operation if there are no more bytes in the serial port receive buffer.
        if ( dwWritten == 0 ) // <- Or, set breakpoint here
        {
            bContinue = FALSE;
        }
        // Copy the received bytes to the thread-safe buffer.
        else if ( !s_pobjRxRingBuffer->Add( pBuf, dwWritten, TRUE ) )
        {
            TRACE(_T("SerialPortRxThreadFn : Failed to add bytes to ring buffer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_INSUFFICIENT_BUFFER, __WFILE__, __LINE__);
            return ERROR_INSUFFICIENT_BUFFER;
        }
        else if ( s_SpCallbackFn != NULL )
        {
            // Notify application of received data.
            if ( (dwRetVal = s_SpCallbackFn( s_pobjRxRingBuffer->ItemsInBuffer() )) != ERROR_SUCCESS )
            {
                TRACE(_T("SerialPortRxThreadFn : Serial port callback function failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwRetVal, __WFILE__, __LINE__);
                return dwRetVal;
            }
        }

        if ( !::ResetEvent( pHandles[1] ) )
        {
            TRACE(_T("SerialPortRxThreadFn : Failed to reset the OVERLAPPED structure event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
            return ::GetLastError();
        }
        break;

    default:
        // Do nothing.
        break;
    }
}

::CloseHandle( s_ov.hEvent );

return ERROR_SUCCESS;

Если я установлю свою точку останова на линии, вызывающей ReadFile все работает, как я ожидаю, и ПК попадает в функцию обратного вызова.Однако, если я установлю свою точку останова на следующей строке, где dwWritten оценивается как ноль, она равна нулю, выражение оценивается как TRUE, и цикл завершается;ПК никогда не добирается до обратного вызова.Что я делаю неправильно?Спасибо.

Ответы [ 4 ]

2 голосов
/ 09 декабря 2010

Я не эксперт по Win32 API, но это точно звучит как проблема синхронизации (которая является частой причиной heisenbugs .) Скажем, к тому времени, как вы доберетесь до ReadFile, естьнет данных для чтения.Взлом в отладчике может дать ему достаточную паузу для поступления данных, поэтому, когда вы возобновите / перешагните через ReadFile, это будет успешно.

Есть много вещей, кроме поступления данных, которые могутвызвать событие.Вы можете проверить dwEventMask, чтобы проверить, верна ли моя гипотеза.

1 голос
/ 09 декабря 2010

Довольно больно смотреть этот код, написано кое-что из него. Его многословие лучше всего застряло в чужой библиотеке классов. Пара красных флагов. Вы предполагаете, что завершение WaitCommEvent () означает, что вы можете вызвать ReadFile (). Обычно маска события, которую вы использовали, не видна, но есть много других причин, по которым последовательный порт хочет вам что-то сказать. Другая проблема заключается в том, что WaitCommEvent может завершиться сразу же. Это не редкость, что-то доступное в буфере приема.

Украсть этот код откуда-то, это жесткий код. Это было сделано.

0 голосов
/ 09 декабря 2010

Вам не нужны комм события для асинхронного чтения данных. Просто позвоните ReadFile, вы получите «ошибку» ERROR_IO_PENDING, и когда данные поступят, событие будет сигнализировано, и вы можете получить число байтов GetOverlappedResult, данные будут в буфере, который вы изначально предоставили до ReadFile.

0 голосов
/ 09 декабря 2010

В документации WaiCommEvent говорится, что после того, как вы использовали функцию ожидания (например, WaitForMultipleEObjects (...)), вы используете функцию GetOverlappedResult (...) для получения результатов вашей операции. Не должно быть необходимости в Read \ Write-File (...).

...