-
Notifications
You must be signed in to change notification settings - Fork 1
Fix subscription reference counting with proper ownership separation #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Problem: - Subscriptions had only 1 reference shared between connection and user - unsubscribe() would destroy subscription immediately, causing potential use-after-free if user code still held a pointer to it Solution: - Subscriptions now start with 2 references: * Connection reference (for hashmap storage) * User reference (for returned pointer) - subscription.deinit() unsubscribes AND releases user reference - connection.unsubscribe() only releases connection reference - Subscription is destroyed only when all references are released Changes: - subscription.zig: Add extra retain() in create(), update deinit() - connection.zig: Add comment clarifying reference release in unsubscribe() - response_manager.zig: Fix cleanup to call sub.deinit() instead of null - jetstream.zig: Fix PullSubscription cleanup to call sub.deinit() This follows standard reference counting patterns like std::shared_ptr and ensures thread-safe, memory-safe subscription lifecycle management. All tests pass with zero memory leaks.
WalkthroughAdjusts subscription lifecycle and cleanup: adds retain/release handling in Subscription, switches certain callers to call sub.deinit() directly, and explicitly deinitializes a response mux subscription during shutdown. Also adds a comment in Connection.unsubscribe without behavior change. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Pre-merge checks (3 passed)✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🧰 Additional context used🧠 Learnings (2)📓 Common learnings📚 Learning: 2025-08-25T02:49:59.119ZApplied to files:
🔇 Additional comments (5)
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Problem
The subscription reference counting had a fundamental flaw:
unsubscribe()would immediately destroy the subscription even if user code still held a pointerSolution
Implemented proper two-reference ownership model:
Key Changes
subscription.zig:create()now callssub.retain()to add user reference (starts with 2 refs total)deinit()now unsubscribes from server AND releases user referenceconnection.zig:unsubscribe()only releases connection reference (with clarifying comment)response_manager.zig:sub.deinit()instead of just setting to nulljetstream.zig:PullSubscriptioncleanup to callsub.deinit()Benefits
std::shared_ptrTesting
This fix ensures users can safely hold subscription pointers after unsubscribing, while maintaining proper cleanup when all references are released.