Skip to content

Bump cuvis.swig to develop for the GIL release and string fixes - #39

Merged
birkholz-cubert merged 2 commits into
developfrom
poc/gil-release
Aug 20, 2026
Merged

Bump cuvis.swig to develop for the GIL release and string fixes#39
birkholz-cubert merged 2 commits into
developfrom
poc/gil-release

Conversation

@birkholz-cubert

@birkholz-cubert birkholz-cubert commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Submodule bump only. No changes to this repo's own sources.

cuvis.swig goes from 280b487 to 0ccf8ca, picking up cuvis.swig#15, which is merged on develop. Content at 0ccf8ca is byte identical to the 8997137 branch tip everything below was measured against.

What the bump brings in

  • The GIL is released around every call into cuvis, by hand inside the existing %exception block, at 311 call sites, behind #ifdef SWIGPYTHON so the C# target keeps the original body.
  • The nine %inline string helpers return std::string by value instead of char const*. They previously returned .c_str() from a function-local static, one buffer shared by every thread in the process; the GIL was the only thing serialising it.
  • cuvis_measurement_get_data_string_swig is sized from cuvis_measurement_get_data_string_length instead of a fixed 2048 byte buffer.

No API change: the helpers still yield str, so cuvis.python needs no edits.

Why this repo cares

Two reasons beyond carrying the pointer.

It unblocks threaded consumers. Nothing in this binding released the GIL, so any blocking cuvis call froze the whole interpreter for its duration. Measured on the cuvis-ai-dataloader cu3s read path, GPU processing, 20 cores / RTX 4070:

threads before after
1 15.42 fps 15.47 fps
4 14.92 fps 45.24 fps
8 14.40 fps 58.67 fps

Before, throughput slightly degrades with threads, because the extra threads add scheduling cost and can contribute no work. Single thread is unchanged, so the extra save/restore pair per call costs nothing at these rates.

It is the prerequisite for the real-awaitable work. A completion callback arriving from an SDK thread would deadlock against a Python thread blocked inside cuvis while holding the GIL. See the poc/real-awaitable branch in cuvis.python.

Verification against this bump

Built from this branch with the SDK at C:\Program Files\Cuvis (3.5.3), Python 3.12:

  • cuvis.python suite: 134 passed.
  • Generated wrapper: 311 PyEval_SaveThread sites, zero static std::string.
  • 2, 4 and 8 concurrent threads run clean; 4 threads previously died with KeyError: 0 out of Measurement.refresh.
  • settings_rec returns its full 3231 characters as well-formed XML, against 2054 bytes ending in uninitialised stack before.
  • Unicode round trips byte exact through Measurement.name and .comment.

Linux, cubertgmbh/cuvis_pyil:3.5.3-ubuntu24.04: 134 tests pass, GIL release emitted at the same sites.

Pitfalls

  • Every wrapped call now pays a save/restore pair, including trivial getters. Unmeasured for a tight loop over per-pixel getters, which is the one place it could matter.
  • The GIL was protecting more than the string buffers. The SDK error channel is process-wide, so SDKException can report another thread's message: 0.9% of reads at 3 threads, and 0.2% already before this change. Only the message text is affected, never the status code. Real fix is thread_local in the SDK's ErrorHandler, which is a cuvis.c change.
  • Sharing a single Measurement across threads is now a hard crash, against an AttributeError and lost calls before. Sessions and processing contexts survive sharing. Callers need a thread-affinity contract, which is a cuvis.python concern.
  • Measurement._refresh still ignores the status of cuvis_measurement_get_data_image, _gps and _sensor_info. That is what turned the string race into a KeyError three frames from its cause. cuvis.python change.
  • Consumers still need their own change to benefit. torch's num_workers is multiprocessing only, so cuvis-ai-dataloader needs a thread pool prefetcher before any of the numbers above reach training.
  • No ThreadSanitizer build exists. It would have found the string race mechanically, and it is the cheapest guard against whatever else the GIL was hiding.

Not a merge candidate. Carries cuvis.swig poc/gil-release, which
releases the GIL around calls into cuvis so the SDK's blocking waits
can be awaited rather than polled. Without this bump the binding still
holds the GIL for the duration of every wait, and the awaitables in
cuvis.python poc/real-awaitable block the interpreter instead of
freeing it.
Was pinned to the poc/gil-release branch tip; cuvis.swig#15 landed as
0ccf8ca on develop with identical content.
@birkholz-cubert birkholz-cubert changed the title POC: carry the cuvis.swig GIL release branch Bump cuvis.swig to develop for the GIL release and string fixes Aug 20, 2026
@birkholz-cubert
birkholz-cubert marked this pull request as ready for review August 20, 2026 07:19
@birkholz-cubert
birkholz-cubert merged commit 9ed1ca6 into develop Aug 20, 2026
31 checks passed
@birkholz-cubert
birkholz-cubert deleted the poc/gil-release branch August 20, 2026 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant