locked
Serial Communications Overlapped I/O WaitForMultipleObjects hangs RRS feed

  • Question

  • Hi,

    I want to read data from serial port. The packet are variable size (12 to 1024 bytes).  Before we poll data byte to byte, but it seems too much to use CPU and I think it is not very effective.

    Here is what I have so far in my Server RX Thread:

    // Main thread loop
    DWORD dwErrors = NO_ERROR;
    DWORD dwNumberOfReceiving = 0;
    BYTE arbChars[8] = {0};
    DWORD dwWaitTimeout = 0;
    BOOL bSuccess = TRUE;
    BOOL bLastCharWasDLE = FALSE;
    BOOL bStartOfFrameDetected = TRUE;
    COMSTAT comStat;
    OVERLAPPED ovInternal = {0};
    ovInternal.hEvent = m_hComReceiveOverlappedEvent;
    BOOL bNeedWaitingOnStatusHandle = FALSE;
    HANDLE hWaitKillEventAndOverlappedEvent[2] = {m_hEventKill, m_hComReceiveOverlappedEvent};
    while (WaitForSingleObject(m_hEventKill, dwWaitTimeout) != WAIT_OBJECT_0)
    {
    	// Initiate a read operation
    	bSuccess = ReadFile(m_hCom, &arbChars, TOOL_MEMORY_COUNT_OF(arbChars), &dwNumberOfReceiving, &ovInternal);
    	if (bSuccess == FALSE)
    	{
    		// Test for error
    		dwErrors = GetLastError();
    		if (dwErrors == ERROR_IO_PENDING)
    		{
    			// Operation delayed...
    			bNeedWaitingOnStatusHandle = TRUE;
    		}
    		else
    		{
    			// Error in ReadFile
    			dwWaitTimeout = 20;
    			continue;
    		}
    	}
    	else
    	{
    		// No need to wait on the overlapped handle, data is ready
    		bNeedWaitingOnStatusHandle = FALSE;
    	}
    	// Test if we need to wait on the overlapped handle
    	if (bNeedWaitingOnStatusHandle == TRUE)
    	{
    		// Wait for the thread kill event at index 0
    		// Wait for the read event of the serial com at index 1
    		switch (WaitForMultipleObjects(TOOL_MEMORY_COUNT_OF(hWaitKillEventAndOverlappedEvent), hWaitKillEventAndOverlappedEvent, FALSE, INFINITE))
    		{
    			// Kill event occurred
    			case WAIT_OBJECT_0: 
    			{
    				// Can IO operation
    				CancelIo(m_hCom);
    				continue;
    				
    				// Thread will be shutdown...
    			}
    			break;
    			// Overlapped event occurred
    			case WAIT_OBJECT_0 + 1: 
    			{
    				// Get read overlapped result
    				if (GetOverlappedResult(m_hCom, &ovInternal, &dwNumberOfReceiving, TRUE) == FALSE)
    				{
    					// An error occurred in the overlapped operation...
    					dwWaitTimeout = 20;
    					continue;
    				}
    				else
    				{
    					// Data has been properly received
    				}
    			}
    			break;
    			default:
    			{
    				// Unknown error in the WaitForSingleObject
    				dwWaitTimeout = 20;
    				continue;
    			}
    		}
    	}
    	// No error, then no delay to add on the Wait for KillEvent
    	dwWaitTimeout = 0;
    	// Is there any char read on the serial COM
    	if (dwNumberOfReceiving <= 0)
    	{
    		continue;
    	}
    	// Handle read serial data
    	handleSerialRead(arbChars, dwNumberOfReceiving);
    }
    // Return result
    return 0;
    }

    Now, this is well working in general. However, if the serial COM client send a packet of 12 bytes using multiple WriteFile, then here's what happens in the server RX thread.

    1 - [Server Thread RX] - First a ReadFile is initiated

    2 - [Server Thread RX] - I get a ERROR_IO_PENDING error, so it calling WaitForMultipleObject where he get stuck until an event occured...

    3 - [Client] - Send a packet having a size of 12 bytes using multiple WriteFile. I'm presuming that server will start reading before client finished sending the whole packet.

    4 - [Server Thread RX] - Unblock from the WaitForMultipleObject and return WAIT_OBJECT_0 + 1. This mean that something happen on the the Read operation.

    5 - [Server Thread RX] - GetOverlappedResult is called and it returns TRUE, with 8 bytes ready to read. Now I process those 8 bytes.

    6 - [Server Thread RX] - Another call to ReadFile is done, getting ERROR_IO_PENDING but after that I'm stuck forever in the WaitForMultipleObject. Yet other 2 bytes are available !!!

    The reason why I'm stuck in the WaitForMultipleObjects is probably because I'm asking for 8 bytes in the ReadFile but there only 2 bytes ready in the COM. It is ?

    Now, what to do to get thoses 2 bytes remaining ???

    I've play a little with the CommTimeOut

    COMMTIMEOUTS commTimeOuts;
    commTimeOuts.ReadIntervalTimeout			= 0;
    commTimeOuts.ReadTotalTimeoutConstant		= 100;
    commTimeOuts.ReadTotalTimeoutMultiplier		= 1 + (8 / m_dwBaudRate);
    commTimeOuts.ReadTotalTimeoutConstant		= 0;
    commTimeOuts.ReadTotalTimeoutMultiplier		= 0;
    commTimeOuts.WriteTotalTimeoutConstant		= 0;
    commTimeOuts.WriteTotalTimeoutMultiplier		= 0;
    presuming that after 100 milliseconds plus the multiplier I should get *unstuck* from the WaitForMultipleObjects, but NO !

    Another idea was to set a timeout value in the WaitForMultipleObjects instead of using INFINITE. But after that I will get a WAIT_TIMEOUT result and what can I do to get the 2 remaining bytes if I cannot read it by anyway ?

    I'm really out of idea on that. I hope someone will help me because it's been three days that I'm wasting my time on this problem :(

    Best regards,
    Martin



    • Edited by Erakis Friday, December 7, 2012 11:25 PM
    • Moved by Jesse Jiang Friday, December 14, 2012 9:23 AM (From:Visual C++ General)
    Friday, December 7, 2012 11:17 PM

