Ruby + Windows + Timeouts + SerialPorts won't work - ruby

I am developing a multiplatform Ruby program that is supposed to connect via USB to a serial device.
First I was using the serialport gem (1.0.4), but then I ran into some strange problems and had to drop it.
I then proceeded to communicate via Ruby's IO class, as follows:
#port = IO.new IO.sysopen(path, mode), mode
Communication via syswrite and sysread is perfect both in Linux as Windows.
With the communication done, I tried setting up timeouts so that the program won't hang if any desync occurs. All done on the Linux side with timeout.rb, but Windows won't get me control of the interpreter again after calling any IO read method (sysread, getc, gets, getbyte... I tried them all!).
I experimented with Terminator, but it wouldn't even run, throwing argument exceptions instead of timing out -- even in Linux!:
require 'terminator'
Terminator.terminate 2 do
sleep 4
end
produces:
/var/lib/gems/1.9.1/gems/terminator-0.4.4/lib/terminator.rb:164: Use RbConfig instead of obsolete and deprecated Config.
ArgumentError: wrong number of arguments (1 for 0)
from /var/lib/gems/1.9.1/gems/terminator-0.4.4/lib/terminator.rb:127:in `block in terminate'
from (irb):12:in `call'
from (irb):12:in `sleep'
from (irb):12:in `block in irb_binding'
from /var/lib/gems/1.9.1/gems/terminator-0.4.4/lib/terminator.rb:134:in `call'
from /var/lib/gems/1.9.1/gems/terminator-0.4.4/lib/terminator.rb:134:in `terminate'
from (irb):11
from /usr/bin/irb:12:in `<main>'
As SystemTimer relies on UNIX signals and doesn't work on Windows (it simply wraps timeout.rb), I am stuck with a multiplatform program that would just eternally hang when running on Windows, although just fine on Linux.
Is there any way I could set timeouts on serial port reading on Windows? Perhaps a win32api call?
Thank you for your time. :)
Edit:
I believe I found a way around the problem, although it really stinks.
instead of
Timeout::timeout 0.5 do
gets
end
I tried
begin
Timeout::timeout 0.5 do
Thread.new do
gets
end.join
end
rescue
"Expired :)"
end
and it seems to be fine on IRB.
I'm going to implement and test it and then I'll post the results here. :)
However, any prettier solution is most than welcome!

Related

Why does JRuby sometimes not print a newline at the end of the string when "puts" is being called from within a thread?

In Ruby, I'm used to seeing puts always append a newline to the string being printed before printing it.
While playing around with multi-threading in Ruby, including running it with JRuby, I noticed some odd behavior. Sometimes, two lines will be printed on the same line instead of being printed on separate lines like I expected. In my first time trying this, my script was using a Semaphore from concurrent-ruby because I was testing out concurrency and parallelism patterns. I was using the semaphore to only allow a configured number of threads from being started at a time. It was about a 20% chance of it happening.
To create a minimal reproduction for Stack Overflow, I took out concurrent-ruby and created a smaller version of my program that just created threads that ran immediately, without first storing the blocks of code in procs and without using a semaphore. With this new program, this issue happens 100% of the time (at least for how many times I've tried running it so far).
I understand that with multi-threading, the order of the two lines being printed could be different each time, depending on which thread runs first. But I didn't expect that sometimes, the newline that should be on the end of every string printed (because I print them with puts) would sometimes not be there.
Program:
def work(n)
puts "Started unit of work ##{n}."
i = 0
100000000.times { i += 1 }
puts "Finished unit of work ##{n}."
end
threads = []
2.times do |i|
thread = Thread.new do
work(i + 1)
end
threads << thread
end
threads.each do |t|
t.join
end
puts "\nEnd."
Output:
Started unit of work #1.Started unit of work #2.
Finished unit of work #2.
Finished unit of work #1.
End.
Note how the output includes Started unit of work #1.Started unit of work #2. and not Started unit of work #1.\nStarted unit of work #2.. Also note how this issue doesn't occur for the second call to puts in each thread. Those are on separate lines, as expected.
This output was consistent every time I ran the Ruby script with JRuby. I couldn't get a run where it printed a newline after Started unit of work #1. or Started unit of work #2..
System details:
> jruby --version
jruby 9.3.8.0 (2.6.8) 2022-09-13 98d69c9461 OpenJDK 64-Bit Server VM 19+36-2238 on 19+36-2238 +jit [x86_64-linux]
> java --version
openjdk 19 2022-09-20
OpenJDK Runtime Environment (build 19+36-2238)
OpenJDK 64-Bit Server VM (build 19+36-2238, mixed mode, sharing)
> lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.1 LTS
Release: 22.04
Codename: jammy
You are not flushing the output stream nor are you using the output stream in synchronous mode.
Output streams typically work in a buffered mode where they collect a certain amount of characters in a buffer and only writing them out in larger "chunks".
As you can see in your output, the newlines are being written, but since your output is not synchronized, there is no guarantee as to which order the characters from the individual threads are written in.
So, what is written is:
The text from thread #1
The text from thread #2
A newline (we don't know from which thread)
Another newline (we don't know from which thread)
Depending on how exactly JRuby's output streams are implemented, it would be perfectly possible (and perfectly valid, according to the code you have written) to even get an output like this:
SSttaarrtteedd uunniitt ooff wwoorrkk ##21..\n\n
You can manually flush the output stream using the IO#flush method:
def work(n)
puts "Started unit of work ##{n}."
i = 0
100000000.times { i += 1 }
puts "Finished unit of work ##{n}."
$stdout.flush
end
Or alternatively, you can set $stdout to synchronous mode using the IO#sync= method:
$stdout.sync = true
However, please note that this only affects Ruby's own buffers. It does not affect the JRE's, the JVM's, or the OS's buffers.

What's the meaning of `0x0100` for a `Process::Status` value?

I have code:
Process.spawn(RbConfig.ruby, "a ruby file", "arg")
and I wait and check its status by:
Process.wait
$?.success?
Most of the time, it works well. But sometimes, $?.success? is false and $?.to_i is 0x0100. It seems the failed process didn't get a chance to run any code before 0x0100 was returned (I didn't send any signal to the process). I wonder the meaning of 0x0100. I further want to know if Ruby's spawn may fail when the command is all right. Could anyone help?
Here is a quote from the Process::Status class documentation:
Posix systems record information on processes using a 16-bit integer. The lower bits record the process status (stopped, exited, signaled) and the upper bits possibly contain additional information (for example the program's return code in the case of exited processes). Pre Ruby 1.8, these bits were exposed directly to the Ruby program. Ruby now encapsulates these in a Process::Status object. To maximize compatibility, however, these objects retain a bit-oriented interface. In the descriptions that follow, when we talk about the integer value of stat, we're referring to this 16 bit value.
The method Process::Status#to_i returns this stat as a Fixnum.
OK, finally I got the answer: when a ruby process throws an uncaught exception, the process' exit code will be 0x0100. This is from my observation on Ubuntu 14.04 and Ruby 2.2. For example: there's ruby file a.rb, and in another file, say src.rb, there's a code snippet:
Process.spawn(RbConfig.ruby, "a.rb", "arg")
Process.wait
If a.rb throws an uncaught exception, then $?.to_i will be 0x0100. What's more, I also observed that a.rb sometimes didn't get executed before its process failed with 0x0100. So I guess it may have something to do with the Ruby interpreter since I'm sure a.rb is OK.
Anyway, there' no official document mentioning the exact behavior. So my experience is for your reference.

Threading Around a Blocking Read

I'm doing some joystick programming in Ruby on Linux using a Ruby extension which wraps the basic functionality of joystick.h. Getting a joystick event is a blocking read by default, but I don't want that to interrupt the game loop.
Currently I'm hacking around it by making non-blocking calls to the joystick and running that in a really fast loop. That works, but it also makes the script use 100% CPU because I want the joystick events as close to real time as possible.
I'm trying to do something like
input = Thread.new do
while e = joystick.event
#event = e
end
end
main = Thread.new do
while true
sleep 0.1
puts #event
end
end
But even then, the joystick.event call blocks the main thread. Am I totally misunderstanding how Ruby threads work, or how joysticks work on Linux? Or is there a totally different way of approaching this that is better?
I needed to make the read call in the C extension using rb_thread_blocking_region. Works perfectly now!

Reading from a Serial Port with Ruby

I'm trying to use ruby to do a simple read + write operation to a serial port.
This is the code I've got so far. I'm using the serialport gem.
require 'rubygems'
require 'serialport'
ser = SerialPort.new("/dev/ttyACM0", 9600, 8, 1, SerialPort::NONE)
ser.write "ab\r\n"
puts ser.read
But the script hangs when it is run.
I had the problem to. It's because using ser.read tells Ruby to keep reading forever and Ruby never stops reading and thus hangs the script. The solution is to only read a particular amount of characters.
So for example:
ser.readline(5)
To echo what user968243 said, that ser.read call is going to wait for EOF. If your device is not sending EOF, you will wait forever. You can read only a certain number of characters as suggested.
Your device may be ending every response with an end of line character. Try reading up to the next carriage return:
response = ser.readline("\r")
response.chomp!
print "#{response}\n"
I ran into this same problem and found a solution. Use the Ruby IO method readlines.
puts ser.readlines
Maybe your device is waiting for some input. Check this answer and see if it helps: https://stackoverflow.com/a/10534407/1006863
Try setting the #read_timeout value for the serial port. Note that the same thing can be done for a write operation using the #write_timeout value.

How to get data to a running ruby process?

I have a datastore with a cache and a db, simple. The tricksy part is that I want a way to control if the the datastore hits the db in a real-time way. That is to say while the process is running I want to be able to toggle if it's connected to the db or not.
I looked into env variables, but it doesn't seem like those get updated as the process runs. Is there a simple way to get a bit from the command line into the running process, or do I just need to rely on ops being able to drop the db listeners in case of disaster?
Note that this is all being done in vanilla ruby - not ruby on rails.
Thanks!
-Jess
I think you can use named pipes for simple communication:
#pipes.rb:
f = File.open 'mypipe', 'r+'
loop do
begin
s = f.read_nonblock 1
rescue Exception
end
case s
when '0'
puts 'Turn off DB access!'
when '1'
puts 'Turn on DB access!'
end
sleep 1
end
And you can control your db access externally by writing to the named pipe:
jablan-mbp:dev $ echo 101 > mypipe
Which results in:
jablan-mbp:dev $ ruby pipes.rb
Turn on DB access!
Turn off DB access!
Turn on DB access!
A shared-memory strategy might be worth considering. Assuming you're running on a POSIX system, check out mmap for memory-mapped files, and SysVIPC for message queues, semaphores, and shared memory.
Assuming *NIX, have you considered signals? (kill -HUP pid) - http://ruby-doc.org/core/classes/Signal.html

Resources