Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/refactor-ios-stdout-stderr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri": patch:bug
"tauri-macros": patch:bug
---

Fix iOS deadlock when running on the simulator from Xcode by properly piping stdout/stderr messages through the Xcode console and OSLog.
83 changes: 82 additions & 1 deletion crates/tauri/mobile/ios-api/Sources/Tauri/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,86 @@

import os.log
import UIKit
import Foundation

class StdoutRedirector {
private var originalStdout: Int32 = -1
private var originalStderr: Int32 = -1
private var stdoutPipe: [Int32] = [-1, -1]
private var stderrPipe: [Int32] = [-1, -1]
private var stdoutReadSource: DispatchSourceRead?
private var stderrReadSource: DispatchSourceRead?

func start() {
originalStdout = dup(STDOUT_FILENO)
originalStderr = dup(STDERR_FILENO)

guard Darwin.pipe(&stdoutPipe) == 0,
Darwin.pipe(&stderrPipe) == 0 else {
Logger.error("Failed to create stdout/stderr pipes")
return
}

dup2(stdoutPipe[1], STDOUT_FILENO)
dup2(stderrPipe[1], STDERR_FILENO)
close(stdoutPipe[1])
close(stderrPipe[1])

stdoutReadSource = createReader(
readPipe: stdoutPipe[0],
writeToOriginal: originalStdout,
label: "stdout"
)

stderrReadSource = createReader(
readPipe: stderrPipe[0],
writeToOriginal: originalStderr,
label: "stderr"
)
}

private func createReader(
readPipe: Int32,
writeToOriginal: Int32,
label: String
) -> DispatchSourceRead {
let source = DispatchSource.makeReadSource(
fileDescriptor: readPipe,
queue: .global(qos: .utility)
)

source.setEventHandler {
let bufferSize = 4096
var buffer = [UInt8](repeating: 0, count: bufferSize)
let bytesRead = read(readPipe, &buffer, bufferSize)

if bytesRead > 0 {
let output = String(
bytes: buffer[0..<bytesRead],
encoding: .utf8
) ?? ""

let trimmed = output.trimmingCharacters(in: .newlines)
if !trimmed.isEmpty {
// we're sending stderr to oslog, so we need to avoid recursive calls
if trimmed.hasPrefix("OSLOG-") {
// make sure the system can parse the oslogs
write(writeToOriginal, &buffer, bytesRead)
} else {
Logger.info("[\(label)] \(trimmed)")
}
}
}
}

source.setCancelHandler {
close(readPipe)
}

source.resume()
return source
}
}

/// Wrapper class for os_log function
public class Logger {
Expand All @@ -21,7 +101,7 @@ public class Logger {
}
}

static func log(_ items: Any..., category: String, type: OSLogType) {
static func log(_ items: [Any], category: String, type: OSLogType) {
if Logger.enabled {
var message = ""
let last = items.count - 1
Expand All @@ -31,6 +111,7 @@ public class Logger {
message += " "
}
}

let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "-", category: category)
os_log("%{public}@", log: log, type: type, String(message.prefix(4068)))
}
Expand Down
8 changes: 8 additions & 0 deletions crates/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ extension PluginManager: NSCopying {
}
}

private var stdoutRedirector: StdoutRedirector?

@_cdecl("log_stdout")
func logStdout() {
stdoutRedirector = StdoutRedirector()
stdoutRedirector!.start()
}

@_cdecl("register_plugin")
func registerPlugin(name: SRString, plugin: NSObject, config: SRString, webview: WKWebView?) {
PluginManager.shared.load(
Expand Down
1 change: 1 addition & 0 deletions crates/tauri/src/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ swift!(pub fn register_plugin(
webview: *const c_void
));
swift!(pub fn on_webview_created(webview: *const c_void, controller: *const c_void));
swift!(pub fn log_stdout());
32 changes: 2 additions & 30 deletions crates/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,38 +248,10 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg(target_os = "ios")]
#[doc(hidden)]
pub fn log_stdout() {
use std::{
ffi::CString,
fs::File,
io::{BufRead, BufReader},
os::unix::prelude::*,
thread,
};

let mut logpipe: [RawFd; 2] = Default::default();
#[cfg(target_os = "ios")]
unsafe {
libc::pipe(logpipe.as_mut_ptr());
libc::dup2(logpipe[1], libc::STDOUT_FILENO);
libc::dup2(logpipe[1], libc::STDERR_FILENO);
crate::ios::log_stdout();
}
thread::spawn(move || unsafe {
let file = File::from_raw_fd(logpipe[0]);
let mut reader = BufReader::new(file);
let mut buffer = String::new();
loop {
buffer.clear();
if let Ok(len) = reader.read_line(&mut buffer) {
if len == 0 {
break;
} else if let Ok(msg) = CString::new(buffer.as_bytes())
.map_err(|_| ())
.and_then(|c| c.into_string().map_err(|_| ()))
{
log::info!("{}", msg);
}
}
}
});
}

/// The user event type.
Expand Down