Skip to content

Fix data race in ConcurrentQueue dequeue methods#35

Open
osalloum wants to merge 1 commit intoaptabase:mainfrom
osalloum:fix/concurrent-queue-data-race
Open

Fix data race in ConcurrentQueue dequeue methods#35
osalloum wants to merge 1 commit intoaptabase:mainfrom
osalloum:fix/concurrent-queue-data-race

Conversation

@osalloum
Copy link
Copy Markdown

Summary

  • dequeue() and dequeue(count:) use queue.sync (a reader lock on the concurrent queue) but call removeFirst() which mutates the array
  • When two flush() calls run concurrently (e.g. from timerFlushSync and stopPolling), both enter the read lock simultaneously — the first drains the array, the second crashes with Fatal error: Array replace: subrange extends past the end
  • Fix: use queue.sync(flags: .barrier) for both dequeue methods, matching the barrier pattern already used by the enqueue methods

Crash backtrace

* thread #7092, stop reason = Fatal error: Array replace: subrange extends past the end
    frame #5: libswiftCore.dylib`Array.replaceSubrange(_:with:)
    frame #6: libswiftCore.dylib`RangeReplaceableCollection.removeFirst(_:)
    frame #7: libswiftCore.dylib`RangeReplaceableCollection.removeFirst()
  * frame #8: ConcurrentQueue.dequeue(count:) at ConcurrentQueue.swift:33
    frame #13: ConcurrentQueue.dequeue(count:) at ConcurrentQueue.swift:31
    frame #14: EventDispatcher.flush() at EventDispatcher.swift:60
    frame #15: AptabaseClient.flush() at AptabaseClient.swift:64

Fixes #23

The dequeue() and dequeue(count:) methods use queue.sync (a reader lock
on the concurrent queue) but call removeFirst() which mutates the array.
When two flush() calls run concurrently — e.g. from timerFlushSync and
stopPolling — both enter the read lock simultaneously. The first drains
the array, the second hits removeFirst() on an empty array, crashing
with "Array replace: subrange extends past the end".

Fix: use queue.sync(flags: .barrier) for both dequeue methods, matching
the barrier pattern already used by the enqueue methods.

Fixes aptabase#23
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.

closure #1 in AptabaseClient.stopPolling() + 1 (AptabaseClient.swift:64)

1 participant