Skip to content

Commit 190d97b

Browse files
SakuzySakuzy
authored andcommitted
fix(ffi): guard unix sigpipe and add windows pipe/file detect regression
1 parent c1ed812 commit 190d97b

File tree

6 files changed

+66
-0
lines changed

6 files changed

+66
-0
lines changed

.github/workflows/windows-winui.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,36 @@ jobs:
435435
- name: Build CLI core (release, full-cli)
436436
run: cargo build --bin awmkit-core --features full-cli --release --target x86_64-pc-windows-msvc
437437

438+
- name: Windows pipe/file detect regression
439+
shell: pwsh
440+
run: |
441+
$coreExe = "target/x86_64-pc-windows-msvc/release/awmkit-core.exe"
442+
if (-not (Test-Path $coreExe)) {
443+
throw "CLI core binary not found: $coreExe"
444+
}
445+
446+
$ffmpegRuntime = (Resolve-Path "ffmpeg-dist/lib").Path
447+
if (-not (Test-Path $ffmpegRuntime)) {
448+
throw "Missing FFmpeg runtime directory: $ffmpegRuntime"
449+
}
450+
$env:PATH = "$ffmpegRuntime;$env:PATH"
451+
452+
foreach ($input in @("test-audio/sample_wm.wav", "test-audio/multichannel_sample_wm.wav")) {
453+
& $coreExe detect $input
454+
if ($LASTEXITCODE -ne 0) {
455+
throw "Default detect failed for $input (exit=$LASTEXITCODE)"
456+
}
457+
}
458+
459+
$env:AWMKIT_DISABLE_PIPE_IO = "1"
460+
foreach ($input in @("test-audio/sample_wm.wav", "test-audio/multichannel_sample_wm.wav")) {
461+
& $coreExe detect $input
462+
if ($LASTEXITCODE -ne 0) {
463+
throw "File-mode detect failed for $input (exit=$LASTEXITCODE)"
464+
}
465+
}
466+
Remove-Item Env:AWMKIT_DISABLE_PIPE_IO -ErrorAction SilentlyContinue
467+
438468
- name: Build FFI cdylib (release)
439469
run: cargo build --lib --features ffi,app,bundled --release --target x86_64-pc-windows-msvc
440470

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ members = ["."]
200200
[target.'cfg(windows)'.dependencies.windows-dpapi]
201201
version = "0.1"
202202

203+
[target.'cfg(unix)'.dependencies]
204+
libc = "0.2"
205+
203206
[dependencies.dirs]
204207
version = "5.0"
205208
optional = true

docs/en-US/troubleshooting/common-issues.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Fix:
5656
## 7. Pipe I/O Compatibility (`stdin/stdout`)
5757

5858
Note: runtime now prefers `audiowmark` pipe I/O (`-` as input/output). If the local `audiowmark` build does not support this mode, AWMKit automatically falls back to file I/O.
59+
In recent builds, Unix `SIGPIPE` is guarded in the FFI path (Swift/ObjC/.NET), so pipe write failures are converted to normal errors and fallback can proceed instead of crashing the host process.
5960

6061
Force-disable pipe mode for troubleshooting:
6162

@@ -66,3 +67,4 @@ Recommended checks:
6667

6768
1. Run `awmkit status --doctor` to confirm the active `audiowmark` binary and version.
6869
2. If failures disappear when pipe mode is disabled, upgrade or replace the `audiowmark` binary first.
70+
3. `AWMKIT_DISABLE_PIPE_IO=1` changes only the transport strategy, not watermark business semantics.

docs/zh-CN/troubleshooting/common-issues.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
## 7. 管道 I/O 兼容问题(`stdin/stdout`
5757

5858
说明:当前默认优先使用 `audiowmark` 管道 I/O(`-` 作为输入/输出)。若本地 `audiowmark` 构建不支持该模式,运行时会自动回退到文件 I/O。
59+
在新版中,FFI(Swift/ObjC/.NET)主链已对 Unix `SIGPIPE` 做了防护,管道写入失败会转为普通错误并进入回退,而不是导致宿主进程直接闪退。
5960

6061
手动强制关闭管道模式(便于排查):
6162

@@ -66,3 +67,4 @@
6667

6768
1. 先执行 `awmkit status --doctor` 确认当前 `audiowmark` 来源和版本。
6869
2. 若出现异常且关闭 pipe 后恢复,优先升级/替换 `audiowmark` 二进制。
70+
3. `AWMKIT_DISABLE_PIPE_IO=1` 只改变 I/O 通道策略,不改变水印业务语义。

src/ffi.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ const fn u8_to_c_char(byte: u8) -> c_char {
8080
i8::from_ne_bytes([byte])
8181
}
8282

83+
#[cfg(unix)]
84+
fn ensure_sigpipe_ignored_once() {
85+
static ONCE: std::sync::Once = std::sync::Once::new();
86+
ONCE.call_once(|| {
87+
// FFI host process (Swift/ObjC/.NET) should never be terminated by SIGPIPE.
88+
// Convert broken pipe to regular EPIPE so Rust fallback logic can run.
89+
unsafe {
90+
libc::signal(libc::SIGPIPE, libc::SIG_IGN);
91+
}
92+
});
93+
}
94+
95+
#[cfg(not(unix))]
96+
fn ensure_sigpipe_ignored_once() {}
97+
8398
/// Write UTF-8 string into C buffer with two-step size negotiation.
8499
///
85100
/// # Safety
@@ -520,6 +535,7 @@ impl AWMEmbedEvidenceResult {
520535
/// 返回的指针需要通过 `awm_audio_free` 释放
521536
#[no_mangle]
522537
pub extern "C" fn awm_audio_new() -> *mut AWMAudioHandle {
538+
ensure_sigpipe_ignored_once();
523539
match Audio::new() {
524540
Ok(audio) => Box::into_raw(Box::new(AWMAudioHandle { inner: audio })),
525541
Err(_) => ptr::null_mut(),
@@ -535,6 +551,7 @@ pub extern "C" fn awm_audio_new() -> *mut AWMAudioHandle {
535551
pub unsafe extern "C" fn awm_audio_new_with_binary(
536552
binary_path: *const c_char,
537553
) -> *mut AWMAudioHandle {
554+
ensure_sigpipe_ignored_once();
538555
if binary_path.is_null() {
539556
return ptr::null_mut();
540557
}
@@ -2150,3 +2167,14 @@ pub const extern "C" fn awm_channel_layout_channels(layout: AWMChannelLayout) ->
21502167
AWMChannelLayout::Auto => 0,
21512168
}
21522169
}
2170+
2171+
#[cfg(all(test, unix))]
2172+
mod tests {
2173+
use super::ensure_sigpipe_ignored_once;
2174+
2175+
#[test]
2176+
fn test_ensure_sigpipe_ignored_once_is_idempotent() {
2177+
ensure_sigpipe_ignored_once();
2178+
ensure_sigpipe_ignored_once();
2179+
}
2180+
}

0 commit comments

Comments
 (0)