2026-11-06 00:49:37 +01:00
|
|
|
#define _XOPEN_SOURCE 700
|
2026-10-27 03:17:29 +01:00
|
|
|
/* GNU ld allocation interposition: fail preparation at every allocation and
|
|
|
|
|
* reject host allocation/reclamation inside processing. No production hooks. */
|
|
|
|
|
#include "xtc/xtc_realtime.h"
|
|
|
|
|
#include "xtc/xtc_native.h"
|
2026-10-27 16:49:30 +01:00
|
|
|
#include "xtc/xtc_control.h"
|
2026-11-05 23:44:48 +01:00
|
|
|
#include "xtc/xtc_plugin.h"
|
2026-11-06 00:25:23 +01:00
|
|
|
#include "xtc/xtc_editing.h"
|
2026-11-06 00:49:37 +01:00
|
|
|
#include "xtc/xtc_audio.h"
|
2026-10-27 03:17:29 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2026-10-27 17:07:37 +01:00
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <unistd.h>
|
2026-11-05 23:50:14 +01:00
|
|
|
#include <string.h>
|
2026-10-27 03:17:29 +01:00
|
|
|
|
|
|
|
|
void *__real_malloc(size_t size);
|
|
|
|
|
void *__real_calloc(size_t count, size_t size);
|
|
|
|
|
void *__real_realloc(void *pointer, size_t size);
|
|
|
|
|
void __real_free(void *pointer);
|
|
|
|
|
static int processing, fail_after = -1;
|
2026-10-27 17:07:37 +01:00
|
|
|
/* Catch direct host I/O and blocking synchronization in guarded callbacks.
|
|
|
|
|
* Dynamic dependencies have their own symbols and are outside this audit. */
|
|
|
|
|
FILE *__real_fopen(const char *path, const char *mode);
|
|
|
|
|
size_t __real_fread(void *buffer, size_t size, size_t count, FILE *file);
|
|
|
|
|
size_t __real_fwrite(const void *buffer, size_t size, size_t count, FILE *file);
|
|
|
|
|
ssize_t __real_read(int fd, void *buffer, size_t size);
|
|
|
|
|
ssize_t __real_write(int fd, const void *buffer, size_t size);
|
|
|
|
|
int __real_pthread_mutex_lock(pthread_mutex_t *mutex);
|
|
|
|
|
int __real_pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *mutex);
|
|
|
|
|
FILE *__wrap_fopen(const char *path, const char *mode)
|
|
|
|
|
{ assert(!processing); return __real_fopen(path, mode); }
|
|
|
|
|
size_t __wrap_fread(void *buffer, size_t size, size_t count, FILE *file)
|
|
|
|
|
{ assert(!processing); return __real_fread(buffer, size, count, file); }
|
|
|
|
|
size_t __wrap_fwrite(const void *buffer, size_t size, size_t count, FILE *file)
|
|
|
|
|
{ assert(!processing); return __real_fwrite(buffer, size, count, file); }
|
|
|
|
|
ssize_t __wrap_read(int fd, void *buffer, size_t size)
|
|
|
|
|
{ assert(!processing); return __real_read(fd, buffer, size); }
|
|
|
|
|
ssize_t __wrap_write(int fd, const void *buffer, size_t size)
|
|
|
|
|
{ assert(!processing); return __real_write(fd, buffer, size); }
|
|
|
|
|
int __wrap_pthread_mutex_lock(pthread_mutex_t *mutex)
|
|
|
|
|
{ assert(!processing); return __real_pthread_mutex_lock(mutex); }
|
|
|
|
|
int __wrap_pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *mutex)
|
|
|
|
|
{ assert(!processing); return __real_pthread_cond_wait(condition, mutex); }
|
2026-10-27 03:17:29 +01:00
|
|
|
static unsigned int prepared, destroyed;
|
|
|
|
|
static int fail_allocation(void)
|
|
|
|
|
{
|
|
|
|
|
assert(!processing);
|
|
|
|
|
if (fail_after < 0) return 0;
|
|
|
|
|
if (fail_after == 0) return 1;
|
|
|
|
|
--fail_after;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
void *__wrap_malloc(size_t size) { return fail_allocation() ? NULL : __real_malloc(size); }
|
|
|
|
|
void *__wrap_calloc(size_t count, size_t size) { return fail_allocation() ? NULL : __real_calloc(count, size); }
|
|
|
|
|
void *__wrap_realloc(void *pointer, size_t size) { return fail_allocation() ? NULL : __real_realloc(pointer, size); }
|
|
|
|
|
void __wrap_free(void *pointer) { assert(!processing); __real_free(pointer); }
|
|
|
|
|
static xtc_result prepare(double rate, size_t frames, float parameter, void **state)
|
|
|
|
|
{
|
|
|
|
|
(void)rate; (void)frames;
|
|
|
|
|
assert(!processing);
|
|
|
|
|
*state = malloc(sizeof(float));
|
|
|
|
|
if (!*state) return XTC_ERR_OUT_OF_MEMORY;
|
|
|
|
|
*(float *)*state = parameter;
|
|
|
|
|
++prepared;
|
|
|
|
|
return XTC_OK;
|
|
|
|
|
}
|
|
|
|
|
static void reset(void *state) { (void)state; }
|
|
|
|
|
static xtc_result process(void *state, const xtc_native_block *block)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
assert(processing);
|
|
|
|
|
for (i = 0; i < block->frames; ++i)
|
|
|
|
|
block->output[i] = block->input[i] * *(float *)state;
|
|
|
|
|
return XTC_OK;
|
|
|
|
|
}
|
|
|
|
|
static void destroy(void *state)
|
|
|
|
|
{
|
|
|
|
|
assert(!processing);
|
|
|
|
|
if (state) ++destroyed;
|
|
|
|
|
free(state);
|
|
|
|
|
}
|
|
|
|
|
static void render(xtc_realtime *engine, float expected)
|
|
|
|
|
{
|
|
|
|
|
float input[17], left[17], right[17];
|
|
|
|
|
size_t i;
|
|
|
|
|
for (i = 0; i < 17; ++i) input[i] = 1;
|
|
|
|
|
processing = 1;
|
|
|
|
|
assert(xtc_realtime_process_stereo(engine, input, NULL, NULL, NULL, NULL, left, right, 17) == XTC_OK);
|
|
|
|
|
processing = 0;
|
|
|
|
|
for (i = 0; i < 17; ++i) assert(left[i] == expected && right[i] == expected);
|
|
|
|
|
}
|
|
|
|
|
/* Maximum-sized reverse-insertion chain: long cycle rollback must preserve
|
|
|
|
|
* routing, and compiled snapshots survive destruction of the editable model. */
|
|
|
|
|
static void graph_limits(void)
|
|
|
|
|
{
|
|
|
|
|
xtc_graph *graph = NULL;
|
|
|
|
|
xtc_plan *plan = NULL;
|
|
|
|
|
size_t nodes[256], i;
|
|
|
|
|
float input[3] = {1, -0.5f, 0.25f}, output[3];
|
|
|
|
|
assert(xtc_graph_create(256, &graph) == XTC_OK);
|
|
|
|
|
for (i = 0; i < 256; ++i)
|
|
|
|
|
assert(xtc_graph_add_gain(graph, 1, &nodes[i]) == XTC_OK);
|
|
|
|
|
for (i = 255; i > 0; --i)
|
|
|
|
|
assert(xtc_graph_connect(graph, nodes[i], nodes[i - 1]) == XTC_OK);
|
|
|
|
|
assert(xtc_graph_connect(graph, nodes[0], nodes[255]) == XTC_ERR_GRAPH_CYCLE);
|
|
|
|
|
assert(xtc_graph_validate(graph) == XTC_OK);
|
|
|
|
|
assert(xtc_graph_compile(graph, 48000, 3, &plan) == XTC_OK);
|
|
|
|
|
xtc_graph_destroy(graph);
|
|
|
|
|
processing = 1;
|
|
|
|
|
assert(xtc_plan_process(plan, input, output, 3) == XTC_OK);
|
|
|
|
|
processing = 0;
|
|
|
|
|
for (i = 0; i < 3; ++i) assert(output[i] == input[i]);
|
|
|
|
|
xtc_plan_destroy(plan);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-27 16:49:30 +01:00
|
|
|
static void history_reservations(void)
|
|
|
|
|
{
|
|
|
|
|
xtc_project *project = NULL, *snapshot = NULL;
|
|
|
|
|
xtc_history *history = NULL;
|
|
|
|
|
uint64_t track, bus;
|
|
|
|
|
unsigned int map[3] = {1,74,11}, actual[3];
|
|
|
|
|
size_t undo_count, redo_count, tracks, clips;
|
|
|
|
|
int failure, succeeded = 0;
|
|
|
|
|
assert(xtc_project_create(&project) == XTC_OK);
|
|
|
|
|
assert(xtc_project_add_track(project,"History",&track) == XTC_OK);
|
|
|
|
|
assert(xtc_history_create(project,&history) == XTC_OK);
|
|
|
|
|
assert(xtc_project_set_mix(project,track,.5f,0,0,0) == XTC_OK);
|
|
|
|
|
assert(xtc_history_record(history,project) == XTC_OK);
|
|
|
|
|
assert(xtc_history_checkout(history,0,&snapshot) == XTC_OK && xtc_history_accept(history,0) == XTC_OK);
|
|
|
|
|
xtc_project_destroy(project); project = snapshot; snapshot = NULL;
|
|
|
|
|
for (failure = 0; failure < 12; ++failure) {
|
|
|
|
|
xtc_result r;
|
|
|
|
|
fail_after = failure;
|
|
|
|
|
r = xtc_history_reserve(history);
|
|
|
|
|
fail_after = -1;
|
|
|
|
|
assert(xtc_history_counts(history,&undo_count,&redo_count) == XTC_OK && undo_count == 0 && redo_count == 1);
|
|
|
|
|
if (r == XTC_OK) { succeeded = 1; break; }
|
|
|
|
|
assert(r == XTC_ERR_OUT_OF_MEMORY);
|
|
|
|
|
}
|
|
|
|
|
assert(succeeded);
|
|
|
|
|
fail_after = 0; // Fail every allocation: reserved record must still succeed.
|
|
|
|
|
assert(xtc_history_reserve(history) == XTC_OK);
|
|
|
|
|
assert(xtc_project_add_bus(project,"Bus",&bus) == XTC_OK);
|
|
|
|
|
assert(xtc_project_route_track(project,track,bus) == XTC_OK);
|
|
|
|
|
assert(xtc_project_set_control_map(project,0,map) == XTC_OK);
|
|
|
|
|
assert(xtc_history_record(history,project) == XTC_OK);
|
|
|
|
|
fail_after = -1;
|
|
|
|
|
assert(xtc_history_counts(history,&undo_count,&redo_count) == XTC_OK && undo_count == 1 && redo_count == 0);
|
|
|
|
|
assert(xtc_history_checkout(history,0,&snapshot) == XTC_OK);
|
|
|
|
|
assert(xtc_project_get_counts(snapshot,&tracks,&clips) == XTC_OK && tracks == 1 && clips == 0);
|
|
|
|
|
assert(xtc_project_get_control_map(snapshot,0,actual) == XTC_OK && actual[0] == 7);
|
|
|
|
|
xtc_project_destroy(snapshot); snapshot = NULL;
|
|
|
|
|
assert(xtc_history_accept(history,0) == XTC_OK && xtc_history_checkout(history,1,&snapshot) == XTC_OK);
|
|
|
|
|
assert(xtc_project_get_counts(snapshot,&tracks,&clips) == XTC_OK && tracks == 2);
|
|
|
|
|
assert(xtc_project_get_control_map(snapshot,0,actual) == XTC_OK && actual[0] == 1 && actual[1] == 74);
|
|
|
|
|
xtc_project_destroy(snapshot); xtc_project_destroy(project); xtc_history_destroy(history);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-05 23:44:48 +01:00
|
|
|
static void builtin_failures(void)
|
|
|
|
|
{
|
|
|
|
|
unsigned int id;
|
2026-11-05 23:53:03 +01:00
|
|
|
for(id=0;id<7;++id) {
|
2026-11-05 23:44:48 +01:00
|
|
|
int failure, succeeded=0; xtc_plugin_spec spec;
|
|
|
|
|
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&spec)==XTC_OK);
|
2026-11-05 23:50:14 +01:00
|
|
|
if(id==4) {assert(getcwd(spec.sample_path,sizeof(spec.sample_path)));strcat(spec.sample_path,"/examples/projects/original-pluck.wav");}
|
2026-11-05 23:44:48 +01:00
|
|
|
for(failure=0;failure<16;++failure) {
|
|
|
|
|
xtc_plugin *plugin=NULL; xtc_result result; float in[256]={0},out[256];
|
|
|
|
|
fail_after=failure;result=xtc_plugin_create(&spec,48000,256,&plugin);fail_after=-1;
|
|
|
|
|
if(result!=XTC_OK) {assert(result==XTC_ERR_OUT_OF_MEMORY && !plugin);continue;}
|
|
|
|
|
processing=1;
|
2026-11-05 23:50:14 +01:00
|
|
|
if(spec.instrument) {xtc_midi_event event={0,XTC_MIDI_NOTE_ON,0,60,100};
|
2026-11-05 23:44:48 +01:00
|
|
|
assert(xtc_plugin_process_midi(plugin,&event,1,out,256)==XTC_OK);
|
2026-11-05 23:53:03 +01:00
|
|
|
} else if(id>=5) {float right[256];assert(xtc_plugin_process_stereo_at(plugin,in,in,out,right,256,0)==XTC_OK);}
|
|
|
|
|
else assert(xtc_plugin_process(plugin,in,out,256)==XTC_OK);
|
2026-11-05 23:55:56 +01:00
|
|
|
xtc_plugin_reset_history(plugin);
|
2026-11-05 23:44:48 +01:00
|
|
|
processing=0;xtc_plugin_destroy(plugin);succeeded=1;break;
|
|
|
|
|
}
|
|
|
|
|
assert(succeeded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-06 00:25:23 +01:00
|
|
|
static void modern_edit_failures(void)
|
|
|
|
|
{
|
|
|
|
|
xtc_project *p=NULL;xtc_clipboard *board=NULL;xtc_mix_snapshot *mix=NULL;
|
|
|
|
|
uint64_t track,clip,note;int operation,failure;
|
|
|
|
|
xtc_midi_transform edit={0};edit.grid=240;edit.swing=50;edit.strength=100;
|
|
|
|
|
assert(xtc_project_create(&p)==XTC_OK);
|
|
|
|
|
assert(xtc_project_add_track(p,"Track",&track)==XTC_OK);
|
|
|
|
|
assert(xtc_project_add_midi_clip(p,track,960,1920,&clip)==XTC_OK);
|
|
|
|
|
assert(xtc_project_add_note(p,clip,230,120,61,100,0,¬e)==XTC_OK);
|
|
|
|
|
assert(xtc_clipboard_create(p,&clip,1,&board)==XTC_OK);
|
|
|
|
|
assert(xtc_mix_snapshot_create(p,&mix)==XTC_OK);
|
|
|
|
|
for(operation=0;operation<7;++operation) {
|
|
|
|
|
int succeeded=0;
|
|
|
|
|
for(failure=0;failure<32;++failure) {
|
|
|
|
|
xtc_project *copy=NULL;xtc_clipboard *created=NULL;xtc_mix_snapshot *captured=NULL;
|
|
|
|
|
uint64_t output=999;xtc_clip_info c;xtc_note_info n;size_t tracks,clips;xtc_result r;
|
|
|
|
|
assert(xtc_project_clone(p,©)==XTC_OK);
|
|
|
|
|
fail_after=failure;
|
|
|
|
|
switch(operation) {
|
|
|
|
|
case 0:r=xtc_project_transform_clips(copy,&clip,1,XTC_CLIPS_DUPLICATE,1920,&output);break;
|
|
|
|
|
case 1:r=xtc_clipboard_create(copy,&clip,1,&created);break;
|
|
|
|
|
case 2:r=xtc_clipboard_paste(board,copy,track,3840,&output);break;
|
|
|
|
|
case 3:r=xtc_project_repeat_clips(copy,&clip,1,2);break;
|
|
|
|
|
case 4:r=xtc_project_transform_midi(copy,&clip,1,&edit);break;
|
|
|
|
|
case 5:r=xtc_mix_snapshot_create(copy,&captured);break;
|
|
|
|
|
default:r=xtc_mix_snapshot_recall(mix,copy);break;
|
|
|
|
|
}
|
|
|
|
|
fail_after=-1;
|
|
|
|
|
if(r==XTC_OK)succeeded=1;
|
|
|
|
|
else {
|
|
|
|
|
assert(r==XTC_ERR_OUT_OF_MEMORY && output==999 && !created && !captured);
|
|
|
|
|
assert(xtc_project_get_counts(copy,&tracks,&clips)==XTC_OK && clips==1);
|
|
|
|
|
assert(xtc_project_find_clip(copy,clip,&c)==XTC_OK && c.start_tick==960);
|
|
|
|
|
assert(xtc_project_get_note(copy,0,&n)==XTC_OK && n.start_tick==230 && n.id==note);
|
|
|
|
|
{uint64_t probe;assert(xtc_project_duplicate_clip(copy,clip,track,3840,&probe)==XTC_OK && probe==clip+1);}
|
|
|
|
|
}
|
|
|
|
|
xtc_clipboard_destroy(created);xtc_mix_snapshot_destroy(captured);xtc_project_destroy(copy);
|
|
|
|
|
if(succeeded)break;
|
|
|
|
|
}
|
|
|
|
|
assert(succeeded);
|
|
|
|
|
}
|
|
|
|
|
xtc_clipboard_destroy(board);xtc_mix_snapshot_destroy(mix);xtc_project_destroy(p);
|
|
|
|
|
}
|
2026-11-06 00:49:37 +01:00
|
|
|
static void composition_failures(void)
|
|
|
|
|
{
|
|
|
|
|
xtc_project *p=NULL;uint64_t t,ids[2],note,audio;char sample[4096],root[]="/tmp/xtc-compose-fail-XXXXXX";
|
|
|
|
|
int operation,failure;xtc_midi_transform human={0};int intervals[2]={4,7};
|
|
|
|
|
assert(mkdtemp(root));assert(getcwd(sample,sizeof(sample)));assert(strlen(sample)+40<sizeof(sample));strcat(sample,"/examples/projects/original-pluck.wav");
|
|
|
|
|
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Track",&t)==XTC_OK);
|
|
|
|
|
assert(xtc_project_add_midi_clip(p,t,0,1920,&ids[0])==XTC_OK && xtc_project_add_midi_clip(p,t,3840,1920,&ids[1])==XTC_OK);
|
|
|
|
|
assert(xtc_project_add_note(p,ids[0],480,240,60,100,0,¬e)==XTC_OK && xtc_project_add_note(p,ids[1],480,240,64,100,0,¬e)==XTC_OK);
|
|
|
|
|
assert(xtc_project_import_audio(p,t,sample,7680,&audio)==XTC_OK);
|
|
|
|
|
fail_after=0;assert(xtc_project_set_marker(p,3840,"Section")==XTC_OK);fail_after=-1;
|
|
|
|
|
human.type=XTC_MIDI_TRANSFORM_HUMANIZE;human.seed=42;human.timing_variation=12;human.velocity_variation=8;
|
|
|
|
|
for(operation=0;operation<9;++operation) {
|
|
|
|
|
int succeeded=0;
|
|
|
|
|
for(failure=0;failure<128;++failure) {
|
|
|
|
|
xtc_project *copy=NULL;xtc_clip_info c;uint64_t output=999;xtc_result r;size_t tracks,clips,notes;
|
|
|
|
|
char dest[512],file[512];snprintf(dest,sizeof(dest),"%s/copy-%d-%d",root,operation,failure);
|
|
|
|
|
assert(xtc_project_clone(p,©)==XTC_OK);fail_after=failure;
|
|
|
|
|
switch(operation) {
|
|
|
|
|
case 0:r=xtc_project_ripple_time(copy,1920,960,0);break;
|
|
|
|
|
case 1:r=xtc_project_duplicate_track(copy,t,"Copy",&output);break;
|
|
|
|
|
case 2:r=xtc_project_split_midi(copy,ids[0],960,&output);break;
|
|
|
|
|
case 3:r=xtc_project_merge_midi(copy,ids,2,&output);break;
|
|
|
|
|
case 4:r=xtc_project_transform_midi(copy,ids,2,&human);break;
|
|
|
|
|
case 5:r=xtc_project_chordify(copy,ids,2,intervals,2);break;
|
|
|
|
|
case 6:r=xtc_project_arpeggiate(copy,ids,2,120,50,0);break;
|
|
|
|
|
case 7:r=xtc_project_normalize_audio(copy,&audio,1,.25f);break;
|
|
|
|
|
default:r=xtc_project_collect(copy,dest);break;
|
|
|
|
|
}
|
|
|
|
|
fail_after=-1;
|
|
|
|
|
if(r==XTC_OK)succeeded=1;
|
|
|
|
|
else {
|
|
|
|
|
assert((r==XTC_ERR_OUT_OF_MEMORY || (operation==8 && r==XTC_ERR_IO)) && output==999);
|
|
|
|
|
assert(xtc_project_get_counts(copy,&tracks,&clips)==XTC_OK && tracks==1 && clips==3);
|
|
|
|
|
assert(xtc_project_note_count(copy,¬es)==XTC_OK && notes==2);
|
|
|
|
|
assert(xtc_project_find_clip(copy,ids[1],&c)==XTC_OK && c.start_tick==3840);
|
|
|
|
|
assert(access(dest,F_OK)!=0);
|
|
|
|
|
{uint64_t probe;assert(xtc_project_duplicate_clip(copy,ids[0],t,9600,&probe)==XTC_OK && probe==3);}
|
|
|
|
|
}
|
|
|
|
|
if(operation==8 && succeeded){snprintf(file,sizeof(file),"%s/copy-%d-%d/project.xtc",root,operation,failure);assert(unlink(file)==0);snprintf(file,sizeof(file),"%s/copy-%d-%d/media-000.wav",root,operation,failure);assert(unlink(file)==0 && rmdir(dest)==0);}
|
|
|
|
|
xtc_project_destroy(copy);if(succeeded)break;
|
|
|
|
|
}
|
|
|
|
|
assert(succeeded);
|
|
|
|
|
}
|
|
|
|
|
xtc_project_destroy(p);assert(rmdir(root)==0);
|
|
|
|
|
}
|
2026-10-27 03:17:29 +01:00
|
|
|
int main(void)
|
|
|
|
|
{
|
2026-11-06 00:49:37 +01:00
|
|
|
composition_failures();
|
2026-11-06 00:25:23 +01:00
|
|
|
modern_edit_failures();
|
2026-11-05 23:44:48 +01:00
|
|
|
builtin_failures();
|
2026-10-27 03:17:29 +01:00
|
|
|
graph_limits();
|
2026-10-27 16:49:30 +01:00
|
|
|
history_reservations();
|
2026-10-27 03:17:29 +01:00
|
|
|
xtc_native_descriptor descriptor = {XTC_NATIVE_ABI_VERSION, sizeof(descriptor),
|
|
|
|
|
"reliability.gain", XTC_NATIVE_EFFECT, prepare, reset, process, destroy};
|
|
|
|
|
xtc_graph *graph = NULL;
|
|
|
|
|
size_t node;
|
|
|
|
|
unsigned int iteration;
|
|
|
|
|
assert(xtc_graph_create(1, &graph) == XTC_OK);
|
|
|
|
|
assert(xtc_graph_add_native(graph, &descriptor, 2, &node) == XTC_OK);
|
|
|
|
|
for (iteration = 0; iteration < 100; ++iteration) {
|
|
|
|
|
xtc_realtime *engine = NULL;
|
|
|
|
|
int failure, succeeded = 0;
|
|
|
|
|
assert(xtc_realtime_create(48000, 17, &engine) == XTC_OK);
|
|
|
|
|
assert(xtc_realtime_submit(engine, graph) == XTC_OK);
|
|
|
|
|
render(engine, 2);
|
|
|
|
|
for (failure = 0; failure < 64; ++failure) {
|
|
|
|
|
xtc_result result;
|
|
|
|
|
fail_after = failure;
|
|
|
|
|
result = xtc_realtime_submit(engine, graph);
|
|
|
|
|
fail_after = -1;
|
|
|
|
|
if (result == XTC_OK) { succeeded = 1; break; }
|
|
|
|
|
assert(result == XTC_ERR_OUT_OF_MEMORY);
|
|
|
|
|
/* Failed replacement preserves the active DSP and revisions. */
|
|
|
|
|
{
|
|
|
|
|
xtc_realtime_status status;
|
|
|
|
|
assert(xtc_realtime_get_status(engine, &status) == XTC_OK);
|
|
|
|
|
assert(status.active_revision == 1 && status.submitted_revision == 1);
|
|
|
|
|
}
|
|
|
|
|
render(engine, 2);
|
|
|
|
|
}
|
|
|
|
|
assert(succeeded);
|
|
|
|
|
render(engine, 2); /* Leaves previous native instances retired. */
|
|
|
|
|
assert(destroyed + 4 == prepared);
|
|
|
|
|
xtc_realtime_collect(engine);
|
|
|
|
|
assert(destroyed + 2 == prepared);
|
|
|
|
|
assert(xtc_realtime_submit(engine, graph) == XTC_OK);
|
|
|
|
|
/* Destruction must also clean an unadopted pending plan. */
|
|
|
|
|
xtc_realtime_destroy(engine);
|
|
|
|
|
assert(prepared == destroyed);
|
|
|
|
|
}
|
|
|
|
|
xtc_graph_destroy(graph);
|
|
|
|
|
puts("Allocation failures, callback allocation guard, and native reclamation passed.");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|