WaitForSingleObject () получает сигнал без возникновения ожидаемого события - PullRequest
0 голосов
/ 16 апреля 2019

Я пишу класс для COM-портов в C ++, используя win-api.Прямо сейчас я тестирую функциональность на RS232 с подключенными выводами Rx и Tx.

Я столкнулся с несколько странной проблемой.Я использую отдельный поток для чтения из COM-порта.В потоке я использую SetCommMask, WaitCommEvent и WaitForSingleObject для ожидания поступления символа в буфер.Однако WaitForSingleObject имеет тенденцию выходить без фактического получения символов.

Я предположил, что это было вызвано неправильным использованием упомянутых функций, но потом я обнаружил, что преждевременный выход не происходит каждый раз (первый раз всегда работаеткак предполагалось).

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

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

Вызов ClearCommError для проверки входного буфера с ReadFile не вариант, потому что в таком случае InQue всегда равен 0. Поэтому я не могу сказать, действительно ли ReadFile читает или ждет.

    //following code runs in separate thread
    DWORD dwEventMask1, dwEventMask2, LastError, Status;
    OVERLAPPED Overlapped; HANDLE Serial_Port_Handle;
    std::string stringBuffer("");
    const size_t ReadBufferLength = 256;
    char tempBuffer[ReadBufferLength];

    GetCommMask(Serial_Port_Handle, &dwEventMask1);



    if (dwEventMask1)   // Before starting the thread I check the state of Input Buffer with GetCommError().
    {                   // If Buffer is not empty, CommMask is set to 0 signaling there is no need for waiting.
        Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

        //wait for comm event
        if (!WaitCommEvent(Serial_Port_Handle, &dwEventMask1, &Overlapped))
        {
            if ((LastError = GetLastError()) == ERROR_IO_PENDING)
            {
                Waiting = true; //signal bool for synchronization purposes
                if ((Status = WaitForSingleObject(Overlapped.hEvent, INFINITE)) == WAIT_OBJECT_0)
                {
                    GetCommMask(Serial_Port_Handle, &dwEventMask2);
                    Waiting = false;
                    CloseHandle(Overlapped.hEvent); 
                    // I close handle and set all members of Overlapped struct to 0
                } 

                if (dwEventMask2 !== dwEventMask1) // check if wait have not exited because of SetCommMast()
                    return;
            }
        }
    }
    do  // this loop reads from input buffer until empty
    {
        Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);//set up read overlapped operation

        if (ReadFile(Serial_Port_Handle, tempBuffer, ReadBufferLength - 1, &NoBytesRead, &Overlapped)) //start read opperation
        { //Read operation done on 1 go
            GetOverlappedResult(Serial_Port_Handle, &Overlapped, &NoBytesRead, FALSE); //get NoBytesRead

            CloseHandle(Overlapped.hEvent)
            stringBuffer.append(tempBuffer, (size_t) NoBytesRead); // save read data
        }
        else if ((LastError = GetLastError()) == ERROR_IO_PENDING) //operation not yet complete
        {
            GetOverlappedResult(Serial_Port_Handle, &Overlapped, &NoBytesRead, TRUE); // wait for completion
            stringBuffer.append(tempBuffer, (size_t)NoBytesRead);
        }
        else
        {
            CloseHandle(Overlapped.hEvent)
            return;
        }
    } while ((NoBytesRead == (ReadBufferLength - 1)) && NoBytesRead);
    // Loop runs while tempBuffer's full capacity is used.
    // I realize that since I don't use Total Timeout there is a possibility
    // of the loop getting stuck. If you can suggest other solution than using
    // Total Timeout or use GetCommError() to get input buffer state, please do so.
    return;

Этот код несколько упрощен (проверка возвращаемых значений и т. Д.).

1) Кто-нибудь из вас сталкивался с таким поведением?

2) Я используюПЕРЕКРЫТЫЕ операции в коде.После завершения операции я всегда использую CloseHandle и повторно инициализирую структуру OVERLAPPED, прежде чем использовать ее для другой операции.Это правильно или сброса структуры достаточно?

Ответы [ 2 ]

1 голос
/ 16 апреля 2019

Это плохая логика в целом. Например, есть следующие проблемы.

  • CreateEvent / CloseHandle не должен выполняться при каждом ReadFile / WaitCommEvent.
  • Использование GetCommMask / WaitCommEvent также неправильно.
  • Размер считываемых данных, указанный в ReadFile, фиксируется независимо от ситуации.
  • Также включает комментарий к @ RbMm.

Возможно, вы захотите изменить дизайн своей программы со ссылкой на следующие статьи и исходный код:

Синхронизация и перекрытие входа и выхода

Последовательная связь

BMO / mttty


Дополнительно:
Я не заметил, что для WaitForSingleObject был задан дескриптор файла (не дескриптор события), на что указал @Rita Han.
Самая большая проблема в этом.

Однако ситуация с улучшением дизайна не изменилась.
В источнике ответа @Rita Han нет описания WaitCommEvent и Overlapped для него. Кроме того, размер чтения данных фиксирован в ReadFile.


