?
Solved

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

Posted on 2011-09-10
17
Medium Priority
?
304 Views
Last Modified: 2012-05-12
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.Write(msg);
    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.
0
Comment
Question by:deleyd
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 6
  • 3
  • +1
17 Comments
 
LVL 1

Expert Comment

by:ananken
ID: 36517263
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.

/ananken
0
 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 1000 total points
ID: 36517267
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.
0
 
LVL 83

Assisted Solution

by:Dave Baldwin
Dave Baldwin earned 1000 total points
ID: 36517274
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.
0
RHCE - Red Hat OpenStack Prep Course

This course will provide in-depth training so that students who currently hold the EX200 & EX210 certifications can sit for the EX310 exam. Students will learn how to deploy & manage a full Red Hat environment with Ceph block storage, & integrate Ceph into other OpenStack service

 

Author Comment

by:deleyd
ID: 36517372
He coded it in MATLAB. The code essentially goes like this:
s = serial(port,'Name',...
fopen(s)
fwrite(s,pingmsg)
<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.
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36517458
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.
0
 

Author Comment

by:deleyd
ID: 36517621
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.)
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36517665
Right...so just create another thread to place your I/O operations on.  Then you can send --> wait --> {know what the response was} for each transmission.
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36518289
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.
0
 

Author Comment

by:deleyd
ID: 36543916
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
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 36544205
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.
http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx

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().
http://msdn.microsoft.com/en-us/library/tdykks7z.aspx
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36544637
@deleyd, that's almost exactly what has always been done for serial port messaging (post #36543916).  I think you understand the process now.
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36544654
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.
0
 

Author Comment

by:deleyd
ID: 36546156
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.)
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36546446
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?  http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.aspx  If so, I don't see any status info there either.  Maybe USB adapter isn't that different then.
0
 

Author Comment

by:deleyd
ID: 36546455
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.)
0
 

Author Closing Comment

by:deleyd
ID: 36578257
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.)
0
 
LVL 83

Expert Comment

by:Dave Baldwin
ID: 36578504
Thanks for the points, glad you got it working.
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This document covers how to connect to SQL Server and browse its contents.  It is meant for those new to Visual Studio and/or working with Microsoft SQL Server.  It is not a guide to building SQL Server database connections in your code.  This is mo…
Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
Michael from AdRem Software outlines event notifications and Automatic Corrective Actions in network monitoring. Automatic Corrective Actions are scripts, which can automatically run upon discovery of a certain undesirable condition in your network.…
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
Suggested Courses

741 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question