Files

47 KiB
Raw Permalink Blame History

20260815 — KẾ HOẠCH THỰC HIỆN G2 (PERSISTENCE RESTART)

Nguồn: big_tasks.md — header G2 — Persistence restart (2-3 tuần). Trạng thái roadmap: G0 ✅, G1 ✅ (G1.1–G1.5), G2 ✅ (G2.0–G2.6), G3 ✅ (G3.1–G3.3), G4 ✅ (G4.1–G4.3 sandbox PoC). Branch: standalone-shm-bridge. File này là plan G2; G3/G4 thực hiện tiếp nối (nhật ký ở mục 5).


1. PHÂN TÍCH G2

1.1 Mục tiêu

Restart không mất instrument — sau khi bridge respawn (stall 3s / crash / restart sample-rate / DAW mở lại), người dùng bấm PLAY nghe lại ngay, preset + vị trí transport được khôi phục tự động.

1.2 Hiện trạng (đã verify trong code)

Thành phần File Hiện trạng liên quan G2
Bridge C++ (daw_vst_bridge) native_bridge/src/main.cpp (1250 dòng) Control loop: LOAD type=2, TRANSPORT type=3 (0=STOP, 1=PLAY, 2=SET_POSITION), OPEN_GUI type=4. playheadSamples lưu trong audio loop. Watchdog thread (parent chết → TerminateProcess).
Quản lý instrument native_bridge/src/NativeInstrumentEngine.cpp/.h InstrumentEngineManager: map channel→instance + paths_ (path/channel). FluidSynthInstrument::bank_[16] giữ bank mỗi channel.
VST3 preset native_bridge/src/Vst3Instrument.cpp (G0.4) captureState()/restoreState() CHỈ trong RAM (savedComponentState_/savedControllerState_ buffers) — mất khi process chết.
Rust shell src-tauri/src/lib.rs spawn_bridge, pump thread (stall 3s → kill + restart 1 lần → bridge-down), kill_bridge, restart_bridge_with_sample_rate, transport_control, load_native_instrument, bridge_status. SHUTTING_DOWN khi window đóng → kill bridge KHÔNG dump.
JS client app/static/js/services/nativeBridgeService.js loadInstrument, transport(kind, playhead), onAudio, onStatusChange, queryStatus.
JS app app/static/js/app.precompiled.js loadVstToBridge/loadToBridge (LOAD), __bridgeLoadedChannels (reset {} khi restart → mất), updatePlayhead gửi set_position ~1/s (JS là nguồn playhead duy nhất), bootstrap gọi restart_bridge_with_sample_rate ở startup.

1.3 Khoảng trống (gap)

  1. Không có gì ghi ra đĩa — G0.4 preset chỉ sống trong RAM của bridge; respawn = mất hết channel/preset/transport.
  2. Kill không có cơ hội dump — pump thấy stall → kill_bridge ngay; window đóng → kill_process_tree; cả hai đều không báo bridge ghi state trước.
  3. Respawn không tự load — bridge mới start với SHM trống; JS __bridgeLoadedChannels={} và chỉ load lại khi track phát/đổi (không tự động, không khôi phục preset).
  4. Guard chặn restore khi PLAY — G2.4: luồng restore phải chạy được khi isPlayingRef.current === true (hiện guard if(window.__bridgePlaying) return trong openVstGuiRetry, và !isPlayingRef.current quanh openNativeGUI dòng ~817 — áp dụng cho restore cũng phải gỡ).

1.4 Nguyên tắc thiết kế (đề xuất)

  • Write-on-change là chính: bridge tự ghi state ra đĩa NGAY khi state đổi (LOAD/TRANSPORT/params) — an toàn cả khi crash đột ngột, không phụ thuộc cơ hội dump trước kill. Dump-before-kill chỉ là lớp phụ (khi có thời gian: restart sample-rate, window close).
  • Bridge tự restore khi start: bridge đọc file state khi khởi động → tự push LOAD các channel + restore preset + set transport — Rust không cần biết preset blob (giữ IPC đơn giản).
  • File state dùng chung: %APPDATA%/SonicForgeDAW/state/bridge_state.json (cùng cây logs/ipc — đã có sẵn layout).
  • Không đụng audio loop: mọi IO đĩa chạy trên UiThread (worker job queue) hoặc thread riêng; tuyệt đối không trong vòng render.

2. PHÂN RÃ TASKS + ESTIMATE

ID Task File đích Nội dung Estimate
G2.0 Format + module state file native_bridge/src/StateStore.h/.cpp (mới) + CMakeLists.txt JSON (path/type/channel/preset-base64/transport) + atomic write (write tmp → rename). API: load(), save(StateSnapshot), clear(). 1 ngày
G2.1 Save đầy đủ state ra đĩa main.cpp, Vst3Instrument.cpp, NativeInstrumentEngine.cpp/.h Snapshot toàn bộ channel: path, type, VST3 component+controller state (từ captureState → base64), SF2 bank/program, transport playheadSamples. Ghi khi: LOAD xong, TRANSPORT đổi, params đổi (định kỳ 1-2s nếu dirty). 2 ngày
G2.2 Dump state trước kill src-tauri/src/lib.rs, main.cpp (a) Write-on-change phủ hầu hết. (b) Trước kill_bridge/restart: Rust push control type=5 (DUMP) + chờ ≤300ms → kill. (c) Window close: gửi DUMP trước kill_process_tree. 1.5 ngày
G2.3 Auto-reload sau respawn main.cpp (startup), lib.rs (env SF_STATE_FILE) Bridge start: đọc state file (nếu có) → uiWorker.post LOAD từng channel theo thứ tự → restore preset → nếu từng PLAY thì set transport + PLAY. Guard: chỉ restore khi channel trống; log vào bridge.log. 2 ngày
G2.4 Gỡ guard chặn restore khi PLAY app/static/js/app.precompiled.js Trong luồng restore, bỏ điều kiện dừng vì isPlayingRef.current/__bridgePlaying: LOAD instrument + restore preset khi đang PLAY vẫn phải chạy (khác với open GUI — G0.2 vẫn giữ defer cho OPEN_GUI). 0.5 ngày
G2.5 Regression driver stress native_bridge/debug/test_g2_stress.py (mới, theo mẫu test_g15_stress.py) N vòng: LOAD 2 inst (Nexus+EZkeys) → PLAY → kill bridge → respawn → verify writeIndex advancing + preset giữ + 0 crash. 1 ngày
G2.6 Verify + bắt bugs + fix — Chạy selfcheck/stress/probe trên Windows; thu logs; sửa bug tìm được. 2-4 ngày
Tổng 10-12 ngày (~2-3 tuần — khớp roadmap)

Phụ thuộc

G2.1 → G2.0 · G2.2 → G2.1 (dùng StateStore) · G2.3 → G2.1 + G2.2 · G2.5 → G2.3 + G2.4 · G2.6 → tất cả.

