Files
SonicForgeStudio/20260815-tasks-g3.md

102 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 20260815 — KẾ HOẠCH THỰC HIỆN G3 (HEALTH / WATCHDOG THÔNG MINH)
Nguồn: `big_tasks.md` — header **G3 — Health / Watchdog thông minh (1-2 tuần)**.
Trạng thái roadmap: G0 ✅, G1 ✅, G2 ✅ (G2.0–G2.6). Branch: `standalone-shm-bridge`.
---
## 1. PHÂN TÍCH G3
### 1.1 Mục tiêu
Watchdog thông minh, cô lập lỗi:
- G3.1 heartbeat riêng: heartbeat bridge tách khỏi logic audio.
- G3.2 phân loại stall: phân biệt hang/crash/stall để xử lý đúng mức.
- G3.3 SEH quanh process từng channel: 1 plugin crash → chỉ mute channel đó, không sập cả bridge.
### 1.2 Hiện trạng (đã verify trong code)
| Thành phần | File | Hiện trạng liên quan G3 |
|---|---|---|
| SHM layout | `native_bridge/include/SharedMemoryIPC.h` + `src-tauri/src/shm.rs` (phải khớp, assert size) | `bridgeWriteIndex` @4 (mỗi block render), `attachInFlight` @11160 (G0.3). Tổng 11164B. Không có heartbeat riêng. |
| Watchdog Rust | `src-tauri/src/lib.rs` pump thread | Liveness = `write_index()` đổi trong 3s; nếu `attach_in_flight` → deadline 15s; stall → kill + spawn lại 1 lần rồi `bridge-down`. |
| Bridge C++ | `native_bridge/src/main.cpp` | Main loop = audio loop (render + drain control); mọi attach/load chạy trên UiThread (`uiWorker.post`); heartbeat không tồn tại. |
### 1.3 Khoảng trống (gap)
1. **Liveness gắn với audio loop** — writeIndex chỉ tăng khi audio loop còn render; attach job dài (Nexus >3s, G0.3) làm audio loop nghẽn → watchdog tưởng bridge chết → phải kéo deadline lên 15s (đánh đổi: bridge thật chết cũng chờ 15s).
2. **Không phân loại được stall** — hang (audio loop kẹt, process còn sống) vs crash (process chết) xử lý giống nhau.
3. **1 plugin chết kéo cả bridge** — chưa có SEH/process con từng channel (G3.3).
### 1.4 Nguyên tắc thiết kế (đề xuất)
- **Heartbeat là nguồn liveness duy nhất** (G3.1): bridge chạy thread riêng tăng `heartbeat` ~10Hz trong SHM, độc lập audio loop → attach dài không còn ảnh hưởng deadline; bỏ ngoại lệ `attach_in_flight` trong watchdog.
- **Audio stall ≠ bridge chết** (G3.1 lót đường G3.2): heartbeat còn mà writeIndex đứng → log "audio stall (heartbeat OK)" — không restart.
- **Field append-only** — giữ offset cũ (như G0.3): thêm `heartbeat` sau `attachInFlight` @11164.
- **Không đụng audio loop** — heartbeat thread riêng, Sleep(100ms), không lock.
---
## 2. PHÂN RÃ TASKS + ESTIMATE
| ID | Task | File đích | Nội dung | Estimate |
|---|---|---|---|---|
| G3.1 | Heartbeat riêng | `SharedMemoryIPC.h`, `shm.rs`, `main.cpp`, `lib.rs`, `shm_selfcheck.cpp` | SHM thêm `heartbeat:u32` (append @11164, size 11168); bridge thread riêng tăng 100ms; Rust pump dùng heartbeat cho liveness (deadline 3s, bỏ ngoại lệ attach); writeIndex stall khi heartbeat còn → log warning, không restart. | 1 ngày |
| G3.2 | Phân loại stall | `lib.rs`, `main.cpp` | hang vs crash vs stall; xử lý đúng mức (restart / mute / chỉ log). | 2 ngày |
| G3.3 | SEH từng channel | `main.cpp` | 1 plugin crash → chỉ mute channel, không sập bridge. | 3 ngày |
| | **Tổng** | | | **6 ngày (~1.5 tuần — khớp roadmap)** |
### Phụ thuộc
`G3.2 → G3.1` (cần heartbeat) · `G3.3 → G3.2` (cần phân loại).
### Thứ tự triển khai khuyến nghị
G3.1 → G3.2 → G3.3.
---
## 3. KẾ HOẠCH THỰC HIỆN (CÀI ĐẶT → KIỂM TRA → BẮT BUGS → FIX → REPORT)
### G3.1 — Heartbeat riêng
- **Cài đặt**:
- `SharedMemoryIPC.h`: thêm `volatile uint32_t heartbeat;` sau `attachInFlight` (append-only, offset 11164).
- `shm.rs`: thêm field `heartbeat: u32` + accessor `heartbeat()` + sửa assert size 11168.
- `main.cpp`: trước main loop, spawn thread `[shmIPC]{ for(;;){ Sleep(100); shmIPC->heartbeat++; } }` detach.
- `lib.rs` pump: liveness = heartbeat (đổi trong 3s → restart 1 lần → `bridge-down`); writeIndex chỉ dùng emit audio; heartbeat còn + writeIndex đứng ≥3s → log `[tauri] audio stall (heartbeat OK)`, không restart. Bỏ `attach_in_flight` khỏi deadline.
- `shm_selfcheck.cpp`: assert size 11168.
- **Kiểm tra**: build bridge (build_bridge.ps1) + `cargo build --release` (src-tauri) → copy exe vào install/ → deploy → probe SHM: heartbeat tăng ~10Hz; kill bridge → heartbeat đứng → watchdog restart (log) → heartbeat tăng lại; regression G1.5 subset + G2.5 stress vẫn PASS.
- **Bắt bugs**: attach dài không còn risk restart (bỏ ngoại lệ 15s); heartbeat sai offset (assert size bắt).
- **Fix + report**: mục 5.
- **📌 Lưu ý & quy trình**:
- **Windows** (sửa code): patch bytes cho file CRLF (SharedMemoryIPC.h, main.cpp), text thường cho lib.rs (LF). Build bridge + cargo release. Deploy: kill 3 proc → copy `target/release/sonicforge-daw.exe` + `native_bridge/build/Release/daw_vst_bridge.exe` vào `install/` → start → PIDs mới. Chạy probe heartbeat + stress. Commit: `G3.1: dedicated bridge heartbeat — watchdog liveness independent of audio loop`.
- **Linux** (ghi chú): pull + review; không chạy được (cần SHM Windows); cập nhật mục 5.
---
## 4. RỦI RO
| Rủi ro | Ảnh hưởng | Giảm thiểu |
|---|---|---|
| Heartbeat thread bị kill cùng process | Vẫn restart như cũ (không tệ hơn) | OK — heartbeat chết = process chết = restart đúng. |
| Heartbeat sai offset → đè field khác | Hỏng SHM | Assert size 11168 ở cả 2 phía (shm.rs test + shm_selfcheck.cpp). |
| Bỏ ngoại lệ attach 15s → attach dài bị restart | Mất GUI giữa attach | Heartbeat thread độc lập → attach không làm heartbeat đứng; chỉ restart khi process thật chết. |
| Audio loop kẹt vĩnh viễn (plugin hang) nhưng heartbeat còn | Không restart → câm vĩnh viễn | G3.2 phân loại + xử lý (log cảnh báo ở G3.1, có dấu hiệu trong log). |
---
## 5. BÁO CÁO KẾT QUẢ (điền sau khi thực hiện)
| ID | Cài đặt | Kiểm tra | Bugs bắt được | Fix | Trạng thái |
|---|---|---|---|---|---|
| G3.1 | Heartbeat @11164 (size 11168), thread riêng Sleep(100) tăng `heartbeat`; pump liveness = heartbeat (deadline 3s, restart 1 lần); bỏ ngoại lệ attach 15s; writeIndex chỉ emit audio + stall log. | Probe G3.1 PASS (hb 10Hz; kill bridge → restart 3.8s, log stalled); G2.5 stress PASS; G1.5 stress PASS (fix test script đọc log 1 lần/poll); G0.4 probe PASS. | G1.5 fail trước đó là test-env: test script gọi log_lines() mỗi index (140k dòng) + window VST cũ dính → OPEN_GUI dedupe silent; flood 896 dòng allNotesOff pre-existing. | Không cần fix thêm. | ✅ |
| G3.2 | lib.rs: khi heartbeat stalled 3s + restart_attempts==0, lấy pid từ BridgeProcess state → process_alive(pid) (OpenProcess + GetExitCodeProcess, windows-sys) → log `HUNG (pid N alive, heartbeat stalled)` / `CRASHED (pid N gone)` / `UNKNOWN (no tracked pid)`; Cargo.toml thêm feature Win32_System_Threading. | Probe CRASH PASS (kill bridge → log `CRASHED (pid 4432 gone)` sau ~10s → restart); Probe HUNG PASS (NtSuspendProcess → log `HUNG (pid 6232 alive, heartbeat stalled)` → TerminateProcess → restart). Regression G1.5 + G3.1 PASS. | Không có. | Không cần. | ✅ |
| G3.3 | NativeInstrumentEngine.h/cpp: `crashedCh_[16]` + markCrashed/isCrashed; `channelQuiet` trả true khi crashed (mute dispatch+render); SEH helper `SafeProcessChannel` (`__try/__except EXCEPTION_EXECUTE_HANDLER`, tách khỏi hàm có C++ unwinding để tránh C2712); renderAll nếu false → `crashedCh_[ch]=true` + log muted until reload + continue (không cộng output); assign() reset flag; /EHa đã bật sẵn. CMakeLists thêm target `seh_channel_test`. | Bridge build PASS; Cargo build PASS; `seh_channel_test.exe` PASS (stub ném access violation → SafeProcessChannel false, block zero, manager isCrashed set, renderAll no-op, process sống); regression G1.5 + G3.1 PASS. | Không có. | Không cần. | ✅ |
### Nhật ký thực hiện
### G3.1 — Hoàn tất (2026-08-15)
- SHM: `heartbeat` append @11164, tổng 11168B (SharedMemoryIPC.h + shm.rs field/accessor/assert + shm_selfcheck.cpp assert).
- main.cpp: thread riêng `Sleep(100)` → `shmIPC->heartbeat++` (độc lập audio loop).
- lib.rs pump: liveness = heartbeat, deadline 3s → kill+spawn 1 lần/bridge-down; bỏ attach_deadline 15s; writeIndex chỉ emit audio; heartbeat còn + writeIndex đứng → log `[tauri] audio stall (heartbeat OK)`, không restart.
- Verify: probe heartbeat PASS (tăng ~10Hz, kill → watchdog restart 3.8s, spawn.log `bridge heartbeat stalled 3s — restart attempt #1`); G2.5 stress PASS; G1.5 stress PASS; G0.4 probe PASS.
- G1.5 fail trước đó = test-env, không phải regression G3.1: (1) test script `wait_log` đọc cả file log mỗi index truy cập (140k+ dòng) → poll chậm >10s → STOP/OPEN_GUI timeout giả; fix: đọc 1 lần/poll. (2) window VST cũ (0x870D76, 0x2F0754) giữ view giữa các lần chạy → OPEN_GUI dedupe silent (handleOpenGui `hasAttachedView()` return không log). (3) flood 896 dòng `[midi] OFF` = allNotesOff() log từng pitch (7 ch × 128) — pre-existing, STOP log vẫn 0.15s.
### G3.2 + G3.3 — Hoàn tất (2026-08-15)
- G3.2: pump watchdog phân loại stall — khi heartbeat stalled 3s + chưa restart lần nào, lấy pid từ BridgeProcess state, kiểm tra process còn sống (OpenProcess/GetExitCodeProcess) → log HUNG (pid alive) / CRASHED (pid gone) / UNKNOWN (no tracked pid). Verify: CRASH test (kill bridge → `CRASHED (pid 4432 gone)` → restart bridge mới); HUNG test (NtSuspendProcess giữ process sống nhưng heartbeat đứng → `HUNG (pid 6232 alive, heartbeat stalled)` → TerminateProcess → restart). Cả 2 PASS.
- G3.3: SEH per-channel — `SafeProcessChannel` bọc processAudioBlock trong `__try/__except(EXCEPTION_EXECUTE_HANDLER)`; plugin fault → mute channel (crashedCh_), log `[NativeBridge] G3.3: channel N plugin crashed in processAudioBlock (SEH) — muted until reload`, các channel khác tiếp tục render, bridge không sập; assign() (reload instance) reset mute. Helper tách riêng vì SEH không hợp lệ trong hàm có C++ unwinding (C2712). Verify: `seh_channel_test.exe` (stub processAudioBlock gây access violation → SafeProcessChannel trả false, crash bị nuốt, crashed flag set, renderAll no-op) PASS; bridge + cargo build PASS.
- Regression: G1.5 stress 1 iter PASS (đã fix test script đọc log 1 lần/poll + đóng GUI VST dính giữa các lần chạy); G3.1 heartbeat probe PASS (hb ~10Hz, kill → restart 3.7s). G2.5 không chạy lại (không đụng audio loop).
- Commit `0b8e80f` (6 files: lib.rs, Cargo.toml, NativeInstrumentEngine.h/.cpp, CMakeLists.txt, tests/seh_channel_test.cpp). `install/*.exe` không commit; `native_bridge/debug/test_g15_stress.py` (fix helper) commit riêng `85273a6`.
- G4 (sandbox plugin): tuỳ chọn, 2-3 tháng sau.