All replies

  • I'm not sure about this, but here's something to try: Change the bWait parameter of GetOverlappedResult from TRUE to FALSE.

    You should not need to wait there since you have already waited for completion with the previous WaitForMultipleObjects call, and that call resets the overlapped event.

    Saturday, December 8, 2012 12:40 AM
  • Thanks Scott for your help.

    I've already tried changing the bWait parameter of GetOverlappedResult to FALSE but I still stuck in the WaitForMultipleObjects.

    Another idea ?

    Saturday, December 8, 2012 1:04 AM
  • Common.... nobody knows how to do this ?

    I
    'm probably not the only person who needs to read via the serial port in overlapped mode ?

    I've read the article Serial Communications in Win32 and I'm still stuck on this problem. This is really anoying.
    Monday, December 10, 2012 12:24 PM
  • Hi,

    Welcome here.

    Based on your description, your issue is about reading data from serial port, so I’d like to move this thread to Microsoft SDK for better support.

    Thanks for your understanding.

    Regards,


    Elegentin Xie
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, December 14, 2012 9:30 AM
  • I had the exact same problem, and it issue turned out the be how I was resetting the events.  The events need to be created as manual reset events, but you should not reset them yourself; the wait functions will reset them for you. 

    The following worked for me:

    void ReceiveMessageThreadFunc()
    {
    	// Set which com events we care about.  In this case, only receive events
    	if (!::SetCommMask(m_hSerial, EV_RXCHAR))
    	{
    		LogError("Failed to Set Comm Mask. Reason: %d", GetLastError());
    		return;
    	}
    
    	// Create an event to use for notifications from the com port
    	HANDLE serialEvent = CreateEvent(
    		0,			// Event Attributes
    		true,		// Manual Reset
    		FALSE,		// Initial State
    		0);			// Name
    
    	// Create a separate event to wait for reads to finish
    	HANDLE readEvent = CreateEvent(
    		0,			// Event Attributes
    		true,		// Manual Reset
    		FALSE,		// Initial State
    		0);			// Name
    
    	// Create the overlapped structure to wait for events from the serial port
    	OVERLAPPED ov;
    	memset(&ov, 0, sizeof(ov));
    	ov.hEvent = serialEvent;
    
    	// Set the two events that this thread will wait for
    	HANDLE arHandles[2];
    	arHandles[0] = m_threadExitEvent;
    	arHandles[1] = ov.hEvent;
    
    	// Reserve space in a buffer to receive the incoming data.  Size is arbitrary
    	Buffer incomingDataBuffer(1024);
    
    	bool keepRunning = true;
    
    	while (keepRunning)
    	{
    		DWORD comEventMask = 0;	// NOTE: this is different from the value passed to SecCommMask
    
    		BOOL abRet = ::WaitCommEvent(m_hSerial, &comEventMask, &ov);
    		if (!abRet)
    		{
    			DWORD errorCode = GetLastError();
    			if (errorCode != ERROR_IO_PENDING)
    			{
    				LogError("Error occurred reading from COM port: %d", errorCode);
    				continue;
    			}
    		}
    
    		DWORD waitResult = WaitForMultipleObjects(2, arHandles, FALSE, INFINITE);
    		switch (waitResult)
    		{
    			// If the thread exit event is the one that was triggered...
    			case WAIT_OBJECT_0:
    			{
    				// Allow the thread to end by returning from this function
    				keepRunning = false;
    				continue;
    			}
    			break;
    
    			// If the comm event is the one that was triggered...
    			case WAIT_OBJECT_0 + 1:
    			{
    				// Read all the available data from the comm port into a buffer
    
    				// Clear the buffer that accumulates the data
    				incomingDataBuffer.Clear();
    
    				OVERLAPPED ovRead;
    				memset(&ovRead, 0, sizeof(ovRead));
    				ovRead.hEvent = readEvent;
    
    				byte szTmp[1024];
    				int iSize = sizeof(szTmp);
    				memset(szTmp, 0, iSize);
    
    				DWORD bytesRead = 0;
    
    				do
    				{
    					memset(szTmp, 0, iSize);
    					bytesRead = 0;
    
    					if (!ReadFile(m_hSerial, szTmp, iSize, NULL, &ovRead))
    					{
    						DWORD errorCode = GetLastError();
    						if (errorCode == ERROR_IO_PENDING)
    						{
    							WaitForSingleObject(ovRead.hEvent, INFINITE);
    
    							if (!GetOverlappedResult(m_hSerial, &ovRead, &bytesRead, false))
    							{
    								LogError("Error waiting for read from serial port");
    								break;
    							}
    						}
    						else
    						{
    							LogError("Error reading from serial port");
    							break;
    						}
    					}
    
    					if (bytesRead > 0)
    					{
    						incomingDataBuffer.Append(szTmp, bytesRead);
    					}
    
    				} while (bytesRead > 0);
    
    				// Pass the buffered data to any listeners
    				if (incomingDataBuffer.GetSize() > 0)
    				{
    					if (m_readCallback.IsBound())
    					{
    						m_readCallback.Call(incomingDataBuffer);
    					}
    				}
    			}
    			break;
    		}
    	}
    
    	// Close the events before the thread exits
    	CloseHandle(serialEvent);
    	CloseHandle(readEvent);
    }
    

    Wednesday, September 23, 2015 9:31 PM