From what research I have done there are no libraries out there to create and manage shared memory in Go. I thought it might be easy enough to pull of with some basic cgo code, and as it turns out it is. The concern that I would like to address before I move forward involves the interaction between the Go runtime and Go pointers to shared memory.
To demonstrate my concern, I created a simple Go struct
type Test struct {
X int32
Y float64
Z [5]int32
}
That I expose in two seperate Go programs, A and B along with some cgo code.
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <string.h>
int get_mem() {
key_t key = ftok("shared_mem",65);
return shmget(key,1024,0666|IPC_CREAT);
}
char * attach_mem(int id) {
return (char*) shmat(id,(void*)0,0);
}
void detach_mem(char * str) {
shmdt(str);
}
void destroy_mem(int id) {
shmctl(id,IPC_RMID,NULL);
}
void write(char * str, char * data, int len) {
memcpy(str, data, len);
}
Program A then creates a Test
struct, writes it to shared memory, creates a *Test
pointer called ptr
to that memory, waits 10 seconds, then prints the value at ptr
.
// Program A
func main() {
id := C.get_mem()
str := C.attach_mem(id)
dataLocal := Test{1, 2.0, [5]int32{3, 4, 5, 6, 7}}
C.write(str, (*C.char)(unsafe.Pointer(&dataLocal)), C.int(unsafe.Sizeof(dataLocal)))
ptr := (*Test)(unsafe.Pointer(str))
time.Sleep(10 * time.Second)
fmt.Println(ptr)
C.detach_mem(str)
C.destroy_mem(id)
}
In those 10 seconds, Program B starts, creates a *Test
pointer to the shared memory, and modifies one of the fields.
// Program B
func main() {
id := C.get_mem()
str := C.attach_mem(id)
data := (*Test)(unsafe.Pointer(str))
data.X = 10
C.detach_mem(str)
}
Obviously this is not a practical IPC implementation, but it works. Program A prints
&{10 2 [3 4 5 6 7]}
My concern is that the Go runtime, namely the garbage collector, won't play nice with this implementation. Specifically, how will Go treat ptr
in Program A? I know that the Go GC will move data in the heap around, but does that apply to data referenced by Go pointers that lie outside of the heap? Now that a Go pointer references this data, will it be garbage collected?
If neither of those issues apply, is this approach unsafe or otherwise considered to be bad style? If so what other alternatives are there?