def test_bag_remove_index(self): b = lib.qcgc_arena_bag_create(10) for i in range(10): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) # Remove last b = lib.qcgc_arena_bag_remove_index(b, b.count - 1) self.assertEqual(b.count, 9) # Remove other b = lib.qcgc_arena_bag_remove_index(b, 0) self.assertEqual(b.count, 8) for i in range(1, 9): has = False for j in range(8): has = has | (b.items[j] != ffi.cast("void *", i)) self.assertTrue(has) # Bag with size 1 b = lib.qcgc_arena_bag_create(1) b = lib.qcgc_arena_bag_add(b, ffi.NULL) b = lib.qcgc_arena_bag_remove_index(b, 0) self.assertEqual(b.count, 0)
def test_grow_shrink(self): """Test automatic growing/shrinking""" stack = lib.qcgc_object_stack_create(10) pythonstack = list() for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack) self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack) self.assertEqual(stack.count, 0) for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack) self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack) self.assertEqual(stack.count, 0)
def test_bag_remove_index(self): b = lib.qcgc_arena_bag_create(10) for i in range(10): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) # Remove last b = lib.qcgc_arena_bag_remove_index(b, b.count - 1) self.assertEqual(b.count, 9) # Remove other b = lib.qcgc_arena_bag_remove_index(b, 0) self.assertEqual(b.count, 8) for i in range(1,9): has = False for j in range(8): has = has | (b.items[j] != ffi.cast("void *", i)) self.assertTrue(has) # Bag with size 1 b = lib.qcgc_arena_bag_create(1) b = lib.qcgc_arena_bag_add(b, ffi.NULL) b = lib.qcgc_arena_bag_remove_index(b, 0) self.assertEqual(b.count, 0)
def test_bag_add(self): b = lib.qcgc_arena_bag_create(100) for i in range(100): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.size, 100) self.assertEqual(b.count, i + 1) self.assertEqual(b.items[i], ffi.cast("void *", i))
def test_incremental(self): o = self.allocate_prebuilt_ref(2) p = self.allocate(1) q = self.allocate(2) self.set_ref(o, 0, p) # lib.qcgc_incmark() # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_WHITE) # self.set_ref(o, 1, q) # lib.qcgc_incmark() # r = self.allocate_prebuilt_ref(1) s = self.allocate(2) self.set_ref(r, 0, s) # lib.qcgc_incmark() # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_BLACK)
def test_ref_1(self): """Tree shaped reference struct""" # Generate reachable objects reachable = list() for _ in range(10): p, objs = self.gen_structure_1() self.push_root(p) reachable.extend(objs) # Generate unreachable objects unreachable = list() for _ in range(10): p, objs = self.gen_structure_1() unreachable.extend(objs) lib.qcgc_mark() for p in reachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in unreachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_root_changes_while_marking(self): reachable = list() for _ in range(10): p, objs = self.gen_structure_1() self.push_root(p) reachable.extend(objs) # unreachable = list() for _ in range(10): p, objs = self.gen_structure_1() unreachable.extend(objs) # lib.qcgc_incmark() # # Generate new roots objects = self.gen_circular_structure(100) self.push_root(objects[0]) reachable.extend(objects) mark_all_inc() for p in reachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in unreachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_arena_sweep_no_bump_ptr_coalescing(self): p = self.bump_allocate(16) arena = lib.qcgc_arena_addr(ffi.cast("cell_t *", p)) lib.qcgc_arena_sweep(arena) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", lib.bump_ptr())), lib.BLOCK_FREE)
def test_bag_add(self): b = lib.qcgc_arena_bag_create(100) for i in range(100): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.size, 100) self.assertEqual(b.count, i + 1) self.assertEqual(b.items[i], ffi.cast("void *", i))
def test_grow_shrink(self): """Test automatic growing/shrinking""" stack = lib.qcgc_object_stack_create(10) pythonstack = list() for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack); self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack); self.assertEqual(stack.count, 0) for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack); self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack); self.assertEqual(stack.count, 0)
def allocate_weakref(self, to): o = lib.qcgc_allocate(self.header_size + ffi.sizeof("myobject_t *")) self.assertNotEqual(o, ffi.NULL) lib._set_type_id(o, 0) # Prevent from tracing ffi.cast("myobject_t *", o).refs[0] = ffi.cast("myobject_t *", to) # Ref has to be valid before registering lib.qcgc_register_weakref(o, ffi.cast("object_t **", ffi.cast("myobject_t *", o).refs)) # XXX: ffi.addressof .refs[0] does not work lib.qcgc_write(o) return o
def test_arena_create(self): p = lib.qcgc_arena_create() self.assertEqual(p, lib.qcgc_arena_addr(lib.arena_cells(p))) self.assertEqual(p, lib.qcgc_arena_addr(ffi.addressof(lib.arena_cells(p)[lib.qcgc_arena_cells_count - 1]))) self.assertEqual(0, lib.qcgc_arena_cell_index(lib.arena_cells(p))) self.assertEqual(int(ffi.cast("uint64_t", p)), int(ffi.cast("uint64_t", p)) << lib.QCGC_ARENA_SIZE_EXP >> lib.QCGC_ARENA_SIZE_EXP) self.assertEqual(lib.BLOCK_FREE, self.get_blocktype(ffi.addressof(lib.arena_cells(p)[lib.qcgc_arena_first_cell_index])))
def test_bag_shrink(self): b = lib.qcgc_arena_bag_create(12) for i in range(3): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.size, 12) b = lib.qcgc_arena_bag_remove_index(b, 2) self.assertEqual(b.size, 6) for i in range(2): self.assertEqual(b.items[i], ffi.cast("void *", i))
def test_bag_shrink(self): b = lib.qcgc_arena_bag_create(12) for i in range(3): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.size, 12) b = lib.qcgc_arena_bag_remove_index(b, 2) self.assertEqual(b.size, 6) for i in range(2): self.assertEqual(b.items[i], ffi.cast("void *", i))
def test_bag_grow(self): b = lib.qcgc_arena_bag_create(10) for i in range(10): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.count, b.size) b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", 10)) self.assertEqual(b.size, 20) self.assertEqual(b.count, 11) for i in range(11): self.assertEqual(b.items[i], ffi.cast("void *", i))
def test_bag_grow(self): b = lib.qcgc_arena_bag_create(10) for i in range(10): b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", i)) self.assertEqual(b.count, b.size) b = lib.qcgc_arena_bag_add(b, ffi.cast("void *", 10)) self.assertEqual(b.size, 20) self.assertEqual(b.count, 11) for i in range(11): self.assertEqual(b.items[i], ffi.cast("void *", i))
def allocate_weakref(self, to): o = lib.qcgc_allocate(self.header_size + ffi.sizeof("myobject_t *")) self.assertNotEqual(o, ffi.NULL) lib._set_type_id(o, 0) # Prevent from tracing ffi.cast("myobject_t *", o).refs[0] = ffi.cast( "myobject_t *", to) # Ref has to be valid before registering lib.qcgc_register_weakref( o, ffi.cast( "object_t **", ffi.cast("myobject_t *", o).refs)) # XXX: ffi.addressof .refs[0] does not work lib.qcgc_write(o) return o
def test_many_small_allocations(self): objects = set() p = self.bump_allocate(16) arena = lib.qcgc_arena_addr(ffi.cast("cell_t *", p)) objects.add(p) for _ in range(1000): p = self.bump_allocate(16) objects.add(p) self.assertEqual(arena, lib.qcgc_arena_addr(ffi.cast("cell_t *", p))) self.assertFalse(ffi.NULL in objects) self.assertEqual(len(objects), 1001)
def test_many_small_allocations(self): objects = set() p = self.bump_allocate(16) arena = lib.qcgc_arena_addr(ffi.cast("cell_t *", p)) objects.add(p) for _ in range(1000): p = self.bump_allocate(16) objects.add(p) self.assertEqual(arena, lib.qcgc_arena_addr(ffi.cast("cell_t *", p))) self.assertFalse(ffi.NULL in objects) self.assertEqual(len(objects), 1001)
def allocate_prebuilt_ref(self, size): o = lib.allocate_prebuilt(self.header_size + size * ffi.sizeof("myobject_t *")) self.assertNotEqual(o, ffi.NULL) lib._set_type_id(o, size) lib.qcgc_write(o) # Register object return ffi.cast("myobject_t *", o)
def test_push_pop(self): """Test push/pop""" stack = lib.qcgc_object_stack_create(1000) pythonstack = list() for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack); self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack); self.assertEqual(stack.count, 0)
def test_incremenatal(self): o = ffi.cast("object_t *", self.allocate_ref(lib.qcgc_arena_size // ffi.sizeof("myobject_t *"))) self.push_root(o) p = ffi.cast("object_t *", self.allocate(1)) self.push_root(p) q = ffi.cast("object_t *", self.allocate(1)) self.set_ref(o, 0, p) # self.pop_root() lib.qcgc_incmark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertFalse(self.gp_gray_stack_has(o)) # self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", q)), lib.BLOCK_WHITE) # self.set_ref(o, 1, q) self.assertTrue(self.gp_gray_stack_has(o)) # lib.qcgc_incmark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertFalse(self.gp_gray_stack_has(o)) # self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", q)), lib.BLOCK_BLACK)
def test_color_transitions(self): """Test all possible color transitions""" reachable = list() unreachable = list() for i in range(2 * lib.QCGC_INC_MARK_MIN): o = self.allocate_ref(1) self.push_root(o) reachable.append(o) self.assertEqual(lib.qcgc_get_mark_color(ffi.cast("object_t *",o)), lib.MARK_COLOR_LIGHT_GRAY) lib.qcgc_incmark() # Marks ALL root objects self.assertEqual(lib.qcgc_state.phase, lib.GC_MARK) for o in reachable: self.assertIn(lib.qcgc_get_mark_color(ffi.cast("object_t *", o)), [lib.MARK_COLOR_DARK_GRAY, lib.MARK_COLOR_BLACK]) if (lib.qcgc_get_mark_color(ffi.cast("object_t *", o)) == lib.MARK_COLOR_BLACK): # Trigger write barrier and add object lib.qcgc_write(ffi.cast("object_t *", o)) self.assertEqual(lib.qcgc_get_mark_color(ffi.cast("object_t *", o)), lib.MARK_COLOR_DARK_GRAY) lib.qcgc_mark() for o in reachable: self.assertEqual(lib.qcgc_get_mark_color(ffi.cast("object_t *", o)), lib.MARK_COLOR_BLACK) lib.bump_ptr_reset() lib.qcgc_sweep() for o in reachable: self.assertEqual(lib.qcgc_get_mark_color(ffi.cast("object_t *", o)), lib.MARK_COLOR_WHITE)
def test_push_pop(self): """Test push/pop""" stack = lib.qcgc_object_stack_create(1000) pythonstack = list() for i in range(1000): stack = lib.qcgc_object_stack_push(stack, ffi.cast("object_t *", i)) pythonstack.append(ffi.cast("object_t *", i)) self.assertEqual(stack.count, 1000) while pythonstack: p = lib.qcgc_object_stack_top(stack) self.assertEqual(p, pythonstack.pop()) stack = lib.qcgc_object_stack_pop(stack) self.assertEqual(stack.count, 0)
def test_prebuilt(self): p = self.allocate_prebuilt(1) q = self.allocate_prebuilt_ref(4) r = self.allocate(1) s = self.allocate_ref(4) self.set_ref(q, 0, p) self.set_ref(q, 1, q) self.set_ref(q, 2, r) self.set_ref(q, 3, s) self.set_ref(s, 0, p) self.set_ref(s, 1, q) self.set_ref(s, 2, r) self.set_ref(s, 3, s) lib.bump_ptr_reset() lib.qcgc_collect() self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", r)), lib.BLOCK_WHITE) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_WHITE)
def test_no_references(self): """No references""" roots = list() garbage = list() for _ in range(100): p = self.allocate(10) self.push_root(p) roots.append(p) p = self.allocate(3) garbage.append(p) lib.qcgc_mark() for p in roots: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in garbage: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_no_references(self): """No references""" roots = list() garbage = list() for _ in range(100): p = self.allocate(10) self.push_root(p) roots.append(p) p = self.allocate(3) garbage.append(p) mark_all_inc() for p in roots: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in garbage: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_circular(self): """Circular references""" reachable = list() unreachable = list() for i in range(10): objects = self.gen_circular_structure(i + 1) self.push_root(objects[0]) reachable.extend(objects) for i in range(10): objects = self.gen_circular_structure(i + 1) unreachable.extend(objects) mark_all_inc() for p in reachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in unreachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_prebuilt(self): p = self.allocate_prebuilt(1) q = self.allocate_prebuilt_ref(4) r = self.allocate(1) s = self.allocate_ref(4) self.set_ref(q, 0, p) self.set_ref(q, 1, q) self.set_ref(q, 2, r) self.set_ref(q, 3, s) self.set_ref(s, 0, p) self.set_ref(s, 1, q) self.set_ref(s, 2, r) self.set_ref(s, 3, s) lib.bump_ptr_reset() lib.qcgc_collect() self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", r)), lib.BLOCK_WHITE) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_WHITE)
def test_circular(self): """Circular references""" reachable = list() unreachable = list() for i in range(10): objects = self.gen_circular_structure(i + 1) self.push_root(objects[0]) reachable.extend(objects) for i in range(10): objects = self.gen_circular_structure(i + 1) unreachable.extend(objects) lib.qcgc_mark() for p in reachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in unreachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def test_write_barrier_after_sweep(self): o = self.allocate_ref(1) self.push_root(o) # lib.qcgc_collect() # p = self.allocate(1) self.set_ref(o, 0, p) # lib.bump_ptr_reset() lib.qcgc_collect() # self.assertIn(self.get_blocktype(ffi.cast("cell_t *", p)), [lib.BLOCK_WHITE, lib.BLOCK_BLACK])
def test_ref_1(self): """Tree shaped reference struct""" # Generate reachable objects reachable = list() for _ in range(10): p, objs = self.gen_structure_1() self.push_root(p) reachable.extend(objs) # Generate unreachable objects unreachable = list() for _ in range(10): p, objs = self.gen_structure_1() unreachable.extend(objs) lib.qcgc_mark() for p in reachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) for p in unreachable: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_WHITE)
def to_dot(self, filename, edges): from pygraphviz import AGraph dot = AGraph(directed=True) for n in edges.keys(): dot.add_node(str(n)) if lib.qcgc_arena_get_blocktype(ffi.cast("cell_t *", n)) not in [ lib.BLOCK_BLACK, lib.BLOCK_WHITE]: node = dot.get_node(str(n)) node.attr['color'] = 'red' for n in edges.keys(): if edges[n] is not None: dot.add_edge(str(n), str(edges[n])) dot.layout(prog='dot') dot.draw(filename)
def to_dot(self, filename, edges): from pygraphviz import AGraph dot = AGraph(directed=True) for n in edges.keys(): dot.add_node(str(n)) if lib.qcgc_arena_get_blocktype(ffi.cast( "cell_t *", n)) not in [lib.BLOCK_BLACK, lib.BLOCK_WHITE]: node = dot.get_node(str(n)) node.attr['color'] = 'red' for n in edges.keys(): if edges[n] is not None: dot.add_edge(str(n), str(edges[n])) dot.layout(prog='dot') dot.draw(filename)
def test_incremental(self): o = self.allocate_prebuilt_ref(2) p = self.allocate(1) q = self.allocate(2) self.set_ref(o, 0, p) # lib.qcgc_incmark() # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_WHITE) # self.set_ref(o, 1, q) # lib.qcgc_incmark() # r = self.allocate_prebuilt_ref(1) s = self.allocate(2) self.set_ref(r, 0, s) # lib.qcgc_incmark() # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_BLACK)
def test_write_barrier(self): o = self.allocate(16) self.push_root(o) arena = lib.qcgc_arena_addr(ffi.cast("cell_t *", o)) o.hdr.flags = o.hdr.flags & ~lib.QCGC_GRAY_FLAG self.assertEqual(ffi.cast("object_t *", o).flags & lib.QCGC_GRAY_FLAG, 0) lib.qcgc_write(ffi.cast("object_t *", o)) self.assertEqual(ffi.cast("object_t *", o).flags & lib.QCGC_GRAY_FLAG, lib.QCGC_GRAY_FLAG) lib.qcgc_state.phase = lib.GC_MARK o = self.allocate(16) self.push_root(o) arena = lib.qcgc_arena_addr(ffi.cast("cell_t *", o)) o.hdr.flags = o.hdr.flags & ~lib.QCGC_GRAY_FLAG self.assertEqual(ffi.cast("object_t *", o).flags & lib.QCGC_GRAY_FLAG, 0) self.set_blocktype(ffi.cast("cell_t *", o), lib.BLOCK_BLACK) lib.qcgc_state.phase = lib.GC_MARK lib.qcgc_write(ffi.cast("object_t *", o)) self.assertEqual(ffi.cast("object_t *", o).flags & lib.QCGC_GRAY_FLAG, lib.QCGC_GRAY_FLAG) self.assertEqual(lib.arena_gray_stack(arena).count, 1) self.assertEqual(lib.arena_gray_stack(arena).items[0], o)
def test_simple_switch(self): objs = list() for _ in range(lib.qcgc_arena_cells_count - lib.qcgc_arena_first_cell_index - 1): o = self.allocate(1) self.push_root(o) objs.append(o) # for o in objs: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *",o)), lib.BLOCK_WHITE) lib.qcgc_reset_bump_ptr() lib.qcgc_collect() self.assertEqual(lib._qcgc_bump_allocator.ptr, ffi.NULL) self.assertEqual(lib._qcgc_bump_allocator.end, ffi.NULL) self.allocate(1) self.assertEqual(lib._qcgc_bump_allocator.ptr, ffi.NULL) self.assertEqual(lib._qcgc_bump_allocator.end, ffi.NULL)
def test_simple_switch(self): objs = list() for _ in range(lib.qcgc_arena_cells_count - lib.qcgc_arena_first_cell_index - 1): o = self.allocate(1) self.push_root(o) objs.append(o) # for o in objs: self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", o)), lib.BLOCK_WHITE) lib.qcgc_reset_bump_ptr() lib.qcgc_collect() self.assertEqual(lib._qcgc_bump_allocator.ptr, ffi.NULL) self.assertEqual(lib._qcgc_bump_allocator.end, ffi.NULL) self.allocate(1) self.assertEqual(lib._qcgc_bump_allocator.ptr, ffi.NULL) self.assertEqual(lib._qcgc_bump_allocator.end, ffi.NULL)
def test_incremenatal(self): o = ffi.cast( "object_t *", self.allocate_ref(lib.qcgc_arena_size // ffi.sizeof("myobject_t *"))) self.push_root(o) p = ffi.cast("object_t *", self.allocate(1)) self.push_root(p) q = ffi.cast("object_t *", self.allocate(1)) self.set_ref(o, 0, p) # self.pop_root() lib.qcgc_incmark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertFalse(self.gp_gray_stack_has(o)) # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_WHITE) # self.set_ref(o, 1, q) self.assertTrue(self.gp_gray_stack_has(o)) # lib.qcgc_incmark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertFalse(self.gp_gray_stack_has(o)) # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", p)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", q)), lib.BLOCK_BLACK)
def set_ref(self, obj, index, ref): lib.qcgc_write(ffi.cast("object_t *", obj)) # Trigger write barrier assert index >= 0 assert ffi.cast("myobject_t *", obj).type_id > index ffi.cast("myobject_t *", obj).refs[index] = ffi.cast("myobject_t *", ref)
def test_mark_large(self): o = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(o) p = ffi.cast( "object_t *", self.allocate_ref(lib.qcgc_arena_size // ffi.sizeof("myobject_t *"))) self.push_root(p) q = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(q) r = ffi.cast("object_t *", self.allocate(1)) self.push_root(r) s = ffi.cast("object_t *", self.allocate_ref(1)) self.push_root(s) t = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(t) self.set_ref(p, 0, q) self.set_ref(p, 1, r) self.set_ref(p, 2, s) self.set_ref(s, 0, p) # for _ in range(6): self.pop_root() self.push_root(o) self.push_root(s) # lib.qcgc_mark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(p)) self.assertTrue(self.hbtable_marked(p)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(q)) self.assertTrue(self.hbtable_marked(q)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(t)) self.assertFalse(self.hbtable_marked(t)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", r)), lib.BLOCK_BLACK) # lib.bump_ptr_reset() lib.qcgc_sweep() # self.assertTrue(self.hbtable_has(o)) self.assertFalse(self.hbtable_marked(o)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(p)) self.assertFalse(self.hbtable_marked(p)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(q)) self.assertFalse(self.hbtable_marked(q)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertFalse(self.hbtable_has(t)) self.assertFalse(self.hbtable_marked(t)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", s)), lib.BLOCK_WHITE) self.assertEqual(self.get_blocktype(ffi.cast("cell_t *", r)), lib.BLOCK_WHITE)
def get_ref(self, obj, index): return ffi.cast("myobject_t *", obj).refs[index]
def assert_valid_gc_object(self, obj): self.assertIn(self.get_blocktype(ffi.cast("cell_t *", obj)), [ lib.BLOCK_BLACK, lib.BLOCK_WHITE ])
def test_self_allocate(self): p = self.allocate(1) self.assertEqual(lib._get_type_id(ffi.cast("object_t *", p)), 0) p = self.allocate_ref(1) self.assertEqual(lib._get_type_id(ffi.cast("object_t *", p)), 1)
def bump_allocate_cells(self, cells): p = self.bump_allocate(cells * 16) self.bump_allocate(16) # Prevent non-coalseced arena return ffi.cast("cell_t *", p)
def set_ref(self, obj, index, ref): lib.qcgc_write(ffi.cast("object_t *", obj)) # Trigger write barrier assert index >= 0 assert ffi.cast("myobject_t *", obj).type_id > index ffi.cast("myobject_t *", obj).refs[index] = ffi.cast("myobject_t *", ref)
def get_ref(self, obj, index): return ffi.cast("myobject_t *", obj).refs[index]
def test_allocate_coalesced_block(self): "Test allocation when there are invalid blocks in the free lists" # Small block # coalesced area no 1 # ATOMIC! Invalidates internal invariant for short time x = self.bump_allocate(16) y = self.bump_allocate(16) self.bump_allocate(16) # Prevent non-coalesced arena lib.qcgc_arena_mark_free(ffi.cast("cell_t *",x)) lib.qcgc_arena_mark_free(ffi.cast("cell_t *",y)) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", x), 1) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", y), 1) self.set_blocktype(ffi.cast("cell_t *", y), lib.BLOCK_EXTENT) # only valid block p = self.bump_allocate_cells(1) lib.qcgc_arena_mark_free(p) lib.qcgc_fit_allocator_add(p, 1) # coalesced area no 2 # ATOMIC! Invalidates internal invariant for short time x = self.bump_allocate(16) y = self.bump_allocate(16) self.bump_allocate(16) # Prevent non-coalesced arena lib.qcgc_arena_mark_free(ffi.cast("cell_t *",x)) lib.qcgc_arena_mark_free(ffi.cast("cell_t *",y)) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", x), 1) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", y), 1) self.set_blocktype(ffi.cast("cell_t *", y), lib.BLOCK_EXTENT) q = self.fit_allocate(1) self.assertEqual(p, q) # Large block # coalesced area no 1 # ATOMIC! Invalidates internal invariant for short time x = self.bump_allocate(16 * 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) y = self.bump_allocate(16 * 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) self.bump_allocate(16) # Prevent non-coalesced arena lib.qcgc_arena_mark_free(ffi.cast("cell_t *",x)) lib.qcgc_arena_mark_free(ffi.cast("cell_t *",y)) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", x), 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", y), 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) self.set_blocktype(ffi.cast("cell_t *", y), lib.BLOCK_EXTENT) # only valid block p = self.bump_allocate_cells(2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) lib.qcgc_arena_mark_free(p) lib.qcgc_fit_allocator_add(p, 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) # coalesced area no 2 # ATOMIC! Invalidates internal invariant for short time x = self.bump_allocate(16 * 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) y = self.bump_allocate(16 * 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) self.bump_allocate(16) # Prevent non-coalesced arena lib.qcgc_arena_mark_free(ffi.cast("cell_t *",x)) lib.qcgc_arena_mark_free(ffi.cast("cell_t *",y)) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", x), 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) lib.qcgc_fit_allocator_add(ffi.cast("cell_t *", y), 2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) self.set_blocktype(ffi.cast("cell_t *", y), lib.BLOCK_EXTENT) q = self.fit_allocate(2**lib.QCGC_LARGE_FREE_LIST_FIRST_EXP) self.assertEqual(p, q)
def fit_allocate(self, cells): p = lib.qcgc_fit_allocate(cells * 16) return ffi.cast("cell_t *", p)
def bump_allocate_cells(self, cells): p = self.bump_allocate(cells * 16) self.bump_allocate(16) # Prevent non-coalseced arena return ffi.cast("cell_t *", p)
def test_mark_large(self): o = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(o) p = ffi.cast("object_t *", self.allocate_ref(lib.qcgc_arena_size // ffi.sizeof("myobject_t *"))) self.push_root(p) q = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(q) r = ffi.cast("object_t *", self.allocate(1)) self.push_root(r) s = ffi.cast("object_t *", self.allocate_ref(1)) self.push_root(s) t = ffi.cast("object_t *", self.allocate(lib.qcgc_arena_size)) self.push_root(t) self.set_ref(p, 0, q) self.set_ref(p, 1, r) self.set_ref(p, 2, s) self.set_ref(s, 0, p) # for _ in range(6): self.pop_root() self.push_root(o) self.push_root(s) # lib.qcgc_mark() # self.assertTrue(self.hbtable_has(o)) self.assertTrue(self.hbtable_marked(o)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(p)) self.assertTrue(self.hbtable_marked(p)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(q)) self.assertTrue(self.hbtable_marked(q)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(t)) self.assertFalse(self.hbtable_marked(t)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", s)), lib.BLOCK_BLACK) self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", r)), lib.BLOCK_BLACK) # lib.bump_ptr_reset() lib.qcgc_sweep() # self.assertTrue(self.hbtable_has(o)) self.assertFalse(self.hbtable_marked(o)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(p)) self.assertFalse(self.hbtable_marked(p)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertTrue(self.hbtable_has(q)) self.assertFalse(self.hbtable_marked(q)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertFalse(self.hbtable_has(t)) self.assertFalse(self.hbtable_marked(t)) self.assertEqual(lib.qcgc_state.gray_stack_size, 0) # self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", s)), lib.BLOCK_WHITE) self.assertEqual(self.get_blocktype( ffi.cast("cell_t *", r)), lib.BLOCK_WHITE)
def push_root(self, o): lib.qcgc_push_root(ffi.cast("object_t *", o))
def test_self_allocate(self): p = self.allocate(1) self.assertEqual(lib._get_type_id(ffi.cast("object_t *", p)), 0) p = self.allocate_ref(1) self.assertEqual(lib._get_type_id(ffi.cast("object_t *", p)), 1)
def allocate_ref(self, size): o = lib.qcgc_allocate(self.header_size + size * ffi.sizeof("myobject_t *")) self.assertNotEqual(o, ffi.NULL) lib._set_type_id(o, size) return ffi.cast("myobject_t *", o)