Add ipc module.
This commit is contained in:
parent
1bb3743b47
commit
1d5c1f91ef
@ -0,0 +1,137 @@
|
|||||||
|
package io.izzel.taboolib.module.ipc;
|
||||||
|
|
||||||
|
import io.izzel.taboolib.util.UNSAFE;
|
||||||
|
import sun.nio.ch.FileChannelImpl;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
public class MemoryMappedFile {
|
||||||
|
private static final Method mmap;
|
||||||
|
private static final Method unmmap;
|
||||||
|
private static final int BYTE_ARRAY_OFFSET;
|
||||||
|
|
||||||
|
private long addr, size;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
mmap = getMethod(FileChannelImpl.class, "map0", int.class, long.class, long.class);
|
||||||
|
unmmap = getMethod(FileChannelImpl.class, "unmap0", long.class, long.class);
|
||||||
|
BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Method getMethod(Class<?> cls, String name, Class<?>... params) throws Exception {
|
||||||
|
Method m = cls.getDeclaredMethod(name, params);
|
||||||
|
m.setAccessible(true);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MemoryMappedFile(FileChannel ch, long len) throws Exception {
|
||||||
|
this.size = len;
|
||||||
|
this.addr = (long) mmap.invoke(ch, 1, 0L, this.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void unmap() throws Exception {
|
||||||
|
unmmap.invoke(null, addr, this.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAddress() {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getByte(long pos) {
|
||||||
|
return UNSAFE.getByte(pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte getByteVolatile(long pos) {
|
||||||
|
return UNSAFE.getByteVolatile(null, pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(long pos) {
|
||||||
|
return UNSAFE.getInt(pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getIntVolatile(long pos) {
|
||||||
|
return UNSAFE.getIntVolatile(null, pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong(long pos) {
|
||||||
|
return UNSAFE.getLong(pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getLongVolatile(long pos) {
|
||||||
|
return UNSAFE.getLongVolatile(null, pos + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putByte(long pos, byte val) {
|
||||||
|
UNSAFE.putByte(pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putByteVolatile(long pos, byte val) {
|
||||||
|
UNSAFE.putByteVolatile(null, pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInt(long pos, int val) {
|
||||||
|
UNSAFE.putInt(pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putIntVolatile(long pos, int val) {
|
||||||
|
UNSAFE.putIntVolatile(null, pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putLong(long pos, long val) {
|
||||||
|
UNSAFE.putLong(pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void putLongVolatile(long pos, long val) {
|
||||||
|
UNSAFE.putLongVolatile(null, pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getBytes(long pos, byte[] data, int offset, int length) {
|
||||||
|
UNSAFE.copyMemory(null, pos + addr, data, BYTE_ARRAY_OFFSET + offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(long pos, byte[] data, int offset, int length) {
|
||||||
|
UNSAFE.copyMemory(data, BYTE_ARRAY_OFFSET + offset, null, pos + addr, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean compareAndSwapInt(long pos, int expected, int value) {
|
||||||
|
return UNSAFE.compareAndSwapInt(null, pos + addr, expected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean compareAndSwapLong(long pos, long expected, long value) {
|
||||||
|
return UNSAFE.compareAndSwapLong(null, pos + addr, expected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getAndAddInt(long pos, int delta) {
|
||||||
|
return UNSAFE.getAndAddInt(null, pos + addr, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getAndAddLong(long pos, long delta) {
|
||||||
|
return UNSAFE.getAndAddLong(null, pos + addr, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int addAndGetInt(long pos, int delta) {
|
||||||
|
return UNSAFE.getAndAddInt(null, pos + addr, delta) + delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long addAndGetLong(long pos, long delta) {
|
||||||
|
return UNSAFE.getAndAddLong(null, pos + addr, delta) + delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getAndSetInt(long pos, int val) {
|
||||||
|
return UNSAFE.getAndSetInt(null, pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long getAndSetLong(long pos, long val) {
|
||||||
|
return UNSAFE.getAndSetLong(null, pos + addr, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setMemory(long pos, long bytes, byte value) {
|
||||||
|
UNSAFE.setMemory(pos + addr, bytes, value);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
package io.izzel.taboolib.module.ipc;
|
||||||
|
|
||||||
|
public class MessageBlock {
|
||||||
|
|
||||||
|
private final MemoryMappedFile file;
|
||||||
|
private final long baseOffset;
|
||||||
|
private final long size;
|
||||||
|
|
||||||
|
public MessageBlock(MemoryMappedFile file, long baseOffset, long size) {
|
||||||
|
this.file = file;
|
||||||
|
this.baseOffset = baseOffset;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
int id = getId();
|
||||||
|
file.setMemory(baseOffset, size, (byte) 0x00);
|
||||||
|
putInt(0, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return getLong(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateTimestamp() {
|
||||||
|
putLong(4, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPayloadSize() {
|
||||||
|
return size - 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAddress() {
|
||||||
|
return file.getAddress() + baseOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getByte(long pos) {
|
||||||
|
return file.getByte(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getByteVolatile(long pos) {
|
||||||
|
return file.getByteVolatile(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(long pos) {
|
||||||
|
return file.getInt(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIntVolatile(long pos) {
|
||||||
|
return file.getIntVolatile(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLong(long pos) {
|
||||||
|
return file.getLong(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLongVolatile(long pos) {
|
||||||
|
return file.getLongVolatile(mapAddress(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putByte(long pos, byte val) {
|
||||||
|
file.putByte(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putByteVolatile(long pos, byte val) {
|
||||||
|
file.putByteVolatile(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInt(long pos, int val) {
|
||||||
|
file.putInt(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putIntVolatile(long pos, int val) {
|
||||||
|
file.putIntVolatile(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putLong(long pos, long val) {
|
||||||
|
file.putLong(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putLongVolatile(long pos, long val) {
|
||||||
|
file.putLongVolatile(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getBytes(long pos, byte[] data, int offset, int length) {
|
||||||
|
file.getBytes(mapAddress(pos), data, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBytes(long pos, byte[] data, int offset, int length) {
|
||||||
|
file.setBytes(mapAddress(pos), data, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean compareAndSwapInt(long pos, int expected, int value) {
|
||||||
|
return file.compareAndSwapInt(mapAddress(pos), expected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean compareAndSwapLong(long pos, long expected, long value) {
|
||||||
|
return file.compareAndSwapLong(mapAddress(pos), expected, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAndAddInt(long pos, int delta) {
|
||||||
|
return file.getAndAddInt(mapAddress(pos), delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAndAddLong(long pos, long delta) {
|
||||||
|
return file.getAndAddLong(mapAddress(pos), delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int addAndGetInt(long pos, int delta) {
|
||||||
|
return file.addAndGetInt(mapAddress(pos), delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long addAndGetLong(long pos, long delta) {
|
||||||
|
return file.addAndGetLong(mapAddress(pos), delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAndSetInt(long pos, int val) {
|
||||||
|
return file.getAndSetInt(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAndSetLong(long pos, long val) {
|
||||||
|
return file.getAndSetLong(mapAddress(pos), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long mapAddress(long pos) {
|
||||||
|
if (pos < size) {
|
||||||
|
return baseOffset + pos;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package io.izzel.taboolib.module.ipc;
|
||||||
|
|
||||||
|
public interface TabooIpcClient {
|
||||||
|
|
||||||
|
boolean connect(TabooIpcConfig config) throws Exception;
|
||||||
|
|
||||||
|
void disconnect() throws Exception;
|
||||||
|
|
||||||
|
boolean sendMessage(byte[] bytes, int index, int length, MessageBlock target);
|
||||||
|
|
||||||
|
default boolean sendMessage(byte[] bytes, MessageBlock target) {
|
||||||
|
return sendMessage(bytes, 0, bytes.length, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean readMessage(byte[] buf);
|
||||||
|
|
||||||
|
int getId();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,87 @@
|
|||||||
|
package io.izzel.taboolib.module.ipc;
|
||||||
|
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
|
public class TabooIpcClientImpl implements TabooIpcClient {
|
||||||
|
|
||||||
|
private volatile boolean available = false;
|
||||||
|
private MemoryMappedFile file;
|
||||||
|
private int id;
|
||||||
|
private MessageBlock block;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean connect(TabooIpcConfig config) throws Exception {
|
||||||
|
if (available) throw new IllegalStateException();
|
||||||
|
long size = round(config.memorySize());
|
||||||
|
long blockSize = round(config.blockSize());
|
||||||
|
if (blockSize < 128 || size < (blockSize << 1) || size % blockSize != 0) return false;
|
||||||
|
try (RandomAccessFile raf = new RandomAccessFile(config.fileLocation(), "rw")) {
|
||||||
|
raf.setLength(size);
|
||||||
|
try (FileChannel channel = raf.getChannel()) {
|
||||||
|
this.file = new MemoryMappedFile(channel, size);
|
||||||
|
this.id = file.getAndAddInt(0, 1);
|
||||||
|
if (!login(size, blockSize, config.timeout())) {
|
||||||
|
this.file.unmap();
|
||||||
|
return available = false;
|
||||||
|
}
|
||||||
|
return available = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void disconnect() throws Exception {
|
||||||
|
if (!available) throw new IllegalStateException();
|
||||||
|
if (file != null) file.unmap();
|
||||||
|
available = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendMessage(byte[] bytes, int index, int length, MessageBlock target) {
|
||||||
|
if (!available) throw new IllegalStateException();
|
||||||
|
if (bytes.length < length || target.getPayloadSize() < length)
|
||||||
|
throw new IllegalArgumentException("message too long");
|
||||||
|
if (target.compareAndSwapInt(16, 0, 1)) {
|
||||||
|
target.setBytes(64, bytes, index, length);
|
||||||
|
return target.compareAndSwapInt(16, 1, 2);
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readMessage(byte[] buf) {
|
||||||
|
if (!available) throw new IllegalStateException();
|
||||||
|
if (this.block.getInt(16) == 2) {
|
||||||
|
this.block.getBytes(64, buf, 0, (int) this.block.getPayloadSize());
|
||||||
|
return this.block.compareAndSwapInt(16, 2, 0);
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
if (!available) throw new IllegalStateException();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean login(long size, long blockSize, long timeout) {
|
||||||
|
long offset = blockSize;
|
||||||
|
while (offset < size) {
|
||||||
|
int prevId = file.getInt(offset);
|
||||||
|
long cur = System.currentTimeMillis();
|
||||||
|
long prev = file.getAndSetLong(offset + 4, cur);
|
||||||
|
if (Math.abs(prev - cur) > timeout) {
|
||||||
|
if (file.compareAndSwapInt(offset, prevId, id)) {
|
||||||
|
this.block = new MessageBlock(file, offset, blockSize);
|
||||||
|
this.block.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += blockSize;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long round(long i) {
|
||||||
|
return (i + 0xfffL) & ~0xfffL;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package io.izzel.taboolib.module.ipc;
|
||||||
|
|
||||||
|
public interface TabooIpcConfig {
|
||||||
|
|
||||||
|
default long memorySize() {
|
||||||
|
return 1 << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long blockSize() {
|
||||||
|
return 1 << 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long period() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
default long timeout() {
|
||||||
|
return 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileLocation();
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
rootProject.name = 'TabooLib'
|
rootProject.name = 'TabooLib'
|
||||||
include 'injector'
|
include 'injector'
|
||||||
|
include 'module-ipc'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user