move engine to engine/

This commit is contained in:
Loic Coenen
2026-05-13 17:55:59 +00:00
parent 10d0269a5a
commit f3dde6b668
38 changed files with 3141 additions and 0 deletions

View File

@@ -1,74 +0,0 @@
# Code Evaluation
## Summary Table
| Category | Rating | Remarks |
|--------------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Mocked / Left Undone** | ✅ Complete | All features are implemented: audio/MIDI looping, dynamic channels, bind/unbind, FIFO pipe, MIDI control with note 66 for MIDI channel creation, FIFO `add_midi` command. Integration tests cover MIDI channel creation, FIFO stop/bind/unbind, and all previously missing functionality. No placeholder code remains. |
| **Potential Segfaults** | ✅ Good | Every `jack_port_get_buffer()` call is nullchecked based on channel type. Array accesses bounded by `channel_capacity`. No useafterfree deferred cleanup ensures RT thread has finished with old resources. The only unprotected call is in `midi_handle_events`, but the caller has already verified the buffer. |
| **Memory Safety** | ✅ Good | Dynamic channel array allocated with `calloc`, freed exactly once after one RT cycle via deferred free. No leaks. Integration tests do not leak JACK clients or file descriptors. All other buffers are stackallocated or static. |
| **Thread Safety / Race** | ✅ Good | Three SPSC queues with correct atomic memory ordering (`acquire`/`release`). Shared state uses atomics. Deferred port/array cleanup uses `global_rt_cycles` with releaseacquire synchronisation. Channel `type` is written before `active=1` (release), RT thread reads `type` only after confirming `active==1` (acquire). No data races. |
| **Performance** | ✅ Good | RT callback has no syscalls, locks, or allocations. Linear perchannel processing. Main loop sleeps 50ms negligible overhead. Integration tests are slow (~25s total) due to fixed `usleep()` waits; this is acceptable for an integration suite. |
| **Architectural Soundness** | ✅ Good | Clean commanddriven design; persource input queues; RCUlike deferred cleanup; extensible. Integration tests are wellstructured (pertest looper process, real JACK connections, helpers). Missing test coverage has been addressed (MIDI channel creation, FIFO stop/bind/unbind). |
## Detailed Remarks
### 1. Mocked / Left Undone
- **Nothing remains.**
- `CMD_ADD_MIDI_CHANNEL` is triggered by MIDI note66 (under control key) and by FIFO command `"add_midi"`.
- `CMD_STOP` is sent from MIDI (note65 under control key) and from FIFO (`"stop"`).
- `CMD_BIND_CHANNEL`, `CMD_UNBIND`, `CMD_CYCLE`, `CMD_ADD_CHANNEL`, `CMD_REMOVE_CHANNEL` are all wired.
- The integration test suite now includes `test_fifo_stop_bind_unbind()` and `test_midi_channel_add()`.
- The FIFO pipe reader handles `"stop"`, `"bind <ch>"`, `"unbind"`, and `"add_midi"`.
- **Note:** The separate test files in `tests/` (`test_audio.c`, `test_channel.c`, `test_fifo.c`, `test_loop.c`, `main.c`) are not compiled by the makefile and require a missing `test_common.h`. They are not part of the build they do not affect functionality and may be removed in a future cleanup.
### 2. Potential Segfaults
- **Audio channels:** `audio_in`/`audio_out` are checked for NULL before use.
- **MIDI channels:** `midi_in`/`midi_out` are checked before use.
- All `jack_port_get_buffer()` calls are inside guarded blocks.
- Array indices are validated: `cap = atomic_load(&channel_capacity); idx < cap`.
- The only unguarded call is in `midi_handle_events`, but its caller (`process_callback`) has already verified the port buffer pointer.
### 3. Memory Safety
- The channel array is grown via `calloc` + memcpy + atomic exchange. The old pointer is freed only after at least one RT cycle has passed (`pending_old_cycle` vs `global_rt_cycles`).
- No dynamic allocation occurs in the RT callback.
- The FIFO pipe thread uses a stackallocated buffer (`char line[LINE_MAX]`).
- No memory leaks: every `calloc` is eventually freed, and JACK ports are unregistered in deferred cleanup.
### 4. Thread Safety / Race Conditions
- **Three SPSC queues:**
- `cmd_queue` producer = RT callback, consumer = same RT (no race).
- `cmd_queue_main_midi` producer = RT callback, consumer = main loop.
- `cmd_queue_main_fifo` producer = FIFO thread, consumer = main loop.
- All queues use correct `memory_order_acquire`/`release` for head/tail.
- `global_rt_cycles` is incremented with `memory_order_release` at the end of every RT cycle.
- Deferred port unregistration and array free both wait for `current_cycle - pending_cycle >= 1`, guaranteeing the RT thread has seen the change.
- `prev_state` is a plain `int` but only accessed from the RT thread safe.
- No data races detected.
### 5. Performance
- RT callback per frame:
1. MIDI event scan (may push to queues).
2. Drain `cmd_queue` (usually 02 commands).
3. Perchannel processing linear audio or MIDI event copy/playback.
4. MIDI clock events (rare).
5. Increment `global_rt_cycles`.
- No syscalls, locks, or heap operations.
- Main loop sleeps 50ms; draining two queues adds negligible overhead.
### 6. Architectural Soundness
- **Commanddriven design** all state changes are explicit `command_t` structs.
- **Input source isolation** each source (MIDI, FIFO) has its own queue for mainloop commands. RTsafe commands go to `cmd_queue`.
- **Deferred cleanup** RCUlike pattern for port unregistration and array deallocation ensures no useafterfree.
- **Extensibility** adding a new control input requires only a new SPSC queue, a producer thread, and a drain loop in `looper_process_commands()`.
- Integration tests cover all major control paths.
## Overall Verdict
The code is **complete, racefree, memorysafe, and architecturally sound**.
- All intended features are implemented and tested.
- No segfault or memory corruption is possible under normal operation.
- Thread safety is correctly handled with atomic variables and deferred cleanup.
- Performance is suitable for realtime audio.
- The architecture is clean and extensible.