Rust program using CPython FFI silently getting killed - rust

I have a Rust program which is exiting silently without any trace of the reason in the logs. This would happen after several successful calls to the same method. The last log I see is one after which a FFI call is made. I do not get a log after the return of the FFI call.
use cpython;
use cpython::ObjectProtocol;
use cpython::PyResult;
fn is_complete(&self, check: bool) -> Result<bool, PyModuleError> {
let gil = cpython::Python::acquire_gil();
let py = gil.python();
debug!("Calling complete"); //This is the last log
let res = self
.py_complete
.call_method(py, "complete", (check,), None)
.expect("No method complete on python module")
.extract::<bool>(py).unwrap();
if res {
debug!("Returning true"); //This does not appear
Ok(true)
}
else {
debug!("Returning false"); //This does not appear
Ok(false)
}
}
The Python module does return a value as I had debug logs there as well to confirm.
I have tried using RUST_BACKTRACE=1 but in vain.

Related

How does Tokio's Handle::block_on differ from Runtime::block_on?

How does tokio::runtime::Handle.block_on differ to tokio::runtime::Runtime.block_on? The Handle.block_on causes some code to hang whereas the Runtime.block_on works fine.
This is how I create the Handle. The Runtime is the same minus the last 2 lines.
let runtime = runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.handle() // not needed for Runtime
.clone(); // ---
Then I call a function which with this:
async fn run(){
// calls get data
}
self.runtime.block_on(run())
This is the code where it hangs. When running from the Runtime it works fine, with the Handle it hangs at TcpStream::connect().
async fn get_data(addr: String) -> Result<Data> {
let c = TcpStream::connect(addr.clone()).await?; // hangs here
let t = get_data_from_connect(c).await?;
return Ok(t);
}
I fixed this by making sure that the Runtime object does not go out of scope and get dropped. I was under the impression that only the Handle is needed to keep the runtime alive but only the Runtime object itself can.

Some errors E0425 & E0599 write_fmt

mod loginfo{
use std::io::Error;
use chrono::prelude::*;
use std::io::prelude::*;
use std::fs::OpenOptions;
const LOG_SYS :&'static str = "log.txt";
const LOG_ERR :&'static str = "log_error.txt";
pub fn set_log_error(info: String)->Result<(), String>{
let mut handler = OpenOptions::new().append(true)
.open(LOG_ERR);
if handler.is_err(){
create_file(LOG_ERR.to_owned()).unwrap();
set_log_error(info).unwrap();
}
if let Err(_errno) = handler.write_fmt(
format_args!("{:?}\t{:?} ->[Last OS error({:?})]\n",
Utc::now().to_rfc2822().to_string(), info,
Error::last_os_error()) ){
panic!(
"\nCannot write info log error\t Info\t:{:?}\n",
Error::last_os_error());
}
Ok(())
}
pub fn set_log(info: String)->Result<(), String>{
let mut handler = OpenOptions::new().append(true)
.open(LOG_SYS);
if handler.is_err(){
set_log_error("Cannot write info log".to_owned())
.unwrap();
}
if let Err(_errno) = handler.write_fmt(
format_args!("{:?}\t{:?}\n",
Utc::now().to_rfc2822().to_string(), info)){
set_log_error("Cannot write data log file".to_owned())
.unwrap();
}
Ok(())
}
pub fn create_file(filename : String)->Result<(), String>{
let handler = OpenOptions::new().write(true)
.create(true).open(filename);
if handler.is_err(){
panic!(
"\nCannot create log file\t Info\t:{:?}\n",
Error::last_os_error());
}
Ok(())
}
}
When compiling, I get the following errors, "error[E0599]: no method named write_fmt found for enum std::result::Result<std::fs::File, std::io::Error> in the current scope --> src/loginfo.rs:19:38`"
but despite using the right imports, I still get the same errors. Is this due to a bad implementation of the module?
Thank you in advance for your answers and remarks?
+1 #Masklinn Ok I think I understand it would be easier to just write
pub fn foo_write_log( info: String){
let mut handler = OpenOptions::new().append(true)
.create(true).open(LOG_SYS).expect("Cannot create log");
handler.write_fmt(
format_args!("{:?}\t{:?} ->[Last OS error({:?})]\n",
Utc::now().to_rfc2822().to_string(), info,
Error::last_os_error())).unwrap();
}
but despite using the right imports, I still get the same errors. Is this due to a bad implementation of the module?
Kind-of? If you look at the type specified in the error, handler is a Result<File, Error>. And while io::Write is implemented on File, it's not implemented on Result.
The problem is that while you're checking whether handler.is_err() you never get the file out of it, nor do you ever return in the error case. Normally you'd use something like match or if let or one of the higher-order methods (e.g. Result::map, Result::and_then) in order to handle or propagate the various cases.
And to be honest the entire thing is rather odd and awkward e.g. your functions can fail but they panic instead (you never actually return an Err); if you're going to try and create a file when opening it for writing fails, why not just do that directly[0]; you are manually calling write_fmt and format_args why not just write!; write_fmt already returns an io::Error why do you discard it then ask for it again via Error::last_os_error; etc...
It's also a bit strange to hand-roll your own logger thing when the rust ecosystem already has a bunch of them though you do you; and the naming is also somewhat awkward e.g. I'd expect something called set_X to actually set the X, so to me set_log would be a way to set the file being logged to.
[0] .create(true).append(true) should open the file in append mode if it exists and create it otherwise; not to mention your version has a concurrency issue: if the open-for-append fails you create the file in write mode, but someone else could have created the file -- with content -- between the two calls, in which case you're going to partially overwrite the file

