Skip to content

Fix crash in DebounceAsyncSequence#61

Open
macdrevx wants to merge 2 commits into
reddavis:mainfrom
macdrevx:issue60
Open

Fix crash in DebounceAsyncSequence#61
macdrevx wants to merge 2 commits into
reddavis:mainfrom
macdrevx:issue60

Conversation

@macdrevx

@macdrevx macdrevx commented Aug 4, 2025

Copy link
Copy Markdown

The expression UInt64(self.dueTime - Date().timeIntervalSince(lastEmission)) crashes if self.dueTime - Date().timeIntervalSince(lastEmission) is less than UInt64.min.

Since Date().timeIntervalSince(lastEmission) seems like it normally evaluates to something near 0, the suspicion is that the crash must occur when the process is suspended in between lastEmission = Date() and Date().timeIntervalSince(lastEmission).

This commit removes lastEmission entirely and ensures that the value passed to UInt64.min is non-negative.

Fixes #60

The expression `UInt64(self.dueTime - Date().timeIntervalSince(lastEmission))`
crashes if `self.dueTime - Date().timeIntervalSince(lastEmission)` is
less than `UInt64.min`.

Since `Date().timeIntervalSince(lastEmission)` seems like it normally
evaluates to something near 0, the suspicion is that the crash must
occur when the process is suspended in between `lastEmission = Date()`
and `Date().timeIntervalSince(lastEmission)`.

This commit removes `lastEmission` entirely and ensures that the value
passed to `UInt64.min` is non-negative.

Fixes reddavis#60
try? await Task.sleep(nanoseconds: delay)

let sleep = Task<RaceResult, Never> { [dueTime] in
try? await Task.sleep(nanoseconds: UInt64(Swift.max(dueTime, 0)) * NSEC_PER_SEC)

@macdrevx macdrevx Aug 6, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An unrelated problem here is that dueTime is converted to an integer before multiplication with NSEC_PER_SEC which means that any fractional seconds are lost.

Mx-Iris added a commit to MxIris-Library-Forks/Asynchrone that referenced this pull request Jun 11, 2026
- Debounce: negative/sub-second dueTime converted through UInt64 crashed or
  truncated to a zero-length sleep (upstream issue reddavis#60 / PR reddavis#61); sleep the
  full dueTime per race round instead
- Task.sleep(seconds:): clamp negative durations instead of trapping
- Zip/Zip3: finish as soon as the first sequence ends instead of awaiting
  the remaining iterators, which hung forever on silent sequences
- CombineLatest/CombineLatest3: rewrite with true combineLatest semantics
  (upstream issue reddavis#55) - race both iterators, combine each new element with
  the latest from the other side, Combine-style completion rules
- Timer: idempotent start (second iteration no longer spawns and leaks a
  duplicate timer task), thread-safe via NSLock, always finish the stream on
  cancellation, support cancel-before-iteration, clamp negative intervals
- Shared: subscription task held the manager strongly for the whole base
  iteration, forming a retain cycle that leaked forever on infinite bases
  and made deinit cancellation dead code; hold self only per-element
- Throttle: emit the collected trailing element when the base finishes
- Delay: do not emit an element after the sleep was cut short by cancellation
- Extract TaskRaceCoordinator + Task.firstToComplete(of:) into
  Common/TaskRace.swift, shared by Debounce and CombineLatest
- Remove RethrowingAccessor.swift (dead duplicate of _ErrorMechanism)
- Document a Swift runtime pitfall: multi-sequence operators lose errors in
  generic rethrows contexts when the first base sequence is non-throwing and
  a later one throws (caller suspends forever)
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.

Crash in DebounceAsyncSequence

1 participant