Add ipc module.

master
IzzelAliz 2019-12-04 22:54:02 +08:00
parent 1bb3743b47
commit 1d5c1f91ef
7 changed files with 406 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -1,3 +1,4 @@
rootProject.name = 'TabooLib'
include 'injector'
include 'module-ipc'