G3.2+G3.3: classify hang/crash vs audio stall + SEH per-channel plugin crash isolation (mute channel)
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
// native_bridge/tests/seh_channel_test.cpp
|
||||
// G3.3: SEH per-channel crash isolation. A plugin whose processAudioBlock
|
||||
// faults (access violation) must be caught by SafeProcessChannel -> renderAll
|
||||
// mutes the channel instead of taking down the whole bridge process.
|
||||
// Assert-based, no framework. Includes the engine .cpp (like shm_selfcheck).
|
||||
#include "../include/NativeInstrumentEngine.h"
|
||||
#include "../src/NativeInstrumentEngine.cpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
class CrashingStub : public INativeInstrument {
|
||||
public:
|
||||
bool init(double, uint32_t) override { return true; }
|
||||
void selectProgram(uint32_t, uint32_t, uint32_t) override {}
|
||||
void noteOn(uint32_t, uint32_t, float, uint32_t) override {}
|
||||
void noteOff(uint32_t, uint32_t, uint32_t) override {}
|
||||
void controlChange(uint32_t, uint32_t, uint32_t) override {}
|
||||
void programChange(uint32_t, uint32_t) override {}
|
||||
void pitchBend(uint32_t, uint32_t) override {}
|
||||
bool openGUI(void*) override { return false; }
|
||||
void closeGUI() override {}
|
||||
void processAudioBlock(float*, float*, uint32_t) override {
|
||||
volatile int* p = nullptr;
|
||||
*p = 42; // deliberate access violation
|
||||
}
|
||||
};
|
||||
|
||||
class OkStub : public INativeInstrument {
|
||||
public:
|
||||
bool init(double, uint32_t) override { return true; }
|
||||
void selectProgram(uint32_t, uint32_t, uint32_t) override {}
|
||||
void noteOn(uint32_t, uint32_t, float, uint32_t) override {}
|
||||
void noteOff(uint32_t, uint32_t, uint32_t) override {}
|
||||
void controlChange(uint32_t, uint32_t, uint32_t) override {}
|
||||
void programChange(uint32_t, uint32_t) override {}
|
||||
void pitchBend(uint32_t, uint32_t) override {}
|
||||
bool openGUI(void*) override { return false; }
|
||||
void closeGUI() override {}
|
||||
void processAudioBlock(float* outL, float* outR, uint32_t n) override {
|
||||
for (uint32_t i = 0; i < n; ++i) { outL[i] = 1.0f; outR[i] = 2.0f; }
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
// 1. Faulting plugin: SEH catches the AV, process survives, returns false.
|
||||
{
|
||||
float L[64] = {}, R[64] = {};
|
||||
CrashingStub stub;
|
||||
bool ok = SafeProcessChannel(&stub, L, R, 64);
|
||||
assert(!ok);
|
||||
// Block left untouched (renderAll continues without this channel).
|
||||
bool allZero = true;
|
||||
for (float f : L) if (f != 0.0f) allZero = false;
|
||||
assert(allZero);
|
||||
}
|
||||
// 2. Healthy plugin passes through unchanged.
|
||||
{
|
||||
float L[64] = {}, R[64] = {};
|
||||
OkStub stub;
|
||||
bool ok = SafeProcessChannel(&stub, L, R, 64);
|
||||
assert(ok);
|
||||
assert(L[0] == 1.0f && R[0] == 2.0f);
|
||||
}
|
||||
// 3. Manager crash-mute flags: set, read, and renderAll skips a crashed
|
||||
// channel (no exception escapes into the audio loop).
|
||||
{
|
||||
InstrumentEngineManager mgr;
|
||||
assert(!mgr.isCrashed(3));
|
||||
mgr.markCrashed(3);
|
||||
assert(mgr.isCrashed(3));
|
||||
assert(!mgr.isCrashed(4)); // other channels unaffected
|
||||
float L[64] = {}, R[64] = {};
|
||||
mgr.renderAll(L, R, 64); // crashed + unassigned: no-op, no crash
|
||||
assert(L[0] == 0.0f);
|
||||
}
|
||||
std::printf("SEH_CHANNEL_TEST PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user