evanphx / rubinius

Rubinius, the Ruby VM

This URL has Read+Write access

rubinius / lib / mark_sweepgc.rb
100644 159 lines (133 sloc) 2.982 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
require 'heap'
require 'object'
require 'gc'
 
class MarkSweepGC < GarbageCollector
  def initialize(size)
    super()
    @heap = Heap.new(size)
    @size = size
    @used = 0
    @start = @heap.allocate(size)
    @allocations = [[@start, size]]
    @robject = RObject.new(0) # Used to calculate object memory sizes.
  end
  
  def destroy!
    @heap.deallocate
    @size = 0
    @used = 0
  end
  
  def space_at_end?(sz)
    sz < @size - @used
  end
  
  def allocate(size)
    if false and space_at_end?(size)
      addr = @heap.allocate(size)
      @used += size
      return addr
    end
    
    addr = find_allocation(size)
    return addr
  end
  
  def find_allocation(size)
    @allocations.each do |ent|
      addr, addr_size = ent
      
      # puts "msgc: #{[addr_size, size, addr].inspect}"
      
      if addr_size >= size
        rem = addr_size - size
        if rem == 0
          @allocations.delete(ent)
        else
          ent[0] = addr + size
          ent[1] = rem
        end
        # puts " ment: #{ent.inspect}"
        return addr
      end
    end
    
    return nil
  end
  
  def reclaim_address(addr)
    @robject.address = addr
    @allocations.unshift [addr, @robject.memory_size]
  end
  
  def contains?(obj)
    @heap.contains?(obj.address)
  end
  
  alias :mutate_object :mark_object
    
  alias :already_mutated? :object_marked?
  
  # Mark/Sweep doesn't move a mutate object, so we just return the same object.
  def fetch_new_mutation(obj)
    obj
  end
  
  alias :collect :mutate
    
  def sweep
    each_object do |obj|
      if object_marked?(obj)
        unmark_object obj
      else
        reclaim_address obj.address
      end
    end
  end
  
  def sort_allocations
    @allocations.sort do |a,b|
      a[0] <=> b[0]
    end
  end
  
  def reorder!
    lst = sort_allocations
    @allocations = [lst.shift]
    
    lst.each do |addr, size|
      curr = @allocations.last
      # puts "CURR: #{curr.inspect}, #{addr}, #{curr.first + curr.last}"
      if curr.first + curr.last == addr
        curr[-1] += size
      else
        @allocations << [addr, size]
      end
    end
  end
  
  def regions_in_use
    holes = sort_allocations
    
    if holes.first[0] == @start
      ent = holes.shift
      start = ent[0] + ent[1]
    else
      start = @start
    end
    
    out = []
    curr = start
    until holes.empty?
      ent = holes.shift
      sz = ent[0] - curr
      if sz > 0
        out << [curr, sz]
      end
      curr = ent[0] + ent[1]
    end
    return out
  end
  
  def each_object
    regions_in_use.each do |addr, addr_size|
      curr = addr
      last = addr + addr_size
      while curr < last
        obj = RObject.new(curr)
        yield obj
        curr += obj.memory_size
      end
    end
  end
  
  def clear_marks!
    each_object do |obj|
      unmark_object(obj)
    end
  end
  
  def after_mutation
  end
  
  def complete_cycle(roots)
    collect roots
    sweep
    reorder!
  end
end