UnsafeBuffer implementation, based on sun.misc.Unsafe
Motivation: When Unsafe is available, we can supposedly do certain things faster than when it is not. Modification: Add a Buffer implementation that take advantage of sun.misc.Unsafe. It has not yet been verified if this is faster in any way than, say the ByteBuffer implementation or the MemorySegment implementation. Result: Another Buffer implementation that can be used when Unsafe is available.
This commit is contained in:
parent
e7f7335804
commit
c73dd07384
2
pom.xml
2
pom.xml
@ -78,6 +78,8 @@
|
|||||||
-server
|
-server
|
||||||
-dsa -da -ea:io.netty...
|
-dsa -da -ea:io.netty...
|
||||||
-XX:+HeapDumpOnOutOfMemoryError
|
-XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
-Dio.netty.tryReflectionSetAccessible=true
|
||||||
|
--add-opens java.base/java.nio=io.netty.common
|
||||||
</argLine.common>
|
</argLine.common>
|
||||||
<!-- <argLine.printGC>-XX:+PrintGCDetails</argLine.printGC>-->
|
<!-- <argLine.printGC>-XX:+PrintGCDetails</argLine.printGC>-->
|
||||||
<argLine.printGC/>
|
<argLine.printGC/>
|
||||||
|
@ -26,5 +26,6 @@ public interface MemoryManager {
|
|||||||
Drop<Buffer> drop();
|
Drop<Buffer> drop();
|
||||||
Object unwrapRecoverableMemory(Buffer buf);
|
Object unwrapRecoverableMemory(Buffer buf);
|
||||||
int capacityOfRecoverableMemory(Object memory);
|
int capacityOfRecoverableMemory(Object memory);
|
||||||
|
// todo should recoverMemory re-attach a cleaner?
|
||||||
Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop);
|
Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.ReadOnlyBufferException;
|
import java.nio.ReadOnlyBufferException;
|
||||||
|
|
||||||
|
import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
|
||||||
|
import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
|
||||||
|
|
||||||
class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, ReadableComponent, WritableComponent {
|
class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, ReadableComponent, WritableComponent {
|
||||||
private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
|
private static final ByteBuffer CLOSED_BUFFER = ByteBuffer.allocate(0);
|
||||||
|
|
||||||
@ -417,9 +420,9 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
|
|||||||
}
|
}
|
||||||
var drop = (ArcDrop<NioBuffer>) unsafeGetDrop();
|
var drop = (ArcDrop<NioBuffer>) unsafeGetDrop();
|
||||||
unsafeSetDrop(new ArcDrop<>(drop));
|
unsafeSetDrop(new ArcDrop<>(drop));
|
||||||
var bifurcatedSeg = rmem.slice(0, woff);
|
var bifurcatedBuffer = rmem.slice(0, woff);
|
||||||
// TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop.
|
// TODO maybe incrementing the existing ArcDrop is enough; maybe we don't need to wrap it in another ArcDrop.
|
||||||
var bifurcatedBuf = new NioBuffer(base, bifurcatedSeg, control, new ArcDrop<>(drop.increment()));
|
var bifurcatedBuf = new NioBuffer(base, bifurcatedBuffer, control, new ArcDrop<>(drop.increment()));
|
||||||
bifurcatedBuf.woff = woff;
|
bifurcatedBuf.woff = woff;
|
||||||
bifurcatedBuf.roff = roff;
|
bifurcatedBuf.roff = roff;
|
||||||
bifurcatedBuf.order(order());
|
bifurcatedBuf.order(order());
|
||||||
@ -893,7 +896,7 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
|
|||||||
wmem.putInt(woff, value);
|
wmem.putInt(woff, value);
|
||||||
return this;
|
return this;
|
||||||
} catch (IndexOutOfBoundsException e) {
|
} catch (IndexOutOfBoundsException e) {
|
||||||
throw checkWriteState(e, NioBuffer.this.woff);
|
throw checkWriteState(e, this.woff);
|
||||||
} catch (ReadOnlyBufferException e) {
|
} catch (ReadOnlyBufferException e) {
|
||||||
throw bufferIsReadOnly();
|
throw bufferIsReadOnly();
|
||||||
}
|
}
|
||||||
@ -918,7 +921,7 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
|
|||||||
wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
|
wmem.putInt(woff, (int) (value & 0xFFFFFFFFL));
|
||||||
return this;
|
return this;
|
||||||
} catch (IndexOutOfBoundsException e) {
|
} catch (IndexOutOfBoundsException e) {
|
||||||
throw checkWriteState(e, NioBuffer.this.woff);
|
throw checkWriteState(e, this.woff);
|
||||||
} catch (ReadOnlyBufferException e) {
|
} catch (ReadOnlyBufferException e) {
|
||||||
throw bufferIsReadOnly();
|
throw bufferIsReadOnly();
|
||||||
}
|
}
|
||||||
@ -1135,14 +1138,6 @@ class NioBuffer extends RcSupport<Buffer, NioBuffer> implements Buffer, Readable
|
|||||||
return outOfBounds(index);
|
return outOfBounds(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IllegalStateException bufferIsClosed() {
|
|
||||||
return new IllegalStateException("This buffer is closed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IllegalStateException bufferIsReadOnly() {
|
|
||||||
return new IllegalStateException("This buffer is read-only.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IndexOutOfBoundsException outOfBounds(int index) {
|
private IndexOutOfBoundsException outOfBounds(int index) {
|
||||||
return new IndexOutOfBoundsException(
|
return new IndexOutOfBoundsException(
|
||||||
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
||||||
|
@ -68,4 +68,12 @@ public interface Statics {
|
|||||||
dest.order(prevOrder);
|
dest.order(prevOrder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IllegalStateException bufferIsClosed() {
|
||||||
|
return new IllegalStateException("This buffer is closed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static IllegalStateException bufferIsReadOnly() {
|
||||||
|
return new IllegalStateException("This buffer is read-only.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,8 @@ import jdk.incubator.foreign.ResourceScope;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import static io.netty.buffer.api.internal.Statics.bufferIsClosed;
|
||||||
|
import static io.netty.buffer.api.internal.Statics.bufferIsReadOnly;
|
||||||
import static jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
|
import static jdk.incubator.foreign.MemoryAccess.getByteAtOffset;
|
||||||
import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset;
|
import static jdk.incubator.foreign.MemoryAccess.getCharAtOffset;
|
||||||
import static jdk.incubator.foreign.MemoryAccess.getDoubleAtOffset;
|
import static jdk.incubator.foreign.MemoryAccess.getDoubleAtOffset;
|
||||||
@ -1168,14 +1170,6 @@ class MemSegBuffer extends RcSupport<Buffer, MemSegBuffer> implements Buffer, Re
|
|||||||
return outOfBounds(index);
|
return outOfBounds(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IllegalStateException bufferIsClosed() {
|
|
||||||
return new IllegalStateException("This buffer is closed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IllegalStateException bufferIsReadOnly() {
|
|
||||||
return new IllegalStateException("This buffer is read-only.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private IndexOutOfBoundsException outOfBounds(int index) {
|
private IndexOutOfBoundsException outOfBounds(int index) {
|
||||||
return new IndexOutOfBoundsException(
|
return new IndexOutOfBoundsException(
|
||||||
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
"Index " + index + " is out of bounds: [read 0 to " + woff + ", write 0 to " +
|
||||||
|
55
src/main/java/io/netty/buffer/api/unsafe/CleanerDrop.java
Normal file
55
src/main/java/io/netty/buffer/api/unsafe/CleanerDrop.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.buffer.api.unsafe;
|
||||||
|
|
||||||
|
import io.netty.buffer.api.Buffer;
|
||||||
|
import io.netty.buffer.api.Drop;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
public class CleanerDrop implements Drop<Buffer> {
|
||||||
|
private final Drop<Buffer> drop;
|
||||||
|
|
||||||
|
public CleanerDrop(UnsafeMemory memory, Drop<Buffer> drop, Cleaner cleaner) {
|
||||||
|
this.drop = drop;
|
||||||
|
long address = memory.address;
|
||||||
|
cleaner.register(memory, new FreeAddress(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drop(Buffer obj) {
|
||||||
|
drop.drop(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(Buffer obj) {
|
||||||
|
drop.attach(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FreeAddress implements Runnable {
|
||||||
|
private final long address;
|
||||||
|
|
||||||
|
FreeAddress(long address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
PlatformDependent.freeMemory(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1557
src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
Normal file
1557
src/main/java/io/netty/buffer/api/unsafe/UnsafeBuffer.java
Normal file
File diff suppressed because it is too large
Load Diff
28
src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
Normal file
28
src/main/java/io/netty/buffer/api/unsafe/UnsafeMemory.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.buffer.api.unsafe;
|
||||||
|
|
||||||
|
class UnsafeMemory {
|
||||||
|
final Object base;
|
||||||
|
final long address;
|
||||||
|
final int size;
|
||||||
|
|
||||||
|
UnsafeMemory(Object base, long address, int size) {
|
||||||
|
this.base = base;
|
||||||
|
this.address = address;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.buffer.api.unsafe;
|
||||||
|
|
||||||
|
import io.netty.buffer.api.AllocatorControl;
|
||||||
|
import io.netty.buffer.api.Buffer;
|
||||||
|
import io.netty.buffer.api.Drop;
|
||||||
|
import io.netty.buffer.api.MemoryManager;
|
||||||
|
import io.netty.buffer.api.internal.Statics;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
|
import java.lang.ref.Cleaner;
|
||||||
|
|
||||||
|
import static io.netty.buffer.api.internal.Statics.convert;
|
||||||
|
|
||||||
|
public class UnsafeMemoryManager implements MemoryManager {
|
||||||
|
private final boolean offheap;
|
||||||
|
|
||||||
|
public UnsafeMemoryManager(boolean offheap) {
|
||||||
|
this.offheap = offheap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNative() {
|
||||||
|
return offheap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Buffer allocateConfined(AllocatorControl allocatorControl, long size, Drop<Buffer> drop, Cleaner cleaner) {
|
||||||
|
return allocateShared(allocatorControl, size, drop, cleaner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Buffer allocateShared(AllocatorControl allocatorControl, long size, Drop<Buffer> drop, Cleaner cleaner) {
|
||||||
|
final Object base;
|
||||||
|
final long address;
|
||||||
|
final UnsafeMemory memory;
|
||||||
|
final int size32 = Math.toIntExact(size);
|
||||||
|
if (cleaner == null) {
|
||||||
|
cleaner = Statics.CLEANER;
|
||||||
|
}
|
||||||
|
if (offheap) {
|
||||||
|
base = null;
|
||||||
|
address = PlatformDependent.allocateMemory(size);
|
||||||
|
PlatformDependent.setMemory(address, size, (byte) 0);
|
||||||
|
memory = new UnsafeMemory(base, address, size32);
|
||||||
|
drop = new CleanerDrop(memory, drop, cleaner);
|
||||||
|
} else {
|
||||||
|
base = new byte[size32];
|
||||||
|
address = PlatformDependent.byteArrayBaseOffset();
|
||||||
|
memory = new UnsafeMemory(base, address, size32);
|
||||||
|
}
|
||||||
|
return new UnsafeBuffer(memory, 0, size32, allocatorControl, convert(drop));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Drop<Buffer> drop() {
|
||||||
|
// We cannot reliably drop unsafe memory. We have to rely on the cleaner to do that.
|
||||||
|
return Statics.NO_OP_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object unwrapRecoverableMemory(Buffer buf) {
|
||||||
|
return ((UnsafeBuffer) buf).recover();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int capacityOfRecoverableMemory(Object memory) {
|
||||||
|
return ((UnsafeMemory) memory).size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Buffer recoverMemory(AllocatorControl allocatorControl, Object recoverableMemory, Drop<Buffer> drop) {
|
||||||
|
UnsafeMemory memory = (UnsafeMemory) recoverableMemory;
|
||||||
|
return new UnsafeBuffer(memory, 0, memory.size, allocatorControl, convert(drop));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.buffer.api.unsafe;
|
||||||
|
|
||||||
|
import io.netty.buffer.api.MemoryManager;
|
||||||
|
import io.netty.buffer.api.MemoryManagers;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
|
public class UnsafeMemoryManagers implements MemoryManagers {
|
||||||
|
public UnsafeMemoryManagers() {
|
||||||
|
if (!PlatformDependent.hasUnsafe()) {
|
||||||
|
throw new UnsupportedOperationException("Unsafe is not available.");
|
||||||
|
}
|
||||||
|
if (!PlatformDependent.hasDirectBufferNoCleanerConstructor()) {
|
||||||
|
throw new UnsupportedOperationException("DirectByteBuffer internal constructor is not available.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryManager getHeapMemoryManager() {
|
||||||
|
return new UnsafeMemoryManager(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryManager getNativeMemoryManager() {
|
||||||
|
return new UnsafeMemoryManager(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "US";
|
||||||
|
}
|
||||||
|
}
|
20
src/main/java/io/netty/buffer/api/unsafe/package-info.java
Normal file
20
src/main/java/io/netty/buffer/api/unsafe/package-info.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link io.netty.buffer.api.Buffer} implementation that is based on {@code sun.misc.Unsafe}.
|
||||||
|
*/
|
||||||
|
package io.netty.buffer.api.unsafe;
|
@ -32,5 +32,6 @@ module netty.incubator.buffer {
|
|||||||
|
|
||||||
provides io.netty.buffer.api.MemoryManagers with
|
provides io.netty.buffer.api.MemoryManagers with
|
||||||
io.netty.buffer.api.memseg.SegmentMemoryManagers,
|
io.netty.buffer.api.memseg.SegmentMemoryManagers,
|
||||||
io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers;
|
io.netty.buffer.api.bytebuffer.ByteBufferMemoryManagers,
|
||||||
|
io.netty.buffer.api.unsafe.UnsafeMemoryManagers;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user