| | |
| | | package shm |
| | | |
| | | import ( |
| | | "golang.org/x/sys/unix" |
| | | "os" |
| | | "sync/atomic" |
| | | "unsafe" |
| | | |
| | | "github.com/tmthrgd/go-sem" |
| | | "github.com/tmthrgd/go-shm" |
| | | "golang.org/x/sys/unix" |
| | | ) |
| | | |
| | | func CreateSimplex(name string, perm os.FileMode, blockCount, blockSize int) (*ReadWriteCloser, error) { |
| | |
| | | return nil, ErrNotMultipleOf64 |
| | | } |
| | | |
| | | file, err := shm.Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm) |
| | | file, err := Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | |
| | | */ |
| | | *(*uint32)(&shared.BlockCount), *(*uint64)(&shared.BlockSize) = uint32(blockCount), uint64(blockSize) |
| | | |
| | | if err = ((*sem.Semaphore)(&shared.SemSignal)).Init(0); err != nil { |
| | | if err = ((*Semaphore)(&shared.SemSignal)).Init(0); err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | if err = ((*sem.Semaphore)(&shared.SemAvail)).Init(0); err != nil { |
| | | if err = ((*Semaphore)(&shared.SemAvail)).Init(0); err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | |
| | | return nil, ErrNotMultipleOf64 |
| | | } |
| | | |
| | | file, err := shm.Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm) |
| | | file, err := Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | |
| | | */ |
| | | *(*uint32)(&shared.BlockCount), *(*uint64)(&shared.BlockSize) = uint32(blockCount), uint64(blockSize) |
| | | |
| | | if err = ((*sem.Semaphore)(&shared.SemSignal)).Init(0); err != nil { |
| | | if err = ((*Semaphore)(&shared.SemSignal)).Init(0); err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | if err = ((*sem.Semaphore)(&shared.SemAvail)).Init(0); err != nil { |
| | | if err = ((*Semaphore)(&shared.SemAvail)).Init(0); err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | |
| | | package shm |
| | | |
| | | import ( |
| | | "golang.org/x/sys/unix" |
| | | "sync/atomic" |
| | | "unsafe" |
| | | |
| | | "github.com/tmthrgd/go-shm" |
| | | "golang.org/x/sys/unix" |
| | | ) |
| | | |
| | | func OpenSimplex(name string) (*ReadWriteCloser, error) { |
| | | file, err := shm.Open(name, unix.O_RDWR, 0) |
| | | file, err := Open(name, unix.O_RDWR, 0) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | |
| | | } |
| | | |
| | | func OpenDuplex(name string) (*ReadWriteCloser, error) { |
| | | file, err := shm.Open(name, unix.O_RDWR, 0) |
| | | file, err := Open(name, unix.O_RDWR, 0) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | |
| | | package shm |
| | | |
| | | import ( |
| | | "golang.org/x/sys/unix" |
| | | "io" |
| | | "sync/atomic" |
| | | "unsafe" |
| | | |
| | | "github.com/tmthrgd/go-sem" |
| | | "golang.org/x/sys/unix" |
| | | ) |
| | | |
| | | const ( |
| | |
| | | // Name returns the name of the shared memory. |
| | | func (rw *ReadWriteCloser) Name() string { |
| | | return rw.name |
| | | } |
| | | |
| | | // Unlink removes the shared memory. |
| | | // |
| | | // It is the equivalent to calling Unlink(string) with |
| | | // the same name as Create* or Open*. |
| | | // |
| | | // Taken from shm_unlink(3): |
| | | // The operation of shm_unlink() is analogous to unlink(2): it removes a |
| | | // shared memory object name, and, once all processes have unmapped the |
| | | // object, de-allocates and destroys the contents of the associated memory |
| | | // region. After a successful shm_unlink(), attempts to shm_open() an |
| | | // object with the same name will fail (unless O_CREAT was specified, in |
| | | // which case a new, distinct object is created). |
| | | func (rw *ReadWriteCloser) Unlink() error { |
| | | return Unlink(rw.name) |
| | | } |
| | | |
| | | // Read |
| | |
| | | block = (*sharedBlock)(unsafe.Pointer(blocks + uintptr(uint64(blockIndex)*rw.fullBlockSize))) |
| | | |
| | | if blockIndex == atomic.LoadUint32((*uint32)(&rw.readShared.WriteEnd)) { |
| | | if err := ((*sem.Semaphore)(&rw.readShared.SemSignal)).Wait(); err != nil { |
| | | if err := ((*Semaphore)(&rw.readShared.SemSignal)).Wait(); err != nil { |
| | | return Buffer{}, err |
| | | } |
| | | |
| | |
| | | atomic.CompareAndSwapUint32((*uint32)(&rw.readShared.ReadEnd), blockIndex, uint32(block.Next)) |
| | | |
| | | if uint32(block.Prev) == atomic.LoadUint32((*uint32)(&rw.readShared.WriteStart)) { |
| | | if err := ((*sem.Semaphore)(&rw.readShared.SemAvail)).Post(); err != nil { |
| | | if err := ((*Semaphore)(&rw.readShared.SemAvail)).Post(); err != nil { |
| | | return err |
| | | } |
| | | } |
| | |
| | | block = (*sharedBlock)(unsafe.Pointer(blocks + uintptr(uint64(blockIndex)*rw.fullBlockSize))) |
| | | |
| | | if uint32(block.Next) == atomic.LoadUint32((*uint32)(&rw.writeShared.ReadEnd)) { |
| | | if err := ((*sem.Semaphore)(&rw.writeShared.SemAvail)).Wait(); err != nil { |
| | | if err := ((*Semaphore)(&rw.writeShared.SemAvail)).Wait(); err != nil { |
| | | return Buffer{}, err |
| | | } |
| | | |
| | |
| | | atomic.CompareAndSwapUint32((*uint32)(&rw.writeShared.WriteEnd), blockIndex, uint32(block.Next)) |
| | | |
| | | if blockIndex == atomic.LoadUint32((*uint32)(&rw.writeShared.ReadStart)) { |
| | | if err := ((*sem.Semaphore)(&rw.writeShared.SemSignal)).Post(); err != nil { |
| | | if err := ((*Semaphore)(&rw.writeShared.SemSignal)).Post(); err != nil { |
| | | return len(buf.Data), err |
| | | } |
| | | } |
New file |
| | |
| | | // Created by cgo -godefs - DO NOT EDIT |
| | | // cgo -godefs sem_linux.go |
| | | |
| | | package shm |
| | | |
| | | import ( |
| | | "sync/atomic" |
| | | "syscall" |
| | | "unsafe" |
| | | |
| | | "golang.org/x/sys/unix" |
| | | ) |
| | | |
| | | type Semaphore [32]byte |
| | | |
| | | type newSem struct { |
| | | Value uint32 |
| | | Private int32 |
| | | NWaiters uint64 |
| | | } |
| | | |
| | | func New(value uint) (*Semaphore, error) { |
| | | sem := new(Semaphore) |
| | | |
| | | if err := sem.Init(value); err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | return sem, nil |
| | | } |
| | | |
| | | func atomicDecrementIfPositive(mem *uint32) uint32 { |
| | | for { |
| | | if old := atomic.LoadUint32(mem); old == 0 || atomic.CompareAndSwapUint32(mem, old, old-1) { |
| | | return old |
| | | } |
| | | } |
| | | } |
| | | |
| | | func (sem *Semaphore) Wait() error { |
| | | isem := (*newSem)(unsafe.Pointer(sem)) |
| | | |
| | | if atomicDecrementIfPositive(&isem.Value) > 0 { |
| | | return nil |
| | | } |
| | | |
| | | atomic.AddUint64(&isem.NWaiters, 1) |
| | | |
| | | for { |
| | | t := syscall.Timeval{3 /*sec*/, 0 /*usec*/} |
| | | if _, _, err := unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&isem.Value)), uintptr(0x0), 0, uintptr(unsafe.Pointer(&t)), 0, 0); err != 0 && err != unix.EWOULDBLOCK { |
| | | atomic.AddUint64(&isem.NWaiters, ^uint64(0)) |
| | | return err |
| | | } |
| | | |
| | | if atomicDecrementIfPositive(&isem.Value) > 0 { |
| | | atomic.AddUint64(&isem.NWaiters, ^uint64(0)) |
| | | return nil |
| | | } |
| | | } |
| | | } |
| | | |
| | | func (sem *Semaphore) TryWait() error { |
| | | isem := (*newSem)(unsafe.Pointer(sem)) |
| | | |
| | | if atomicDecrementIfPositive(&isem.Value) > 0 { |
| | | return nil |
| | | } |
| | | |
| | | return unix.EAGAIN |
| | | } |
| | | |
| | | func (sem *Semaphore) Post() error { |
| | | isem := (*newSem)(unsafe.Pointer(sem)) |
| | | |
| | | for { |
| | | cur := atomic.LoadUint32(&isem.Value) |
| | | |
| | | if cur == 0x7fffffff { |
| | | return unix.EOVERFLOW |
| | | } |
| | | |
| | | if atomic.CompareAndSwapUint32(&isem.Value, cur, cur+1) { |
| | | break |
| | | } |
| | | } |
| | | |
| | | if atomic.LoadUint64(&isem.NWaiters) <= 0 { |
| | | return nil |
| | | } |
| | | |
| | | if _, _, err := unix.Syscall6(unix.SYS_FUTEX, uintptr(unsafe.Pointer(&isem.Value)), uintptr(0x1), 1, 0, 0, 0); err != 0 { |
| | | return err |
| | | } |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (sem *Semaphore) Init(value uint) error { |
| | | if value > 0x7fffffff { |
| | | return unix.EINVAL |
| | | } |
| | | |
| | | isem := (*newSem)(unsafe.Pointer(sem)) |
| | | isem.Value = uint32(value) |
| | | isem.Private = 0 |
| | | isem.NWaiters = 0 |
| | | |
| | | return nil |
| | | } |
| | | |
| | | func (sem *Semaphore) Destroy() error { |
| | | return nil |
| | | } |
New file |
| | |
| | | // Copyright 2016 Tom Thorogood. All rights reserved. |
| | | // Use of this source code is governed by a |
| | | // Modified BSD License license that can be found in |
| | | // the LICENSE file. |
| | | |
| | | // Package shm provides functions to open and unlink shared memory. |
| | | package shm |
| | | |
| | | import ( |
| | | "os" |
| | | |
| | | "golang.org/x/sys/unix" |
| | | ) |
| | | |
| | | const devShm = "/dev/shm/" |
| | | |
| | | // Taken from shm_open(3): |
| | | // shm_open() creates and opens a new, or opens an existing, POSIX shared |
| | | // memory object. A POSIX shared memory object is in effect a handle which |
| | | // can be used by unrelated processes to mmap(2) the same region of shared |
| | | // memory. The shm_unlink() function performs the converse operation, |
| | | // removing an object previously created by shm_open(). |
| | | // |
| | | // The operation of shm_open() is analogous to that of open(2). name |
| | | // specifies the shared memory object to be created or opened. For |
| | | // portable use, a shared memory object should be identified by a name of |
| | | // the form /somename; that is, a null-terminated string of up to NAME_MAX |
| | | // (i.e., 255) characters consisting of an initial slash, followed by one |
| | | // or more characters, none of which are slashes. |
| | | func Open(name string, flag int, perm os.FileMode) (*os.File, error) { |
| | | fileName := name |
| | | |
| | | for len(name) != 0 && name[0] == '/' { |
| | | name = name[1:] |
| | | } |
| | | |
| | | if len(name) == 0 { |
| | | return nil, &os.PathError{Op: "open", Path: fileName, Err: unix.EINVAL} |
| | | } |
| | | |
| | | o := uint32(perm.Perm()) |
| | | if perm&os.ModeSetuid != 0 { |
| | | o |= unix.S_ISUID |
| | | } |
| | | if perm&os.ModeSetgid != 0 { |
| | | o |= unix.S_ISGID |
| | | } |
| | | if perm&os.ModeSticky != 0 { |
| | | o |= unix.S_ISVTX |
| | | } |
| | | |
| | | fd, err := unix.Open(devShm+name, flag|unix.O_CLOEXEC, o) |
| | | if err != nil { |
| | | return nil, &os.PathError{Op: "open", Path: fileName, Err: err} |
| | | } |
| | | |
| | | return os.NewFile(uintptr(fd), fileName), nil |
| | | } |
| | | |
| | | // Taken from shm_unlink(3): |
| | | // The operation of shm_unlink() is analogous to unlink(2): it removes a |
| | | // shared memory object name, and, once all processes have unmapped the |
| | | // object, de-allocates and destroys the contents of the associated memory |
| | | // region. After a successful shm_unlink(), attempts to shm_open() an |
| | | // object with the same name will fail (unless O_CREAT was specified, in |
| | | // which case a new, distinct object is created). |
| | | func Unlink(name string) error { |
| | | fileName := name |
| | | |
| | | for len(name) != 0 && name[0] == '/' { |
| | | name = name[1:] |
| | | } |
| | | |
| | | if len(name) == 0 { |
| | | return &os.PathError{Op: "unlink", Path: fileName, Err: unix.EINVAL} |
| | | } |
| | | |
| | | if err := unix.Unlink(devShm + name); err != nil { |
| | | return &os.PathError{Op: "unlink", Path: fileName, Err: err} |
| | | } |
| | | |
| | | return nil |
| | | } |