There is a device that periodically sends packets to the com port. You can also send a command to the device, to which it must respond with a confirmation.

I want to implement a blocking command transfer method that will return control when an answer is received, or throw an exception if the waiting time expires.

Since the device itself periodically sends messages that need to be sent, data is received in a separate stream, and after receiving the entire packet, a packet reception event is generated. The structure is as follows:

Frame lastTxFrame; // последняя отправленная команда // Отправить пакет и дождаться ответа или таймаута void Send(Frame frame) { lastTxFrame = frame; serialPort.Write(frame); /* заблокировать поток, дождаться ответа или таймаута */ } // Принято сообщение void OnFrameReceived(object sender, Frame rxFrame) { if(lastTxFrame != null && IsReply(lastTxFrame, rxFrame)) { /* разблокировать поток, ожидающий ответ */ } } // Проверить является ли сообщение reply ответом на команду command bool IsReply(Frame command, Frame reply) { return true; } 

How can you implement this algorithm so that there is no race race? You can start a timer after sending a packet, and detect a timeout in the callback method, and if a packet arrives, stop the timer. But in the description of System.Threading.Timer it is said that after calling Dispose a callback call may occur. So theoretically, we can both get an answer and catch a timeout.

You can use Interlocked methods to determine whether the callback timer or the message receiving handler was called:

  int timeout = 0; void OnFrameReceived(object sender, Frame rxFrame) { if(Interlocked.CompareExchange(ref timeout, 1, 0) == 0) // таймаута не было { } } void TimerCallback(object state) { if (Interlocked.CompareExchange(ref timeout, 2, 0) == 0) // таймаут { } } 

But in this case, it is not clear how to reset the timeout variable to its original value in order to send the next message.

How to correctly implement the wait?

  • a queue of commands that are processed in one stream comes to mind - tym32167
  • The easiest option is to send and receive data in one stream and poll the message queue for sending. But I want to deal with synchronization. It seems that such problems occur very often and there must be some kind of trodden solution. - swd
  • if Send is single-threaded, this can be easily implemented via a ManualResetEvent or ManualResetEventSlim . - Vasek
  • @Vasek An interesting general case is when several threads call Send (frame), packets are added to a common queue and threads are blocked, and then unlocked one by one when a response is received to the corresponding command or after a timeout. But even if the packet sends only one stream, how to get rid of the race? In the Send method, we call mre.WaitOne (timeout), but we still have a race: if ManualResetEvent returns control on timeout, and in the reading stream we received a response to the command, then we detect both the timeout and the response. - swd
  • Is there some query_id in the protocol? If not, how is it supposed to distinguish between the answers for the two waiting Send ? - Vasek

0