Skip to content

Commit af34189

Browse files
committed
Generalize log-forwarding setup and stop thread on IO errors
When `read_line()` starts returning `Err` the current `if let Ok` condition ignores those, likely causing the `loop` to spin indefinitely while this function keeps returning errors. Note that we don't currently store the join handle for this thread anywhere, so won't see the error surface either (just like how the join handle for the main thread is never checked). Perhaps we should call `log::error!()` to make the user aware that their IO logging has mysteriously terminated.
1 parent 98aef99 commit af34189

File tree

3 files changed

+55
-73
lines changed

3 files changed

+55
-73
lines changed

android-activity/src/game_activity/mod.rs

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
#![cfg(feature = "game-activity")]
22

33
use std::collections::HashMap;
4-
use std::ffi::{CStr, CString};
5-
use std::fs::File;
6-
use std::io::{BufRead, BufReader};
74
use std::marker::PhantomData;
85
use std::ops::Deref;
9-
use std::os::unix::prelude::*;
106
use std::panic::catch_unwind;
7+
use std::ptr;
118
use std::ptr::NonNull;
129
use std::sync::Weak;
1310
use std::sync::{Arc, Mutex, RwLock};
1411
use std::time::Duration;
15-
use std::{ptr, thread};
1612

1713
use libc::c_void;
18-
use log::{error, trace, Level};
14+
use log::{error, trace};
1915

2016
use jni_sys::*;
2117

@@ -29,9 +25,9 @@ use ndk::native_window::NativeWindow;
2925
use crate::error::InternalResult;
3026
use crate::input::{Axis, KeyCharacterMap, KeyCharacterMapBinding};
3127
use crate::jni_utils::{self, CloneJavaVM};
32-
use crate::util::{abort_on_panic, android_log, log_panic};
28+
use crate::util::{abort_on_panic, forward_stdio_to_logcat, log_panic, try_get_path_from_ptr};
3329
use crate::{
34-
util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
30+
AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
3531
};
3632

3733
mod ffi;
@@ -617,21 +613,21 @@ impl AndroidAppInner {
617613
pub fn internal_data_path(&self) -> Option<std::path::PathBuf> {
618614
unsafe {
619615
let app_ptr = self.native_app.as_ptr();
620-
util::try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath)
616+
try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath)
621617
}
622618
}
623619

624620
pub fn external_data_path(&self) -> Option<std::path::PathBuf> {
625621
unsafe {
626622
let app_ptr = self.native_app.as_ptr();
627-
util::try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath)
623+
try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath)
628624
}
629625
}
630626

