Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multithreaded read_timeout? #25

Open
stegmannt opened this issue Mar 7, 2011 · 14 comments
Open

Multithreaded read_timeout? #25

stegmannt opened this issue Mar 7, 2011 · 14 comments

Comments

@stegmannt
Copy link

I just ran into the problem with sp.getc not timing out if it's used in a new Thread.

I've tried using Timeout::timeout() but I guess this will break at least on multicore systems, if the block gets killed while getc is processing the input.

Is there any way to workaround/fix this?

@hparra
Copy link
Owner

hparra commented Mar 9, 2011

I don't know. I use serialport in a multithreaded environment as well. Are you using Ruby 1.9?

@stefanlindbohm
Copy link

I'm having the same issue on OS X Lion with both 1.8.7 and 1.9.3. Writes works but reads doesn't seem to work at all, they just get stuck even if there's data to be read.

@hparra
Copy link
Owner

hparra commented May 26, 2012

I had a similar problem in Windows using 1.8, but I haven't experienced it since. Particular examples?

@davidkirwan
Copy link

Hi there,

I'm using SerialPort version 1.1.0, on Ruby 1.9.3p327 on an Archlinux machine running the 3.6.6-1 kernel. I'm using the SerialPort gem within a loop running inside a thread:

device = "/dev/ttyUSB0"
speed = 9600
timeout = 100

sp = SerialPort.new(device, speed)
sp.read_timeout = timeout

t = Thread.new do
  while(true)
    value = sp.read
    puts value
  end
end

gets
t.kill
sp.close

It seems the second I attempt to run the SerialPort code within a thread, or attempt to use other ruby classes which make use of threads, the system appears to go into read_timeout and hangs..

I've also tried to use the rufus-scheduler gem to attempt the following:

scheduler.every '1s' do

  device = "/dev/ttyUSB0"
  speed = 9600
  timeout = 100

  sp = SerialPort.new(device, speed)
  sp.read_timeout = timeout 

  value = sp.read
  puts value

end

Not sure what else to try :/ any ideas ?

@GarthSnyder
Copy link

I'm seeing a similar lockup on sp.read from a second thread, but in my case it does not appear to matter what the read_timeout value is set to (negative, zero, or positive). The system is Solaris Express 11 running MRI 1.9.2p290, and ruby-serialport compiles and installs without issue.

Here's some code that demonstrates the issue:

require 'serialport'
require 'thread'

rfid = SerialPort.new("/dev/term/0", 9600, 7, 1, SerialPort::NONE)

def read_tags(port)
  while 1
    puts "Selecting..."
    IO::select([port])
    puts "Reading..."
    segment = port.read 
    puts "Segment: #{segment}"
  end
end

Thread.new do
  read_tags rfid
end

while 1
  sleep 100
end

If you remove the Thread.new wrapper around "read_tags rfid", the reading loop works as you'd expect. But as written, with the loop running in a separate thread, the IO::select call works correctly (the thread pauses at the select until the remote serial peer produces output) but the port.read following it hangs indefinitely without ever returning any data. (The select is not necessary to reproduce the problem; it's just there to verify the presence of readable data.)

It does not matter which thread executes which component. Putting the sleep loop into a thread and running read_tags on the main thread produces the same behavior. Whatever the underlying issue is, it seems to be triggered just by the existence of multiple threads.

I'm afraid I don't have experience writing extensions for Ruby, but looking over the C code I get the impression that sp.read is inherited directly from IO and that there's nothing ruby-serialport could be actively doing to create a deadlock. So I'm not convinced that the issue is in ruby-serialport. But since it does affect use of the library and may be part of a pattern, I thought I'd mention it here.

@GarthSnyder
Copy link

Regarding the above, everything works fine for me on ruby-2.0.0-p195. There was substantial revision in the IO library implementation in MRI coming into 2.0.0, so I assume that whatever the underlying issue was, it was addressed by those patches. Again, this is under Solaris, so YMMV.

@davidkirwan
Copy link

Very interesting, thanks Garth I'll give this a go later today :)

@davidkirwan
Copy link

👍 working now!

@GarthSnyder
Copy link

Thanks for the update!

@ghost
Copy link

ghost commented Oct 25, 2013

could this issue be closed now then?

@GarthSnyder
Copy link

There's probably a legitimate issue in MRI 1.9, but I doubt that ruby-serialport is implicated.

@ghost
Copy link

ghost commented Nov 6, 2013

@hparra : it sounds like this can be closed now.

@kd-2020
Copy link

kd-2020 commented Jun 10, 2014

I'm having the same issues. Running windows 7, ruby 2.0.0p481 32bit, serialport-1.3.0. I have tried ruby 1.9 and 2.0 64bit.

My script hangs when I try to write after I set up a read on another thread, unless I set a read timeout, in which case it wont hang but still doesn't read any thing at all.

My code WILL WORK if the serial device (arduino) is looping a char every second or so. It seems to keep the serialport rolling along. It seems like some sort of read or write buffer issue. IE from the arduino side:

void setup() {
  Serial.begin(9600);
}

void loop() {
  //Serial.print("X");  // WITH THESE TWO LINES UNCOMMENTED EVERYTHING WORKS
  //delay(500);         //    Commented out and the serialport gem will hang
  while (Serial.available())
    ruby::rx(Serial.read(), processCmd); // this bit just (for now) just spits back
                                                              //    what it recieves, does work when 
                                                              //    above lines are not commented out.
}

I'm confident the micro controller code isn't the issue, just posting the above to try and make it clear what I'm talking about in the paragraph above.

Here's the test ruby script I'm working with:

class Arduino
  def initialize(port="COM3", baud=9600)
    @serial_port = SerialPort.new(port, baud)#, 8, 1, SerialPort::NONE)
    sleep 5
  end

  def write(cmd)
    @serial_port.syswrite cmd
  end

  def read
    @read_thread = Thread.new do
      loop do
        c = @serial_port.read
        if c
          yield c
        end
        sleep 0.005
      end
    end
  end
end

jig = Arduino.new

jig.read do |c|
  print c
end

puts "Ready..."

for i  in 0..3
  x = gets
  puts "Sending message"
  jig.write "Hello"
  puts "Sent message"
end

gets

Again, with periodic 'x' chars from the arduino I get the 'x's and the expected response, with out the periodic 'x's my ruby script will hang on write.

@kd-2020
Copy link

kd-2020 commented Jun 10, 2014

By the way, just tested my code and it works as expected in Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants