CNA-VGAP-055: Process exit with live EasyGL-family resources whose creating thread has already ended is untested; a binding recorded SIGABRT on an earlier CNA revision
Evidence basis: source-verified at the pinned commit; inferred from the source (the behaviour was not run); tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
cna-java recorded that a process exiting with a live vertex buffer whose creating thread had ended aborts on the EasyGL renderer identities; CNA's own process-exit teardown test runs on one thread, so this case has no evidence at this snapshot.
- Identifier
CNA-VGAP-055- Category
- Verification gap
- Subsystem
- C API & bindings
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Evidence basis
- Inferred (probable): depends on a platform or toolchain step nobody executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Process-exit teardown of live C API handles (the runtime handle registry) on the renderer identities served by EasyGL
Expected behaviour
CNA treats process exit with live C objects as a fallback that must not crash: TeardownLifetimeSmoke.c asserts a clean exit in six fallback modes (game-alive, game-alive-no-frame, children-alive, partial, cycles and device-alive), besides its explicit-release control. Whether that includes objects whose creating thread ended before exit (what every JVM does, because the java launcher runs main on a thread it creates) is either tested or documented as unsupported.
Actual behaviour at TARGET
Every TeardownLifetimeSmoke.c mode runs on the process's main thread, and the C API documentation says only that handles are thread-affine. cna-java@6661173 (JAVA-UPSTREAM-014, pure-C probe tools/native-abi/probes/exit_with_live_graph.c, against CNA 599d14e5 at ABI 0.21.0) observed exit status 134, “terminate called without an active exception”, on OPENGLES3 and OPENGL33 only when a vertex buffer was live and its creating thread had ended; HEADLESS, SOFTWARE and OPENGL4 exited 0. Reading at 009d40f5 shows the mechanism that would explain it is still present: VertexBuffer::Dispose(bool) resets its renderer, ~EasyGLVertexBufferRenderer's easygl::Buffer member deletes its GL name through metagl::glDeleteBuffers, and meta-gl keeps its function table thread_local and calls std::terminate() when the calling thread has none loaded. CNA's process-exit teardown was reworked after the binding's revision (the handle registry no longer destroys a Game into dead statics), so the observation cannot be carried over as a TARGET result.
Source locations
modules/c-api/tests/pure_c/TeardownLifetimeSmoke.c— process-exit modes, all on the main threadmodules/graphics/src/Xna/VertexBuffer.cpp— VertexBuffer::Dispose(bool) resets renderer_modules/renderers/easygl/src/EasyGLRenderer.cpp— ~EasyGLVertexBufferRenderer; ~EasyGLRenderer relies on the context still being currentplans/plan_capi_smoke_stability.md— CSS-2/CSS-3 teardown repair and the CSS-5 test
Evidence
Checked by reading the TARGET files above and, for the mechanism, the sibling heads easy-gl develop @deda7a4 (Buffer::destroy) and meta-gl develop @20c8b2d (thread_local GlTable g_gl, the std::terminate() guard in every wrapper); TARGET does not pin either revision. Not executed: reproducing needs the C library and an EasyGL renderer built, which was out of scope. The cna-java result is evidence for its own CNA revision only. Uncertain: the exit-time destruction order at TARGET and whether every resource type reaches a GL call on the exiting thread.
Independent re-verification: Checked by reading the TARGET files above and, for the mechanism, the sibling heads easy-gl develop @deda7a4 (Buffer::destroy) and meta-gl develop @20c8b2d (thread_local GlTable g_gl, the std::terminate() guard in every wrapper); TARGET does not pin either revision. The abort is a result recorded by the cna-java binding at its own CNA revision (docs/backlog.json JAVA-UPSTREAM-014 and tools/native-abi/probes/exit_with_live_graph.c at cna-java 6661173: exit status 134 on OPENGLES3 and OPENGL33 with a live buffer on a thread that ended first, 0 elsewhere). It was not reproduced here and nobody has run it at TARGET, so the basis is inference from code, not a TARGET observation.
Focused reproduction
Illustrative outline of cna-java's probe, not built at this snapshot:
/* on a pthread: cna_game_create, cna_graphics_device_manager_create,
cna_game_run_one_frame whose update callback calls cna_vertex_buffer_create
and never destroys it; the thread returns; main joins it and returns 0.
Run with CNA_GRAPHICS_RENDERER=OPENGLES3. The binding observed exit status 134. */
Current tests
TeardownLifetimeSmoke.c covers game-alive, children-alive (textures), partial, cycles and device-alive exits on one thread; the C tests are not run in CI.
Regression test
A TeardownLifetimeSmoke mode that creates the game, a frame and a live vertex buffer on a thread that ends before main returns, registered for a GL-profile renderer; its exit status is the assertion.
Blast radius
Hosts whose CNA thread ends before the process exits with objects still alive: every JVM-hosted program that exits via System.exit or an uncaught exception, and similar embedders, on the EasyGL-served identities. Programs that destroy their objects are unaffected.
Workaround
Destroy vertex buffers and other GPU resources on their creating thread before it ends.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Architecture
- Ownership and shutdown: order
- Known issues
- Verification gap index