# Bug? Serial ports and threads don't mix

**URL:** <https://rubytalk.org/t/bug-serial-ports-and-threads-dont-mix/13391>\
**Category:** ruby-talk\
**Created:** [2 September 2004 18:54 UTC](https://rubytalk.org/t/bug-serial-ports-and-threads-dont-mix/13391 "2004-09-02T18:54:55Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Brian\_Candler](https://avatars.discourse-cdn.com/v4/letter/b/5f9b8f/32.png) [@Brian\_Candler](https://rubytalk.org/u/Brian_Candler)\
**Post date:** [2 September 2004 18:54 UTC](https://rubytalk.org/t/bug-serial-ports-and-threads-dont-mix/13391/1 "2004-09-02T18:54:55Z")

</div>

[repost, looks like it didn't make it through the first time]

Hello,

I have come across a strange problem with using serial ports under Ruby,  
which comes to light when you try to use a ruby thread (even if this is the  
only thread which is active).

The attached program demonstrates the problem; it sends three bytes down the  
serial port, then waits for three bytes to be received and displays them. At  
the end, you'll see two very similar ways of starting the object. If you use

&nbsp;&nbsp;&nbsp;&nbsp;a = SerialTest.new('/dev/ttyS0')  
&nbsp;&nbsp;&nbsp;&nbsp;a.test

then it works; but if you use

&nbsp;&nbsp;&nbsp;&nbsp;t = Thread.new do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b = SerialTest.new('/dev/ttyS0')  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b.test  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;t.join

then it doesn't, although clearly the two progams should behave identically.  
Note that I'm using ruby-termios, but only to set the serial port to the  
desired speed characteristics; otherwise I'm just using Ruby's native I/O.

What seems to happen in the second case is that no data is actually sent  
down the serial port, until you terminate the program with ^C. At that point  
the three bytes are sent. It seems to be blocking, wrongly, waiting for the  
port to become available.

You can see I've included two different bits of code to open the serial  
port. The first came from the 'examples' directory in ruby-termios; for some  
reason it opens non-blocking and then puts blocking mode back on. The second  
just does a straightforward open in read-write mode. However the program  
seems to fail in the same way regardless of which you use.

This is causing me major headaches, in particular because constructs like

&nbsp;&nbsp;&nbsp;&nbsp;timeout(5) do  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...  
&nbsp;&nbsp;&nbsp;&nbsp;end

don't work - the timeout library works by starting the block within a new  
thread, and the port hangs when I try to perform I/O.

Am I doing something stupid here? Or is there something in the ruby core  
which isn't behaving as advertised? Or something fundamental about opening a  
device in read-write mode?

I was using ruby-1.8.1, but I have just upgraded to ruby-1.8.2-preview2 and  
the problem is still there just the same.

Thanks...

Brian Candler.

--- 8\< ------------------------------------------------------------------  
#!/usr/local/bin/ruby -w

# Program to demonstrate issues with serial ports and threads in Ruby  
# Tested under Red Hat 9.0  
# ruby 1.8.1 (2003-12-25) [i686-linux]  
# with ruby-termios-0.9.4

# Either use a loopback cable (DB9: link pins 2-3), or connect with a  
# null-modem cable to another machine running a terminal emulator  
# set to 9600 8N1, and look for the three characters it sends (and send  
# three characters back to it)

require 'fcntl'  
require 'termios'

class SerialTest  
&nbsp;&nbsp;def initialize(devname)

&nbsp;&nbsp;&nbsp;&nbsp;# Open port (version 1, taken from ruby-termios examples)  
&nbsp;&nbsp;&nbsp;&nbsp;@dev = open(devname, File::RDWR | File::NONBLOCK)  
&nbsp;&nbsp;&nbsp;&nbsp;mode = @dev.fcntl(Fcntl::F\_GETFL, 0)  
&nbsp;&nbsp;&nbsp;&nbsp;@dev.fcntl(Fcntl::F\_SETFL, mode & ~File::NONBLOCK)

&nbsp;&nbsp;&nbsp;&nbsp;# Open port (version 2)  
&nbsp;&nbsp;&nbsp;&nbsp;#@dev = File::open(devname, File::RDWR)

&nbsp;&nbsp;&nbsp;&nbsp;tio = Termios::new\_termios  
&nbsp;&nbsp;&nbsp;&nbsp;tio.iflag = 0  
&nbsp;&nbsp;&nbsp;&nbsp;tio.oflag = 0  
&nbsp;&nbsp;&nbsp;&nbsp;tio.cflag = Termios::CS8 | Termios::CREAD | Termios::CLOCAL  
&nbsp;&nbsp;&nbsp;&nbsp;tio.lflag = 0  
&nbsp;&nbsp;&nbsp;&nbsp;[tio.cc](http://tio.cc)[Termios::VTIME] = 0  
&nbsp;&nbsp;&nbsp;&nbsp;[tio.cc](http://tio.cc)[Termios::VMIN] = 1  
&nbsp;&nbsp;&nbsp;&nbsp;tio.ispeed = tio.ospeed = Termios::B9600  
&nbsp;&nbsp;&nbsp;&nbsp;Termios::flush(@dev, Termios::TCIOFLUSH)  
&nbsp;&nbsp;&nbsp;&nbsp;Termios::setattr(@dev, Termios::TCSANOW, tio)  
&nbsp;&nbsp;end

&nbsp;&nbsp;MSG\_SEND = "XXX"  
&nbsp;&nbsp;def test  
&nbsp;&nbsp;&nbsp;&nbsp;sleep 0.5  
&nbsp;&nbsp;&nbsp;&nbsp;@dev.write(MSG\_SEND)  
&nbsp;&nbsp;&nbsp;&nbsp;STDERR.puts "Sent #{MSG\_SEND.inspect}"  
&nbsp;&nbsp;&nbsp;&nbsp;r = @dev.read(3)  
&nbsp;&nbsp;&nbsp;&nbsp;STDERR.puts "Read #{r.inspect}"  
&nbsp;&nbsp;end

&nbsp;&nbsp;def close  
&nbsp;&nbsp;&nbsp;&nbsp;@dev.close  
&nbsp;&nbsp;end  
end

# This works:

#a = SerialTest.new('/dev/ttyS0')  
#a.test  
#exit

# But this doesn't

t = Thread.new do  
&nbsp;&nbsp;b = SerialTest.new('/dev/ttyS0')  
&nbsp;&nbsp;b.test  
end  
t.join
