-
Notifications
You must be signed in to change notification settings - Fork 4
/
manager.go
134 lines (117 loc) · 3.75 KB
/
manager.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
// In cases where Go's garbage collecter is unresponsive, or even broken, this
// allows you to easily manage blocks of memory. This can be useful if you
// anticipate churning through lots of memory very quickly. This package
// creates a default Manager that can be used through the package functions,
// but you can create your own Manager if you want to eventually return the
// memory back to Go.
package memory
import (
"fmt"
"sync"
)
// We don't bother allocating blocks smaller than this. This choice is
// somewhat arbitrary, but should probably be appropriate for most
// applications that want to manage their own memory.
const smallestBlockSize = 1024
// A Manager allocates memory as necessary and won't free it as long as it
// exists. If you call GetBlock() then it should be followed by a FreeBlock()
//when you are done with it.
type Manager struct {
mutex sync.Mutex
// blocks[size] = a slice of blocks of length 2^(10+size)
blocks [][][]byte
// Map of used blocks. The key is the address of the first byte in the
// block.
used map[*byte]bool
}
func NewManager() *Manager {
var m Manager
m.blocks = make([][][]byte, 21)
m.used = make(map[*byte]bool)
return &m
}
// Returns a slice of length n of the smallest block available that is at
// least that large. Will reuse an existing block if possible, otherwise
// it will allocate a new one.
func (m *Manager) GetBlock(n int) []byte {
m.mutex.Lock()
defer m.mutex.Unlock()
// Find the smallest block that can accomodate the request.
c := smallestBlockSize
s := 0
for c < n {
c *= 2
s++
}
// Check if a block already exists of the appropriate size.
for i := range m.blocks[s] {
if !m.used[&m.blocks[s][i][0]] {
m.used[&m.blocks[s][i][0]] = true
for j := range m.blocks[s][i] {
m.blocks[s][i][j] = 0
}
return m.blocks[s][i]
}
}
// Otherwise allocate a new block to use.
new_block := make([]byte, c)
m.blocks[s] = append(m.blocks[s], new_block)
m.used[&new_block[0]] = true
return new_block[0:n]
}
// Returns a block back to the Manager so that it can be reused. Panics if
// this block has already been freed, or if it was not allocated by this
// manager.
func (m *Manager) FreeBlock(b []byte) {
m.mutex.Lock()
defer m.mutex.Unlock()
if _, ok := m.used[&b[0]]; !ok {
panic("Tried to free an unused block")
}
delete(m.used, &b[0])
}
// Human-readable rundown on how many blocks have been reserved by this
// manager, and which ones are currently in use.
func (m *Manager) TotalAllocations() string {
c := smallestBlockSize
var ret string
total_used := 0
total_allocated := 0
for s := range m.blocks {
used := 0
for i := range m.blocks[s] {
if m.used[&m.blocks[s][i][0]] {
used++
}
}
if used > 0 {
ret += fmt.Sprintf("%d bytes: %d/%d blocks in use.\n", c, used, len(m.blocks[s]))
}
total_used += used * c
total_allocated += len(m.blocks[s]) * c
c *= 2
}
ret += fmt.Sprintf("Total memory used/allocated: %d/%d\n", total_used, total_allocated)
return ret
}
var manager *Manager
func init() {
manager = NewManager()
}
// Returns a slice of length n of the smallest block available that is at
// least that large. Will reuse an existing block if possible, otherwise
// it will allocate a new one.
func GetBlock(n int) []byte {
return manager.GetBlock(n)
}
// Returns a block back to the Manager so that it can be reused. Panics if
// this block has already been freed, or if it was not allocated by this
// manager.
func FreeBlock(b []byte) {
manager.FreeBlock(b)
}
// Human-readable rundown on how many blocks have been reserved by this
// manager, and which ones are currently in use.
func TotalAllocations() string {
return manager.TotalAllocations()
}