Thứ tự triển khai khuyến nghị

G2.0 → G2.1 → G2.3 (lõi: save + tự restore) → G2.4 (guard) → G2.2 (dump trước kill — lớp phụ) → G2.5 → G2.6. Lý do: G2.2 phụ thuộc G2.1 nhưng giá trị thấp hơn G2.3; làm G2.3 sớm để stress sớm.


3. KẾ HOẠCH THỰC HIỆN (CÀI ĐẶT → KIỂM TRA → BẮT BUGS → FIX → REPORT)

PHÂN CÔNG MÁY (bắt buộc):

  • Máy Linux (máy này): CHỈ lập kế hoạch, review code, ghi chú, theo dõi tiến độ, cập nhật file báo cáo. KHÔNG sửa code C++/Rust/JS, không build bridge (thiếu vcpkg/fluidsynth/sfizz/VST3 SDK/rustc-MSVC). Verify tối đa tại đây: node --check (JS), ast.parse (py), review, shm_selfcheck (đã pass Linux trước đây).
  • Máy Windows (máy chạy app thật): SỬA CODE, build (build_bridge.ps1), deploy (deploy_g12.ps1), chạy probe/stress, bắt bugs, fix, commit + push. Mọi thay đổi code G2 đều phát sinh ở đây.
  • Luồng: Windows sửa code + commit + push → Linux pull, review, cập nhật ghi chú/report + push → Windows pull lại, verify tiếp.

Sau MỖI task: mục "📌 Lưu ý & quy trình" ghi rõ việc nào làm ở máy nào.

