-
Notifications
You must be signed in to change notification settings - Fork 4k
GH-37286: [Java] Start adding nullability/nullness annotations #37723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
428641e
cd4a048
4ac2002
27f7046
daaf63d
70146d1
c10b5d5
71aef53
2d5f864
ab54e2d
919b301
0230b91
46dd019
c06945a
6e2876a
dde4e2a
0b92c5f
4de6d64
c7fb710
d6534b4
f2371ba
1a8edf5
9c155a2
4e011c0
f92f376
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,7 @@ | |
| import javax.annotation.concurrent.ThreadSafe; | ||
|
|
||
| import org.apache.arrow.util.Preconditions; | ||
| import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
|
||
| /** | ||
| * Provides a concurrent way to manage account for memory usage without locking. Used as basis | ||
|
|
@@ -34,7 +35,7 @@ class Accountant implements AutoCloseable { | |
| /** | ||
| * The parent allocator. | ||
| */ | ||
| protected final Accountant parent; | ||
| protected final @Nullable Accountant parent; | ||
|
|
||
| private final String name; | ||
|
|
||
|
|
@@ -59,7 +60,7 @@ class Accountant implements AutoCloseable { | |
| */ | ||
| private final AtomicLong locallyHeldMemory = new AtomicLong(); | ||
|
|
||
| public Accountant(Accountant parent, String name, long reservation, long maxAllocation) { | ||
| public Accountant(@Nullable Accountant parent, String name, long reservation, long maxAllocation) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @davisusanibar how about annotating the Since Preconditions.checkNotNull(name, "name must not be null");
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default is to assume non-null, I believe |
||
| Preconditions.checkNotNull(name, "name must not be null"); | ||
| Preconditions.checkArgument(reservation >= 0, "The initial reservation size must be non-negative."); | ||
| Preconditions.checkArgument(maxAllocation >= 0, "The maximum allocation limit must be non-negative."); | ||
|
|
@@ -73,12 +74,13 @@ public Accountant(Accountant parent, String name, long reservation, long maxAllo | |
| this.allocationLimit.set(maxAllocation); | ||
|
|
||
| if (reservation != 0) { | ||
| Preconditions.checkArgument(parent != null, "parent must not be null"); | ||
| // we will allocate a reservation from our parent. | ||
| final AllocationOutcome outcome = parent.allocateBytes(reservation); | ||
| if (!outcome.isOk()) { | ||
| throw new OutOfMemoryException(String.format( | ||
| "Failure trying to allocate initial reservation for Allocator. " + | ||
| "Attempted to allocate %d bytes.", reservation), outcome.getDetails()); | ||
| "Failure trying to allocate initial reservation for Allocator. " + | ||
| "Attempted to allocate %d bytes.", reservation), outcome.getDetails()); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -103,7 +105,7 @@ AllocationOutcome allocateBytes(long size) { | |
| } | ||
| } | ||
|
|
||
| private AllocationOutcome.Status allocateBytesInternal(long size, AllocationOutcomeDetails details) { | ||
| private AllocationOutcome.Status allocateBytesInternal(long size, @Nullable AllocationOutcomeDetails details) { | ||
| final AllocationOutcome.Status status = allocate(size, | ||
| true /*incomingUpdatePeek*/, false /*forceAllocation*/, details); | ||
| if (!status.isOk()) { | ||
|
|
@@ -168,7 +170,7 @@ public boolean forceAllocate(long size) { | |
| * @return The outcome of the allocation. | ||
| */ | ||
| private AllocationOutcome.Status allocate(final long size, final boolean incomingUpdatePeak, | ||
| final boolean forceAllocation, AllocationOutcomeDetails details) { | ||
| final boolean forceAllocation, @Nullable AllocationOutcomeDetails details) { | ||
| final long oldLocal = locallyHeldMemory.getAndAdd(size); | ||
| final long newLocal = oldLocal + size; | ||
| // Borrowed from Math.addExact (but avoid exception here) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |
| package org.apache.arrow.memory; | ||
|
|
||
| import org.apache.arrow.util.Preconditions; | ||
| import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
|
||
| /** | ||
| * An AllocationManager is the implementation of a physical memory allocation. | ||
|
|
@@ -48,8 +49,9 @@ public abstract class AllocationManager { | |
| // This is mostly a semantic constraint on the API user: if the reference count reaches 0 in the owningLedger, then | ||
| // there are not supposed to be any references through other allocators. In practice, this doesn't do anything | ||
| // as the implementation just forces ownership to be transferred to one of the other extant references. | ||
| private volatile BufferLedger owningLedger; | ||
| private volatile @Nullable BufferLedger owningLedger; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this nullable because its volatile? It does't look like it should be nullable based on its usage. maybe we just need to add a null check in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there is a point here. It could be initially non-null but after calling the state changes to null. So we could determine it as nullable? @danepitkin what do you think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @davisusanibar can you review this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there is one way to assign null, then the variable must be mapped as Nullable. It is possible to make some changes, but that is the rule. |
||
|
|
||
| @SuppressWarnings("nullness:method.invocation") //call to associate(a, b) not allowed on the given receiver | ||
| protected AllocationManager(BufferAllocator accountingAllocator) { | ||
| Preconditions.checkNotNull(accountingAllocator); | ||
| accountingAllocator.assertOpen(); | ||
|
|
@@ -61,7 +63,7 @@ protected AllocationManager(BufferAllocator accountingAllocator) { | |
| this.owningLedger = associate(accountingAllocator, false); | ||
| } | ||
|
|
||
| BufferLedger getOwningLedger() { | ||
| @Nullable BufferLedger getOwningLedger() { | ||
| return owningLedger; | ||
| } | ||
|
|
||
|
|
@@ -133,9 +135,9 @@ void release(final BufferLedger ledger) { | |
| // remove the <BaseAllocator, BufferLedger> mapping for the allocator | ||
| // of calling BufferLedger | ||
| Preconditions.checkState(map.containsKey(allocator), | ||
| "Expecting a mapping for allocator and reference manager"); | ||
| "Expecting a mapping for allocator and reference manager"); | ||
| final BufferLedger oldLedger = map.remove(allocator); | ||
|
|
||
| Preconditions.checkState(oldLedger != null, "Expecting a mapping for allocator and reference manager"); | ||
| BufferAllocator oldAllocator = oldLedger.getAllocator(); | ||
| if (oldAllocator instanceof BaseAllocator) { | ||
| // needed for debug only: tell the allocator that AllocationManager is removing a | ||
|
|
@@ -168,7 +170,7 @@ void release(final BufferLedger ledger) { | |
| // the release call was made by a non-owning reference manager, so after remove there have | ||
| // to be 1 or more <allocator, reference manager> mappings | ||
| Preconditions.checkState(map.size() > 0, | ||
| "The final removal of reference manager should be connected to owning reference manager"); | ||
| "The final removal of reference manager should be connected to owning reference manager"); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,15 +19,18 @@ | |
|
|
||
| import java.util.Optional; | ||
|
|
||
| import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
|
||
|
|
||
| /** | ||
| * Describes the type of outcome that occurred when trying to account for allocation of memory. | ||
| */ | ||
| public class AllocationOutcome { | ||
| private final Status status; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @davisusanibar should we annotate
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, by default CheckerFramewrok assume all variables as @NonNull. |
||
| private final AllocationOutcomeDetails details; | ||
| private final @Nullable AllocationOutcomeDetails details; | ||
| static final AllocationOutcome SUCCESS_INSTANCE = new AllocationOutcome(Status.SUCCESS); | ||
|
|
||
| AllocationOutcome(Status status, AllocationOutcomeDetails details) { | ||
| AllocationOutcome(Status status, @Nullable AllocationOutcomeDetails details) { | ||
| this.status = status; | ||
| this.details = details; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,7 @@ | |
| import org.apache.arrow.memory.util.MemoryUtil; | ||
| import org.apache.arrow.util.Preconditions; | ||
| import org.apache.arrow.util.VisibleForTesting; | ||
| import org.checkerframework.checker.nullness.qual.Nullable; | ||
|
|
||
| /** | ||
| * ArrowBuf serves as a facade over underlying memory by providing | ||
|
|
@@ -68,11 +69,11 @@ public final class ArrowBuf implements AutoCloseable { | |
| private static final int LOG_BYTES_PER_ROW = 10; | ||
| private final long id = idGenerator.incrementAndGet(); | ||
| private final ReferenceManager referenceManager; | ||
| private final BufferManager bufferManager; | ||
| private final @Nullable BufferManager bufferManager; | ||
| private final long addr; | ||
| private long readerIndex; | ||
| private long writerIndex; | ||
| private final HistoricalLog historicalLog = BaseAllocator.DEBUG ? | ||
| private final @Nullable HistoricalLog historicalLog = BaseAllocator.DEBUG ? | ||
| new HistoricalLog(BaseAllocator.DEBUG_LOG_LENGTH, "ArrowBuf[%d]", id) : null; | ||
| private volatile long capacity; | ||
|
|
||
|
|
@@ -84,7 +85,7 @@ public final class ArrowBuf implements AutoCloseable { | |
| */ | ||
| public ArrowBuf( | ||
| final ReferenceManager referenceManager, | ||
| final BufferManager bufferManager, | ||
| final @Nullable BufferManager bufferManager, | ||
| final long capacity, | ||
| final long memoryAddress) { | ||
| this.referenceManager = referenceManager; | ||
|
|
@@ -93,7 +94,7 @@ public ArrowBuf( | |
| this.capacity = capacity; | ||
| this.readerIndex = 0; | ||
| this.writerIndex = 0; | ||
| if (BaseAllocator.DEBUG) { | ||
| if (historicalLog != null) { | ||
| historicalLog.recordEvent("create()"); | ||
| } | ||
| } | ||
|
|
@@ -244,7 +245,7 @@ public int hashCode() { | |
| } | ||
|
|
||
| @Override | ||
| public boolean equals(Object obj) { | ||
| public boolean equals(@Nullable Object obj) { | ||
| // identity equals only. | ||
| return this == obj; | ||
| } | ||
|
|
@@ -313,7 +314,7 @@ private void checkIndexD(long index, long fieldLength) { | |
| // check bounds | ||
| Preconditions.checkArgument(fieldLength >= 0, "expecting non-negative data length"); | ||
| if (index < 0 || index > capacity() - fieldLength) { | ||
| if (BaseAllocator.DEBUG) { | ||
| if (historicalLog != null) { | ||
| historicalLog.logHistory(logger); | ||
| } | ||
| throw new IndexOutOfBoundsException(String.format( | ||
|
|
@@ -736,7 +737,7 @@ public void getBytes(long index, byte[] dst, int dstIndex, int length) { | |
| if (length != 0) { | ||
| // copy "length" bytes from this ArrowBuf starting at addr(index) address | ||
| // into dst byte array at dstIndex onwards | ||
| MemoryUtil.UNSAFE.copyMemory(null, addr(index), dst, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, length); | ||
| MemoryUtil.copyMemory(null, addr(index), dst, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, length); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not following this change, could you please explain a bit?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, when we implement CheckerFramework then at compile time some validation are executed (eg: nullness validation). This is the example of error message when we are trying to consume Unsafe library. We are not in a position to fork or modify the third dependency, so we need to add some kind of @SuppressWarning annotation. Because UNSAFE.copyMemory is used at several places, we create a proxy and apply the exclusion in MemoryUtil.copyMemory, which then calls UNSAFE.copyMemory.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the explanation @davisusanibar |
||
| } | ||
| } | ||
|
|
||
|
|
@@ -773,7 +774,7 @@ public void setBytes(long index, byte[] src, int srcIndex, long length) { | |
| if (length > 0) { | ||
| // copy "length" bytes from src byte array at the starting index (srcIndex) | ||
| // into this ArrowBuf starting at address "addr(index)" | ||
| MemoryUtil.UNSAFE.copyMemory(src, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + srcIndex, null, addr(index), length); | ||
| MemoryUtil.copyMemory(src, MemoryUtil.BYTE_ARRAY_BASE_OFFSET + srcIndex, null, addr(index), length); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -799,15 +800,15 @@ public void getBytes(long index, ByteBuffer dst) { | |
| // at address srcAddress into the dst ByteBuffer starting at | ||
| // address dstAddress | ||
| final long dstAddress = MemoryUtil.getByteBufferAddress(dst) + dst.position(); | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, dst.remaining()); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, dst.remaining()); | ||
| // after copy, bump the next write position for the dst ByteBuffer | ||
| dst.position(dst.position() + dst.remaining()); | ||
| } else if (dst.hasArray()) { | ||
| // copy dst.remaining() bytes of data from this ArrowBuf starting | ||
| // at address srcAddress into the dst ByteBuffer starting at | ||
| // index dstIndex | ||
| final int dstIndex = dst.arrayOffset() + dst.position(); | ||
| MemoryUtil.UNSAFE.copyMemory( | ||
| MemoryUtil.copyMemory( | ||
| null, srcAddress, dst.array(), MemoryUtil.BYTE_ARRAY_BASE_OFFSET + dstIndex, dst.remaining()); | ||
| // after copy, bump the next write position for the dst ByteBuffer | ||
| dst.position(dst.position() + dst.remaining()); | ||
|
|
@@ -836,14 +837,14 @@ public void setBytes(long index, ByteBuffer src) { | |
| // copy src.remaining() bytes of data from src ByteBuffer starting at | ||
| // address srcAddress into this ArrowBuf starting at address dstAddress | ||
| final long srcAddress = MemoryUtil.getByteBufferAddress(src) + src.position(); | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| // after copy, bump the next read position for the src ByteBuffer | ||
| src.position(src.position() + length); | ||
| } else if (src.hasArray()) { | ||
| // copy src.remaining() bytes of data from src ByteBuffer starting at | ||
| // index srcIndex into this ArrowBuf starting at address dstAddress | ||
| final int srcIndex = src.arrayOffset() + src.position(); | ||
| MemoryUtil.UNSAFE.copyMemory( | ||
| MemoryUtil.copyMemory( | ||
| src.array(), MemoryUtil.BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddress, length); | ||
| // after copy, bump the next read position for the src ByteBuffer | ||
| src.position(src.position() + length); | ||
|
|
@@ -896,7 +897,7 @@ public void setBytes(long index, ByteBuffer src, int srcIndex, int length) { | |
| // srcAddress into this ArrowBuf at address dstAddress | ||
| final long srcAddress = MemoryUtil.getByteBufferAddress(src) + srcIndex; | ||
| final long dstAddress = addr(index); | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| } else { | ||
| if (srcIndex == 0 && src.capacity() == length) { | ||
| // copy the entire ByteBuffer from start to end of length | ||
|
|
@@ -936,7 +937,7 @@ public void getBytes(long index, ArrowBuf dst, long dstIndex, int length) { | |
| // dstAddress | ||
| final long srcAddress = addr(index); | ||
| final long dstAddress = dst.memoryAddress() + (long) dstIndex; | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -966,7 +967,7 @@ public void setBytes(long index, ArrowBuf src, long srcIndex, long length) { | |
| // dstAddress | ||
| final long srcAddress = src.memoryAddress() + srcIndex; | ||
| final long dstAddress = addr(index); | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -986,7 +987,7 @@ public void setBytes(long index, ArrowBuf src) { | |
| checkIndex(index, length); | ||
| final long srcAddress = src.memoryAddress() + src.readerIndex; | ||
| final long dstAddress = addr(index); | ||
| MemoryUtil.UNSAFE.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| MemoryUtil.copyMemory(null, srcAddress, null, dstAddress, length); | ||
| src.readerIndex(src.readerIndex + length); | ||
| } | ||
|
|
||
|
|
@@ -1011,7 +1012,7 @@ public int setBytes(long index, InputStream in, int length) throws IOException { | |
| if (readBytes > 0) { | ||
| // copy readBytes length of data from the tmp byte array starting | ||
| // at srcIndex 0 into this ArrowBuf starting at address addr(index) | ||
| MemoryUtil.UNSAFE.copyMemory(tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, null, addr(index), readBytes); | ||
| MemoryUtil.copyMemory(tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, null, addr(index), readBytes); | ||
| } | ||
| } | ||
| return readBytes; | ||
|
|
@@ -1033,7 +1034,7 @@ public void getBytes(long index, OutputStream out, int length) throws IOExceptio | |
| // copy length bytes of data from this ArrowBuf starting at | ||
| // address addr(index) into the tmp byte array starting at index 0 | ||
| byte[] tmp = new byte[length]; | ||
| MemoryUtil.UNSAFE.copyMemory(null, addr(index), tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, length); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: is it possible to make the |
||
| MemoryUtil.copyMemory(null, addr(index), tmp, MemoryUtil.BYTE_ARRAY_BASE_OFFSET, length); | ||
| // write the copied data to output stream | ||
| out.write(tmp); | ||
| } | ||
|
|
@@ -1109,7 +1110,7 @@ public long getId() { | |
| public void print(StringBuilder sb, int indent, Verbosity verbosity) { | ||
| CommonUtil.indent(sb, indent).append(toString()); | ||
|
|
||
| if (BaseAllocator.DEBUG && verbosity.includeHistoricalLog) { | ||
| if (historicalLog != null && verbosity.includeHistoricalLog) { | ||
| sb.append("\n"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should move |
||
| historicalLog.buildHistory(sb, indent + 1, verbosity.includeStackTraces); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to add this profile to the top-level java/pom.xml, but only enable it for a list of modules to make future development easier?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can save this as follow up for the next nullability annotations PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@davisusanibar can you file a follow up isuse?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just filled https://github.com/apache/arrow/issues/39354