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'
|
||||
include 'injector'
|
||||
include 'module-ipc'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user