Why does Command.output() execution sometimes return None for status.code()

My rust project uses Command to execute a process.
Sometimes (low frequency) when I run this code the call to status.code() returns None. I am usually using Mac OS Catalina Beta 1, rustc 1.36.0 - but it happens in Travis too (will have to go and find logs of OS/rustc there).
I was treating this as an error but "randomly" it would cause local and travis builds to fail, so now I'm ignoring it - but it would be nice to understand what's causing it.
In failure cases, re-running immediately will cause it to succeed.
let output = Command::new(&command)
.args(command_args)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::piped())
.output()
.chain_err(|| "Error while attempting to spawn command to compile and run flow")?;
match output.status.code() {
Some(0) => Ok("Flow ran to completion".to_string()),
Some(code) => {
error!(
"Process STDERR:\n{}",
String::from_utf8_lossy(&output.stderr)
);
bail!("Exited with status code: {}", code)
}
None => Ok("No return code - ignoring".to_string()),
}
My question is not why this could happen (I know that the docs say "terminated by signal") but why it is happening, as no-one AFAIK is sending a signal to it, I seriously doubt any OOM or other such issues.
Read the manual:
On Unix, this will return None if the process was terminated by a signal; std::os::unix provides an extension trait for extracting the signal and other details from the ExitStatus.
use std::os::unix::process::ExitStatusExt;
use std::process::Command;
fn main() {
let mut child = Command::new("sleep")
.args(&["10"])
.spawn()
.expect("failed to spawn child");
child.kill().expect("failed to kill on child");
let status = child.wait().expect("failed to wait on child");
match status.code() {
None => {
println!("{:?}", status.signal());
()
}
_ => (),
}
}
You could use from_c_int() to have a pretty print of the signal type.

Second gtk_main_quit() not working

