Release the error context when a path resolves against its root
akgl_path_relative took its ENOENT branch by returning from inside the HANDLE block, which skips the RELEASE_ERROR that FINISH ends with. The handled context was never given back, so one entry of AKERR_ARRAY_ERROR was lost per call and the 129th call hit "Unable to pull an error context from the array!" and exited the process. That branch is not an edge case: every asset path inside a tilemap is resolved relative to the map's own directory, so it is taken several times per map load. A game that loaded fifty levels died in the loader. It was found by a benchmark that loaded the fixture map in a loop, which is the first thing in this tree to call it more than a hundred times. The fallback now runs after FINISH, so the context is released on the way past. tests/util.c resolves AKERR_MAX_ARRAY_ERROR * 2 paths through the branch and asserts the pool is where it started -- against the old code that test does not fail, it terminates the suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
17
src/util.c
17
src/util.c
@@ -99,6 +99,7 @@ akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst)
|
||||
PREPARE_ERROR(e);
|
||||
akgl_String *strbuf;
|
||||
char *result;
|
||||
bool relative_to_root = false;
|
||||
|
||||
FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument");
|
||||
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument");
|
||||
@@ -115,10 +116,20 @@ akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst)
|
||||
IGNORE(akgl_heap_release_string(strbuf));
|
||||
} PROCESS(e) {
|
||||
} HANDLE(e, ENOENT) {
|
||||
// Path is not relative to our current working directory
|
||||
// Noop - execution proceeds after the break
|
||||
return akgl_path_relative_root(root, path, dst);
|
||||
// Path is not relative to our current working directory. Resolve it
|
||||
// against root instead -- but after FINISH, not from in here. Returning
|
||||
// from inside a HANDLE block skips the RELEASE_ERROR that FINISH ends
|
||||
// with, so the handled context is never given back to
|
||||
// AKERR_ARRAY_ERROR. That leaks one context per call, and the 129th
|
||||
// call takes the whole process down with "Unable to pull an error
|
||||
// context from the array!". Every map load resolves several paths this
|
||||
// way.
|
||||
relative_to_root = true;
|
||||
} FINISH(e, true);
|
||||
|
||||
if ( relative_to_root == true ) {
|
||||
PASS(e, akgl_path_relative_root(root, path, dst));
|
||||
}
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user