mirror of
https://github.com/asticode/go-astikit.git
synced 2025-12-24 11:50:53 +08:00
Split ipc posix/systemv in different files
This commit is contained in:
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -31,7 +31,9 @@ jobs:
|
||||
run: go mod download
|
||||
|
||||
- name: Run tests
|
||||
run: go test -race -covermode atomic -coverprofile=covprofile ./...
|
||||
run: |
|
||||
echo "CGO_ENABLED=0" >> $env:GITHUB_ENV
|
||||
go test -race -covermode atomic -coverprofile=covprofile ./...
|
||||
|
||||
- if: github.event_name != 'pull_request'
|
||||
name: Send coverage
|
||||
|
||||
27
ipc.h
27
ipc.h
@@ -1,27 +0,0 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
Posix
|
||||
*/
|
||||
|
||||
int astikit_close(int fd, int *errno_ptr);
|
||||
int astikit_fstat(int fd, struct stat *s, int *errno_ptr);
|
||||
int astikit_ftruncate(int fd, off_t length, int *errno_ptr);
|
||||
void *astikit_mmap(size_t length, int fd, int *errno_ptr);
|
||||
int astikit_munmap(void *addr, size_t length, int *errno_ptr);
|
||||
int astikit_shm_open(char *name, int flags, mode_t mode, int *errno_ptr);
|
||||
int astikit_shm_unlink(char *name, int *errno_ptr);
|
||||
|
||||
/*
|
||||
System V
|
||||
*/
|
||||
|
||||
int astikit_ftok(char *path, int project_id, int *errno_ptr);
|
||||
int astikit_sem_get(key_t key, int flags, int *errno_ptr);
|
||||
int astikit_sem_close(int id, int *errno_ptr);
|
||||
int astikit_sem_lock(int id, int *errno_ptr);
|
||||
int astikit_sem_unlock(int id, int *errno_ptr);
|
||||
void *astikit_shm_at(int id, int *errno_ptr);
|
||||
int astikit_shm_get(key_t key, int size, int flags, int *errno_ptr);
|
||||
int astikit_shm_close(int id, const void *addr, int *errno_ptr);
|
||||
75
ipc_posix.c
Normal file
75
ipc_posix.c
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int astikit_close(int fd, int *errno_ptr)
|
||||
{
|
||||
int ret = close(fd);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_fstat(int fd, struct stat *s, int *errno_ptr)
|
||||
{
|
||||
int ret = fstat(fd, s);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_ftruncate(int fd, off_t length, int *errno_ptr)
|
||||
{
|
||||
int ret = ftruncate(fd, length);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *astikit_mmap(size_t length, int fd, int *errno_ptr)
|
||||
{
|
||||
void *addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (addr == MAP_FAILED)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
return NULL;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
int astikit_munmap(void *addr, size_t length, int *errno_ptr)
|
||||
{
|
||||
int ret = munmap(addr, length);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_shm_open(char *name, int flags, mode_t mode, int *errno_ptr)
|
||||
{
|
||||
int fd = shm_open(name, flags, mode);
|
||||
if (fd < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int astikit_shm_unlink(char *name, int *errno_ptr)
|
||||
{
|
||||
int ret = shm_unlink(name);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
296
ipc_posix.go
Normal file
296
ipc_posix.go
Normal file
@@ -0,0 +1,296 @@
|
||||
//go:build !windows
|
||||
|
||||
package astikit
|
||||
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
//#include "ipc_posix.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PosixSharedMemory struct {
|
||||
addr unsafe.Pointer
|
||||
cname string
|
||||
fd *C.int
|
||||
name string
|
||||
size int
|
||||
unlink bool
|
||||
}
|
||||
|
||||
func newPosixSharedMemory(name string, flags, mode int, cb func(shm *PosixSharedMemory) error) (shm *PosixSharedMemory, err error) {
|
||||
// Create shared memory
|
||||
shm = &PosixSharedMemory{name: name}
|
||||
|
||||
// To have a similar behavior with python, we need to handle the leading slash the same way:
|
||||
// - make sure the "public" name has no leading "/"
|
||||
// - make sure the "internal" name has a leading "/"
|
||||
shm.name = strings.TrimPrefix(shm.name, "/")
|
||||
shm.cname = "/" + shm.name
|
||||
|
||||
// Get c name
|
||||
cname := C.CString(shm.cname)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
|
||||
// Get file descriptor
|
||||
var errno C.int
|
||||
fd := C.astikit_shm_open(cname, C.int(flags), C.mode_t(mode), &errno)
|
||||
if fd < 0 {
|
||||
err = fmt.Errorf("astikit: shm_open failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
shm.fd = &fd
|
||||
|
||||
// Make sure to close shared memory in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
shm.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Callback
|
||||
if cb != nil {
|
||||
if err = cb(shm); err != nil {
|
||||
err = fmt.Errorf("astikit: callback failed: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get size
|
||||
var stat C.struct_stat
|
||||
if ret := C.astikit_fstat(*shm.fd, &stat, &errno); ret < 0 {
|
||||
err = fmt.Errorf("astikit: fstat failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
shm.size = int(stat.st_size)
|
||||
|
||||
// Map memory
|
||||
addr := C.astikit_mmap(C.size_t(shm.size), *shm.fd, &errno)
|
||||
if addr == nil {
|
||||
err = fmt.Errorf("astikit: mmap failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
|
||||
// Update addr
|
||||
shm.addr = addr
|
||||
return
|
||||
}
|
||||
|
||||
func CreatePosixSharedMemory(name string, size int) (*PosixSharedMemory, error) {
|
||||
return newPosixSharedMemory(name, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600, func(shm *PosixSharedMemory) (err error) {
|
||||
// Shared memory needs to be unlink on close
|
||||
shm.unlink = true
|
||||
|
||||
// Truncate
|
||||
var errno C.int
|
||||
if ret := C.astikit_ftruncate(*shm.fd, C.off_t(size), &errno); ret < 0 {
|
||||
err = fmt.Errorf("astikit: ftruncate failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func OpenPosixSharedMemory(name string) (*PosixSharedMemory, error) {
|
||||
return newPosixSharedMemory(name, os.O_RDWR, 0600, nil)
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Close() error {
|
||||
// Unlink
|
||||
if shm.unlink {
|
||||
// Get c name
|
||||
cname := C.CString(shm.cname)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
|
||||
// Unlink
|
||||
var errno C.int
|
||||
if ret := C.astikit_shm_unlink(cname, &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: unlink failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.unlink = false
|
||||
}
|
||||
|
||||
// Unmap memory
|
||||
if shm.addr != nil {
|
||||
var errno C.int
|
||||
if ret := C.astikit_munmap(shm.addr, C.size_t(shm.size), &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: munmap failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.addr = nil
|
||||
}
|
||||
|
||||
// Close file descriptor
|
||||
if shm.fd != nil {
|
||||
var errno C.int
|
||||
if ret := C.astikit_close(*shm.fd, &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: close failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.fd = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Write(src unsafe.Pointer, size int) error {
|
||||
// Unmapped
|
||||
if shm.addr == nil {
|
||||
return errors.New("astikit: shared memory is unmapped")
|
||||
}
|
||||
|
||||
// Copy
|
||||
C.memcpy(shm.addr, src, C.size_t(size))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) WriteBytes(b []byte) error {
|
||||
// Get c bytes
|
||||
cb := C.CBytes(b)
|
||||
defer C.free(cb)
|
||||
|
||||
// Write
|
||||
return shm.Write(cb, len(b))
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) ReadBytes(size int) ([]byte, error) {
|
||||
// Unmapped
|
||||
if shm.addr == nil {
|
||||
return nil, errors.New("astikit: shared memory is unmapped")
|
||||
}
|
||||
|
||||
// Get bytes
|
||||
return C.GoBytes(shm.addr, C.int(size)), nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Name() string {
|
||||
return shm.name
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Size() int {
|
||||
return shm.size
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Addr() unsafe.Pointer {
|
||||
return shm.addr
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryWriter struct {
|
||||
m sync.Mutex // Locks write operations
|
||||
prefix string
|
||||
shm *PosixSharedMemory
|
||||
}
|
||||
|
||||
func NewPosixVariableSizeSharedMemoryWriter(prefix string) *PosixVariableSizeSharedMemoryWriter {
|
||||
return &PosixVariableSizeSharedMemoryWriter{prefix: prefix}
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) closeSharedMemory() {
|
||||
if w.shm != nil {
|
||||
w.shm.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) Close() {
|
||||
w.closeSharedMemory()
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) Write(src unsafe.Pointer, size int) (ro PosixVariableSizeSharedMemoryReadOptions, err error) {
|
||||
// Lock
|
||||
w.m.Lock()
|
||||
defer w.m.Unlock()
|
||||
|
||||
// Shared memory has not yet been created or previous shared memory segment is too small
|
||||
if w.shm == nil || size > w.shm.Size() {
|
||||
// Close previous shared memory
|
||||
w.closeSharedMemory()
|
||||
|
||||
// Create shared memory
|
||||
var shm *PosixSharedMemory
|
||||
if shm, err = CreatePosixSharedMemory(w.prefix+"-"+strconv.Itoa(size), size); err != nil {
|
||||
err = fmt.Errorf("astikit: creating shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store shared memory
|
||||
w.shm = shm
|
||||
}
|
||||
|
||||
// Write
|
||||
if err = w.shm.Write(src, size); err != nil {
|
||||
err = fmt.Errorf("astikit: writing to shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create read options
|
||||
ro = PosixVariableSizeSharedMemoryReadOptions{
|
||||
Name: w.shm.Name(),
|
||||
Size: size,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) WriteBytes(b []byte) (PosixVariableSizeSharedMemoryReadOptions, error) {
|
||||
// Get c bytes
|
||||
cb := C.CBytes(b)
|
||||
defer C.free(cb)
|
||||
|
||||
// Write
|
||||
return w.Write(cb, len(b))
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryReader struct {
|
||||
m sync.Mutex // Locks read operations
|
||||
shm *PosixSharedMemory
|
||||
}
|
||||
|
||||
func NewPosixVariableSizeSharedMemoryReader() *PosixVariableSizeSharedMemoryReader {
|
||||
return &PosixVariableSizeSharedMemoryReader{}
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) closeSharedMemory() {
|
||||
if r.shm != nil {
|
||||
r.shm.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) Close() {
|
||||
r.closeSharedMemory()
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryReadOptions struct {
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) ReadBytes(o PosixVariableSizeSharedMemoryReadOptions) (b []byte, err error) {
|
||||
// Lock
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
// Shared memory has not yet been opened or shared memory's name has changed
|
||||
if r.shm == nil || r.shm.Name() != o.Name {
|
||||
// Close previous shared memory
|
||||
r.closeSharedMemory()
|
||||
|
||||
// Open shared memory
|
||||
var shm *PosixSharedMemory
|
||||
if shm, err = OpenPosixSharedMemory(o.Name); err != nil {
|
||||
err = fmt.Errorf("astikit: opening shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store attributes
|
||||
r.shm = shm
|
||||
}
|
||||
|
||||
// Copy
|
||||
b = make([]byte, o.Size)
|
||||
C.memcpy(unsafe.Pointer(&b[0]), r.shm.Addr(), C.size_t(o.Size))
|
||||
return
|
||||
}
|
||||
10
ipc_posix.h
Normal file
10
ipc_posix.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
int astikit_close(int fd, int *errno_ptr);
|
||||
int astikit_fstat(int fd, struct stat *s, int *errno_ptr);
|
||||
int astikit_ftruncate(int fd, off_t length, int *errno_ptr);
|
||||
void *astikit_mmap(size_t length, int fd, int *errno_ptr);
|
||||
int astikit_munmap(void *addr, size_t length, int *errno_ptr);
|
||||
int astikit_shm_open(char *name, int flags, mode_t mode, int *errno_ptr);
|
||||
int astikit_shm_unlink(char *name, int *errno_ptr);
|
||||
110
ipc_posix_test.go
Normal file
110
ipc_posix_test.go
Normal file
@@ -0,0 +1,110 @@
|
||||
//go:build !windows
|
||||
|
||||
package astikit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPosixSharedMemory(t *testing.T) {
|
||||
sm1, err := CreatePosixSharedMemory("/test", 8)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
defer sm1.Close()
|
||||
if sm1.Addr() == nil {
|
||||
t.Fatal("expected not nil, got nil")
|
||||
}
|
||||
if g := sm1.Size(); g <= 0 {
|
||||
t.Fatalf("expected > 0, got %d", g)
|
||||
}
|
||||
if e, g := "test", sm1.Name(); e != g {
|
||||
t.Fatalf("expected %v, got %v", e, g)
|
||||
}
|
||||
if _, err = CreatePosixSharedMemory("/test", 8); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
b1 := []byte("test")
|
||||
if err := sm1.WriteBytes(b1); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
|
||||
sm2, err := OpenPosixSharedMemory("test")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
defer sm2.Close()
|
||||
b2, err := sm2.ReadBytes(len(b1))
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if e, g := b1, b2; !bytes.Equal(b1, b2) {
|
||||
t.Fatalf("expected %s, got %s", e, g)
|
||||
}
|
||||
|
||||
if err = sm1.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if err = sm1.WriteBytes(b1); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if err = sm1.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if err = sm2.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if _, err = sm2.ReadBytes(len(b1)); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if err = sm2.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPosixVariableSizeSharedMemory(t *testing.T) {
|
||||
w := NewPosixVariableSizeSharedMemoryWriter("test-1")
|
||||
defer w.Close()
|
||||
r := NewPosixVariableSizeSharedMemoryReader()
|
||||
defer r.Close()
|
||||
|
||||
b1 := []byte("test")
|
||||
ro1, err := w.WriteBytes(b1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if e, g := w.shm.Name(), ro1.Name; e != g {
|
||||
t.Fatalf("expected %s, got %s", e, g)
|
||||
}
|
||||
if e, g := len(b1), ro1.Size; e != g {
|
||||
t.Fatalf("expected %d, got %d", e, g)
|
||||
}
|
||||
b2, err := r.ReadBytes(ro1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if !bytes.Equal(b1, b2) {
|
||||
t.Fatalf("expected %s, got %s", b1, b2)
|
||||
}
|
||||
|
||||
b3 := make([]byte, w.shm.Size()+1)
|
||||
b3[0] = 'a'
|
||||
b3[len(b3)-1] = 'b'
|
||||
ro2, err := w.WriteBytes(b3)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if ro1.Name == ro2.Name {
|
||||
t.Fatal("expected different, got equalt")
|
||||
}
|
||||
b4, err := r.ReadBytes(ro2)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if !bytes.Equal(b3, b4) {
|
||||
t.Fatalf("expected %s, got %s", b3, b4)
|
||||
}
|
||||
}
|
||||
@@ -1,91 +1,7 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/sem.h>
|
||||
#include <sys/shm.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
Posix
|
||||
*/
|
||||
|
||||
int astikit_close(int fd, int *errno_ptr)
|
||||
{
|
||||
int ret = close(fd);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_fstat(int fd, struct stat *s, int *errno_ptr)
|
||||
{
|
||||
int ret = fstat(fd, s);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_ftruncate(int fd, off_t length, int *errno_ptr)
|
||||
{
|
||||
int ret = ftruncate(fd, length);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *astikit_mmap(size_t length, int fd, int *errno_ptr)
|
||||
{
|
||||
void *addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (addr == MAP_FAILED)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
return NULL;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
int astikit_munmap(void *addr, size_t length, int *errno_ptr)
|
||||
{
|
||||
int ret = munmap(addr, length);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int astikit_shm_open(char *name, int flags, mode_t mode, int *errno_ptr)
|
||||
{
|
||||
int fd = shm_open(name, flags, mode);
|
||||
if (fd < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int astikit_shm_unlink(char *name, int *errno_ptr)
|
||||
{
|
||||
int ret = shm_unlink(name);
|
||||
if (ret < 0)
|
||||
{
|
||||
*errno_ptr = errno;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
System V
|
||||
*/
|
||||
|
||||
int astikit_ftok(char *path, int project_id, int *errno_ptr)
|
||||
{
|
||||
@@ -3,301 +3,19 @@
|
||||
package astikit
|
||||
|
||||
//#include <sys/shm.h>
|
||||
//#include <sys/stat.h>
|
||||
//#include <stdlib.h>
|
||||
//#include <string.h>
|
||||
//#include "ipc.h"
|
||||
//#include "ipc_systemv.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type PosixSharedMemory struct {
|
||||
addr unsafe.Pointer
|
||||
cname string
|
||||
fd *C.int
|
||||
name string
|
||||
size int
|
||||
unlink bool
|
||||
}
|
||||
|
||||
func newPosixSharedMemory(name string, flags, mode int, cb func(shm *PosixSharedMemory) error) (shm *PosixSharedMemory, err error) {
|
||||
// Create shared memory
|
||||
shm = &PosixSharedMemory{name: name}
|
||||
|
||||
// To have a similar behavior with python, we need to handle the leading slash the same way:
|
||||
// - make sure the "public" name has no leading "/"
|
||||
// - make sure the "internal" name has a leading "/"
|
||||
shm.name = strings.TrimPrefix(shm.name, "/")
|
||||
shm.cname = "/" + shm.name
|
||||
|
||||
// Get c name
|
||||
cname := C.CString(shm.cname)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
|
||||
// Get file descriptor
|
||||
var errno C.int
|
||||
fd := C.astikit_shm_open(cname, C.int(flags), C.mode_t(mode), &errno)
|
||||
if fd < 0 {
|
||||
err = fmt.Errorf("astikit: shm_open failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
shm.fd = &fd
|
||||
|
||||
// Make sure to close shared memory in case of error
|
||||
defer func() {
|
||||
if err != nil {
|
||||
shm.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Callback
|
||||
if cb != nil {
|
||||
if err = cb(shm); err != nil {
|
||||
err = fmt.Errorf("astikit: callback failed: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get size
|
||||
var stat C.struct_stat
|
||||
if ret := C.astikit_fstat(*shm.fd, &stat, &errno); ret < 0 {
|
||||
err = fmt.Errorf("astikit: fstat failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
shm.size = int(stat.st_size)
|
||||
|
||||
// Map memory
|
||||
addr := C.astikit_mmap(C.size_t(shm.size), *shm.fd, &errno)
|
||||
if addr == nil {
|
||||
err = fmt.Errorf("astikit: mmap failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
|
||||
// Update addr
|
||||
shm.addr = addr
|
||||
return
|
||||
}
|
||||
|
||||
func CreatePosixSharedMemory(name string, size int) (*PosixSharedMemory, error) {
|
||||
return newPosixSharedMemory(name, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0600, func(shm *PosixSharedMemory) (err error) {
|
||||
// Shared memory needs to be unlink on close
|
||||
shm.unlink = true
|
||||
|
||||
// Truncate
|
||||
var errno C.int
|
||||
if ret := C.astikit_ftruncate(*shm.fd, C.off_t(size), &errno); ret < 0 {
|
||||
err = fmt.Errorf("astikit: ftruncate failed: %w", syscall.Errno(errno))
|
||||
return
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func OpenPosixSharedMemory(name string) (*PosixSharedMemory, error) {
|
||||
return newPosixSharedMemory(name, os.O_RDWR, 0600, nil)
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Close() error {
|
||||
// Unlink
|
||||
if shm.unlink {
|
||||
// Get c name
|
||||
cname := C.CString(shm.cname)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
|
||||
// Unlink
|
||||
var errno C.int
|
||||
if ret := C.astikit_shm_unlink(cname, &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: unlink failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.unlink = false
|
||||
}
|
||||
|
||||
// Unmap memory
|
||||
if shm.addr != nil {
|
||||
var errno C.int
|
||||
if ret := C.astikit_munmap(shm.addr, C.size_t(shm.size), &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: munmap failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.addr = nil
|
||||
}
|
||||
|
||||
// Close file descriptor
|
||||
if shm.fd != nil {
|
||||
var errno C.int
|
||||
if ret := C.astikit_close(*shm.fd, &errno); ret < 0 {
|
||||
return fmt.Errorf("astikit: close failed: %w", syscall.Errno(errno))
|
||||
}
|
||||
shm.fd = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Write(src unsafe.Pointer, size int) error {
|
||||
// Unmapped
|
||||
if shm.addr == nil {
|
||||
return errors.New("astikit: shared memory is unmapped")
|
||||
}
|
||||
|
||||
// Copy
|
||||
C.memcpy(shm.addr, src, C.size_t(size))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) WriteBytes(b []byte) error {
|
||||
// Get c bytes
|
||||
cb := C.CBytes(b)
|
||||
defer C.free(cb)
|
||||
|
||||
// Write
|
||||
return shm.Write(cb, len(b))
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) ReadBytes(size int) ([]byte, error) {
|
||||
// Unmapped
|
||||
if shm.addr == nil {
|
||||
return nil, errors.New("astikit: shared memory is unmapped")
|
||||
}
|
||||
|
||||
// Get bytes
|
||||
return C.GoBytes(shm.addr, C.int(size)), nil
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Name() string {
|
||||
return shm.name
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Size() int {
|
||||
return shm.size
|
||||
}
|
||||
|
||||
func (shm *PosixSharedMemory) Addr() unsafe.Pointer {
|
||||
return shm.addr
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryWriter struct {
|
||||
m sync.Mutex // Locks write operations
|
||||
prefix string
|
||||
shm *PosixSharedMemory
|
||||
}
|
||||
|
||||
func NewPosixVariableSizeSharedMemoryWriter(prefix string) *PosixVariableSizeSharedMemoryWriter {
|
||||
return &PosixVariableSizeSharedMemoryWriter{prefix: prefix}
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) closeSharedMemory() {
|
||||
if w.shm != nil {
|
||||
w.shm.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) Close() {
|
||||
w.closeSharedMemory()
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) Write(src unsafe.Pointer, size int) (ro PosixVariableSizeSharedMemoryReadOptions, err error) {
|
||||
// Lock
|
||||
w.m.Lock()
|
||||
defer w.m.Unlock()
|
||||
|
||||
// Shared memory has not yet been created or previous shared memory segment is too small
|
||||
if w.shm == nil || size > w.shm.Size() {
|
||||
// Close previous shared memory
|
||||
w.closeSharedMemory()
|
||||
|
||||
// Create shared memory
|
||||
var shm *PosixSharedMemory
|
||||
if shm, err = CreatePosixSharedMemory(w.prefix+"-"+strconv.Itoa(size), size); err != nil {
|
||||
err = fmt.Errorf("astikit: creating shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store shared memory
|
||||
w.shm = shm
|
||||
}
|
||||
|
||||
// Write
|
||||
if err = w.shm.Write(src, size); err != nil {
|
||||
err = fmt.Errorf("astikit: writing to shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create read options
|
||||
ro = PosixVariableSizeSharedMemoryReadOptions{
|
||||
Name: w.shm.Name(),
|
||||
Size: size,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *PosixVariableSizeSharedMemoryWriter) WriteBytes(b []byte) (PosixVariableSizeSharedMemoryReadOptions, error) {
|
||||
// Get c bytes
|
||||
cb := C.CBytes(b)
|
||||
defer C.free(cb)
|
||||
|
||||
// Write
|
||||
return w.Write(cb, len(b))
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryReader struct {
|
||||
m sync.Mutex // Locks read operations
|
||||
shm *PosixSharedMemory
|
||||
}
|
||||
|
||||
func NewPosixVariableSizeSharedMemoryReader() *PosixVariableSizeSharedMemoryReader {
|
||||
return &PosixVariableSizeSharedMemoryReader{}
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) closeSharedMemory() {
|
||||
if r.shm != nil {
|
||||
r.shm.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) Close() {
|
||||
r.closeSharedMemory()
|
||||
}
|
||||
|
||||
type PosixVariableSizeSharedMemoryReadOptions struct {
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
func (r *PosixVariableSizeSharedMemoryReader) ReadBytes(o PosixVariableSizeSharedMemoryReadOptions) (b []byte, err error) {
|
||||
// Lock
|
||||
r.m.Lock()
|
||||
defer r.m.Unlock()
|
||||
|
||||
// Shared memory has not yet been opened or shared memory's name has changed
|
||||
if r.shm == nil || r.shm.Name() != o.Name {
|
||||
// Close previous shared memory
|
||||
r.closeSharedMemory()
|
||||
|
||||
// Open shared memory
|
||||
var shm *PosixSharedMemory
|
||||
if shm, err = OpenPosixSharedMemory(o.Name); err != nil {
|
||||
err = fmt.Errorf("astikit: opening shared memory failed: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Store attributes
|
||||
r.shm = shm
|
||||
}
|
||||
|
||||
// Copy
|
||||
b = make([]byte, o.Size)
|
||||
C.memcpy(unsafe.Pointer(&b[0]), r.shm.Addr(), C.size_t(o.Size))
|
||||
return
|
||||
}
|
||||
|
||||
func NewSystemVKey(projectID int, path string) (int, error) {
|
||||
// Get c path
|
||||
cpath := C.CString(path)
|
||||
10
ipc_systemv.h
Normal file
10
ipc_systemv.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
int astikit_ftok(char *path, int project_id, int *errno_ptr);
|
||||
int astikit_sem_get(key_t key, int flags, int *errno_ptr);
|
||||
int astikit_sem_close(int id, int *errno_ptr);
|
||||
int astikit_sem_lock(int id, int *errno_ptr);
|
||||
int astikit_sem_unlock(int id, int *errno_ptr);
|
||||
void *astikit_shm_at(int id, int *errno_ptr);
|
||||
int astikit_shm_get(key_t key, int size, int flags, int *errno_ptr);
|
||||
int astikit_shm_close(int id, const void *addr, int *errno_ptr);
|
||||
@@ -7,108 +7,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPosixSharedMemory(t *testing.T) {
|
||||
sm1, err := CreatePosixSharedMemory("/test", 8)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
defer sm1.Close()
|
||||
if sm1.Addr() == nil {
|
||||
t.Fatal("expected not nil, got nil")
|
||||
}
|
||||
if g := sm1.Size(); g <= 0 {
|
||||
t.Fatalf("expected > 0, got %d", g)
|
||||
}
|
||||
if e, g := "test", sm1.Name(); e != g {
|
||||
t.Fatalf("expected %v, got %v", e, g)
|
||||
}
|
||||
if _, err = CreatePosixSharedMemory("/test", 8); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
|
||||
b1 := []byte("test")
|
||||
if err := sm1.WriteBytes(b1); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
|
||||
sm2, err := OpenPosixSharedMemory("test")
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
defer sm2.Close()
|
||||
b2, err := sm2.ReadBytes(len(b1))
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if e, g := b1, b2; !bytes.Equal(b1, b2) {
|
||||
t.Fatalf("expected %s, got %s", e, g)
|
||||
}
|
||||
|
||||
if err = sm1.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if err = sm1.WriteBytes(b1); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if err = sm1.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
|
||||
if err = sm2.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if _, err = sm2.ReadBytes(len(b1)); err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if err = sm2.Close(); err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPosixVariableSizeSharedMemory(t *testing.T) {
|
||||
w := NewPosixVariableSizeSharedMemoryWriter("test-1")
|
||||
defer w.Close()
|
||||
r := NewPosixVariableSizeSharedMemoryReader()
|
||||
defer r.Close()
|
||||
|
||||
b1 := []byte("test")
|
||||
ro1, err := w.WriteBytes(b1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if e, g := w.shm.Name(), ro1.Name; e != g {
|
||||
t.Fatalf("expected %s, got %s", e, g)
|
||||
}
|
||||
if e, g := len(b1), ro1.Size; e != g {
|
||||
t.Fatalf("expected %d, got %d", e, g)
|
||||
}
|
||||
b2, err := r.ReadBytes(ro1)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if !bytes.Equal(b1, b2) {
|
||||
t.Fatalf("expected %s, got %s", b1, b2)
|
||||
}
|
||||
|
||||
b3 := make([]byte, w.shm.Size()+1)
|
||||
b3[0] = 'a'
|
||||
b3[len(b3)-1] = 'b'
|
||||
ro2, err := w.WriteBytes(b3)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if ro1.Name == ro2.Name {
|
||||
t.Fatal("expected different, got equalt")
|
||||
}
|
||||
b4, err := r.ReadBytes(ro2)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %s", err)
|
||||
}
|
||||
if !bytes.Equal(b3, b4) {
|
||||
t.Fatalf("expected %s, got %s", b3, b4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSystemVKey(t *testing.T) {
|
||||
_, err := NewSystemVKey(1, "testdata/ipc/invalid")
|
||||
if err == nil {
|
||||
Reference in New Issue
Block a user