Author: Armin Rigo <[email protected]>
Branch:
Changeset: r79132:1ed06832e512
Date: 2015-08-22 11:27 +0200
http://bitbucket.org/pypy/pypy/changeset/1ed06832e512/
Log: Add a direct test for a bug. Thanks jerith on irc for prodding us
until we really looked into it (and thanks undodb-gdb for being
essential here).
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -1899,7 +1899,7 @@
#
self.old_objects_pointing_to_pinned.append(parent)
self.updated_old_objects_pointing_to_pinned = True
- self.header(parent).tid |= GCFLAG_PINNED
+ self.header(parent).tid |= GCFLAG_PINNED_OBJECT_PARENT_KNOWN
#
if hdr.tid & GCFLAG_VISITED:
return
@@ -2328,13 +2328,15 @@
while self.objects_to_trace.non_empty():
self.visit_all_objects_step(sys.maxint)
+ TEST_VISIT_SINGLE_STEP = False # for tests
+
def visit_all_objects_step(self, size_to_track):
# Objects can be added to pending by visit
pending = self.objects_to_trace
while pending.non_empty():
obj = pending.pop()
size_to_track -= self.visit(obj)
- if size_to_track < 0:
+ if size_to_track < 0 or self.TEST_VISIT_SINGLE_STEP:
return 0
return size_to_track
diff --git a/rpython/memory/gc/test/test_object_pinning.py
b/rpython/memory/gc/test/test_object_pinning.py
--- a/rpython/memory/gc/test/test_object_pinning.py
+++ b/rpython/memory/gc/test/test_object_pinning.py
@@ -88,7 +88,7 @@
class TestIncminimark(PinningGCTest):
from rpython.memory.gc.incminimark import IncrementalMiniMarkGC as GCClass
- from rpython.memory.gc.incminimark import STATE_SCANNING
+ from rpython.memory.gc.incminimark import STATE_SCANNING, STATE_MARKING
def test_try_pin_gcref_containing_type(self):
# scenario: incminimark's object pinning can't pin objects that may
@@ -917,3 +917,65 @@
py.test.raises(Exception, self.malloc, T)
test_full_pinned_nursery_pin_fail.max_number_of_pinned_objects = 50
+
+ def test_pin_bug1(self):
+ #
+ # * the nursery contains a pinned object 'ptr1'
+ #
+ # * outside the nursery is another object 'ptr2' pointing to 'ptr1'
+ #
+ # * during one incremental tracing step, we see 'ptr2' but don't
+ # trace 'ptr1' right now: it is left behind on the trace-me-later
+ # list
+ #
+ # * then we run the program, unpin 'ptr1', and remove it from 'ptr2'
+ #
+ # * at the next minor collection, we free 'ptr1' because we don't
+ # find anything pointing to it (it is removed from 'ptr2'),
+ # but 'ptr1' is still in the trace-me-later list
+ #
+ # * the trace-me-later list is deep enough that 'ptr1' is not
+ # seen right now! it is only seen at some later minor collection
+ #
+ # * at that later point, crash, because 'ptr1' in the nursery was
+ # overwritten
+ #
+ ptr2 = self.malloc(S)
+ ptr2.someInt = 102
+ self.stackroots.append(ptr2)
+
+ self.gc.collect()
+ ptr2 = self.stackroots[-1] # now outside the nursery
+ adr2 = llmemory.cast_ptr_to_adr(ptr2)
+
+ ptr1 = self.malloc(T)
+ adr1 = llmemory.cast_ptr_to_adr(ptr1)
+ ptr1.someInt = 101
+ self.write(ptr2, 'data', ptr1)
+ res = self.gc.pin(adr1)
+ assert res
+
+ self.gc.minor_collection()
+ assert self.gc.gc_state == self.STATE_SCANNING
+ self.gc.major_collection_step()
+ assert self.gc.objects_to_trace.tolist() == [adr2]
+ assert self.gc.more_objects_to_trace.tolist() == []
+
+ self.gc.TEST_VISIT_SINGLE_STEP = True
+
+ self.gc.minor_collection()
+ assert self.gc.gc_state == self.STATE_MARKING
+ self.gc.major_collection_step()
+ assert self.gc.objects_to_trace.tolist() == [adr1]
+ assert self.gc.more_objects_to_trace.tolist() == [adr2]
+
+ self.write(ptr2, 'data', lltype.nullptr(T))
+ self.gc.unpin(adr1)
+
+ assert ptr1.someInt == 101
+ self.gc.minor_collection() # should free 'ptr1'
+ py.test.raises(RuntimeError, "ptr1.someInt")
+ assert self.gc.gc_state == self.STATE_MARKING
+ self.gc.major_collection_step() # should not crash reading 'ptr1'!
+
+ del self.gc.TEST_VISIT_SINGLE_STEP
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit