Please explain why serial I/O read is a "blocking event" so can't be in button handler

Please explain why in a button event handler I can not send a message to a serial port and then read the reply.
private void button1_Click(object sender, EventArgs e)
    serialPort1.Read(reply);  //can't do this

Open in new window

I've already implemented the read in a serialPort1_DataReceived event handler. It's my boss who knows only old procedural programming languages who doesn't understand why I can't simply translate his old procedural program to C# with a GUI. I try and explain "event-driven" programming to him. he still wants to know why it can't be done. I know it can't be done, and it has something to do with "blocking events". I'd like to learn more of the details behind why this can't be done.
Who is Participating?
Mike TomlinsonConnect With a Mentor Middle School Assistant TeacherCommented:
A WinForms application has an internal message pump that is basically a never ending loop that routes messages generated by the operating system and from user interaction.  If you place a blocking call in the same thread as the main UI, which is the case when placed in a button handler, then the message pump also gets blocked.  This results in an unresponsive application that will not respond to user interaction and if left long enough will experience a "white out" (doesn't repaint).  Therefore, in a WinForms application, blocking calls should be placed into another thread.
The reason is that when you call the Read() method, you rely on the fact that the other side called Write(). Until the other side has actually called the Write() method, your code will block, since it's waiting for that to take place.

Dave BaldwinConnect With a Mentor Fixer of ProblemsCommented:
The last time I wrote for PC serial ports (DOS 6.22!), you wrote your own interrupt service routine for both send and receive and they were usually character based, not 'message' or 'string' based.  I don't think you can even do that past Windows 98 or so, the serial port drivers are in a protected part of the operating system.

In the code you have above, the "serialPort1.Write(msg);" passes a string to a driver and lets the driver and interrupt routines send the data.  If the second part did work, it would block your entire program until it finished.  Even in the 'old' way, we would not have done that.  We would let the driver or ISR receive the bytes and poll the routine to collect them into a buffer where we could see when it had the reply.

If your boss really did that kind of programming and believes that should work, I think he is remembering wrong.
Never miss a deadline with

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

deleydAuthor Commented:
He coded it in MATLAB. The code essentially goes like this:
s = serial(port,'Name',...
<wait for reply (bytes to read)>
out = fread(s,s.BytesAvailable);

Open in new window

You simply open the port, send a ping, wait for a reply.

I tried that in a button event handler. The ping was sent, but program hung on my following 'read reply' statement.

I think it has to do with the message pump that Idle_Mind mentioned. I used to work with those a lot.

So if I understand correctly:

1. Thread 1 has a message pump

2. Thread1 writes to the serial port

3. Bytes get sent out the serial port

4. Thread1 issues a 'read'

5. Thread1 receives a message that there are bytes to read

6. Thread1 never processes the 'there are bytes to read' message because it is waiting for it's 'read' request to complete.

OK I still don't quite see why it doesn't work. Why does the OS send a "there are bytes to read" message to the message pump instead of just completing the 'read' request?

I've always wanted to know what goes on "under the hood" of C# and .NET Framework, but all the books I've found hide that stuff, saying "it just works."

I think there's a "message sniffing" tool out there somewhere. Possibly Sysinternals? I'd learn a lot if I could see the actual messages going through the pump like I used to do.
Dave BaldwinFixer of ProblemsCommented:
I would bet that the Matlab code isn't doing exactly what you think it is.  I would bet that it interprets that code and actually has well written routines that run it in the background.  It is what I would do.

Even in assembly language long ago, you never make your main program wait on the serial port.  Serial ports are very slow in comparison to the CPU.  Putting both the write and the read in that same routine will stall your program if everything works perfectly and crash it if it doesn't respond quickly.

I'd write more but I have to go.  I'll check back tomorrow.
deleydAuthor Commented:
In the MATLAB implementation my boss did a simple

1. Send Message

2. Read Reply

Which was simple with MATLAB. Not so easy with event-driven code since there we're just sending messages, messages, messages,...

and reading replies, replies, replies,...

It's no longer easy to know what reply goes with which message.

Sometimes the device just replies with a NAK. Well thank you, but what message didn't you get?

My boss thought it would be a simple quick process to translate the MATLAB code to C# and that I would be able to do it in a day or two. I have to explain why it's not that simple. He's familiar with FORTRAN, C and MATLAB. I have to explain it's not like translating from French to Spanish, it's more like translating from a book to a movie.

(Yes when I tested his MATLAB code it did hang the entire computer once requiring a hard power down reboot.)
Mike TomlinsonMiddle School Assistant TeacherCommented: just create another thread to place your I/O operations on.  Then you can send --> wait --> {know what the response was} for each transmission.
Dave BaldwinFixer of ProblemsCommented:
Your boss is not familiar enough with writing serial code in C or Matlab.  The language does not change the process.  And it doesn't matter if it worked every other time.  The method of "sending the message" and "waiting on the reply" is guaranteed to fail eventually.  I'm not a C# programmer but the methods haven't changed since 1980 when I wrote my first routine in assembly language.  If you make your program wait on the reply, then something will happen to make it fail.

In a quick search, I don't see anything that looks usable in C# but maybe that's because I don't know C#.  I do kind of like @Idle_Mind's idea of putting it in another thread.  But you still have to be able to check status for things like the serial cable unplugged.
deleydAuthor Commented:
I tried starting a new thread which quickly threw several messages at the port. Discovered the device can't handle having several messages coming at it that fast. It only replied to the first message.

I then added a delay between messages to give the device time to reply. That worked.

However I really need to sync the sending & receiving, so I can match up sent messages with received replies, and know when it's OK to send the next message, which sounds complicated:
create new thread
send a message
wait for a notification that a reply has been received
also set a timeout timer in case there is no reply which sends a notification that the timer has expired
if reply received cancel timeout timer
prevent other messages from being sent via other events while this is happening
Mike TomlinsonMiddle School Assistant TeacherCommented:
The send/receive should be on its own thread.
The timeout can be achieved using a standard timer on the form.
You can use two ManualResetEvents to toggle the status of a receive event and a timeout event.

How to combine them?..yet another thread that listens to the status of both the receive ManualResetEvent and the timeout ManualResetEvent.

This can be done with WaitHandle.WaitAny().
Dave BaldwinFixer of ProblemsCommented:
@deleyd, that's almost exactly what has always been done for serial port messaging (post #36543916).  I think you understand the process now.
Dave BaldwinFixer of ProblemsCommented:
The one thing you might add is a check of the port status before you send a message.  That way if the cable is disconnected or the device has signalled that it's not available, you don't have to wait for the timeout to know that.
deleydAuthor Commented:
Into the deep fun I go.

(This question should have 10,000 points! Thanks for all the help everyone.)

How do I check the port status? I'm looking at SerialPort Class and not seeing any "I'm unplugged" property.

(I've wondered what would happen if I unplugged the USB cable and then plugged it back in. Was going to test that eventually to see what happened.)
Dave BaldwinFixer of ProblemsCommented:
USB cable?  Does that mean you don't have a 'real' serial port?  You have a USB to Serial Port adapter?  That could be interesting because USB is a networking protocol that doesn't provide status from the other end unless it is implemented in the driver.

Is this what you're using?  If so, I don't see any status info there either.  Maybe USB adapter isn't that different then.
deleydAuthor Commented:
Oh I also sometimes get the serialPort1_DataReceived event fired but the serialPort1.BytesToRead = 0.

Don't know why that happens once in awhile.

(I think it happens after a rather long message is received, and I read the whole thing the first time the receive event gets fired, but then I get another firing of the event, except there's 0 bytes to read.)
deleydAuthor Commented:
I discovered I can do on a background worker thread the port write followed by a port read.

(Main UI thread with message pump must have something to do behind the scenes with the read, because I can't do this on the main UI thread, even if I don't mind the program hanging until the read completes.)
Dave BaldwinFixer of ProblemsCommented:
Thanks for the points, glad you got it working.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.