С другой стороны:
Хотя это не происходит в исходном коде вопроса, для WaitCommEvent / WaitForSingleObject возможно генерировать событие, которое не указано в SetCommMask.

  • Пока WaitCommEvent ожидает завершения, измените маску события с помощью SetCommMask.
    Замечания - функция WaitCommEvent

    Если процесс пытается изменить маску события дескриптора устройства с помощью функции SetCommMask, когда выполняется перекрывающаяся операция WaitCommEvent, WaitCommEvent немедленно возвращается. Переменная, на которую указывает параметр lpEvtMask, установлена ​​в ноль.

  • Пока WaitCommEvent ожидает завершения, вызывайте WaitCommEvent несколько раз, используя одну и ту же перекрывающуюся структуру.
    Синхронизация и перекрывающиеся входы и выходы

    При выполнении нескольких одновременных перекрывающихся операций в одном потоке вызывающий поток должен указывать структуру OVERLAPPED для каждой операции. Каждая структура OVERLAPPED должна указывать дескриптор другого объекта события с ручным сбросом.

    Поток не должен повторно использовать событие, предполагая, что событие будет сигнализироваться только перекрытой операцией этого потока. Событие сигнализируется в том же потоке, что и перекрывающаяся операция, которая завершается. Использование одного и того же события в нескольких потоках может привести к условию состязания, при котором событие правильно сигнализируется для потока, операция которого завершается первым и преждевременно для других потоков, использующих это событие.

Документ описан выше, но в зависимости от драйвера / поставщика устройства вызываемое позднее WaitCommEvent завершается с ошибкой, а ожидание завершения WaitCommEvent равно lpEvtMask, возвращаемому с нуля (как в SetCommMask).


Для нескольких перекрывающихся структурных переменных:
Обычное ноу-хау в программировании заключается в том, что использование одной переменной для нескольких целей может привести к ошибкам. Если вы проектируете в асинхронном и / или многопоточном режиме, лучше подготовить как минимум три переменные перекрывающихся структур для ReadFile, WriteFile, WaitCommEvent.

О запуске ReadFile независимо от состояния входного буфера:
Речь идет о вызове ReadFile с фиксированной длиной 256 байтов без получения размера принятых данных во входном буфере драйвера устройства.

Фактически, даже если все данные поступают, если они меньше 256 байтов, они всегда будут задерживаться до тех пор, пока не произойдет тайм-аут приема 256 байтов.

Например, цикл считывает один байт за раз до тех пор, пока не произойдет ошибка тайм-аута, что означает конец принятых данных (тайм-аут чтения 1-байта не окажет влияния).
Или, как ответили в предыдущей статье, используйте ClearCommError, чтобы получить размер данных, хранящихся во входном буфере драйвера устройства, и вызовите ReadFile, указав этот размер.

Не будет проблем с обработкой буфера на стороне приложения, которую вы объясняете.

О поведении WaiCommEvent при вызове SetCommMask:
Это может зависеть отиспользуемый драйвер устройства.

0 голосов
/ 16 апреля 2019

Однако WaitForSingleObject имеет тенденцию завершаться, фактически не получая никаких символов.

Дождитесь дескриптора события вместо дескриптора последовательного устройства.

Я получил его на работу.Ниже приведен пример кода, который вы можете попробовать:

DWORD errCode = 0;
BOOL result = false;

HANDLE serialDeviceHdl = CreateFile(L"COM8", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (!serialDeviceHdl)
{
    errCode = GetLastError();
    cout << "Open device failed. Error code: " << errCode << endl;
    return 0;
}

OVERLAPPED overlappedForWrite = {};
overlappedForWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

DWORD writenSize = 0;
result = WriteFile(serialDeviceHdl, "hello", 5, &writenSize, &overlappedForWrite);
if (FALSE == result)
{
    errCode = GetLastError();
    if (ERROR_IO_PENDING == errCode)
    {
        cout << "Overlapped I/O operation is in progress." << endl;
    }
    else
    {
        cout << "Write to device failed. Error code: " << errCode << endl;
    }
}

DWORD returnValue = WaitForSingleObject(overlappedForWrite.hEvent, INFINITE);
if (WAIT_OBJECT_0 == returnValue)
{
    cout << "The state of the specified object is signaled." << endl;
}
else
{
    cout << "Wait for single object failed. Error code: " << returnValue << endl;
}

CHAR readBuf[5];
DWORD readSize = 0;
OVERLAPPED overlappedForRead = {};
overlappedForRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

result = ReadFile(serialDeviceHdl, readBuf, 5, &readSize, &overlappedForRead);
if (FALSE == result)
{
    errCode = GetLastError();
    if (ERROR_IO_PENDING == errCode)
    {
        cout << "Overlapped I/O operation is in progress." << endl;
    }
    else
    {
        cout << "Write to device failed. Error code: " << errCode << endl;
    }
}

returnValue = WaitForSingleObject(overlappedForRead.hEvent, INFINITE);
if (WAIT_OBJECT_0 == returnValue)
{
    cout << "The state of the specified object is signaled." << endl;
}
else
{
    cout << "Wait for single object failed. Error code: " << returnValue << endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...