Skip to content

Commit

Permalink
[Deque]: Fix bogus assert in Deque._Storage._ensureUnique
Browse files Browse the repository at this point in the history
A non-unique reference to a storage instance can legitimately become unique at any point, due to other references (say, held, by another thread) getting destroyed.

`Deque`’s `ensureUnique` implementation currently invokes the uniqueness check two times in quick succession, expecting it to return false both times. In rare circumstances, this can lead to a spurious trap in concurrent environments.

Fix this by avoiding calling `isUnique` more than once.
  • Loading branch information
lorentey committed May 24, 2024
1 parent 28161fc commit ead6314
Showing 1 changed file with 9 additions and 5 deletions.
14 changes: 9 additions & 5 deletions Sources/DequeModule/Deque._Storage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,25 +187,29 @@ extension Deque._Storage {
) {
let unique = isUnique()
if _slowPath(capacity < minimumCapacity || !unique) {
_ensureUnique(minimumCapacity: minimumCapacity, linearGrowth: linearGrowth)
_ensureUnique(isUnique: unique, minimumCapacity: minimumCapacity, linearGrowth: linearGrowth)
}
}

@inlinable
@inline(never)
internal mutating func _ensureUnique(
isUnique: Bool,
minimumCapacity: Int,
linearGrowth: Bool
) {
if capacity >= minimumCapacity {
assert(!self.isUnique())
assert(!isUnique)
self = self.read { $0.copyElements() }
} else if isUnique() {
let minimumCapacity = _growCapacity(to: minimumCapacity, linearly: linearGrowth)
return
}

let minimumCapacity = _growCapacity(to: minimumCapacity, linearly: linearGrowth)
if isUnique {
self = self.update { source in
source.moveElements(minimumCapacity: minimumCapacity)
}
} else {
let minimumCapacity = _growCapacity(to: minimumCapacity, linearly: linearGrowth)
self = self.read { source in
source.copyElements(minimumCapacity: minimumCapacity)
}
Expand Down

0 comments on commit ead6314

Please sign in to comment.