├── .gitignore ├── .travis.yml ├── LICENSE ├── ReadMe.md ├── go.mod ├── shm.go ├── shm_test.go ├── shmi_darwin.go ├── shmi_linux.go ├── shmi_windows.go └── tools.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9 5 | 6 | script: 7 | - go test -v ./... 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 hidez8891 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # shm 2 | 3 | [![Build Status](https://travis-ci.org/hidez8891/shm.svg?branch=master)](https://travis-ci.org/hidez8891/shm) 4 | 5 | shm is Golang shared memory library. 6 | 7 | ## Example 8 | 9 | ```go 10 | w, _ := shm.Create("shm_name", 256) 11 | defer w.Close() 12 | 13 | r, _ := shm.Open("shm_name", 256) 14 | defer r.Close() 15 | 16 | wbuf := []byte("Hello World") 17 | w.Write(wbuf) 18 | 19 | rbuf := make([]byte, len(wbuf)) 20 | r.Read(rbuf) 21 | // rbuf == wbuf 22 | ``` 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hidez8891/shm 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /shm.go: -------------------------------------------------------------------------------- 1 | package shm 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | // Memory is shared memory struct 9 | type Memory struct { 10 | m *shmi 11 | pos int64 12 | } 13 | 14 | // Create is create shared memory 15 | func Create(name string, size int32) (*Memory, error) { 16 | m, err := create(name, size) 17 | if err != nil { 18 | return nil, err 19 | } 20 | return &Memory{m, 0}, nil 21 | } 22 | 23 | // Open is open exist shared memory 24 | func Open(name string, size int32) (*Memory, error) { 25 | m, err := open(name, size) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &Memory{m, 0}, nil 30 | } 31 | 32 | // Close is close & discard shared memory 33 | func (o *Memory) Close() (err error) { 34 | if o.m != nil { 35 | err = o.m.close() 36 | if err == nil { 37 | o.m = nil 38 | } 39 | } 40 | return err 41 | } 42 | 43 | // Read is read shared memory (current position) 44 | func (o *Memory) Read(p []byte) (n int, err error) { 45 | n, err = o.ReadAt(p, o.pos) 46 | if err != nil { 47 | return 0, err 48 | } 49 | o.pos += int64(n) 50 | return n, nil 51 | } 52 | 53 | // ReadAt is read shared memory (offset) 54 | func (o *Memory) ReadAt(p []byte, off int64) (n int, err error) { 55 | return o.m.readAt(p, off) 56 | } 57 | 58 | // Seek is move read/write position at shared memory 59 | func (o *Memory) Seek(offset int64, whence int) (int64, error) { 60 | switch whence { 61 | case io.SeekStart: 62 | offset += int64(0) 63 | case io.SeekCurrent: 64 | offset += o.pos 65 | case io.SeekEnd: 66 | offset += int64(o.m.size) 67 | } 68 | if offset < 0 || offset >= int64(o.m.size) { 69 | return 0, fmt.Errorf("invalid offset") 70 | } 71 | o.pos = offset 72 | return offset, nil 73 | } 74 | 75 | // Write is write shared memory (current position) 76 | func (o *Memory) Write(p []byte) (n int, err error) { 77 | n, err = o.WriteAt(p, o.pos) 78 | if err != nil { 79 | return 0, err 80 | } 81 | o.pos += int64(n) 82 | return n, nil 83 | } 84 | 85 | // WriteAt is write shared memory (offset) 86 | func (o *Memory) WriteAt(p []byte, off int64) (n int, err error) { 87 | return o.m.writeAt(p, off) 88 | } 89 | -------------------------------------------------------------------------------- /shm_test.go: -------------------------------------------------------------------------------- 1 | package shm_test 2 | 3 | import ( 4 | "io" 5 | "reflect" 6 | "strings" 7 | "sync" 8 | "testing" 9 | 10 | "github.com/hidez8891/shm" 11 | ) 12 | 13 | func create(t *testing.T, tag string, size int32) *shm.Memory { 14 | t.Helper() 15 | 16 | m, err := shm.Create(tag, size) 17 | if err != nil { 18 | t.Fatalf("fail: create shared memroy %v", err) 19 | } 20 | return m 21 | } 22 | 23 | func open(t *testing.T, tag string, size int32) *shm.Memory { 24 | t.Helper() 25 | 26 | m, err := shm.Open(tag, size) 27 | if err != nil { 28 | t.Fatalf("fail: open shared memroy %v", err) 29 | } 30 | return m 31 | } 32 | 33 | func TestNewOpen(t *testing.T) { 34 | for d := uint(1); d <= 30; d++ { 35 | size := int32(1) << d 36 | 37 | // create shared memory 38 | w, err := shm.Create("test_t", size) 39 | if err != nil { 40 | t.Errorf("warn: fail create %d byte shared memroy %v", size, err) 41 | continue 42 | } 43 | 44 | // open shared memory 45 | r, err := shm.Open("test_t", size) 46 | if err != nil { 47 | w.Close() 48 | t.Errorf("warn: fail open %d byte shared memroy %v", size, err) 49 | continue 50 | } 51 | 52 | w.Close() 53 | r.Close() 54 | } 55 | } 56 | 57 | func TestReadWriteAt(t *testing.T) { 58 | tests := []struct { 59 | size int 60 | data string 61 | }{ 62 | {size: 1, data: "a"}, // single 63 | {size: 63, data: strings.Repeat("a", 63)}, // full - 1 64 | {size: 64, data: strings.Repeat("b", 64)}, // full 65 | {size: 64, data: strings.Repeat("c", 65)}, // shrink 66 | } 67 | 68 | // create shared memory 69 | w := create(t, "test_t", 64) 70 | defer w.Close() 71 | 72 | // open shared memory 73 | r := open(t, "test_t", 64) 74 | defer r.Close() 75 | 76 | // read/write test 77 | for _, tt := range tests { 78 | data := []byte(tt.data) 79 | 80 | n, err := w.WriteAt(data, 0) 81 | if err != nil { 82 | t.Fatalf("fail: write shared memroy %v", err) 83 | } 84 | if n != tt.size { 85 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, tt.size) 86 | } 87 | 88 | buf := make([]byte, len(data)) 89 | n, err = r.ReadAt(buf, 0) 90 | if err != nil { 91 | t.Fatalf("fail: read shared memroy %v", err) 92 | } 93 | if n != tt.size { 94 | t.Fatalf("fail: read shared memroy %d byte, want %d byte", n, tt.size) 95 | } 96 | if !reflect.DeepEqual(buf[:tt.size], data[:tt.size]) { 97 | t.Fatalf("fail: read shared memroy %v, want %v", buf[:tt.size], data[:tt.size]) 98 | } 99 | } 100 | } 101 | 102 | func TestReadWriteAt_OverPosition(t *testing.T) { 103 | tests := []struct { 104 | pos int 105 | succ bool 106 | }{ 107 | {pos: 0, succ: true}, 108 | {pos: 63, succ: true}, 109 | {pos: 64, succ: false}, 110 | } 111 | 112 | // create shared memory 113 | w := create(t, "test_t", 64) 114 | defer w.Close() 115 | 116 | // open shared memory 117 | r := open(t, "test_t", 64) 118 | defer r.Close() 119 | 120 | // write dummy 121 | { 122 | data := []byte(strings.Repeat("a", 64)) 123 | n, err := w.WriteAt(data, 0) 124 | if err != nil { 125 | t.Fatalf("fail: write shared memroy %v", err) 126 | } 127 | if n != 64 { 128 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, 64) 129 | } 130 | } 131 | 132 | // read/write test 133 | for _, tt := range tests { 134 | data := []byte("b") 135 | n, err := w.WriteAt(data, int64(tt.pos)) 136 | if tt.succ { 137 | // success 138 | if err != nil { 139 | t.Fatalf("fail: write shared memroy %v", err) 140 | } 141 | if n != 1 { 142 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, 1) 143 | } 144 | } else { 145 | // fail 146 | if err != io.EOF { 147 | t.Fatalf("fail: write shared memroy raise %v, want %v", err, io.EOF) 148 | } 149 | } 150 | 151 | buf := make([]byte, 1) 152 | n, err = r.ReadAt(buf, int64(tt.pos)) 153 | if tt.succ { 154 | // success 155 | if err != nil { 156 | t.Fatalf("fail: read shared memroy %v", err) 157 | } 158 | if n != 1 { 159 | t.Fatalf("fail: read shared memroy %d byte, want %d byte", n, 1) 160 | } 161 | if !reflect.DeepEqual(buf, data) { 162 | t.Fatalf("fail: read shared memroy %v, want %v", buf, data) 163 | } 164 | } else { 165 | // fail 166 | if err != io.EOF { 167 | t.Fatalf("fail: read shared memroy raise %v, want %v", err, io.EOF) 168 | } 169 | } 170 | } 171 | } 172 | 173 | func TestReadWriteAt_MultiThreads(t *testing.T) { 174 | tests := []struct { 175 | size int 176 | data string 177 | }{ 178 | {size: 1, data: "a"}, // single 179 | {size: 63, data: strings.Repeat("a", 63)}, // full - 1 180 | {size: 64, data: strings.Repeat("b", 64)}, // full 181 | {size: 64, data: strings.Repeat("c", 65)}, // shrink 182 | } 183 | 184 | // create shared memory 185 | w := create(t, "test_t", 64) 186 | defer w.Close() 187 | 188 | // open shared memory 189 | r := open(t, "test_t", 64) 190 | defer r.Close() 191 | 192 | wg := new(sync.WaitGroup) 193 | written := make(chan bool) 194 | readone := make(chan bool) 195 | 196 | // write thread 197 | wg.Add(1) 198 | go func() { 199 | defer wg.Done() 200 | for _, tt := range tests { 201 | // write data 202 | data := []byte(tt.data) 203 | n, err := w.WriteAt(data, 0) 204 | if err != nil { 205 | written <- false 206 | t.Fatalf("fail: write shared memroy %v", err) 207 | } 208 | if n != tt.size { 209 | written <- false 210 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, tt.size) 211 | } 212 | written <- true 213 | 214 | // wait 215 | succ := <-readone 216 | if !succ { 217 | return 218 | } 219 | } 220 | }() 221 | 222 | // read thread 223 | wg.Add(1) 224 | go func() { 225 | defer wg.Done() 226 | for _, tt := range tests { 227 | // wait 228 | succ := <-written 229 | if !succ { 230 | return 231 | } 232 | 233 | // read data 234 | data := []byte(tt.data) 235 | buf := make([]byte, len(data)) 236 | n, err := r.ReadAt(buf, 0) 237 | if err != nil { 238 | readone <- false 239 | t.Fatalf("fail: read shared memroy %v", err) 240 | } 241 | if n != tt.size { 242 | readone <- false 243 | t.Fatalf("fail: read shared memroy %d byte, want %d byte", n, tt.size) 244 | } 245 | if !reflect.DeepEqual(buf[:tt.size], data[:tt.size]) { 246 | readone <- false 247 | t.Fatalf("fail: read shared memroy %v, want %v", buf[:tt.size], data[:tt.size]) 248 | } 249 | readone <- true 250 | } 251 | }() 252 | 253 | wg.Wait() 254 | } 255 | 256 | func TestReadWrite(t *testing.T) { 257 | tests := []struct { 258 | succ bool 259 | data string 260 | }{ 261 | {succ: true, data: "a"}, // single 262 | {succ: true, data: strings.Repeat("b", 63)}, // full 263 | {succ: false, data: "c"}, // overflow (EOF) 264 | } 265 | 266 | // create shared memory 267 | w := create(t, "test_t", 64) 268 | defer w.Close() 269 | 270 | // open shared memory 271 | r := open(t, "test_t", 64) 272 | defer r.Close() 273 | 274 | // read/write test 275 | for _, tt := range tests { 276 | data := []byte(tt.data) 277 | 278 | n, err := w.Write(data) 279 | if tt.succ { 280 | // success 281 | if err != nil { 282 | t.Fatalf("fail: write shared memroy %v", err) 283 | } 284 | if n != len(data) { 285 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, len(data)) 286 | } 287 | } else { 288 | // fail 289 | if err != io.EOF { 290 | t.Fatalf("fail: write shared memroy raise %v, want %v", err, io.EOF) 291 | } 292 | } 293 | 294 | buf := make([]byte, len(data)) 295 | n, err = r.Read(buf) 296 | if tt.succ { 297 | // success 298 | if err != nil { 299 | t.Fatalf("fail: read shared memroy %v", err) 300 | } 301 | if n != len(data) { 302 | t.Fatalf("fail: read shared memroy %d byte, want %d byte", n, len(data)) 303 | } 304 | if !reflect.DeepEqual(buf, data) { 305 | t.Fatalf("fail: read shared memroy %v, want %v", buf, data) 306 | } 307 | } else { 308 | // fail 309 | if err != io.EOF { 310 | t.Fatalf("fail: read shared memroy raise %v, want %v", err, io.EOF) 311 | } 312 | } 313 | } 314 | } 315 | 316 | func TestReadWrite_MultiThreads(t *testing.T) { 317 | tests := []struct { 318 | size int 319 | data string 320 | }{ 321 | {size: 1, data: "a"}, // single 322 | {size: 62, data: strings.Repeat("a", 62)}, // full - 1 323 | {size: 1, data: strings.Repeat("b", 10)}, // shrink 324 | } 325 | 326 | // create shared memory 327 | w := create(t, "test_t", 64) 328 | defer w.Close() 329 | 330 | // open shared memory 331 | r := open(t, "test_t", 64) 332 | defer r.Close() 333 | 334 | wg := new(sync.WaitGroup) 335 | written := make(chan bool) 336 | readone := make(chan bool) 337 | 338 | // write thread 339 | wg.Add(1) 340 | go func() { 341 | defer wg.Done() 342 | for _, tt := range tests { 343 | // write data 344 | data := []byte(tt.data) 345 | n, err := w.Write(data) 346 | if err != nil { 347 | written <- false 348 | t.Fatalf("fail: write shared memroy %v", err) 349 | } 350 | if n != tt.size { 351 | written <- false 352 | t.Fatalf("fail: write shared memroy %d byte, want %d byte", n, tt.size) 353 | } 354 | written <- true 355 | 356 | // wait 357 | succ := <-readone 358 | if !succ { 359 | return 360 | } 361 | } 362 | }() 363 | 364 | // read thread 365 | wg.Add(1) 366 | go func() { 367 | defer wg.Done() 368 | for _, tt := range tests { 369 | // wait 370 | succ := <-written 371 | if !succ { 372 | return 373 | } 374 | 375 | // read data 376 | data := []byte(tt.data) 377 | buf := make([]byte, len(data)) 378 | n, err := r.Read(buf) 379 | if err != nil { 380 | readone <- false 381 | t.Fatalf("fail: read shared memroy %v", err) 382 | } 383 | if n != tt.size { 384 | readone <- false 385 | t.Fatalf("fail: read shared memroy %d byte, want %d byte", n, tt.size) 386 | } 387 | if !reflect.DeepEqual(buf[:tt.size], data[:tt.size]) { 388 | readone <- false 389 | t.Fatalf("fail: read shared memroy %v, want %v", buf[:tt.size], data[:tt.size]) 390 | } 391 | readone <- true 392 | } 393 | }() 394 | 395 | wg.Wait() 396 | } 397 | -------------------------------------------------------------------------------- /shmi_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin,cgo 2 | 3 | package shm 4 | 5 | /* 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int _create(const char* name, int size, int flag) { 15 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; 16 | 17 | int fd = shm_open(name, flag, mode); 18 | if (fd < 0) { 19 | return -1; 20 | } 21 | 22 | struct stat mapstat; 23 | int ret = fstat(fd, &mapstat); 24 | if (ret != -1 && mapstat.st_size == 0) { 25 | if (ftruncate(fd, size) != 0) { 26 | close(fd); 27 | return -2; 28 | } 29 | } else if (ret == -1) { 30 | close(fd); 31 | return -3; 32 | } 33 | 34 | return fd; 35 | } 36 | 37 | int Create(const char* name, int size) { 38 | int flag = O_RDWR | O_CREAT; 39 | return _create(name, size, flag); 40 | } 41 | 42 | int Open(const char* name, int size) { 43 | int flag = O_RDWR; 44 | return _create(name, size, flag); 45 | } 46 | 47 | void* Map(int fd, int size) { 48 | void* p = mmap( 49 | NULL, size, 50 | PROT_READ | PROT_WRITE, 51 | MAP_SHARED, fd, 0); 52 | if (p == MAP_FAILED) { 53 | return NULL; 54 | } 55 | return p; 56 | } 57 | 58 | void Close(int fd, void* p, int size) { 59 | if (p != NULL) { 60 | munmap(p, size); 61 | } 62 | if (fd != 0) { 63 | close(fd); 64 | } 65 | } 66 | 67 | void Delete(const char* name) { 68 | shm_unlink(name); 69 | } 70 | */ 71 | import "C" 72 | 73 | import ( 74 | "fmt" 75 | "io" 76 | "unsafe" 77 | ) 78 | 79 | type shmi struct { 80 | name string 81 | fd C.int 82 | v unsafe.Pointer 83 | size int32 84 | parent bool 85 | } 86 | 87 | // create shared memory. return shmi object. 88 | // name should not be more than 31 bytes. 89 | func create(name string, size int32) (*shmi, error) { 90 | name = "/" + name 91 | 92 | fd := C.Create(C.CString(name), C.int(size)) 93 | if fd < 0 { 94 | return nil, fmt.Errorf("create") 95 | } 96 | 97 | v := C.Map(fd, C.int(size)) 98 | if v == nil { 99 | C.Close(fd, nil, C.int(size)) 100 | C.Delete(C.CString(name)) 101 | } 102 | 103 | return &shmi{name, fd, v, size, true}, nil 104 | } 105 | 106 | // open shared memory. return shmi object. 107 | // name should not be more than 31 bytes. 108 | func open(name string, size int32) (*shmi, error) { 109 | name = "/" + name 110 | 111 | fd := C.Open(C.CString(name), C.int(size)) 112 | if fd < 0 { 113 | return nil, fmt.Errorf("open") 114 | } 115 | 116 | v := C.Map(fd, C.int(size)) 117 | if v == nil { 118 | C.Close(fd, nil, C.int(size)) 119 | C.Delete(C.CString(name)) 120 | } 121 | 122 | return &shmi{name, fd, v, size, false}, nil 123 | } 124 | 125 | func (o *shmi) close() error { 126 | if o.v != nil { 127 | C.Close(o.fd, o.v, C.int(o.size)) 128 | o.v = nil 129 | } 130 | if o.parent { 131 | C.Delete(C.CString(o.name)) 132 | } 133 | return nil 134 | } 135 | 136 | // read shared memory. return read size. 137 | func (o *shmi) readAt(p []byte, off int64) (n int, err error) { 138 | if off >= int64(o.size) { 139 | return 0, io.EOF 140 | } 141 | if max := int64(o.size) - off; int64(len(p)) > max { 142 | p = p[:max] 143 | } 144 | return copyPtr2Slice(uintptr(o.v), p, off, o.size), nil 145 | } 146 | 147 | // write shared memory. return write size. 148 | func (o *shmi) writeAt(p []byte, off int64) (n int, err error) { 149 | if off >= int64(o.size) { 150 | return 0, io.EOF 151 | } 152 | if max := int64(o.size) - off; int64(len(p)) > max { 153 | p = p[:max] 154 | } 155 | return copySlice2Ptr(p, uintptr(o.v), off, o.size), nil 156 | } 157 | -------------------------------------------------------------------------------- /shmi_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux,cgo 2 | 3 | package shm 4 | 5 | /* 6 | #cgo LDFLAGS: -lrt 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int _create(const char* name, int size, int flag) { 16 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; 17 | 18 | int fd = shm_open(name, flag, mode); 19 | if (fd < 0) { 20 | return -1; 21 | } 22 | 23 | if (ftruncate(fd, size) != 0) { 24 | close(fd); 25 | return -2; 26 | } 27 | return fd; 28 | } 29 | 30 | int Create(const char* name, int size) { 31 | int flag = O_RDWR | O_CREAT; 32 | return _create(name, size, flag); 33 | } 34 | 35 | int Open(const char* name, int size) { 36 | int flag = O_RDWR; 37 | return _create(name, size, flag); 38 | } 39 | 40 | void* Map(int fd, int size) { 41 | void* p = mmap( 42 | NULL, size, 43 | PROT_READ | PROT_WRITE, 44 | MAP_SHARED, fd, 0); 45 | if (p == MAP_FAILED) { 46 | return NULL; 47 | } 48 | return p; 49 | } 50 | 51 | void Close(int fd, void* p, int size) { 52 | if (p != NULL) { 53 | munmap(p, size); 54 | } 55 | if (fd != 0) { 56 | close(fd); 57 | } 58 | } 59 | 60 | void Delete(const char* name) { 61 | shm_unlink(name); 62 | } 63 | */ 64 | import "C" 65 | 66 | import ( 67 | "fmt" 68 | "io" 69 | "unsafe" 70 | ) 71 | 72 | type shmi struct { 73 | name string 74 | fd C.int 75 | v unsafe.Pointer 76 | size int32 77 | parent bool 78 | } 79 | 80 | // create shared memory. return shmi object. 81 | func create(name string, size int32) (*shmi, error) { 82 | name = "/" + name 83 | 84 | fd := C.Create(C.CString(name), C.int(size)) 85 | if fd < 0 { 86 | return nil, fmt.Errorf("create") 87 | } 88 | 89 | v := C.Map(fd, C.int(size)) 90 | if v == nil { 91 | C.Close(fd, nil, C.int(size)) 92 | C.Delete(C.CString(name)) 93 | } 94 | 95 | return &shmi{name, fd, v, size, true}, nil 96 | } 97 | 98 | // open shared memory. return shmi object. 99 | func open(name string, size int32) (*shmi, error) { 100 | name = "/" + name 101 | 102 | fd := C.Open(C.CString(name), C.int(size)) 103 | if fd < 0 { 104 | return nil, fmt.Errorf("open") 105 | } 106 | 107 | v := C.Map(fd, C.int(size)) 108 | if v == nil { 109 | C.Close(fd, nil, C.int(size)) 110 | C.Delete(C.CString(name)) 111 | } 112 | 113 | return &shmi{name, fd, v, size, false}, nil 114 | } 115 | 116 | func (o *shmi) close() error { 117 | if o.v != nil { 118 | C.Close(o.fd, o.v, C.int(o.size)) 119 | o.v = nil 120 | } 121 | if o.parent { 122 | C.Delete(C.CString(o.name)) 123 | } 124 | return nil 125 | } 126 | 127 | // read shared memory. return read size. 128 | func (o *shmi) readAt(p []byte, off int64) (n int, err error) { 129 | if off >= int64(o.size) { 130 | return 0, io.EOF 131 | } 132 | if max := int64(o.size) - off; int64(len(p)) > max { 133 | p = p[:max] 134 | } 135 | return copyPtr2Slice(uintptr(o.v), p, off, o.size), nil 136 | } 137 | 138 | // write shared memory. return write size. 139 | func (o *shmi) writeAt(p []byte, off int64) (n int, err error) { 140 | if off >= int64(o.size) { 141 | return 0, io.EOF 142 | } 143 | if max := int64(o.size) - off; int64(len(p)) > max { 144 | p = p[:max] 145 | } 146 | return copySlice2Ptr(p, uintptr(o.v), off, o.size), nil 147 | } 148 | -------------------------------------------------------------------------------- /shmi_windows.go: -------------------------------------------------------------------------------- 1 | package shm 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "syscall" 7 | ) 8 | 9 | type shmi struct { 10 | h syscall.Handle 11 | v uintptr 12 | size int32 13 | } 14 | 15 | // create shared memory. return shmi object. 16 | func create(name string, size int32) (*shmi, error) { 17 | key, err := syscall.UTF16PtrFromString(name) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | h, err := syscall.CreateFileMapping( 23 | syscall.InvalidHandle, nil, 24 | syscall.PAGE_READWRITE, 0, uint32(size), key) 25 | if err != nil { 26 | return nil, os.NewSyscallError("CreateFileMapping", err) 27 | } 28 | 29 | v, err := syscall.MapViewOfFile(h, syscall.FILE_MAP_WRITE, 0, 0, 0) 30 | if err != nil { 31 | syscall.CloseHandle(h) 32 | return nil, os.NewSyscallError("MapViewOfFile", err) 33 | } 34 | 35 | return &shmi{h, v, size}, nil 36 | } 37 | 38 | // open shared memory. return shmi object. 39 | func open(name string, size int32) (*shmi, error) { 40 | return create(name, size) 41 | } 42 | 43 | func (o *shmi) close() error { 44 | if o.v != uintptr(0) { 45 | syscall.UnmapViewOfFile(o.v) 46 | o.v = uintptr(0) 47 | } 48 | if o.h != syscall.InvalidHandle { 49 | syscall.CloseHandle(o.h) 50 | o.h = syscall.InvalidHandle 51 | } 52 | return nil 53 | } 54 | 55 | // read shared memory. return read size. 56 | func (o *shmi) readAt(p []byte, off int64) (n int, err error) { 57 | if off >= int64(o.size) { 58 | return 0, io.EOF 59 | } 60 | if max := int64(o.size) - off; int64(len(p)) > max { 61 | p = p[:max] 62 | } 63 | return copyPtr2Slice(o.v, p, off, o.size), nil 64 | } 65 | 66 | // write shared memory. return write size. 67 | func (o *shmi) writeAt(p []byte, off int64) (n int, err error) { 68 | if off >= int64(o.size) { 69 | return 0, io.EOF 70 | } 71 | if max := int64(o.size) - off; int64(len(p)) > max { 72 | p = p[:max] 73 | } 74 | return copySlice2Ptr(p, o.v, off, o.size), nil 75 | } 76 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | package shm 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | func copySlice2Ptr(b []byte, p uintptr, off int64, size int32) int { 9 | h := reflect.SliceHeader{} 10 | h.Cap = int(size) 11 | h.Len = int(size) 12 | h.Data = p 13 | 14 | bb := *(*[]byte)(unsafe.Pointer(&h)) 15 | return copy(bb[off:], b) 16 | } 17 | 18 | func copyPtr2Slice(p uintptr, b []byte, off int64, size int32) int { 19 | h := reflect.SliceHeader{} 20 | h.Cap = int(size) 21 | h.Len = int(size) 22 | h.Data = p 23 | 24 | bb := *(*[]byte)(unsafe.Pointer(&h)) 25 | return copy(b, bb[off:size]) 26 | } 27 | --------------------------------------------------------------------------------