In an application, I nest gtk event loops to be able to return a value in a callback.
However, I have some issues with a callback already calling gtk_main_quit() because multiple calls to this function does not seem to exit as many nestings of event loop.
Here is an example of my issue:
extern crate gtk;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::thread;
use gtk::{Button, ButtonExt, ContainerExt, Continue, Inhibit, WidgetExt, Window, WindowType};
fn main() {
gtk::init().unwrap();
let window = Window::new(WindowType::Toplevel);
let quit = Arc::new(AtomicBool::new(false));
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
let button = Button::new_with_label("Click");
let quit2 = quit.clone();
button.connect_clicked(move |_| {
let quit = quit2.clone();
thread::spawn(move || {
quit.store(true, Ordering::Relaxed);
});
println!("Run");
gtk::main();
});
window.add(&button);
window.show_all();
gtk::idle_add(move || {
if quit.load(Ordering::Relaxed) {
println!("Quit");
gtk::main_quit();
gtk::main_quit();
quit.store(false, Ordering::Relaxed);
}
Continue(true)
});
gtk::main();
}
As you can see in the gtk::idle_add call, I call gtk::main_quit() twice, which should exit the application when the button is pressed because gtk::main() was also called twice (one at the end of the main function, the other one in the button clicked callback).
But the application does not exit when I click the button.
The documentation of gtk seems to indicate that this is the expected behaviour:
Makes the innermost invocation of the main loop return when it regains control.
(emphasis is mine)
So, I believe that this does not exit the application because calling gtk::main_quit() twice won't allow the gtk main loop to "regain control".
My question is, what should I do between the two calls to gtk::main_quit() to stop the 2 nestings of event loop?
Short answer: the solution is to replace the second gtk::main_quit with:
gtk::idle_add(|| {
gtk::main_quit();
Continue(false)
});
As before, the first gtk::main_quit() will arrange for the inner main loop to quit. Additionally, the idle handler will be picked up by the outer main loop, causing it to immediately terminate as well.
This can be generalized with a pseudo-recursive function that repeats the process as many times as necessary:
fn deep_main_quit(n: usize) {
if n == 0 {
return;
}
gtk::main_quit();
gtk::idle_add(move || {
deep_main_quit(n - 1);
Continue(false)
});
}
Note that the use of idle_add to continually check for a flag will result in busy-looping, which you almost certainly want to avoid. (On my machine, running your program takes up a full CPU core.) In general, the preferred approach is to wait on a condition variable. But if you just need to tell the GUI thread to do something, as shown in your code, you can just call glib::idle_add from a different thread. The provided callback will be queued and executed in the GUI thread.
With this change, one thread releasing two levels of gtk_main in the GUI thread would look like this:
fn main() {
gtk::init().unwrap();
let window = Window::new(WindowType::Toplevel);
window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(false)
});
let button = Button::new_with_label("Click");
button.connect_clicked(|_| {
thread::spawn(|| {
glib::idle_add(|| {
println!("Quit");
deep_main_quit(2);
Continue(false)
});
});
println!("Run");
gtk::main();
});
window.add(&button);
window.show_all();
gtk::main();
}
I've fixed my issue by manually creating a glib MainLoop and using it instead of nesting calls gtk event loop.

Check if a command is in PATH/executable as process

I want to execute an external program via std::process::Command::spawn. Furthermore I want to know the reason why spawning the process failed: is it because the given program name doesn't exist/is not in PATH or because of some different error?
Example code of what I want to achieve:
match Command::new("rustc").spawn() {
Ok(_) => println!("Was spawned :)"),
Err(e) => {
if /* ??? */ {
println!("`rustc` was not found! Check your PATH!")
} else {
println!("Some strange error occurred :(");
}
},
}
When I try to execute a program that isn't on my system, I get:
Error { repr: Os { code: 2, message: "No such file or directory" } }
But I don't want to rely on that. Is there a way to determine if a program exists in PATH?
You can use e.kind() to find what ErrorKind the error was.
match Command::new("rustc").spawn() {
Ok(_) => println!("Was spawned :)"),
Err(e) => {
if let NotFound = e.kind() {
println!("`rustc` was not found! Check your PATH!")
} else {
println!("Some strange error occurred :(");
}
},
}
Edit: I didn't find any explicit documentation about what error kinds can be returned, so I looked up the source code. It seems the error is returned straight from the OS. The relevant code seems to be in src/libstd/sys/[unix/windows/..]/process.rs. A snippet from the Unix version:
One more edit: On a second thought, I'm not sure if the licenses actually allows posting parts of Rust sources here, so you can see it on github
Which just returns Error::from_raw_os_err(...). The Windows version seemed more complicated, and I couldn't immediately find where it even returns errors from. Either way, it seems you're at the mercy of your operating system regarding that. At least I found the following test in src/libstd/process.rs:
Same as above: github
That seems to guarantee that an ErrorKind::NotFound should be returned at least when the binary is not found. It makes sense to assume that the OS wouldn't give a NotFound error in other cases, but who knows. If you want to be absolutely sure that the program really was not found, you'll have to search the directories in $PATH manually. Something like:
use std::env;
use std::fs;
fn is_program_in_path(program: &str) -> bool {
if let Ok(path) = env::var("PATH") {
for p in path.split(":") {
let p_str = format!("{}/{}", p, program);
if fs::metadata(p_str).is_ok() {
return true;
}
}
}
false
}
fn main() {
let program = "rustca"; // shouldn't be found
if is_program_in_path(program) {
println!("Yes.");
} else {
println!("No.");
}
}

Resources