I am writing serial port software for Windows. To improve performance, I am trying to convert routines to use asynchronous I / O. I have a code up and it works quite well, but I'm a little newbie in this, and I would like to improve the program performance further. During stress tests of the program (i.e., data packets to / from the port as quickly as possible at a high transfer rate), the CPU load becomes quite high.
If anyone has experience with asynchronous I / O and multithreading on Windows, I would appreciate it if you could take a look at my program. I have two main problems:
Is asynchronous I / O implemented correctly? I found a fairly reliable source on the network, offering you to pass user data into callback functions by implementing your own OVERLAPPED structure with your own data at the end. This seems to work just fine, but for me it looks a bit "hacky." In addition, the program’s performance didn’t improve all this when I converted from synchronous / polling to asynchronous / callback, which made me suspect that I was doing something wrong.
Can I use STL std :: deque for FIFO data buffers? Since the program is currently written, I only allow one byte of data that needs to be received at a time before it needs to be processed. Since I do not know how much data I will receive, it can be an infinite amount. I assume that this 1-byte time will lead to slow behavior behind the deque lines when it has to allocate data. And I don't trust deque to be thread safe (should I?). If using an STL deck is not normal, are there any suggestions for using a better data type? Buffer ring buffer based on static array?
Any other code feedback is also welcome.
, "Comport", -. "ThreadedComport", .
ThreadedComport ( )
class ThreadedComport : public Comport
{
private:
HANDLE _hthread_port; /* thread handle */
HANDLE _hmutex_port; /* COM port access */
HANDLE _hmutex_send; /* send buffer access */
HANDLE _hmutex_rec; /* rec buffer access */
deque<uint8> _send_buf;
deque<uint8> _rec_buf;
uint16 _data_sent;
uint16 _data_received;
HANDLE _hevent_kill_thread;
HANDLE _hevent_open;
HANDLE _hevent_close;
HANDLE _hevent_write_done;
HANDLE _hevent_read_done;
HANDLE _hevent_ext_send; /* notifies external thread */
HANDLE _hevent_ext_receive; /* notifies external thread */
typedef struct
{
OVERLAPPED overlapped;
ThreadedComport* caller; /* add user data to struct */
} OVERLAPPED_overlap;
OVERLAPPED_overlap _send_overlapped;
OVERLAPPED_overlap _rec_overlapped;
uint8* _write_data;
uint8 _read_data;
DWORD _bytes_read;
static DWORD WINAPI _tranceiver_thread (LPVOID param);
void _send_data (void);
void _receive_data (void);
DWORD _wait_for_io (void);
static void WINAPI _send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
static void WINAPI _receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
};
, CreateThread():
DWORD WINAPI ThreadedComport::_tranceiver_thread (LPVOID param)
{
ThreadedComport* caller = (ThreadedComport*) param;
HANDLE handle_array [3] =
{
caller->_hevent_kill_thread,
caller->_hevent_open,
caller->_hevent_close
};
DWORD result;
do
{
result = WaitForMultipleObjects(3,
handle_array,
false,
INFINITE);
if(result == WAIT_OBJECT_1 )
{
do
{
caller->_send_data();
caller->_receive_data();
result = caller->_wait_for_io();
} while (result != WAIT_OBJECT_0 &&
result != WAIT_OBJECT_2);
}
else if(result == WAIT_OBJECT_2)
{
;
}
} while (result != WAIT_OBJECT_0);
return 0;
}
, , :
void ThreadedComport::_send_data (void)
{
uint32 send_buf_size;
if(_send_buf.size() != 0)
{
WaitForSingleObject(_hmutex_port, INFINITE);
if(_is_open)
{
bool result;
WaitForSingleObject(_hmutex_send, INFINITE);
_data_sent = 0;
send_buf_size = _send_buf.size();
if(send_buf_size > (uint32)_MAX_MESSAGE_LENGTH)
{
send_buf_size = _MAX_MESSAGE_LENGTH;
}
_write_data = new uint8 [send_buf_size];
for(uint32 i=0; i<send_buf_size; i++)
{
_write_data[i] = _send_buf.front();
_send_buf.pop_front();
}
_send_buf.clear();
ReleaseMutex(_hmutex_send);
result = WriteFileEx (_hcom,
(void*)_write_data,
send_buf_size,
(LPOVERLAPPED)&_send_overlapped,
(LPOVERLAPPED_COMPLETION_ROUTINE )&_send_callback);
SleepEx(INFINITE, true);
if(result == false)
{
}
}
ReleaseMutex(_hmutex_port);
}
else
{
SetEvent(_hevent_write_done);
}
}
void ThreadedComport::_receive_data (void)
{
WaitForSingleObject(_hmutex_port, INFINITE);
if(_is_open)
{
BOOL result;
_bytes_read = 0;
result = ReadFileEx (_hcom,
(void*)&_read_data,
1,
(OVERLAPPED*)&_rec_overlapped,
(LPOVERLAPPED_COMPLETION_ROUTINE )&_receive_callback);
SleepEx(INFINITE, true);
if(result == FALSE)
{
DWORD last_error = GetLastError();
if(last_error == ERROR_OPERATION_ABORTED)
{
close();
}
}
}
ReleaseMutex(_hmutex_port);
}
DWORD ThreadedComport::_wait_for_io (void)
{
DWORD result;
bool is_write_done = false;
bool is_read_done = false;
HANDLE handle_array [5] =
{
_hevent_kill_thread,
_hevent_open,
_hevent_close,
_hevent_write_done,
_hevent_read_done
};
do
{
result = WaitForMultipleObjects(5,
handle_array,
false,
INFINITE);
if(result <= WAIT_OBJECT_2)
{
break;
}
else if(result == WAIT_OBJECT_3)
{
is_write_done = true;
SetEvent(_hevent_ext_send);
}
else if(result == WAIT_OBJECT_4)
{
is_read_done = true;
if(_bytes_read > 0)
{
uint32 errors = 0;
WaitForSingleObject(_hmutex_rec, INFINITE);
_rec_buf.push_back((uint8)_read_data);
_data_received += _bytes_read;
while((uint16)_rec_buf.size() > _MAX_MESSAGE_LENGTH)
{
_rec_buf.pop_front();
}
ReleaseMutex(_hmutex_rec);
_bytes_read = 0;
ClearCommError(_hcom, &errors, NULL);
SetEvent(_hevent_ext_receive);
}
}
} while(!is_write_done || !is_read_done);
return result;
}
-:
void WINAPI ThreadedComport::_send_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;
if(dwErrorCode == 0)
{
if(dwNumberOfBytesTransfered > 0)
{
_this->_data_sent = dwNumberOfBytesTransfered;
}
}
delete [] _this->_write_data;
SetEvent(lpOverlapped->hEvent);
}
void WINAPI ThreadedComport::_receive_callback (DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
if(dwErrorCode == 0)
{
if(dwNumberOfBytesTransfered > 0)
{
ThreadedComport* _this = ((OVERLAPPED_overlap*)lpOverlapped)->caller;
_this->_bytes_read = dwNumberOfBytesTransfered;
}
}
SetEvent(lpOverlapped->hEvent);
}