Skip to content

Commit 76ec84c

Browse files
authored
Fix iterating Crystal::PointerLinkedList#each after deleting head (#16401)
While importing `Sync::ConditionVariable` from the sync shard and porting from `Sync::Dll` to `Crystal::PointerLinkedList` I hit an issue with the following snippet: ```crystal @waiters.each { |waiter| @waiters.delete(waiter) } ``` The `#each` method correctly gets the next pointer before yielding, but incorrectly compares it to `@head` after the block has run; since the block removed the head pointer, then next == head and the iteration stops (oops).
1 parent b8def0b commit 76ec84c

File tree

2 files changed

+37
-16
lines changed

2 files changed

+37
-16
lines changed

spec/std/crystal/pointer_linked_list_spec.cr

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,23 +201,46 @@ describe Crystal::PointerLinkedList do
201201
end
202202
end
203203

204-
it "does each" do
205-
list = Crystal::PointerLinkedList(TestedObject).new
204+
describe "#each" do
205+
it "iterates everything" do
206+
list = Crystal::PointerLinkedList(TestedObject).new
207+
208+
x = TestedObject.new 1
209+
y = TestedObject.new 2
210+
z = TestedObject.new 4
206211

207-
x = TestedObject.new 1
208-
y = TestedObject.new 2
209-
z = TestedObject.new 4
212+
sum = 0
210213

211-
sum = 0
214+
list.push pointerof(x)
215+
list.push pointerof(y)
216+
list.push pointerof(z)
212217

213-
list.push pointerof(x)
214-
list.push pointerof(y)
215-
list.push pointerof(z)
218+
list.each do |object_ptr|
219+
sum += object_ptr.value.value
220+
end
216221

217-
list.each do |object_ptr|
218-
sum += object_ptr.value.value
222+
sum.should eq(7)
219223
end
220224

221-
sum.should eq(7)
225+
it "can delete while iterating" do
226+
list = Crystal::PointerLinkedList(TestedObject).new
227+
228+
x = TestedObject.new 1
229+
y = TestedObject.new 2
230+
z = TestedObject.new 4
231+
232+
sum = 0
233+
234+
list.push pointerof(x)
235+
list.push pointerof(y)
236+
list.push pointerof(z)
237+
238+
list.each do |obj|
239+
list.delete(obj)
240+
sum += obj.value.value
241+
end
242+
243+
sum.should eq(7)
244+
end
222245
end
223246
end

src/crystal/pointer_linked_list.cr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,11 @@ struct Crystal::PointerLinkedList(T)
105105

106106
# Iterates the list.
107107
def each(&) : Nil
108-
return if empty?
109-
110108
node = @head
111-
loop do
109+
while node
112110
_next = node.value.next
111+
_next = Pointer(T).null if _next == @head
113112
yield node
114-
break if _next == @head
115113
node = _next
116114
end
117115
end

0 commit comments

Comments
 (0)