Repository navigation
Preserve public Rust timer API after cancellation lifetime fix - #120
Merged
Merged
Conversation
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.
Follow-up to merged #119 / issue #118.
The Python timer fix changed the exported Rust signatures of
LoopCore::schedule_timer()andPyTimerHandle::new()fromArc<ReadyCallback>to the internalTimerCallbacktype. This restores the original Rust signatures and explicit shared ownership, while Python bindings use an internal detachable-payload scheduling/handle path. Existing Rust callers can still borrow callback/context and invoke their immutable ReadyCallback; Python cancellation continues releasing callback, args and context safely.A Rust regression compiles and exercises the original typed return value and handle constructor, shared invocation, cancellation and queued removal. Enqueueing also moves its owned Arc directly into the entry, avoiding an extra clone/drop pair introduced by factoring scheduling.
Local checks: 56 Python timer tests passed (6 intentional control skips); 19 new free-threaded rsloop cases plus 5 existing timer tests passed; Clippy with all targets/features, fmt, Ruff and targeted Pyright passed. Rust default: 337 passed and 15 environment-restricted syscall failures; all features: 433 passed and 19 restricted failures. Core Python: 628 passed, with the same two Unix-socket restrictions seen before #119. Full Tests CI passed, including Linux/macOS/Windows, supported Python versions and free-threaded builds. Async ecosystem CI also passed all six jobs; real PostgreSQL on Python 3.10 and 3.14 each passed 39 tests.
Fresh-process 20,000-timer/timeout probes still retain 0 payloads/tasks after loop turns. Timer Python retention is 315 B (was 87.2 MB before #119); timeout retention is 33.8 KB (was 101.0 MB). RSS is 33.4 MiB / 34.4 MiB, versus 125.2 / 150.4 MiB before the original fix. This PR's fresh-process PostgreSQL probes on Python 3.10 and 3.14 each released all 500 caches and 1500 statements, leaving 0 retained caches, statement states and protocols. asyncio and uvloop controls also retained 0.
Final release microbenchmark: 1M operations/sample, two reversed-order pairs, 14 samples per build. Compared with original 0.1.63, retained zero-delay timers +2.9%, discarded +13.4%, cancelled +9.3%; scattered cancelled timers -7.6%. The extra timer cost is approximately 8–34 ns/op. call_soon has no added synchronization; its measurements varied by about 4%, so no speedup is claimed. The 300-second Granian workload was not repeated.