G2.0 — StateStore

  • Cài đặt: thêm StateStore.h/.cpp; JSON đơn giản (viết tay serializer — tránh thêm dep nlohmann vào bridge, dự án không có sẵn). Atomic write: bridge_state.json.tmp → rename.
  • Kiểm tra: unit selfcheck — compile StateStore riêng (Linux: g++ -fsyntax-only nếu muốn xem syntax; Windows: compile thật trong target + test nhỏ ghi/đọc JSON round-trip).
  • Bắt bugs: escape chuỗi path (dấu " \ trong path Windows), rename fail khi file khoá, UTF-8.
  • Fix + report: ghi kết quả vào mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (sửa code): tạo native_bridge/src/StateStore.h/.cpp; thêm 2 file vào native_bridge/CMakeLists.txt target daw_vst_bridge; nhớ CRLF (ghi file qua Python io.open(newline='') hoặc editor giữ CRLF — cả repo native_bridge là CRLF). Build: build_bridge.ps1. Test: chạy exe/test nhỏ in JSON ra, verify round-trip. Commit: G2.0: StateStore (JSON state file + atomic write).
    • Linux (ghi chú): sau khi Windows push, pull + review file mới + cập nhật bảng mục 5 + dòng nhật ký. Không sửa code.

G2.1 — Save đầy đủ

  • Cài đặt: thêm InstrumentEngineManager::snapshot() (dưới mu_) trả về danh sách channel → path/type/state-blob; Vst3Instrument::serializeState() tái dùng captureState() buffers → base64; FluidSynthInstrument expose bank/program; hook ghi vào các điểm LOAD xong / TRANSPORT / timer dirty.
  • Kiểm tra: shm_selfcheck không đổi (layout SHM giữ nguyên — không thêm field vào SHM); review không đụng audio loop; trên Windows: probe LOAD → đọc bridge_state.json thấy đủ path/preset.
  • Bắt bugs: base64 không khớp byte (size/precision), ghi giữa lúc instance đang reload (phải chờ reloadingCh_ off), path empty.
  • Fix + report: mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (sửa code): sửa NativeInstrumentEngine.cpp/.h (thêm snapshot()), Vst3Instrument.cpp (serializeState), main.cpp (hook ghi sau LOAD/TRANSPORT + timer dirty). Build + deploy + probe LOAD → mở %APPDATA%\SonicForgeDAW\state\bridge_state.json kiểm tra path/preset. Commit: G2.1: save full instrument state to disk (path/type/preset/transport).
    • Linux (ghi chú): pull + review: snapshot phải nằm dưới mu_, không gọi IO trong audio loop; shm_selfcheck vẫn pass. Cập nhật mục 5 + nhật ký.

G2.2 — Dump trước kill

  • Cài đặt: thêm control type=5 (DUMP_STATE) trong enum + handler main.cpp (push snapshot qua StateStore); Rust kill_bridge/window-close gửi control 5 + sleep 300ms trước kill; restart_bridge_with_sample_rate làm tương tự.
  • Kiểm tra: review Rust+C++ control type khớp; Linux node --check không liên quan; Windows: kill bridge giữa PLAY → file state phải đủ (write-on-change đã đảm bảo, DUMP là phụ).
  • Bắt bugs: race Rust gửi DUMP sau khi bridge chết (guard shm unavailable), 300ms không đủ khi VST đang createView (attach_in_flight → chờ theo flag SHM offset 11160).
  • Fix + report: mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (sửa code): sửa native_bridge/src/main.cpp (case 5), src-tauri/src/lib.rs (kill_bridge, restart_bridge_with_sample_rate, window-close handler). Build bridge + cargo check (src-tauri). Deploy → kill bridge giữa PLAY → kiểm tra state file đủ. Commit: G2.2: dump state before kill (control type=5 + Rust DUMP before kill_bridge).
    • Linux (ghi chú): pull + review: type 5 không đụng enum cũ (2/3/4 giữ nguyên), Rust guard khi ShmState None; cargo check bắt buộc chạy trên Windows (Linux thiếu toolchain). Cập nhật mục 5 + nhật ký.

G2.3 — Auto-reload sau respawn

  • Cài đặt: bridge startup (sau khi map SHM, trước audio loop): đọc SF_STATE_FILE (Rust set env khi spawn) → uiWorker.post từng channel: LOAD (type 2) → restoreState() → cuối cùng nếu state có playing=true → TRANSPORT play + set_position. Log từng bước vào bridge.log.
  • Kiểm tra: Linux review + shm_selfcheck; Windows: kill bridge khi đang PLAY → chờ respawn → verify writeIndex advancing + preset nghe lại đúng.
  • Bắt bugs: restore chạy trước khi Rust pump gắn bridge-audio (JS _reconnectIfNeeded vẫn cần); load nhiều channel cùng path (g_loadMutex + reloadingCh phải serialize); double-load khi JS cũng tự load (JS phải bỏ __bridgeLoadedChannels reset → giữ state thật).
  • Fix + report: mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (sửa code): sửa main.cpp (startup restore block), lib.rs (set env SF_STATE_FILE khi spawn). Build + deploy + kill bridge giữa PLAY → chờ respawn (spawn.log) → verify writeIndex + preset. Nếu cần phía JS: sửa app.precompiled.js + node --check. Commit: G2.3: auto-reload instruments from state file after bridge respawn.
    • Linux (ghi chú): pull + review: restore phải post qua uiWorker (không chạy trong audio loop), serialize bằng g_loadMutex sẵn có; xác nhận __bridgeLoadedChannels flow trên JS. Cập nhật mục 5 + nhật ký.

G2.4 — Gỡ guard chặn restore khi PLAY

  • Cài đặt: trong app.precompiled.js luồng auto-restore (điểm G2.3 phía JS): cho phép loadVstToBridge/restore chạy khi PLAY; GIỮ NGUYÊN guard __bridgePlaying cho riêng OPEN_GUI (G0.2 — defer GUI vẫn đúng).
  • Kiểm tra: node --check app.precompiled.js; Windows: PLAY đang chạy → kill/respawn → verify restore xong trong khi PLAY, không mất tiếng.
  • Bắt bugs: restore gọi openVstGuiRetry nhầm khi PLAY (guard cũ trả true sớm → GUI không mở nhưng LOAD cũng bị bỏ) — tách 2 guard riêng.
  • Fix + report: mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (sửa code): sửa app/static/js/app.precompiled.js (tách guard restore vs guard OPEN_GUI); chạy node --check app/static/js/app.precompiled.js. Deploy app (build JS bundle → copy vào install) → PLAY → kill/respawn → verify. Commit: G2.4: allow instrument restore while PLAY (keep OPEN_GUI defer).
    • Linux (ghi chú): pull + review: xác nhận G0.2 guard __bridgePlaying vẫn còn cho OPEN_GUI; node --check cũng chạy được tại đây nếu có node. Cập nhật mục 5 + nhật ký.

G2.5 — Stress driver

  • Cài đặt: test_g2_stress.py theo mẫu test_g15_stress.py: 5 + 10 iterations LOAD/OPEN_GUI/PLAY/STOP + kill bridge giữa chừng → verify respawn + writeIndex + preset.
  • Kiểm tra: chạy trên Windows; ghi PASS/FAIL theo iteration.
  • Bắt bugs: crash/restart khi kill giữa attach, preset sai sau lần restore thứ N.
  • Fix + report: mục 5.
  • 📌 Lưu ý & quy trình:
    • Windows (tạo script + chạy): tạo native_bridge/debug/test_g2_stress.py (chép khung test_g15_stress.py: ctypes SHM, write_control, wait_log, bridge_pid, bad_since); thêm bước kill daw_vst_bridge giữa vòng và chờ respawn. Chạy: python native_bridge\debug\test_g2_stress.py <APP_PID> 5 rồi 10. Thu kết quả PASS/FAIL + logs. Commit: G2.5: stress driver for persistence restart (N loop + kill/respawn).
    • Linux (ghi chú): pull + review script (không chạy được — cần SHM Windows); cập nhật mục 5 với kết quả PASS/FAIL do Windows báo.

G2.6 — Verify tổng + bắt bugs + fix

  • Chạy lại toàn bộ regression G1.5 (không hỏng G1) + G2.5 + probe G0.4.
  • Thu bridge.log/spawn.log thực (không tin probe PASS đơn lẻ — learning cũ).
  • Sửa mọi bug tìm được, commit theo từng task (như G1.2–G1.5).
  • 📌 Lưu ý & quy trình:
    • Windows (chạy + fix): chạy test_g15_stress.py (regression G1) + test_g2_stress.py (G2) + probe G0.4; fix bug tìm được, commit từng cái.
    • Linux (báo cáo tổng): sau khi Windows báo kết quả cuối, tổng hợp toàn bộ vào mục 5 + nhật ký, đánh dấu hoàn thành, commit báo cáo cuối: G2: report — persistence restart done (verdict từ Windows). Push.

4. RỦI RO

Rủi ro Ảnh hưởng Giảm thiểu
VST3 state blob không restore được (Nexus setState fail) Mất preset dù load lại được path Fallback: giữ path + chỉ restore blob khi OK; log setState failed; không chặn load.
Ghi file mỗi LOAD → IO chậm Stall audio? Ghi trên UiThread job (không trong audio loop); atomic rename.
JS __bridgeLoadedChannels stale Double-load / bỏ load Reset map theo sự kiện bridge respawn thật (bridge-audio trở lại) thay vì chỉ restart-bootstrap.
Restore khi đang PLAY (G2.4) gây click/glitch Chất lượng âm LOAD đã có reloadingCh_ mute cơ chế sẵn (G1.4) — tái dùng; restore nhanh, không chờ.
Không build được trên Linux Không verify cục bộ Verify Linux: syntax/selfcheck/review; chạy thật trên Windows theo quy trình G1.

5. BÁO CÁO KẾT QUẢ (điền sau khi thực hiện)

Cột Cài đặt/Kiểm tra/Bugs/Fix: Windows điền (người sửa code). Cột Trạng thái + Nhật ký: Linux cập nhật sau khi review.

ID Cài đặt Kiểm tra Bugs bắt được Fix Trạng thái
G2.0 StateStore.h/.cpp (JSON thủ công, atomic tmp+rename, UTF-8, API save/load/clear, defaultPath=%APPDATA%/SonicForgeDAW/state/bridge_state.json); them vao CMakeLists target daw_vst_bridge; test_state_store.cpp (round-trip self-check) Build PASS (daw_vst_bridge.exe), round-trip PASS (save/load 2 inst: path Windows co space+backslash, presetBase64 co newline/quote/backslash; khong con .tmp; clear xoa file) — (module moi, chua hook vao bridge) — ✅ (Win xong + Linux review OK)
G2.1 main.cpp: globals g_stateDirty/g_transportPlaying/g_lastSnapMutex/g_lastSnap, snapshotsEqual (bo playhead); stateFile = SF_STATE_FILE fallback defaultPath; playheadSamples -> std::atomic<uint64_t>; saveStateJob (UiThread, skip khi isReloading, chi save khi snapshot doi); TRANSPORT set g_transportPlaying + dirty; cuoi LOAD dirty=true; periodic 2s dirty + debounce 300ms. Vst3Instrument.h: khai bao bankOf/programOf. StateStore.cpp: ensureParentDir() (CreateDirectoryA chain / mkdir) goi dau save(). Build PASS (daw_vst_bridge.exe). Deploy PASS (PID moi: sonicforge-daw 9876, daw_engine 24632, daw_vst_bridge 7352). Verify live PASS: LOAD Nexus ch5 + PLAY -> bridge_state.json co 7 instrument (ch0-6 frontend da load), playing=true, presetBase64 non-empty (Nexus 3468/3488, EZkeys 24764); STOP -> playing=false. State round-trip test_state_store.cpp PASS. test script ban dau ky vong dung 1 instrument (frontend da load 7) -> FAIL gia, chuc nang PASS assert dung so instrument ✅ (Win xong + Linux review OK)
G2.2 main.cpp: control type=5 DUMP_STATE (post saveStateJob len uiWorker ngay lap tuc). lib.rs: flush_bridge_state() — push_control(5,0,0,0) + sleep 300ms, goi dau kill_bridge() (cover restart_bridge_with_sample_rate + stall-restart) va truoc khi giet bridge trong window-close Destroyed Build PASS (daw_vst_bridge.exe) + cargo check PASS (src-tauri) + deploy + verify live PASS: DUMP qua SHM -> log "DUMP_STATE - flushing state now", state file intact (7 inst), writeIndex advancing Runtime Rust flush chua test duoc khi khong build app Tauri (cargo check xac nhan compile) ✅ (Linux review OK)
G2.3 main.cpp: startup restore block (doc SF_STATE_FILE, guard channel trong, post LOAD job tung channel qua uiWorker, assign + loadSerializedState preset / selectProgram bank-program, g_loadMutex serialize, restore transport PLAY + playhead neu state playing). Vst3Instrument: base64Decode + loadSerializedState (parse [4B BE compLen][comp][4B BE ctrlLen][ctrl], goi restoreState). INativeInstrument.h: virtual loadSerializedState no-op. lib.rs: spawn_bridge set env SF_STATE_FILE = %APPDATA%/SonicForgeDAW/state/bridge_state.json. Build PASS (bridge + cargo check). Deploy PASS. Verify live PASS: LOAD ch5 + PLAY (playhead 5000) -> kill bridge -> respawn -> log "restoring 7 instruments" + "restored ch=0..6" (Nexus + EZkeys) + "restoring transport PLAY playhead=5000" + ch5 preset 3488B giu nguyen; state file playing=true playhead=5000 inst=7; writeIndex advancing (14859->15234). - - ✅ (Win xong + Linux review OK)
G2.4 main.cpp: LOAD case double-load guard — skip khi channel da co cung path (lower_plugin_path(ch) == lower_path_str(path)), giu instance + preset da restore khi JS reload sau respawn/bootstrap Verify live PASS: LOAD lai Nexus ch5 sau respawn -> log "LOAD skip ch=5 (already loaded)", preset ch5 3488B giu nguyen, writeIndex advancing; 15 LOAD skip trong log (JS reload 7ch x2 + test) Khong can sua JS: loadVstToBridge/loadTrackInstrumentToBridge khong co guard __bridgePlaying (da chay khi PLAY); chi openVstGuiRetry giu guard OPEN_GUI (G0.2, dung) ✅ (Linux review OK)
G2.5 native_bridge/debug/test_g2_stress.py (mới, khung test_g15_stress.py: ctypes SHM, write_control, wait_log, bridge_pid, bad_since) + bước kill daw_vst_bridge giữa vòng + chờ watchdog respawn; verify restore + preset giữ + LOAD-skip guard (G2.4) + writeIndex advancing; test_g15_stress.py: LOAD ch5 chấp nhận "LOAD skip" (guard G2.4 đổi log) Chạy 5 + 10 iterations PASS (tổng 20 lần kill/respawn): LOAD skip pre-kill/post-respawn OK, restore ch5/ch6 OK, watchdog "[tauri] bridge restarted" OK, bridge alive; preset Nexus giữ nguyên (exact), EZkeys sim>=0.87; writeIndex advancing (delta 283-376) Assert preset ch6 byte-exact FAIL giả: EZkeys nhúng PlayHeadPos vào state riêng (đổi cả giữa 2 save thường, ratio 0.872) ch5 so exact, ch6 so ratio>=0.7 + len chênh
G2.6 Chạy lại regression G1.5 (test_g15_stress.py) + G2.5 (5+10 iter) + probe G0.4 (bản cập nhật PID + guard-aware, OPEN_GUI full path) G1.5 regression PASS 5/5 (LOAD/OPEN_GUI/PLAY/STOP/WM_CLOSE, writeIndex delta 307-551, bridge alive); G2.5 PASS 5+5+10 (20 lần kill/respawn, preset giữ, không crash); G0.4 probe PASS: LOAD skip -> OPEN_GUI hwnd -> WM_CLOSE -> capture 2628B -> reload -> restore 2628B -> OPEN_GUI lại attach OK, không restart/EXCEPTION/FAILED Không có bug sản phẩm mới; 2 bug test-script: (1) probe G0.4 so sánh PID int vs list -> báo restart giả, (2) preset EZkeys byte-diff do PlayHeadPos runtime (đã phân tích ở G2.5) Sửa test scripts (guard-aware wait, pid compare); không sửa code sản phẩm ✅ (Win xong + Linux review OK)

5.1 Review + verify phía Linux (2026-08-16, Linux — chỉ review/ghi chú, KHÔNG sửa code)

Kết quả review toàn bộ diff G2 (git diff 1fe2d26~1..b59e416, 15 files; không có file JS trong diff).

Code review — nhất trí với báo cáo Windows, không tìm thấy bug sản phẩm:

  • Vst3Instrument.cpp: base64Encode/base64Decode đúng RFC 4648 (padding =, decode bỏ qua whitespace); loadSerializedState có bounds-check cả componentLen lẫn controllerLen trước khi copy — blob hỏng không gây OOB; gán buffer xong mới gọi restoreState() (đúng thứ tự).
  • NativeInstrumentEngine.cpp: snapshot() giữ mu_ suốt captureState() — audio loop cũng serialize bằng mu_ per block nên không race capture với process; skip channel đang reload (reloadingCh_, instance cũ sắp chết); bank_/program_ cập nhật đúng (reset khi loadSoundFontFile, set khi program_select/programChange).
  • main.cpp: saveStateJob defer khi có channel reloading (đặt lại dirty → retry sau debounce) — không ghi state lúc instance nửa chết; snapshotsEqual loại playhead (không ghi file mỗi ~2s khi PLAY); periodic mark 2s + debounce 300ms; startup restore chỉ nhận channel trống (instruments.get(ch)==nullptr) và serialize bằng g_loadMutex; LOAD guard so sánh lower-path — chỉ skip khi CÙNG path, path khác vẫn replace bình thường; case 5 DUMP_STATE post saveStateJob trực tiếp (không chờ debounce).
  • lib.rs: flush_bridge_state an toàn khi bridge treo (SHM do Rust sở hữu, control 5 không được consume — vô hại).
  • test_g2_stress.py + test_state_store.cpp + sửa test_g15_stress.py: logic vững (wait_load chấp nhận real hoặc skip; SequenceMatcher ≥0.7 cho EZkeys vì PlayHeadPos runtime; bad_since watch EXCEPTION/FAILED; round-trip cover escape \n \t \" \\, atomic .tmp, missing file, clear).

Findings (minor, không chặn; Linux không sửa code — chờ Windows quyết định):

  1. F1 (minor): window-close gọi flush_bridge_state() ở run() VÀ kill_bridge() giờ cũng tự gọi → DUMP_STATE 2 lần khi đóng app (+~300ms shutdown). Harmless; có thể bỏ lời gọi thừa trong run().
  2. F2 (portability note): lib.rs luôn set SF_STATE_FILE sang đường dẫn Windows; nếu chạy bridge non-Windows, env thành ./SonicForgeDAW/state/bridge_state.json (theo CWD) thay vì Linux fallback $HOME/.sonicforge/state/ của StateStore::defaultPath. Không ảnh hưởng target Windows (cùng path APPDATA); ghi nhận để sửa khi cần bridge chạy Linux.
  3. F3 (cosmetic): loadSerializedState in cùng message "bad ctrlLen" cho 2 check khác nhau (thiếu header / độ dài vượt) — chỉ log, không ảnh hưởng hành vi.

Verify trên Linux (cú pháp/logic — máy Linux không build Windows):

  • python3 -m py_compile native_bridge/debug/test_g2_stress.py, test_g15_stress.py: PASS.
  • g++ -std=c++17 -Wall -Wextra debug/test_state_store.cpp + src/StateStore.cpp (Linux): compile sạch (1 warning benign multi-line comment) + run "state_store round-trip PASS" → StateStore portable, atomic write + escape verified trên Linux.
  • node --check app/static/js/app.precompiled.js: PASS (G2 không sửa JS — diff stat không có file JS).
  • Rust: rustc/cargo chỉ là rustup proxy, chưa cài default toolchain → không chạy được cargo check trên Linux (khớp ghi chú big_tasks.md "máy Linux không có rustc toolchain"); Windows đã cargo check PASS ở G2.2/G2.3.

Kết luận Linux: code G2 nhất quán với báo cáo Windows; không có bug sản phẩm; 2 findings minor (F1, F2) + 1 cosmetic (F3) không chặn merge. Trạng thái G2.0–G2.6: ✅ (Win xong + Linux review xác nhận).

5.2 Audit bảo mật / bộ nhớ / conflict child process (2026-08-16, Linux — chỉ review/ghi chú, KHÔNG sửa code)

Đọc toàn bộ: native_bridge/src/main.cpp (1410), NativeInstrumentEngine.cpp (439), Vst3Instrument.cpp (873), StateStore.cpp (288), SandboxVst3Host.cpp (192), plugin_host_main.cpp (163), SharedMemoryIPC.cpp (125), src-tauri/src/shm.rs (201), src-tauri/src/lib.rs (791), app/static/js/services/nativeBridgeService.js (180), app/static/js/app.precompiled.js (luồng bridge). Mục tiêu: race UAF / double-buffer / conflict 2 bridge cùng SHM / orphan / command injection / luồng có đúng thiết kế.

KẾT LUẬN: không có bug chặn release; 2 MEDIUM (F-MEM-5 UAF, F-PROC-1 injection), 1 MEDIUM (F-PROC-2 race restart×watchdog), 1 MEDIUM audio-correctness (F-MEM-8), 2 LOW (F-PROC-3, F5). Tất cả nằm ở đường hiếm (deschedule >10ms, path thù địch, restart trùng stall) — đề xuất Windows xử lý theo ưu tiên F-PROC-1 > F-MEM-5 > F-PROC-2.

F-MEM-5 (MEDIUM — UAF trên audio thread): InstrumentEngineManager::get() (NativeInstrumentEngine.cpp:300) lock mu_ rồi trả raw unique_ptr::get() sau unlock. assign()/unload() (dòng 182/240) swap map DƯỚI mu_ nhưng destroy oldInst NGOÀI lock, sau sleep 10ms. Drain loop control queue chạy trên audio thread (main.cpp:1115) gọi instruments.get(ch) rồi deref sau unlock: handleOpenGui dedupe i0->hasAttachedView() (main.cpp:794), pending-GUI sweep (884/909/927/953), STOP transport inst->controlChange(ch,64,0) (main.cpp:1290). Nếu UiThread assign xong + ngủ 10ms + reset oldInst trong lúc audio thread bị deschedule giữa get() và deref → deref instance đã free → UAF (cửa sổ ~10ms+). Các site trong attach job (823) an toàn vì cùng UiThread với assign. Hướng fix: giữ mu_ quanh get+use như MIDI dispatch (đã làm đúng ở dòng 338), hoặc get() trả shared_ptr.

F-MEM-8 (MEDIUM — audio correctness): double-buffer G4.3 không handshake (SharedMemoryIPC.h): bridge đọc writeSlot + audioLeft/Right trong lúc child flip slot → block rách/glitch (không memory corruption — mảng cố định 256). processAudioBlock đọc con trỏ ipc_ non-atomic trong lúc watchdog respawn child (UB lý thuyết, x86 OK). Ghi nhận theo dõi.

F-PROC-1 (MEDIUM — command injection): SandboxVst3Host::spawnChild() (SandboxVst3Host.cpp:76-78) nội suy --path "..." + --sr vào cmdline, gọi CreateProcessA(nullptr, buf) — path lấy từ LOAD control / state file. Path chứa " + metachar (VD " && calc) → thực thi lệnh tuỳ ý (cùng user). Fix: lpApplicationName + lpCommandLine arg đã quote đúng, hoặc truyền path qua env/SHM. Severity giữ MEDIUM vì attacker cần kiểm soát path (file .vst3 thù địch / state file sửa tay).

F-PROC-2 (MEDIUM — race restart × watchdog → 2 bridge cùng SHM): restart_bridge_with_sample_rate (invoke thread) và pump watchdog (thread riêng) đều chạy kill_bridge() → spawn_bridge(). kill_bridge serialize bằng BridgeProcess mutex nhưng kill→spawn không nguyên tử: 2 luồng cùng đến spawn_bridge → 2 daw_vst_bridge.exe cùng map SHM (tên SonicForge_DAW_IPC_<pid> — CÙNG tên) → double LOAD / OPEN_GUI trùng / control queue rối. set_bridge_child overwrite child cũ → rò rỉ bridge đầu (không kill, log redirect task sống theo pipe). Khả năng overlap cao hơn vì cả 2 luồng đều ngủ 300ms trong flush_bridge_state. Kích hoạt thực tế: bootstrap JS gọi restart (mỗi lần webview load) trùng lúc bridge mới không kịp heartbeat → watchdog stall 3s. Fix: 1 mutex phủ cả kill+spawn, hoặc spawn_bridge kiểm tra "không còn child tracked" trước khi spawn.

F-PROC-3 (LOW — orphan race khi đóng app): pump thread check SHUTTING_DOWN ở đầu vòng, nhưng giữa kill_bridge và spawn_bridge trong cùng 1 vòng không check lại → Destroyed lấy None (pump vừa kill), pump spawn bridge sau khi app đang tắt → orphan tạm thời. Child có --parent monitor nên tự thoát khi Tauri exit → transient, không nghiêm trọng. Fix: check SHUTTING_DOWN lần nữa trước spawn_bridge.

F5 (LOW — observability): open_vst_gui command (lib.rs:626) luôn trả Ok(()) kể cả khi SHM unavailable / push fail — lỗi chỉ eprintln vào stderr Tauri (không vào bridge.log) → JS không biết thất bại, retry vô ích. Nên trả Err để JS dừng sớm.

F6 (LOW — comment lệch code): openVstGuiRetry cap thực tế n=min(attempts,2) (2 lần) trong khi comment ghi "Cap 5" (bản cũ ghi 20). Không lỗi hành vi, chỉ lệch comment.

Luồng xác nhận ĐÚNG thiết kế:

  • Control queue drain TRƯỚC audio render trong cùng loop (main.cpp:1115-1315) — control không bị đói.
  • LOAD job post lên UiThread, serialize g_loadMutex; audio thread không stall (comment G3.1) — writeIndex stall 3s chỉ khi heartbeat thread kẹt thật.
  • OPEN_GUI: defer khi PLAY (G0.1) + pending sweep khi transportStopped && instruments.get(ch); dedupe hasAttachedView chống spam retry; attach job trên UiThread (COM STA) + g_loadMutex + AttachInFlightGuard + same-path silence + destroy child window trên owner thread (0xc000041d fix).
  • STOP transport: CC64=0 release sustain TRƯỚC allNotesOff (V8 bug 3) — đúng.
  • DUMP_STATE (type=5) post saveStateJob trực tiếp, không chờ debounce; kill_bridge flush 300ms; Rust pump drop guard trước kill (chống deadlock G3.1).
  • G2.4 LOAD-skip guard (lower-path compare) — idempotent sau respawn, preset giữ.
  • Rust pump: liveness = heartbeat chuyên dụng (không phải writeIndex), phân loại HANG/CRASH, max 1 restart/incident; restart_attempts reset khi hb advance.
  • Destroyed: SHUTTING_DOWN trước kill → pump dừng (trừ race F-PROC-3).
  • JS: __bridgePlaying guard openVstGuiRetry; load instrument im lặng khi PLAY (không tự mở GUI); __bridgeLoadedChannels reset cả connected lẫn !connected (V8 bug 4) → reconnect reload đúng, không mở GUI; C++ LOAD-skip làm reload idempotent.
  • shm.rs layout khớp C++ (selfcheck 12/1040/11168/15268); push_control zero-fill arg2 → std::string(c.arg2) an toàn với mọi writer hiện tại; MIDI count non-atomic (race benign, volatile).

Verify Linux:

  • g++ -std=c++17 -Wall -Wextra -fanalyzer StateStore.cpp: compile sạch (0 warning) + test_state_store.cpp run "state_store round-trip PASS".
  • SharedMemoryIPC.cpp / SandboxVst3Host.cpp / plugin_host_main.cpp: Windows-only (xung đột POSIX shm_open, thiếu HANDLE/CreateProcessA) — không build được trên Linux, đúng kỳ vọng.
  • node --check app.precompiled.js + nativeBridgeService.js: PASS.
  • Rust: không có toolchain (như G2.6).

Nhật ký thực hiện

  • G2.0 (2026-08-16, Windows): tao native_bridge/include/StateStore.h + native_bridge/src/StateStore.cpp (CRLF, JSON viet tay, atomic write .tmp→rename, escape \ "

    , load parser an toan). Them StateStore.cpp vao CMakeLists target daw_vst_bridge. Build bridge PASS. Test round-trip native_bridge/debug/test_state_store.cpp PASS (compile cl vcvars64 + chay). Commit G2.0: StateStore (JSON state file + atomic write).

  • G2.1 (2026-08-16, Windows): save day du state ra disk. main.cpp: globals + snapshotsEqual + stateFile env fallback + playheadSamples atomic + saveStateJob (UiThread, skip isReloading, chi ghi khi snapshot doi) + hook TRANSPORT/LOAD + periodic 2s dirty debounce 300ms. Them khai bao bankOf/programOf vao Vst3Instrument.h (thieu declaration -> build fail). StateStore.cpp: ensureParentDir() (CreateDirectoryA chain, Linux mkdir) goi dau save() (fix C2001 do heredoc backslash mangle qua script fix_bs.py). Build PASS. Deploy PASS (PID 9876/24632/7352). Verify live PASS: LOAD Nexus ch5 + PLAY -> state file 7 inst + playing=true + presetBase64 day du; STOP -> playing=false. StateStore round-trip test PASS. Commit G2.1: save full instrument state to disk (path/type/preset/transport).

  • G2.3 (2026-08-16, Windows): auto-reload sau respawn.

  • G2.2 (2026-08-16, Windows): dump state truoc kill. main.cpp: them control type=5 DUMP_STATE sau case 4 — post saveStateJob len uiWorker ngay (khong cho debounce 300ms/2s), log "DUMP_STATE - flushing state now". lib.rs: them flush_bridge_state(app) — shm.push_control(5,0,0,0,"") roi sleep 300ms (du thoi gian UiThread ghi xong file); goi o dau kill_bridge() (cover ca restart_bridge_with_sample_rate va stall-restart) va trong on_window_event Destroyed truoc khi kill_process_tree(bridge). Build PASS (daw_vst_bridge.exe) + cargo check PASS (src-tauri). Deploy + verify live PASS: push DUMP qua SHM driver -> log "DUMP_STATE - flushing state now" xuat hien, state file intact (7 inst, playing=false), writeIndex advancing. Ghi chu: nhanh Rust flush chi cargo check (khong build app Tauri) — logic don gian, compile OK. Commit G2.2: flush bridge state to disk before kill (control type=5 DUMP + Rust pre-kill).

  • G2.4 (2026-08-16, Windows): go double-load khi JS reload sau respawn. main.cpp: them guard dau LOAD case (truoc setReloading) — neu channel da co cung path (so sanh lower_plugin_path(ch) vs lower_path_str(path)) thi log "LOAD skip ch=N (already loaded)" va return, khong assign lai (thay instance se mat preset da restore tu state file). Ly do: JS reconnect/bootstrap reload gui LOAD cung path sau respawn -> truoc day assign() thay instance -> mat preset. Guard PLAY cua G2.4 (cho restore chay khi PLAY) da co san o JS: loadVstToBridge/loadTrackInstrumentToBridge khong co guard __bridgePlaying, chi openVstGuiRetry giu guard OPEN_GUI (G0.2 — defer GUI khi PLAY van dung). Build PASS (daw_vst_bridge.exe) + deploy + verify live PASS: sau respawn, LOAD lai Nexus ch5 -> "LOAD skip ch=5 (already loaded: ...Nexus.vst3)", preset 3488B giu nguyen, writeIndex advancing; tong 15 LOAD skip (JS reload 7ch x2 + test ch5). Commit G2.4: skip redundant LOAD of already-loaded instrument (keep restored preset). main.cpp: block restore truoc audio loop - doc state file, chi restore channel trong (guard instruments.get(ch)==nullptr), post LOAD job (assign + loadSerializedState / selectProgram) serialize bang g_loadMutex, neu state playing thi set playhead + g_transportPlaying=true + g_stateDirty. Vst3Instrument.cpp: base64Decode (b64val lookup) + loadSerializedState (parse blob, goi restoreState); Vst3Instrument.h + INativeInstrument.h: khai bao. lib.rs: spawn_bridge them env SF_STATE_FILE (fallback StateStore::defaultPath khi khong set). Build PASS (daw_vst_bridge.exe + cargo check). Deploy + verify live PASS: LOAD ch5 + PLAY playhead 5000 -> taskkill bridge -> respawn tu dong -> log restore 7 inst (Nexus ch0-5, EZkeys ch6) + restore transport PLAY playhead=5000 + ch5 preset 3488B giu nguyen; state file playing=true inst=7; writeIndex advancing. Commit G2.3: auto-reload instruments from state file after bridge respawn.

  • G2.5 (2026-08-16, Windows): tao native_bridge/debug/test_g2_stress.py (khung test_g15_stress.py). N vong: LOAD Nexus ch5 + EZkeys ch6 (real hoac skip deu OK) -> PLAY -> taskkill daw_vst_bridge -> cho watchdog "[tauri] bridge restarted" + "restored ch=5/6" -> LOAD lai -> verify "LOAD skip" (guard G2.4) + preset giu (ch5 exact, ch6 sim>=0.7 — EZkeys nhung PlayHeadPos vao state, ratio 0.872 giua save thong thuong) + writeIndex advancing. Chay 5 + 10 iterations: PASS (20 lan kill/respawn, khong crash, bridge alive). Kem sua test_g15_stress.py: LOAD ch5 chap nhan "LOAD skip" (guard G2.4 doi log). Commit G2.5: stress driver for persistence restart (N loop + kill/respawn).

  • G2.6 (2026-08-16, Windows): verify tong.

  • G2.0–G2.6 review + verify (2026-08-16, Linux — máy Linux chỉ review/ghi chú, KHÔNG sửa code): đọc toàn bộ diff G2 (1fe2d26~1..b59e416, 15 files) — Vst3Instrument base64 + loadSerializedState (bounds-check đủ), NativeInstrumentEngine snapshot (mu_ chặn race với audio loop, skip reloading), main.cpp saveStateJob/restore/LOAD-guard/DUMP/debounce, lib.rs flush_bridge_state + env SF_STATE_FILE, test_g2_stress.py + test_state_store.cpp + test_g15_stress.py. Verify Linux: py_compile 2 stress scripts PASS; g++ compile + run test_state_store PASS (StateStore portable); node --check app.precompiled.js PASS; rustc/cargo không có default toolchain → không cargo check (Windows đã check PASS). Findings: F1 double flush_bridge_state khi window-close (minor), F2 SF_STATE_FILE thiếu Linux fallback (portability, không ảnh hưởng target Windows), F3 log "bad ctrlLen" trùng cho 2 check (cosmetic). Không sửa code (đúng phân máy). Chi tiết ở mục 5.1. G1.5 regression PASS 5/5 (LOAD/OPEN_GUI/PLAY/STOP/WM_CLOSE, writeIndex delta 307-551, bridge alive). G2.5 PASS 5+5+10 (20 lan kill/respawn, preset giu, khong EXCEPTION/FAILED). Probe G0.4 (ban cap nhat: PID dong, LOAD guard-aware, OPEN_GUI full path): PASS — LOAD skip -> OPEN_GUI hwnd -> WM_CLOSE -> "state captured ch=5 component=2628B" -> reload -> "state restored ch=5 component=2628B" -> OPEN_GUI lai attach OK, khong restart. Bug tim duoc: khong co bug san pham; 2 bug test-script (probe PID int vs list -> bao restart gia; preset EZkeys byte-diff do PlayHeadPos runtime). Commit G2.6: verify — regression G1.5 + stress G2.5 + probe G0.4 all PASS.

  • Audit bảo mật/bộ nhớ/conflict child process (2026-08-16, Linux — chỉ review/ghi chú, KHÔNG sửa code): đọc toàn bộ main.cpp/NativeInstrumentEngine/Vst3Instrument/StateStore/SandboxVst3Host/plugin_host_main/SharedMemoryIPC/shm.rs/lib.rs/nativeBridgeService.js/app.precompiled.js. Findings: F-MEM-5 UAF audio-thread get()+deref vs assign() destroy oldInst ngoài lock sau 10ms (MEDIUM, sites 794/884/909/927/953/1290); F-MEM-8 double-buffer không handshake (MEDIUM audio); F-PROC-1 command injection --path "..." CreateProcessA (MEDIUM); F-PROC-2 race restart×watchdog → 2 bridge cùng SHM (MEDIUM); F-PROC-3 orphan race Destroyed (LOW); F5 open_vst_gui luôn Ok (LOW); F6 comment cap 2 vs 5 (LOW). Luồng chính đúng thiết kế: drain control trước render, LOAD UiThread+g_loadMutex, OPEN_GUI defer PLAY + pending sweep, STOP flush CC64=0, DUMP_STATE trực tiếp, pump drop-guard trước kill, max 1 restart/incident, JS __bridgePlaying + __bridgeLoadedChannels reset. Verify Linux: StateStore -fanalyzer sạch + round-trip PASS; 3 file Windows-only không build được (đúng); node --check 2 JS PASS. Không sửa code (đúng phân máy). Chi tiết mục 5.2.

  • Fix audit 5.2 (2026-08-16, Windows): xử lý toàn bộ findings 5.2. F-PROC-1: SandboxVst3Host.cpp thêm quote_arg (escape \ và " theo Windows CRT) + CreateProcessA(hostExe.c_str(), ...) + bỏ token exe khỏi cmdline -> path thù địch chứa " giữ 1 argv (probe F-PROC-1 PASS: spawn ch=7 cmdline giữ trọn path trong --path, child không map HACKED_SHM, không nhận --sr 1000, load fail như mong đợi). F-MEM-5: NativeInstrumentEngine.h + .cpp thêm has() + template withInstrument() (giữ mu_ quanh call); main.cpp đổi 6 site audio-thread: 784/794 hasAttachedView dedupe, 1073 restore guard, 1283 CC64=0, 1318 pending-GUI, 1337 state-dirty -> has()/withInstrument(); giữ nguyên 8 site UiThread (an toàn cùng thread assign). F-PROC-2: lib.rs thêm static BRIDGE_RESTART_LOCK serialize kill+spawn giữa restart_bridge_with_sample_rate và pump watchdog. F-PROC-3: pump re-check SHUTTING_DOWN sau lock + sau kill trước spawn. F5: open_vst_gui trả Err (lock poisoned / SHM unavailable / queue full), frontend try/catch sẵn. F6: comment Cap 5 -> Cap 2 (hard min(attempts,2)). F-MEM-8: ghi nhận theo dõi, không fix (đúng audit). Build: daw_vst_bridge + plugin_host PASS; cargo build --release PASS (2m53s). Deploy install/ (kill trước, copy2). Regression: G1.5 PASS 3/3 (writeIndex delta 287), G2.5 PASS 2/2 (watchdog restart + preset giữ exact), probe F-PROC-1 PASS (không injection). Commit: fix 5.2 audit findings on Windows.


6. PHỤ LỤC A — QUY TRÌNH THỰC HIỆN TRÊN MÁY WINDOWS

Quy trình 2 máy: Windows sửa code + build + test + fix + commit + push → Linux pull, review, ghi chú, cập nhật báo cáo + push → Windows pull lại, verify tiếp. Các lệnh dưới chạy trên máy Windows (PowerShell), đường dẫn theo máy dev C:\Users\locpham\SonicForgeStudio.

6.1 Cấu trúc + đường dẫn cố định

Thứ Vị trí
Repo (Windows) C:\Users\locpham\SonicForgeStudio
Build bridge native_bridge\build\ (CMake) — exe: native_bridge\build\Release\daw_vst_bridge.exe
Install (bundle chạy thật) C:\Users\locpham\SonicForgeStudio\install\ — app: sonicforge-daw.exe
Logs %APPDATA%\SonicForgeDAW\logs\ — bridge.log (stdout bridge), spawn.log (watchdog/restart), engine.log
State file G2 (MỚI) %APPDATA%\SonicForgeDAW\state\bridge_state.json (+ .tmp khi atomic write)
VST test C:\Program Files\Common Files\VST3\Nexus.vst3, ...\Toontrack\EZkeys 2.vst3
SHM SonicForge_DAW_IPC_<APP_PID>; offsets: controlQueue @2836 (8×1040), count @11156, attachInFlight @11160, writeIndex @4, size 11164. Types: 2=LOAD, 3=TRANSPORT, 4=OPEN_GUI, 5=DUMP_STATE (G2.2 thêm)
Probe native_bridge\build\Release\gui_probe.exe (variants: main_own/worker_own_nopump/…/ui_thread/shared_worker)

6.2 Build

# trong repo Windows
cd native_bridge\build
cmake -DCMAKE_CXX_FLAGS_RELEASE='/O2 /Ob2 /DNDEBUG /Zi' -DCMAKE_EXE_LINKER_FLAGS_RELEASE='/DEBUG' .
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
cmake --build . --config Release --target daw_vst_bridge

hoặc chạy script có sẵn: native_bridge\debug\build_bridge.ps1 (dùng build dir cũ).

  • Thêm file mới (G2.0 StateStore.cpp) → nhớ thêm vào native_bridge/CMakeLists.txt target daw_vst_bridge, rồi re-run cmake.
  • Lưu ý CRLF: toàn bộ native_bridge/ + CMakeLists.txt là CRLF — patch đa dòng phải qua script Python io.open(newline=''), không heredoc <<'EOF' (khớp sai).

6.3 Deploy (mẫu deploy_g12.ps1)

$install = 'C:\Users\locpham\SonicForgeStudio\install'
$new     = 'C:\Users\locpham\SonicForgeStudio\native_bridge\build\Release\daw_vst_bridge.exe'
Get-Process sonicforge-daw, daw_engine, daw_vst_bridge -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2
Copy-Item -Path $new -Destination (Join-Path $install 'daw_vst_bridge.exe') -Force
Start-Process -FilePath (Join-Path $install 'sonicforge-daw.exe')
Start-Sleep -Seconds 8
Get-Process sonicforge-daw, daw_engine, daw_vst_bridge -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, Path | Format-Table -AutoSize
  • 3 process phải kill hết: sonicforge-daw, daw_engine, daw_vst_bridge (bridge cũ không chết → 2 bridge map chung SHM → race).
  • Sau start: lấy APP_PID mới (tasklist/Get-Process) → truyền vào probe (G2.5) — PID đổi mỗi lần deploy.

6.4 Verify không cần engine (Linux + Windows đều chạy được)

  • shm_selfcheck.cpp (đã build từ trước; layout 11164 → sau G2 không đổi vì state ghi ra đĩa, không thêm field SHM): chạy pass = control queue/attach flag/writeIndex đúng.
  • JS: node --check app/static/js/app.precompiled.js (sau mỗi lần sửa JS).
  • Rust (G2.2): cargo check trong src-tauri/ — máy Linux hiện không có toolchain → bắt buộc trên Windows.

6.5 Stress (G2.5, theo mẫu test_g15_stress.py)

python native_bridge\debug\test_g2_stress.py <APP_PID> <N>
  • Đọc SHM trực tiếp (ctypes), đẩy control LOAD/OPEN_GUI/PLAY/STOP, theo dõi bridge.log.
  • Verify: bridge alive, writeIndex advancing mỗi iter, không EXCEPTION/FAILED/crash.
  • G2 thêm bước kill giữa chừng: Stop-Process daw_vst_bridge → chờ respawn (spawn.log) → verify writeIndex + preset restored.
  • Cảnh báo learning cũ: probe PASS có thể dương tính giả → luôn kiểm tra bridge.log/spawn.log thực; dùng chung since window (log cùng job xuất hiện ~đồng thời), không reset.

6.6 Vòng lặp bắt bugs → fix → report

  1. Windows chạy verify → lỗi → thu log (bridge.log, spawn.log, engine.log) + (nếu crash) minidump/cdb.
  2. Windows xác định lỗi thuộc task nào → sửa code → build → deploy → verify lại → commit + push.
  3. Linux pull → review + ghi chú, cập nhật bảng mục 5 + nhật ký → push.
  4. Windows pull lại, tiếp tục task sau.
  5. Kết thúc G2.6: chạy lại regression G1.5 (test_g15_stress.py) — G2 không được làm hỏng G1.

6.7 Tiện ích / lưu ý khác

  • tasklist CSV: dùng subprocess.run(['tasklist']) full output + line.split()[1].
  • sleep không tồn tại trong sh (máy Windows) → python -c "import time; time.sleep(N)".
  • timeout Windows khác GNU → dùng wrapper python subprocess.
  • Dấu hiệu lỗi quen thuộc: 0xC0000409 ucrtbase = fail-fast/terminate (không bắt được bằng catch(...) thường); 0xC00000FD = stack overflow (IME recursion); 0xc000041d = cross-thread destroy.