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.
Motivation:
The untethered memory allocated by ensureWritable in a direct MemorySegment based non-pooled Buffer would be allocated without having a Cleaner attached to its ResourceScope.
This could cause that memory to leak if the Buffer instance was cast aside.
Modification:
ManagedBufferAllocator now makes sure to attach a cleaner to the buffer and its memory segment, when allocating untethered memory.
Result:
The BufferTest$CleanerTests now pass.
Motivation:
The jextract branch is currently suffering from upstream compilation
errors.
And we don't need jextract - memaccess+abi is enough.
Modification:
Avoid checking out the jextract branch of panama by default, and instead
explicitly check out the foreign-memaccess+abi branch when we clone the
JDK.
Result:
Our build should now be able to proceed again.
Motivation:
We need a new implementation of our new API that supports Java 11, since that is what Netty 5 will most likely baseline on.
We also need an implementation that does not rely on Unsafe.
This leaves us with ByteBuffer as the underlying currency of memory.
Modification:
- Add a NioBuffer implementation and associated supporting classes.
- The entry-point for this is a new MemoryManagers API, which is used to pick the implementation and provide the on-/off-heap MemoryManager implementations.
- Add a mechanism to configure/override which MemoryManagers implementation to use.
- The MemoryManagers implementations are service-loadable, so new ones can be discovered at runtime.
- The existing MemorySegment based implementation also get a MemoryManagers implementation.
- Expand the BufferTest to include all combinations of all implementations. We now run 360.000 tests in BufferTest.
- Some common infrastructure, like ArcDrop, is moved to its own package.
- Add a module-info.java to control the service loading, and the visibility in the various packages.
- Some pom.xml file updates to support our now module based project.
Result:
We have an implementation that should work on Java 11, but we currently don't build or test on 11.
More work needs to happen before that is a reality.
Motivation:
It is kind of a weird internal and hidden state, that slices were special.
For instance, slices could not be sent, and they could never obtain ownership.
This means buffers from slices behaved differently from allocated buffers.
In doing so, they violated both the principle that magic should stay hidden, and the principle of consistent behaviour.
Modification:
- The special reference-counting drop implementation that was added to support bifurcation, has been renamed to ArcDrop (for atomic reference counting).
- The ArcDrop is then used throughout the MemSegBuffer implementation to account for every instance where multiple buffers reference the same memory, e.g. slices and the like.
- Borrows of a buffer is then the sum of borrows from the buffer itself, and its ArcDrop.
- Ownership is thus tied to both the buffer itself being owned, and the ArcDrop being in an owned state.
- SizeClassedMemoryPool is changed to pool recoverable memory instead of sends, because the sends could come from slices.
- We also take care to keep around a "base" memory segment, so that we don't return memory segment slices to the memory pool (doing so would leak the memory from the parent segment that is not part of the slice).
- CleanerPooledDrop now keeps a weak reference to itself, rather than the buffer, which is more correct anyway, but now also required because we cannot rely on the buffer reference the cleaner was created with.
- The CleanerPooledDrop now takes care to drop the buffer that is actually passed to it, rather than what it was referencing from some earlier point.
- MemoryManager can now disclose the size of recoverable memory, so that SizeClassedMemoryPool can pick the correct size pool to return memory to. It cannot rely on the passed down buffer instance for this, because that buffer might have been a slice.
Result:
It is now possible for slices to obtain ownership when their parent buffer is closed.
The get* methods bounds checking accesses between 0 and the write offset, and the tests were confirming this behaviour.
This was wrong because it is not symmetric with the set* methods, which bounds check between 0 and the capacity, and does not modify the write offset.
The tests and methods have been updated so the get* methods now bounds check between 0 and the capacity.
Motivation:
With reference counting it can be difficult to keep track of ownership and references.
When bugs arise in this area, it's good to get help from some tooling.
Modification:
Add a LifecycleTracer which records lifecycle changes.
This information can be attached to any lifecycle/ownership exceptions as suppressed exceptions.
The tracing is off by default, unless assertions are enabled.
Result:
It's now easier to debug reference counting/lifecycle/ownership issues.
Motivation:
This makes it possible to use the new buffer API in Netty as is.
Modification:
Make the MemSegBuffer implementation class implement AsByteBuf and ReferenceCounted.
The produced ByteBuf instance delegates all calls to the underlying Buffer instance as faithfully as possible.
One area where the two deviates, is that it's not possible to create non-retained duplicates and slices with the new buffer API.
Result:
It is now possible to use the new buffer API on both client and server side.
The Echo* examples demonstrate this, and the EchoIT proves it with a test.
The API is used more directly on the client side, since the server-side allocator in Netty does not know how to allocate buffers with the incubating API.
Motivation:
Another way to process the readable data in a buffer.
This might be faster for composite buffers, since their byte cursors are a bit slower than the MemSegBuffer due to the indirections and more complicated logic.
Modification:
ReadableComponent now have openCursor method.
Note that we *don't* add an openReverseCursor method on ReadableComponent.
The reason is that forEachReadable iterates the components in the forward direction, and it's really confusing to then iterate the bytes in a backwards direction.
Working with both directions at the same time is very error prone.
Result:
It is now possible to process readable components with byte cursors.