How does asynchronous Ruby work in Vim? - ruby

If you compile a recent version of Vim with +ruby, you can use the :ruby command inside Vim.
What's happening 'under the hood' when I run some asynchronous Ruby code?
For example:
:ruby <<EOS
print 'hello'
Thread.new do
sleep 1
print 'world'
end
EOS
# hello
:ruby print 'foo'
# world
# foo
This immediately prints 'hello', as expected. However, 'world' doesn't print until I run another :ruby command. Does Vim only support one thread, and push new threads onto some sort of queue for run on the next :ruby command?
I've tried looking through Vim's source for this in src/if_ruby.c, but my Ruby C-Extension reading skills aren't the greatest.
I'm asking, because I'd like to write some Ruby that polls every few seconds and updates a Vim window.

Vim itself is single-threaded. But there are some exceptions or workarounds:
Python threads are working, though not on ARM for some reason. I can’t say though I can predict what would happen if you run vim.* method from non-main thread. I saw it used in some plugins, but without vim.* in threads.
Python multiprocessing module is working perfectly (though you will want to disable all vim signal handlers). I personally use this solution in my aurum plugin. I guess ruby equivalent will work, but AFAIR it is just a fork() call with simple bytes pipe as the only communication, nothing so complicated as multiprocessing.Pipe (pipe that passes a limited set of python objects), multiprocessing.Queue (wrapper around a pipe that implements objects queue), multiprocessing.Value (shared memory storing fixed-sized values with object interface) or multiprocessing.Lock (dunno what it is, but name says for itself about the purpose). At least not in standard library or core.
AFAIK some older ruby versions used green threads thus (from the OS point of view) were single-threaded while newer ruby is now using POSIX threads. You can try to update, maybe this will work. Though you’d better choose something other as the test (like modifying some variable in a separate thread), not a thing that calls vim. Any current python version you can find on users systems is using POSIX threads, this may be the root of the reason why ruby threads do not work while python ones do.

Related

Ruby open interactive sub process within the shell

I want to use ruby and in one moment open another program (cfdisk) and let the user interact with it, then return to my code
I can use
exec "cfdisk; ruby another.rb"
but that is very hacky and certainly not the right thing to do
I know about Open3 but i dont know how to use it properly for my usecase. I know that when I use capture3 on irb on Windows it says:
["Running under a virtual console, enabling 256/true-color support\n", "Screen size 28640x499 is not supported; limit 500x500.\nCheck the TERM environment variable.\n", #]
In Arch it opens it but the graphical output isnt present, the keystrokes are registered, they work (for example mc exits with F10) but the window isnt there, just blinking cursor.
parted exits immediately
Tried popen3 but I had no luck with it either
What is wrong with system for this case?
The exec ruby command replaces the running process, so it will not return to your code.
The Open3 library is used when you want to capture stdout and stderr.
Isn't this what you are looking for?
puts "here"
system "cfdisk"
puts 'there'
If you have some screen related issues, this is another issue that you might be able to resolve with different TERM value in the environment variable.

ruby tk and fork with exit cause an abort: "[xcb] Unknown sequence number while processing queue"

Working with ruby 1.9.3 with tk and I've found that I can't do a fork inside the mainloop that calls "exit" - I need to get out of the fork by doing something like exec().
Example program:
#!/usr/bin/ruby
require 'tk'
root = TkRoot.new
def doit
unless fork
puts "Inside the fork"
exit # This is where it falls apart
end
end
TkButton.new(root) {
text 'go'
command proc { doit }
}.pack
Tk.mainloop()
Press the button and we properly fork, but when fork calls 'exit' then we get:
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
ruby: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
ruby: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
I've discovered that I can hack a workaround by using something like "exec('echo')" instead of the exit, but that's just plain silly. What's going on that exit doesn't work? Is there some way to call XInitThreads from ruby (this isn't jruby or fxruby) that I can use and will that help?
From my research so far, it seems that ruby and Tk are pretty broken the moment you introduce threads or forking, but I have not been able to find a clean way to deal with this problem.
This problem exists with any Tcl built with thread support.
You will have to restructure your program so that the Tcl/Tk processes are separate entities and do not get forked.
The gui will need to send any input commands to the appropriate ruby thread and the threads will need to send data to the gui to be displayed.

How do I wrap a compiled command line tool for use in Ruby?

I have compiled and tested an open-source command line SIP client for my machine which we can assume has the same architecture as all other machines in our shop. By this I mean that I have successfully passed a compiled binary to others in the shop and they were able to use them.
The tool has a fairly esoteric invocation, a simple bash script piped to it prior to execution as follows:
(sleep 3; echo "# 1"; sleep 3; echo h) | pjsua sip:somephonenumber#ip --flag_1 val --flag_2 val
Note that the leading bash script is an essential part of the functioning of the program and that the line itself seems to be the best practice for use.
In the framing of my problem I am considering the following:
I don't think I can expect very many others in the shop to
compile the binary for themselves
Having a common system architecture in the shop it is reasonable to think that a repo can house the most up-to-date version
Having a way to invoke the tool using Ruby would be the most useful and the most accessible to the
most people.
The leading bash script being passed needs to be wholly extensible. These signify modifiable "scenarios" e.g. in this case:
Call
Wait three seconds
Press 1
Wait three seconds
Hang up
There may be as many as a dozen flags. Possibly a configuration file.
Is it a reasonable practice to create a gem that carries at its core a command line tool that has been previously compiled?
It seems reasonable to create a gem that uses a command line tool. The only thing I'd say is to check that the command is available using system('which psjua') and raising an informative error if it hasn't been installed.
So it seems like the vocabulary I was missing is extension. Here is a great stack discussion on wrapping up a Ruby C extension in a Ruby Gem.
Here is a link to the Gem Guides on creating Gems with Extensions.
Apparently not only is it done but there are sets of best practices around its use.

Ruby: getting disk usage information on Linux from /proc (or some other way that is non-blocking and doesn't spawn a new process)

Context:
I'm on Linux. I am writing a disk usage extension for Sensu. Extensions must be non-blocking, because they are included in the agent's main code loop. They also must be very lightweight, because they may be triggered as often as once every 10 seconds, or even down to once per second.
So I cannot spawn a new executable to gather disk usage information. From within Ruby, I can only do stuff like File.open() on /proc and /sys and so on, read the content, parse it, file.close(), then print the result. Repeat.
I've found the sys-filesystem gem, which appears to have everything I need. But I'd rather not force extensions to depend on gems, if it can be avoided. I'll use the gem if it turns out to be the best way, but is there a good alternative? Something that doesn't require a ton of coding?
The information can be accessed via the system call statfs
http://man7.org/linux/man-pages/man2/statfs.2.html
I can see there is a ruby interface to this here:
http://ruby-doc.org/core-trunk/File/Statfs.html

Ruby MRI 1.8.7: Spawn process directly in code?

I understand that there are various ways to spawn new processes in Ruby (e.g. backticks, system(), exec(), etc...)
However, is it possible to spawn a new process directly with code passed as a block?
Just like forks (fork {... block ...}).
My problem is that I don't want to use forks as I don't want to copy all the memory (problematic in my case because of writing), I want to spawn a "fresh" project without calling an external ruby file.
fork is the only way to do this. However, on Linux at least, and I think on OSX too, fork is implemented as copy on write, meaning that until an area of memory is written to in the child process, it points directly to the area of the old parent process. So, no problem.
Edit: Nevermind. The above is wrong. Here's what I would do:
code = "puts 'hi'"
result = nil
popen("ruby") do |pipe|
pipe.puts code
pipe.close_write
result = pipe.read
end

Resources