From e2d31ad7572a717cb53fd44ddb2b35223462f386 Mon Sep 17 00:00:00 2001 From: "Logikoma (Codex GPT-5)" Date: Mon, 3 Aug 2026 13:11:01 -0400 Subject: [PATCH] Test init reservation failure --- CMakeLists.txt | 42 +++++++++++++++++++++++++++++- src/error.c | 5 +++- tests/err_init_reservation_fatal.c | 20 ++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/err_init_reservation_fatal.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ea8499..30474c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,10 +158,18 @@ add_custom_command( VERBATIM ) +# More than one library target consumes the generated sources below. Route +# them through one explicit prerequisite so parallel Make builds cannot invoke +# the same generator twice and interleave writes to errno.c. +add_custom_target(akerror_generated + DEPENDS ${GENERATED_ERRNO_C} ${GENERATED_AKERROR_H} +) + add_library(akerror SHARED src/error.c ${GENERATED_ERRNO_C} ) +add_dependencies(akerror akerror_generated) target_include_directories(akerror PUBLIC $ @@ -200,6 +208,32 @@ set_target_properties(akerror PROPERTIES akerr_instrument_for_coverage(akerror) akerr_instrument_for_sanitizers(akerror) +# akerr_init() must terminate if it cannot reserve the library-owned status +# band. The production table sizes are deliberately PRIVATE, so exercise that +# otherwise unreachable startup failure with a second library target whose +# private registries cannot accept even the first reservation. Keeping this a +# distinct target is the test: no compile definition leaks into consumers or +# weakens the production library. +add_library(akerror_init_failure SHARED + src/error.c + ${GENERATED_ERRNO_C} +) +add_dependencies(akerror_init_failure akerror_generated) +target_include_directories(akerror_init_failure PUBLIC + ${GENERATED_DIR}/include +) +target_compile_definitions(akerror_init_failure + PUBLIC AKERR_USE_STDLIB=${AKERR_USE_STDLIB} + PRIVATE AKERR_STATUS_NAME_SLOTS=8 + PRIVATE AKERR_MAX_RESERVED_STATUS_RANGES=0 + PRIVATE ${AKERR_THREADS_DEFINE} +) +if(AKERR_THREAD_SAFE) + target_link_libraries(akerror_init_failure PRIVATE Threads::Threads) +endif() +akerr_instrument_for_coverage(akerror_init_failure) +akerr_instrument_for_sanitizers(akerror_init_failure) + # Each test is one source file in tests/ built into test_ and registered # as CTest . Tests expected to abort (unhandled error / contract # violation) go in AKERR_WILL_FAIL_TESTS; all others must exit 0. @@ -227,6 +261,7 @@ set(AKERR_TESTS err_registry_init_order err_status_exception err_copy_string + err_init_reservation_fatal err_library_status_fatal err_refcount_double_fail err_stacktrace_bounds @@ -254,13 +289,18 @@ endif() set(AKERR_WILL_FAIL_TESTS err_trace err_improper_closure + err_init_reservation_fatal err_library_status_fatal ) foreach(_test IN LISTS AKERR_TESTS) add_executable(test_${_test} tests/${_test}.c) target_include_directories(test_${_test} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests) - target_link_libraries(test_${_test} PRIVATE akerror) + if(_test STREQUAL "err_init_reservation_fatal") + target_link_libraries(test_${_test} PRIVATE akerror_init_failure) + else() + target_link_libraries(test_${_test} PRIVATE akerror) + endif() if(AKERR_THREAD_SAFE) target_link_libraries(test_${_test} PRIVATE Threads::Threads) endif() diff --git a/src/error.c b/src/error.c index 4182d50..66b3e66 100644 --- a/src/error.c +++ b/src/error.c @@ -88,7 +88,10 @@ typedef struct static akerr_StatusName akerr_status_names[AKERR_STATUS_NAME_SLOTS]; static int akerr_status_name_count; -static akerr_StatusRange akerr_status_ranges[AKERR_MAX_RESERVED_STATUS_RANGES]; +/* C has no portable zero-length arrays. Keep one unused physical slot when a + * test build sets the logical capacity to zero to drive init's fatal path. */ +static akerr_StatusRange akerr_status_ranges[ + AKERR_MAX_RESERVED_STATUS_RANGES > 0 ? AKERR_MAX_RESERVED_STATUS_RANGES : 1]; static int akerr_status_range_count; akerr_ErrorContext AKERR_ARRAY_ERROR[AKERR_MAX_ARRAY_ERROR]; diff --git a/tests/err_init_reservation_fatal.c b/tests/err_init_reservation_fatal.c new file mode 100644 index 0000000..e49e3b6 --- /dev/null +++ b/tests/err_init_reservation_fatal.c @@ -0,0 +1,20 @@ +#include "akerror.h" + +#include + +/* + * This executable links to akerror_init_failure, a test-only library target + * with no status-range slots. The first reservation in akerr_init() must be + * terminal: continuing would leave every library status unowned and make all + * subsequent name registrations invalid. + * + * CTest marks this WILL_FAIL. Reaching the message and returning zero means + * initialization swallowed its own reservation failure. + */ +int main(void) +{ + akerr_init(); + + fprintf(stderr, "err_init_reservation_fatal: akerr_init did not terminate\n"); + return 0; +} -- 2.43.0