summary refs log tree commit diff stats
path: root/lib/core/refs.nim
blob: e1575b68c74dc0bdfe670173b6671913dcf8bf8d (plain) (blame)
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
#
#
#            Nim's Runtime Library
#        (c) Copyright 2017 Nim contributors
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Default ref implementation used by Nim's core.

# We cannot use the allocator interface here as we require a heap walker to
# exist. Thus we import 'alloc' directly here to get our own heap that is
# all under the GC's control and can use the ``allObjects`` iterator which
# is crucial for the "sweep" phase.
import typelayouts, alloc

type
  TracingGc = ptr object of Allocator
    visit*: proc (fieldAddr: ptr pointer; a: Allocator) {.nimcall.}

  GcColor = enum
    white = 0, black = 1, grey = 2 ## to flip the meaning of white/black
                                   ## perform (1 - col)

  GcHeader = object
    t: ptr TypeLayout
    color: GcColor
  Cell = ptr GcHeader

  GcFrame {.core.} = object
    prev: ptr GcFrame
    marker: proc (self: GcFrame; a: Allocator)

  Phase = enum
    None, Marking, Sweeping

  GcHeap = object
    r: MemRegion
    phase: Phase
    currBlack, currWhite: GcColor
    greyStack: seq[Cell]

var
  gch {.threadvar.}: GcHeap

proc `=trace`[T](a: ref T) =
  if not marked(a):
    mark(a)
    `=trace`(a[])

template usrToCell(p: pointer): Cell =

template cellToUsr(cell: Cell): pointer =
  cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(GcHeader)))

template usrToCell(usr: pointer): Cell =
  cast[Cell](cast[ByteAddress](usr)-%ByteAddress(sizeof(GcHeader)))

template markGrey(x: Cell) =
  if x.color == gch.currWhite and phase == Marking:
    x.color = grey
    add(gch.greyStack, x)

proc `=`[T](dest: var ref T; src: ref T) =
  ## full write barrier implementation.
  if src != nil:
    let s = usrToCell(src)
    markGrey(s)
  system.`=`(dest, src)

proc linkGcFrame(f: ptr GcFrame) {.core.}
proc unlinkGcFrame() {.core.}

proc setGcFrame(f: ptr GcFrame) {.core.}

proc registerGlobal(p: pointer; t: ptr TypeLayout) {.core.}
proc unregisterGlobal(p: pointer; t: ptr TypeLayout) {.core.}

proc registerThreadvar(p: pointer; t: ptr TypeLayout) {.core.}
proc unregisterThreadvar(p: pointer; t: ptr TypeLayout) {.core.}

proc newImpl(t: ptr TypeLayout): pointer =
  let r = cast[Cell](rawAlloc(t.size + sizeof(GcHeader)))
  r.typ = t
  result = r +! sizeof(GcHeader)

template new*[T](x: var ref T) =
  x = newImpl(getTypeLayout(x))


when false:
  # implement these if your GC requires them:
  proc writeBarrierLocal() {.core.}
  proc writeBarrierGlobal() {.core.}

  proc writeBarrierGeneric() {.core.}