/
memory.go
100 lines (87 loc) · 2 KB
/
memory.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
package numballoc
import (
"os"
"syscall"
"unsafe"
"github.com/fabiokung/shm"
)
type Memory interface {
Blocks() []uint32
Size() uint32 // in bytes
}
type SharedMemory interface {
Memory
// Close frees up all references (so they can be GC'ed) and unmaps the
// shared memory region
Close() error
}
type sharedMemory struct {
blocks []uint32
raw []byte
}
type sliceType struct {
data unsafe.Pointer
len int
cap int
}
// LoadShared maps a shared memory region. Size must be consistent with all
// others mapping the same region (i.e.: the same name).
//
// Loading a shared memory region using the wrong size can lead to segmentation
// faults (SIGSEGV), or truncated data.
func LoadShared(name string, size uint32) (SharedMemory, error) {
file, err := shm.Open(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
var isNew bool
if err == nil {
isNew = true
if err := syscall.Ftruncate(
int(file.Fd()), int64(size),
); err != nil {
return nil, err
}
} else {
if file, err = shm.Open(name, os.O_RDWR, 0600); err != nil {
return nil, err
}
}
defer file.Close()
data, err := syscall.Mmap(int(file.Fd()), 0, int(size),
syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED,
)
if isNew {
// clear all bits
for i := range data {
data[i] = 0
}
}
// size is in bytes, blocks have 4 bytes (uint32)
blocksLen := size >> 2 // size / 4
if blocksLen == 0 {
blocksLen = 1 // at least 1 block
}
blocks := *(*sliceType)(unsafe.Pointer(&data))
blocks.len = int(blocksLen)
blocks.cap = int(blocksLen)
return &sharedMemory{
blocks: *(*[]uint32)(unsafe.Pointer(&blocks)),
raw: data,
}, nil
}
// DestroyShared cleans up a shared memory region
func DestroyShared(name string) error {
return shm.Unlink(name)
}
func (b *sharedMemory) Blocks() []uint32 {
return b.blocks
}
func (b *sharedMemory) Size() uint32 {
return uint32(len(b.raw))
}
func (b *sharedMemory) Close() error {
if err := syscall.Munmap(b.raw); err != nil {
return err
}
b.blocks = nil
b.raw = nil
return nil
}