What is the difference between exit and abort? - ruby

The abort documentation says abort will
Terminate execution immediately, effectively by calling Kernel.exit(false).
What exactly does "immediately" mean? What is the difference between abort and exit with non-true status?

"Exit, Exit! Abort, Raiseā€¦Get Me Outta Here!" describes everything you'd want to know I think.
In short:
Kernel.exit(code) "exits" the script immediately and returns the code to the OS, however, just before doing it, it calls any registered at_exit handler that your code could have registered.
Kernel.exit!(code) does the same, but exits immediatelly, no at_exit handlers called.
Kernel.abort(message) takes a message that will be printed to STDERR just before exiting with a failure code=1.
Different values of exit codes are barely suitable for detecting problems and debugging the code. However, they are very simple to use and making the parent process read them is almost trivial. Hence, exit and exit!.
If you can spend more time and make the error checking more robust, you'll need some serious error messages, not just codes. Traditionally, you can print them to STDERR if it exists. You can print manually to STDERR via normal puts, but exit-codes will still be used at the lowest level.
Printing to STDERR does not mark your job automatically as failed, so, abort was created to allow you to write and quit easily. A default exit code of 1 is enough to mark the FAIL condition, as it's assumed that all the real contextual information will be included in the error messages provided by you.
Also note that any unhanded exceptions, such as raise "wtf" with no rescue anywhere, actually behave as if calling Kernel.abort: they print to STDERR and use exitcode=1.
You said exit(false) but the exit! documentation says that the parameter is status code to be used.
I've just checked that on Windows and Ruby 1.9.3:
exit 0 # quits with code: 0
exit 1 # quits with code: 1
exit false # quits with code: 1
exit true # quits with code: 0
which really surprises me, as I'd assume that false would be coerced to 0 in the traditional C way. So, maybe you should rather be using integers like 0 or 1 to be perfectly clear about what code will be used.

Related

How to write to and read from the same named pipe in a single ruby script?

edit: I think I fixed the issue: https://gist.github.com/niuage/c0637b8dd10549a12b6a223dbd5f158a
I might have been missing the Process.wait, hence creating a lot of zombie processes.
I have a piece of code that's working most of the time, but "locks" itself after a while, probably because of a race condition.
My code
pipe = "goals.png"
(1..100).each do |time|
fork do
# This runs ffmpeg, and gets it to write 1 frame of a video to the pipe 'goals.png'
print "before ffmpeg"
`#{ffmpeg(time, score_crop_options, pipe)}`
exit
end
# Reads from 'pipe'
print "before read"
blob = File.read(pipe)
image = Rocket::Image.from_blob(blob)
# do stuff with image
end
Things to note:
#{ffmpeg(time, pipe)} writes to pipe, and is blocking until something reads from pipe
File.read(pipe) is blocking until something writes to pipe
My issue
edit: when the script is locked, and I try to read the pipe from another script, I get zsh: fork failed: resource temporarily unavailable. That's probably a good clue...
Most of the time, File.read(pipe) gets executed before the code in fork, so it works great, but after a little while the script just stops: it prints "before ffmpeg" and never gets to "before read"...
First, should I use threads instead of fork? And can I control the order the 2 statements (read and write) get run, to avoid a race condition? Or maybe it's not even about the race condition and I'm missing something?
The issue wasn't caused by a race condition, but too many zombie processes, since I wasn't calling Process.wait
The parent process should use Process.wait to collect the termination statuses of its children or use Process.detach to register disinterest in their status; otherwise, the operating system may accumulate zombie processes.
That's why I was getting zsh: fork failed: resource temporarily unavailable when trying to read from the pipe from another script probably.
Here's something that works:
(1..100) do
if fork
# Parent
image = read_image(pipe)
# do stuff with image
Process.wait # I think that's what was missing previously
else
# Child
Open3.popen3(command(time, score_crop_options, pipe)) do |stdin, stdout, stderr, wait_thr|
# stuff
end
exit!(0)
end
end

ensure block of code runs when program exits

I want to make sure that a peice of code runs when the ruby program ends. I used the following ways but they do not work in some situations.
def a_method
# do some work
ensure
# code that must be run when method ends and if program exits when it still in this method.
end
def a_method
at_exit{
# run code that needs to be run when process exists
}
# do some work
ensure
# do code that needs to be run when method ends
end
those two methods works very well when the process is killed with a signal other than kill -9 'although I didn't tried all the signals'.
So is there a way to make sure that code runs even if the process is killed with this signal?
Signal 9 is non-catchable, non-ignorable kill, by design. Your at_exit will not run because the operating system will simply terminate any process that receives this signal, not giving it any chance to do any extra work.

