github.com/teepark/go-sysvipc@v0.0.0-20200817232735-d7ca6053ea29/shm.go (about)

     1  package sysvipc
     2  
     3  /*
     4  #include <string.h>
     5  #include <sys/ipc.h>
     6  #include <sys/shm.h>
     7  int shmget(key_t key, size_t size, int shmflg);
     8  void *shmat(int shmid, const void *shmaddr, int shmflg);
     9  int shmdt(const void *shmaddr);
    10  int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    11  */
    12  import "C"
    13  import (
    14  	"errors"
    15  	"io"
    16  	"sync/atomic"
    17  	"time"
    18  	"unsafe"
    19  )
    20  
    21  var (
    22  	ErrReadOnlyShm = errors.New("Read-Only shared mem attachment")
    23  )
    24  
    25  // SharedMem is an allocated block of memory sharable with multiple processes.
    26  type SharedMem struct {
    27  	id     int64
    28  	length uint
    29  }
    30  
    31  // GetSharedMem creates or retrieves the shared memory segment for an IPC key
    32  func GetSharedMem(key int64, size uint64, flags *SHMFlags) (*SharedMem, error) {
    33  	rc, err := C.shmget(C.key_t(key), C.size_t(size), C.int(flags.flags()))
    34  	if rc == -1 {
    35  		return nil, err
    36  	}
    37  	return &SharedMem{int64(rc), uint(size)}, nil
    38  }
    39  
    40  // Attach brings a shared memory segment into the current process's memory space.
    41  func (shm *SharedMem) Attach(flags *SHMAttachFlags) (*SharedMemMount, error) {
    42  	ptr, err := C.shmat(C.int(shm.id), nil, C.int(flags.flags()))
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	return &SharedMemMount{ptr, 0, shm.length, flags.ro()}, nil
    48  }
    49  
    50  // Stat produces meta information about the shared memory segment.
    51  func (shm *SharedMem) Stat() (*SHMInfo, error) {
    52  	shmds := C.struct_shmid_ds{}
    53  
    54  	rc, err := C.shmctl(C.int(shm.id), C.IPC_STAT, &shmds)
    55  	if rc == -1 {
    56  		return nil, err
    57  	}
    58  
    59  	shminf := SHMInfo{
    60  		Perms: IpcPerms{
    61  			OwnerUID:   int(shmds.shm_perm.uid),
    62  			OwnerGID:   int(shmds.shm_perm.gid),
    63  			CreatorUID: int(shmds.shm_perm.cuid),
    64  			CreatorGID: int(shmds.shm_perm.cgid),
    65  			Mode:       uint16(shmds.shm_perm.mode),
    66  		},
    67  		SegmentSize:     uint(shmds.shm_segsz),
    68  		LastAttach:      time.Unix(int64(shmds.shm_atime), 0),
    69  		LastDetach:      time.Unix(int64(shmds.shm_dtime), 0),
    70  		LastChange:      time.Unix(int64(shmds.shm_ctime), 0),
    71  		CreatorPID:      int(shmds.shm_cpid),
    72  		LastUserPID:     int(shmds.shm_lpid),
    73  		CurrentAttaches: uint(shmds.shm_nattch),
    74  	}
    75  
    76  	return &shminf, nil
    77  }
    78  
    79  // Set updates parameters of the shared memory segment.
    80  func (shm *SharedMem) Set(info *SHMInfo) error {
    81  	shmds := &C.struct_shmid_ds{
    82  		shm_perm: C.struct_ipc_perm{
    83  			uid:  C.__uid_t(info.Perms.OwnerUID),
    84  			gid:  C.__gid_t(info.Perms.OwnerGID),
    85  			mode: C.ushort(info.Perms.Mode & 0x1FF),
    86  		},
    87  	}
    88  
    89  	rc, err := C.shmctl(C.int(shm.id), C.IPC_SET, shmds)
    90  	if rc == -1 {
    91  		return err
    92  	}
    93  	return nil
    94  }
    95  
    96  // Remove marks the shared memory segment for removal.
    97  // It will be removed when all attachments have been closed.
    98  func (shm *SharedMem) Remove() error {
    99  	rc, err := C.shmctl(C.int(shm.id), C.IPC_RMID, nil)
   100  	if rc == -1 {
   101  		return err
   102  	}
   103  	return nil
   104  }
   105  
   106  // SharedMemMount is the pointer to an attached block of shared memory space.
   107  type SharedMemMount struct {
   108  	ptr            unsafe.Pointer
   109  	offset, length uint
   110  
   111  	// We have to store readonly here to prevent Write and WriteByte.
   112  	// I'd be happy to let it panic, but C segfault panics can't recover.
   113  	readonly bool
   114  }
   115  
   116  // Read pulls bytes out of the shared memory segment.
   117  func (shma *SharedMemMount) Read(p []byte) (int, error) {
   118  	var err error
   119  	l := uint(len(p))
   120  	if l > (shma.length - shma.offset) {
   121  		l = shma.length - shma.offset
   122  		err = io.EOF
   123  	}
   124  	if l == 0 {
   125  		return 0, err
   126  	}
   127  
   128  	src := unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset))
   129  	dest := unsafe.Pointer(&p[0])
   130  
   131  	memmove(dest, src, (uintptr)(l))
   132  	shma.offset += l
   133  	return int(l), err
   134  }
   135  
   136  // Write places bytes into the shared memory segment.
   137  func (shma *SharedMemMount) Write(p []byte) (int, error) {
   138  	if shma.readonly {
   139  		// see comment on readonly field above
   140  		return 0, ErrReadOnlyShm
   141  	}
   142  
   143  	var err error
   144  	l := uint(len(p))
   145  	if l > (shma.length - shma.offset) {
   146  		l = shma.length - shma.offset
   147  		err = io.ErrShortWrite
   148  	}
   149  	if l == 0 {
   150  		return 0, err
   151  	}
   152  
   153  	dest := unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset))
   154  	src := unsafe.Pointer(&p[0])
   155  
   156  	memmove(dest, src, (uintptr)(l))
   157  	shma.offset += l
   158  	return int(l), err
   159  }
   160  
   161  // AtomicWriteUint32 places an uint32 value into the shared memory
   162  // segment atomically (see "sync/atomic").
   163  func (shma *SharedMemMount) AtomicWriteUint32(v uint32) error {
   164  	if shma.readonly {
   165  		// see comment on readonly field above
   166  		return ErrReadOnlyShm
   167  	}
   168  
   169  	if (shma.length - shma.offset) < 4 {
   170  		return io.ErrShortWrite
   171  	}
   172  
   173  	atomic.StoreUint32((*uint32)((unsafe.Pointer)(uintptr(shma.ptr)+uintptr(shma.offset))), v)
   174  	shma.offset += 4
   175  	return nil
   176  }
   177  
   178  // AtomicReadUint32 returns an uint32 value from the current position
   179  // of the shared memory segment using atomic read (see "sync/atomic").
   180  func (shma *SharedMemMount) AtomicReadUint32() (uint32, error) {
   181  	if (shma.length - shma.offset) < 4 {
   182  		return 0, io.EOF
   183  	}
   184  
   185  	v := atomic.LoadUint32((*uint32)((unsafe.Pointer)(uintptr(shma.ptr) + uintptr(shma.offset))))
   186  	shma.offset += 4
   187  	return v, nil
   188  }
   189  
   190  // ReadByte returns a single byte from the current position in shared memory.
   191  func (shma *SharedMemMount) ReadByte() (byte, error) {
   192  	if shma.offset == shma.length {
   193  		return 0, io.EOF
   194  	}
   195  	b := *(*byte)(unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset)))
   196  	shma.offset++
   197  	return b, nil
   198  }
   199  
   200  // UnreadByte sets the position back to before a ReadByte.
   201  func (shma *SharedMemMount) UnreadByte() error {
   202  	if shma.offset == 0 {
   203  		return errors.New("sysvipc: UnreadByte before any ReadByte")
   204  	}
   205  	shma.offset--
   206  	return nil
   207  }
   208  
   209  // WriteByte places a single byte at the current position in shared memory.
   210  func (shma *SharedMemMount) WriteByte(c byte) error {
   211  	if shma.readonly {
   212  		// see comment on readonly field above
   213  		return ErrReadOnlyShm
   214  	}
   215  	if shma.offset == shma.length {
   216  		return io.ErrShortWrite
   217  	}
   218  	b := (*byte)(unsafe.Pointer(uintptr(shma.ptr) + uintptr(shma.offset)))
   219  	*b = c
   220  	shma.offset++
   221  	return nil
   222  }
   223  
   224  // Seek moves the current position in shared memory, according to "whence":
   225  // - 0 makes the offset relative to the beginning
   226  // - 1 makes it relative to the current position
   227  // - 2 makes it relative to the end of the segment
   228  func (shma *SharedMemMount) Seek(offset int64, whence int) (int64, error) {
   229  	var endpos int64
   230  	switch whence {
   231  	case 0:
   232  		endpos = offset
   233  	case 1:
   234  		endpos = int64(shma.offset) + offset
   235  	case 2:
   236  		endpos = int64(shma.length) + offset
   237  	default:
   238  		return 0, errors.New("sysvipc: bad 'whence' value")
   239  	}
   240  
   241  	if endpos < 0 {
   242  		return int64(shma.offset), errors.New("sysvipc: negative offset")
   243  	}
   244  
   245  	if uint(endpos) > shma.length {
   246  		shma.offset = shma.length
   247  	} else {
   248  		shma.offset = uint(endpos)
   249  	}
   250  
   251  	return int64(shma.offset), nil
   252  }
   253  
   254  // Close detaches the shared memory segment pointer.
   255  func (shma *SharedMemMount) Close() error {
   256  	rc, err := C.shmdt(shma.ptr)
   257  	if rc == -1 {
   258  		return err
   259  	}
   260  	return nil
   261  }
   262  
   263  // SHMInfo holds meta information about a shared memory segment.
   264  type SHMInfo struct {
   265  	Perms       IpcPerms
   266  	SegmentSize uint
   267  
   268  	LastAttach time.Time
   269  	LastDetach time.Time
   270  	LastChange time.Time
   271  
   272  	CreatorPID  int
   273  	LastUserPID int
   274  
   275  	CurrentAttaches uint
   276  }
   277  
   278  // SHMFlags holds the options for GetSharedMem
   279  type SHMFlags struct {
   280  	// Create controls whether to create the shared memory segment if it
   281  	// doesn't already exist.
   282  	Create bool
   283  
   284  	// Exclusive causes GetSharedMem to fail if the shared memory already
   285  	// exists (only useful with Create).
   286  	Exclusive bool
   287  
   288  	// Perms is the file-style (rwxrwxrwx) permissions with which to create the
   289  	// shared memory segment (also only useful with Create).
   290  	Perms int
   291  }
   292  
   293  func (sf *SHMFlags) flags() int64 {
   294  	if sf == nil {
   295  		return 0
   296  	}
   297  
   298  	var f int64 = int64(sf.Perms) & 0777
   299  	if sf.Create {
   300  		f |= int64(C.IPC_CREAT)
   301  	}
   302  	if sf.Exclusive {
   303  		f |= int64(C.IPC_EXCL)
   304  	}
   305  
   306  	return f
   307  }
   308  
   309  // SHMAttachFlags holds the options for SharedMem.Attach
   310  type SHMAttachFlags struct {
   311  	// ReadOnly causes the new SharedMemMount to be readable but not writable
   312  	ReadOnly bool
   313  }
   314  
   315  func (sf *SHMAttachFlags) flags() int64 {
   316  	if sf == nil {
   317  		return 0
   318  	}
   319  
   320  	var f int64
   321  	if sf.ReadOnly {
   322  		f |= int64(C.SHM_RDONLY)
   323  	}
   324  
   325  	return f
   326  }
   327  
   328  func (sf *SHMAttachFlags) ro() bool {
   329  	if sf == nil {
   330  		return false
   331  	}
   332  	return sf.ReadOnly
   333  }