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

CNA snapshot 009d40f5  ·  Known Issues › Verification gaps  ·  source links pinned to 009d40f5

✓

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

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.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.