// 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
|
|
import (
|
"os"
|
"sync/atomic"
|
"unsafe"
|
|
"golang.org/x/sys/unix"
|
)
|
|
func CreateSimplex(name string, perm os.FileMode, blockCount, blockSize int) (*ReadWriteCloser, error) {
|
if blockSize&0x3f != 0 {
|
return nil, ErrNotMultipleOf64
|
}
|
|
file, err := Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm)
|
if err != nil {
|
return nil, err
|
}
|
|
defer file.Close()
|
|
fullBlockSize := blockHeaderSize + uint64(blockSize)
|
size := sharedHeaderSize + fullBlockSize*uint64(blockCount)
|
|
if err = file.Truncate(int64(size)); err != nil {
|
return nil, err
|
}
|
|
data, err := unix.Mmap(int(file.Fd()), 0, int(size), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
if err != nil {
|
return nil, err
|
}
|
|
shared := (*sharedMem)(unsafe.Pointer(&data[0]))
|
|
/*
|
* memset already set:
|
* shared.ReadStart, shared.ReadEnd = 0, 0
|
* shared.WriteStart, shared.WriteEnd = 0, 0
|
* shared.block[i].Size = 0
|
* shared.block[i].DoneRead, shared.block[i].DoneWrite = 0, 0
|
*/
|
*(*uint32)(&shared.BlockCount), *(*uint64)(&shared.BlockSize) = uint32(blockCount), uint64(blockSize)
|
|
if err = ((*Semaphore)(&shared.SemSignal)).Init(0); err != nil {
|
return nil, err
|
}
|
|
if err = ((*Semaphore)(&shared.SemAvail)).Init(0); err != nil {
|
return nil, err
|
}
|
|
for i := uint32(0); i < uint32(blockCount); i++ {
|
block := (*sharedBlock)(unsafe.Pointer(&data[sharedHeaderSize+uint64(i)*fullBlockSize]))
|
|
switch i {
|
case 0:
|
block.Next, *(*uint32)(&block.Prev) = 1, uint32(blockCount-1)
|
case uint32(blockCount - 1):
|
block.Next, *(*uint32)(&block.Prev) = 0, uint32(blockCount-2)
|
default:
|
*(*uint32)(&block.Next), *(*uint32)(&block.Prev) = i+1, i-1
|
}
|
}
|
|
atomic.StoreUint32((*uint32)(&shared.Version), version)
|
|
return &ReadWriteCloser{
|
name: name,
|
|
data: data,
|
readShared: shared,
|
writeShared: shared,
|
size: size,
|
fullBlockSize: fullBlockSize,
|
|
Flags: (*[len(shared.Flags)]uint32)(unsafe.Pointer(&shared.Flags[0])),
|
}, nil
|
}
|
|
func CreateDuplex(name string, perm os.FileMode, blockCount, blockSize int) (*ReadWriteCloser, error) {
|
if blockSize&0x3f != 0 {
|
return nil, ErrNotMultipleOf64
|
}
|
|
file, err := Open(name, unix.O_CREAT|unix.O_EXCL|unix.O_TRUNC|unix.O_RDWR, perm)
|
if err != nil {
|
return nil, err
|
}
|
|
defer file.Close()
|
|
fullBlockSize := blockHeaderSize + uint64(blockSize)
|
sharedSize := sharedHeaderSize + fullBlockSize*uint64(blockCount)
|
size := 2 * sharedSize
|
|
if err = file.Truncate(int64(size)); err != nil {
|
return nil, err
|
}
|
|
data, err := unix.Mmap(int(file.Fd()), 0, int(size), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
if err != nil {
|
return nil, err
|
}
|
|
for i := uint64(0); i < 2; i++ {
|
shared := (*sharedMem)(unsafe.Pointer(&data[i*sharedSize]))
|
|
/*
|
* memset already set:
|
* shared.ReadStart, shared.ReadEnd = 0, 0
|
* shared.WriteStart, shared.WriteEnd = 0, 0
|
* shared.Blocks[i].Size = 0
|
* shared.Blocks[i].DoneRead, shared.Blocks[i].DoneWrite = 0, 0
|
*/
|
*(*uint32)(&shared.BlockCount), *(*uint64)(&shared.BlockSize) = uint32(blockCount), uint64(blockSize)
|
|
if err = ((*Semaphore)(&shared.SemSignal)).Init(0); err != nil {
|
return nil, err
|
}
|
|
if err = ((*Semaphore)(&shared.SemAvail)).Init(0); err != nil {
|
return nil, err
|
}
|
|
for j := uint32(0); j < uint32(blockCount); j++ {
|
block := (*sharedBlock)(unsafe.Pointer(&data[i*sharedSize+sharedHeaderSize+uint64(j)*fullBlockSize]))
|
|
switch j {
|
case 0:
|
block.Next, *(*uint32)(&block.Prev) = 1, uint32(blockCount-1)
|
case uint32(blockCount - 1):
|
block.Next, *(*uint32)(&block.Prev) = 0, uint32(blockCount-2)
|
default:
|
*(*uint32)(&block.Next), *(*uint32)(&block.Prev) = j+1, j-1
|
}
|
}
|
}
|
|
readShared := (*sharedMem)(unsafe.Pointer(&data[0]))
|
atomic.StoreUint32((*uint32)(&readShared.Version), version)
|
|
return &ReadWriteCloser{
|
name: name,
|
|
data: data,
|
readShared: readShared,
|
writeShared: (*sharedMem)(unsafe.Pointer(&data[sharedSize])),
|
size: size,
|
fullBlockSize: fullBlockSize,
|
|
Flags: (*[len(readShared.Flags)]uint32)(unsafe.Pointer(&readShared.Flags[0])),
|
}, nil
|
}
|