Return value of kill?

CHILD=$!
sleep 2;
if kill -KILL ${CHILD} 2>/dev/null; then
echo "*** timed out after 2 seconds"
KILLED=yes
else
echo "terminated within time limit"
killed=no
fi
wait ${CHILD}
I'm a little confused on what is going on here and how the 'if' executes. My understanding is that this checks if killing a child process was successful then setting the KILLED variable to yes and printing out a message. Otherwise set KILLED to no and print a different message.
I thought that when a command is successful it returns a 0? If that's true wouldn't the 'if' interpret that as false and execute the else?
I'm also confused on what the messages printed out mean. I think I'm not understanding the difference between 'timed out' and 'terminated'. (i.e. I would assume the 'terminated' message would go where the 'timed out' message is, and vice versa).
Thanks!
It's a little counter-intuitive if you're coming from a language like C or Java, but in bash, a 0 exit status is actually interpreted as true. Here's an excerpt from the manual:
The most compact syntax of the if command is:
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
The TEST-COMMAND list is executed, and if its return status is zero, the CONSEQUENT-COMMANDS list is executed. The return status is the exit status of the last command executed, or zero if no condition tested true.
This is pretty useful, because there are usually a lot of ways a process can fail (giving different non-zero statuses), but only one way for everything to work correctly (zero status).
I think your other questions answer themselves after that :-)
kill returns an exit code of 0 (true) if the process still existed it and was killed. In this case, KILLED=yes.
kill returns an exit code of 1 (false) if the kill failed, probably because the process was no longer running. In this case, KILLED=no.

using $? when running several commands in parallel in bash

I'm creating a startup/shutdown script for WebSEAL. It's written to allow several instances to be stopped/started in parallel. The only problem is verifying that it completed without issue. With other infrastructures, I could simply grep for a particular keyword in the output (which I redirect to a log file), but WebSEAL does not give any success/error message.
Instead, I thought to use the $? to throw the exit status into a dynamic variable that will be checked after the startups have occured (during log consolidation).
Here is the code that starts/stops and then creates the variable
${PDCOMMAND} >> ${LOGDIR}/${APP}.txt 2>&1 &
let return_${APP}=$?
PDCOMMAND is a valid startup/stop command: aka pdweb start my_instance
APP is the name of the instance: aka my_instance
The goal is that return_${APP} (return_my_instance) will have a value of 0 (success) or 1 (failure) when I check it at a later point in the script.
Are there problems using the $? for a command that may have not technically completed at the time that it was set, or does it set it upon completion of that? So let's say I have 3 instances
instance_1, instance_2, instance_3
if I ran the following:
pdweb start instance1 &
let return_instance_1 = $?
pdweb start instance2 &
let return_instance_2 = $?
pdweb start instance_3 &
let_return_instance_3 = $?
would return_instance_[1|2|3] have the correct values if they started in unequal amounts of time? If instance_3 starts before instance_1, for example, will it still output the result of instance_3 to return_instance_3?
Basically, I'm trying to figure out how the command line treats an asynchronous request in regards to the exit status.
Thanks in advance
No; the exit status code is only available when the command finishes. (That's why it's called "exit status".) If you successfully spawned a service and it is up and running, it does not yet have an exit status.
If I am able to correctly guess what you are trying to accomplish, you could reap the values of $! after starting each instance, wait for a "reasonable" time (a few seconds?) and check that the processes you started are still running. If they have terminated, there was a problem.

Exit a Ruby script with status 0 on an exception

I'm currently wrapping scripts with begin; rescue; end. Which works, but is annoying to un/comment at two different places and so on. Is there something like error_reporting(0); in PHP, but applied to the exit code and STDERR output?
You could try trapping the EXIT signal:
The special signal name "EXIT" or signal number zero will be invoked just prior to program termination.
Something like this should guarantee that your script always returns zero to the operating system:
Signal.trap('EXIT') { exit 0 }
For example, this script:
Signal.trap('EXIT') { exit 0 }
exit 1
actually returns zero to the OS despite triggering script's termination with exit 1.
Actually I did not understand what you are asking for. Here is the answer as I understand. But would be useful if you provide some more detail.
def
some code...
rescue
abort
end

Resources