streamrproxyclient: re-initialize the library on API calls after cleanup#97
Merged
Conversation
proxyClientCleanupLibrary() destroys the LibProxyClientApi instance but nothing re-created it — the load-time constructor runs only once — so any API call after a cleanup dereferenced a null pointer and crashed. In practice this made it impossible to run two integration tests in one process: the fixture destructor cleans the library up after every test. CI masked the bug by running each test in its own process. getProxyClientApi() now re-initializes the library on demand, matching the header's documented contract that the library can be re-initialized after a cleanup. The regression test exercises use after cleanup both through the implicit lazy path and through an explicit proxyClientInitLibrary() call, in a single process. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ptesavol
added a commit
that referenced
this pull request
Jul 18, 2026
Rebase fallout: #97 wrote ApiIsUsableAfterCleanupLibrary against the pre-3.0 proxyClient* names, which this branch deprecates — the lint step (deprecation warnings as errors) failed on every CI leg. The test verifies the re-init contract, not the aliases, so it now uses the final names; the dedicated deprecated-alias test keeps covering the old ones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ptesavol
added a commit
that referenced
this pull request
Jul 18, 2026
) * Phase D3a: rename the shared C API infrastructure to streamr* names The proxy-prefixed shared infrastructure both APIs use moves to neutral names in a new streamrcommon.h, per phase D3 of the completion plan (the naming decision deferred from the D1 review): - struct Proxy -> struct StreamrPeer (StreamrEntryPoint stays as an alias of it in streamrnode.h) - struct Error -> struct StreamrError; its 'proxy' member is now 'peer', with the old spelling kept usable through an anonymous union (same storage) - struct ProxyResult -> struct StreamrResult - proxyClientResultDelete -> streamrResultDelete - proxyClientInitLibrary -> streamrInitLibrary - proxyClientCleanupLibrary -> streamrCleanupLibrary The old type names remain as typedef aliases and the old C symbols stay exported (delegating to the new ones), marked deprecated for one release, so existing proxy-API consumers keep compiling and linking. The shared-library major version bumps to 3.0.0 (SOVERSION 3). The proxy client calls (proxyClientNew/Delete/Connect/Publish) keep their names — the API is about proxies; only the shared types in their signatures changed spelling. In-repo consumers (C++ wrapper header, Python wrapper bindings, the integration/end-to-end/ts-interop tests) now use the final names, so the D3b wrapper extensions are written against them once. A dedicated test exercises the deprecated aliases and the pre-3.0 member spelling. All 11 proxy-client integration tests and 9 streamrnode integration tests pass in the per-test-process mode CI uses. (Running several tests in ONE process crashes in the second test — a pre-existing library re-init-after-cleanup bug, unaffected by the rename; follow-up filed.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Phase D3b: C++ and Python wrappers for the streamrNode* full-node API (#96) - wrappers/cpp: new StreamrNode.hpp — RAII node wrapper (create/start/ stop, joinStreamPart/leaveStreamPart, publish, subscribe with a std::function callback, neighborCount, setProxies), errors thrown as StreamrNodeError with a variant code, mirroring the existing StreamrProxyClient wrapper idioms. Subscription callbacks stay owned by the node until its destruction: the C API may still dispatch a message whose delivery already started when unsubscribe returns. - wrappers/python: StreamrNode context manager over the streamrNode* ctypes bindings (same shape as ProxyClient); the message callback is bound with an explicit content length (payloads are opaque bytes). ctypes callback objects are kept alive until node deletion. - Tests: StreamrNodeCppWrapperTest (two wrapper-created nodes form a stream-part topology and exchange a signed and an unsigned message; error mapping) wired into ctest with the streamrnode integration tests' 600 s budget, and the same round-trip in wrappers/python/tests/test_streamr_node.py with scripts/run-python-tests.sh staging the built library (pytest or direct run). - StreamrProxyClient.hpp: one leftover deprecated-alias use (std::vector<Proxy>) moved to the final StreamrPeer name. Local runs: cpp wrapper round-trip 2/2 in ~850 ms; python round-trip green against the freshly built 3.0.0 dylib. Swift (dist/ios-swift-package) and Kotlin (dist/android-library-module) follow in the next increment together with the D2 mobile-platform verification they depend on; the Go wrapper (wrappers/go, an uninitialized submodule pointing at a separate repo) is proposed for dropping from the D3 scope — decision left to review. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Port the use-after-cleanup regression test to the streamr* names Rebase fallout: #97 wrote ApiIsUsableAfterCleanupLibrary against the pre-3.0 proxyClient* names, which this branch deprecates — the lint step (deprecation warnings as errors) failed on every CI leg. The test verifies the re-init contract, not the aliases, so it now uses the final names; the dedicated deprecated-alias test keeps covering the old ones. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * CI: bump the build-tree cache generation to v2 The buildtree cache restores the newest previous tree on any key miss and rebuilds incrementally. The repeated force-pushes on this branch fed the legs an inconsistent restored state: ninja under-rebuilt, linking the freshly named libstreamrproxyclient from a stale pre-rename object (undefined streamrResultDelete/streamrInitLibrary in the wrapper test) and failing dyndep generation in streamr-proto-rpc. Bumping the generation forces one clean full build per leg and starts a fresh cache lineage — the same documented remedy the v1 suffix itself came from. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Point the go wrapper submodule at the streamrNode* API branch goproxyclient PR 1 (branch streamr-node-api, commit c500e43) adds the full-node API to the Go wrapper — the last wrapper of phase D3b's native-host set. The pointer references the pushed PR branch; advance it to the merge commit when that PR lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
proxyClientCleanupLibrary()destroys theLibProxyClientApiinstance and nulls the pointer, but only the load-time__attribute__((constructor))ever created it. Any API call made after a cleanup therefore dereferenced a null pointer ingetProxyClientApi()and crashed with SIGSEGV.In practice this made it impossible to run two integration tests in one process, because the test fixture destructor cleans the library up after every test:
CI masked the bug by running each test in its own process.
Fix
getProxyClientApi()now callsproxyClientInitLibrary()before dereferencing, so the library re-creates itself on demand after a cleanup. This matches the contract already documented instreamrproxyclient.h(init "can be called explicitly to force a re-initialization" after cleanup); the cleanup doc now also states that any API call after cleanup re-initializes automatically.Regression test
StreamrProxyClientTest.ApiIsUsableAfterCleanupLibraryexercises use after cleanup within a single test — both the implicit lazy path (cleanup → API call) and the explicit one (cleanup →proxyClientInitLibrary()→ API call) — so it catches the bug even under one-process-per-test isolation. Verified: SIGSEGV on the unfixed library, passes with the fix.Verification
🤖 Generated with Claude Code