-
Notifications
You must be signed in to change notification settings - Fork 5
/
mem.go
142 lines (136 loc) · 3.7 KB
/
mem.go
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
// Package mem implements a memory allocator and deallocator.
// It currently uses mmap on unix and VirtualAlloc on windows
// to request pages of memory from the operating system, and
// munmap and VirtualFree to release pages of memory to the
// operating system. The allocator uses a first-fit algorithm
// on a singly-linked free list of blocks. Blocks are divided
// into sets called arenas, which correspond to the chunk of
// memory mapped from the operating system. When all of the
// blocks in a set are freed, the arena is unmapped.
package mem
import (
"os"
"sync"
"unsafe"
)
var (
szpage = uint(os.Getpagesize())
szheader = uint(unsafe.Sizeof(header{}))
freep = new(header) // head of linked list
m sync.Mutex
)
type header struct {
size uint
allocated bool
next *header
arena unsafe.Pointer // arena of which this block is a member
}
// return n rounded up to a multiple of k.
func roundMultiple(n, k uint) uint {
if mod := n % k; mod != 0 {
return n + (k - mod)
}
return n
}
// Alloc allocates size bytes of memory, and returns a pointer to it.
// It is goroutine-safe and attempts to preserve the semantics of
// POSIX libc's malloc. However, Alloc panics if an error occurs when
// requesting more memory from the operating system.
func Alloc(size uint) unsafe.Pointer {
if size == 0 {
return nil
}
m.Lock()
defer m.Unlock()
// Iterate through linked list of headers.
p := freep
for {
// block is free
if !p.allocated {
// block is large enough (first-fit)
if p.size >= size {
ret := uintptr(unsafe.Pointer(p)) + uintptr(szheader)
// split block if enough space for header
if gap := p.size - size; gap >= szheader {
h := (*header)(unsafe.Pointer(ret + uintptr(size)))
h.size = gap - szheader
h.allocated = false
h.next = p.next
h.arena = p.arena
p.next = h
p.size = size
}
p.allocated = true
return unsafe.Pointer(ret)
}
}
// allocate memory
if p.next == nil {
// Allocated space must be enough to hold header and
// size bytes. Simplify alignment by rounding up to
// the next multiple of the header size.
szalign := roundMultiple(szheader+size, szheader)
// round bytes to the next multiple of the page size
szalloc := roundMultiple(szalign, szpage)
pblock, err := mmap(szalloc)
if err != nil {
panic(err)
}
h := (*header)(pblock)
h.size = szalloc - szheader
h.allocated = false
h.next = nil
h.arena = pblock
p.next = h
}
p = p.next
}
}
// Free deallocates the memory pointed to by p. It is goroutine-safe
// and attempts to preserve the semantics of POSIX libc's free.
// However, Free panics if an error occurs when releasing memory to
// the operating system.
func Free(p unsafe.Pointer) {
if p == nil {
return
}
m.Lock()
defer m.Unlock()
h := (*header)(unsafe.Pointer(uintptr(p) - uintptr(szheader)))
arcurr := h.arena
h.allocated = false
// Coalesce adjacent free blocks from the same arena.
if next := h.next; next != nil && next.arena == h.arena && !next.allocated {
h.size += next.size + szheader
h.next = next.next
}
freeArena := true
var first, prev *header
for it := freep; ; it = it.next {
if it.arena == arcurr {
if it.allocated {
freeArena = false
} else if next := it.next; next != nil && next.arena == arcurr && !next.allocated {
it.size += next.size + szheader
it.next = next.next
}
if first == nil {
first = it
}
}
if it.next == nil {
break
}
if it.arena != arcurr && it.next.arena == arcurr {
prev = it
}
}
if !freeArena {
return
}
// If there are no allocated blocks in arena, munmap it.
prev.next = first.next
if err := munmap(unsafe.Pointer(first)); err != nil {
panic(err)
}
}