631627
pub fn obb_path(&self) -> Option<std::path::PathBuf> {
632628
unsafe {
633629
let app_ptr = self.native_app.as_ptr();
634-
util::try_get_path_from_ptr((*(*app_ptr).activity).obbPath)
630+
try_get_path_from_ptr((*(*app_ptr).activity).obbPath)
635631
}
636632
}
637633
}
@@ -913,33 +909,7 @@ extern "Rust" {
913909
#[no_mangle]
914910
pub unsafe extern "C" fn _rust_glue_entry(native_app: *mut ffi::android_app) {
915911
abort_on_panic(|| {
916-
// Maybe make this stdout/stderr redirection an optional / opt-in feature?...
917-
918-
let file = {
919-
let mut logpipe: [RawFd; 2] = Default::default();
920-
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
921-
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
922-
libc::dup2(logpipe[1], libc::STDERR_FILENO);
923-
libc::close(logpipe[1]);
924-
925-
File::from_raw_fd(logpipe[0])
926-
};
927-
928-
thread::spawn(move || {
929-
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
930-
let mut reader = BufReader::new(file);
931-
let mut buffer = String::new();
932-
loop {
933-
buffer.clear();
934-
if let Ok(len) = reader.read_line(&mut buffer) {
935-
if len == 0 {
936-
break;
937-
} else if let Ok(msg) = CString::new(buffer.clone()) {
938-
android_log(Level::Info, tag, &msg);
939-
}
940-
}
941-
}
942-
});
912+
let _join_log_forwarder = forward_stdio_to_logcat();
943913

944914
let jvm = unsafe {
945915
let jvm = (*(*native_app).activity).vm;

android-activity/src/native_activity/glue.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,17 @@
33
//! synchronization between the two threads.
44
55
use std::{
6-
ffi::{CStr, CString},
7-
fs::File,
8-
io::{BufRead, BufReader},
96
ops::Deref,
10-
os::unix::prelude::{FromRawFd, RawFd},
117
panic::catch_unwind,
128
ptr::{self, NonNull},
139
sync::{Arc, Condvar, Mutex, Weak},
1410
};
1511

16-
use log::Level;
1712
use ndk::{configuration::Configuration, input_queue::InputQueue, native_window::NativeWindow};
1813

1914
use crate::{
2015
jni_utils::CloneJavaVM,
21-
util::android_log,
22-
util::{abort_on_panic, log_panic},
16+
util::{abort_on_panic, forward_stdio_to_logcat, log_panic},
2317
ConfigurationRef,
2418
};
2519

@@ -834,32 +828,7 @@ extern "C" fn ANativeActivity_onCreate(
834828
saved_state_size: libc::size_t,
835829
) {
836830
abort_on_panic(|| {
837-
// Maybe make this stdout/stderr redirection an optional / opt-in feature?...
838-
let file = unsafe {
839-
let mut logpipe: [RawFd; 2] = Default::default();
840-
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
841-
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
842-
libc::dup2(logpipe[1], libc::STDERR_FILENO);
843-
libc::close(logpipe[1]);
844-
845-
File::from_raw_fd(logpipe[0])
846-
};
847-
848-
std::thread::spawn(move || {
849-
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
850-
let mut reader = BufReader::new(file);
851-
let mut buffer = String::new();
852-
loop {
853-
buffer.clear();
854-
if let Ok(len) = reader.read_line(&mut buffer) {
855-
if len == 0 {
856-
break;
857-
} else if let Ok(msg) = CString::new(buffer.clone()) {
858-
android_log(Level::Info, tag, &msg);
859-
}
860-
}
861-
}
862-
});
831+
let _join_log_forwarder = forward_stdio_to_logcat();
863832

864833
log::trace!(
865834
"Creating: {:p}, saved_state = {:p}, save_state_size = {}",

android-activity/src/util.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
use log::Level;
1+
use log::{error, Level};
22
use std::{
33
ffi::{CStr, CString},
4-
os::raw::c_char,
4+
fs::File,
5+
io::{BufRead as _, BufReader, Result},
6+
os::{
7+
fd::{FromRawFd as _, RawFd},
8+
raw::c_char,
9+
},
510
};
611

712
pub fn try_get_path_from_ptr(path: *const c_char) -> Option<std::path::PathBuf> {
@@ -31,6 +36,44 @@ pub(crate) fn android_log(level: Level, tag: &CStr, msg: &CStr) {
3136
}
3237
}
3338

39+
pub(crate) fn forward_stdio_to_logcat() -> std::thread::JoinHandle<Result<()>> {
40+
// XXX: make this stdout/stderr redirection an optional / opt-in feature?...
41+
42+
let file = unsafe {
43+
let mut logpipe: [RawFd; 2] = Default::default();
44+
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC);
45+
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
46+
libc::dup2(logpipe[1], libc::STDERR_FILENO);
47+
libc::close(logpipe[1]);
48+
49+
File::from_raw_fd(logpipe[0])
50+
};
51+
52+
std::thread::Builder::new()
53+
.name("stdio-to-logcat".to_string())
54+
.spawn(move || -> Result<()> {
55+
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap();
56+
let mut reader = BufReader::new(file);
57+
let mut buffer = String::new();
58+
loop {
59+
buffer.clear();
60+
let len = match reader.read_line(&mut buffer) {
61+
Ok(len) => len,
62+
Err(e) => {
63+
error!("Logcat forwarder failed to read stdin/stderr: {e:?}");
64+
break Err(e);
65+
}
66+
};
67+
if len == 0 {
68+
break Ok(());
69+
} else if let Ok(msg) = CString::new(buffer.clone()) {
70+
android_log(Level::Info, tag, &msg);
71+
}
72+
}
73+
})
74+
.expect("Failed to start stdout/stderr to logcat forwarder thread")
75+
}
76+
3477
pub(crate) fn log_panic(panic: Box<dyn std::any::Any + Send>) {
3578
let rust_panic = unsafe { CStr::from_bytes_with_nul_unchecked(b"RustPanic\0") };
3679

0 commit comments

Comments
 (0)