mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +07:00
[refactoring] replace AlreadyDisposed with ClosedStorageException in StreamlinedBlobStorage
- Preparation to move between packages there `AlreadyDisposedException` become unavailable - Make storage after-close behavior more consistent: _most_ access methods must throw exception GitOrigin-RevId: 67d99338c031bb7fb63468efe54a82820a2e438f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f999a929ef
commit
b66a4b570c
@@ -224,7 +224,7 @@ public final class AttributesStorageOverBlobStorage implements VFSAttributesStor
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
public boolean isEmpty() throws IOException {
|
||||
return storage.liveRecordsCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
public static final int DATA_FORMAT_VERSION_OFFSET = 48; //int32
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static final int FIRST_UNUSED_FIELD_OFFSET = 52;
|
||||
|
||||
//Bytes [52..64] is reserved for the generations to come:
|
||||
@@ -233,39 +234,50 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
//monitoring:
|
||||
|
||||
@Override
|
||||
public int liveRecordsCount() {
|
||||
public int liveRecordsCount() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return recordsAllocated.get() - recordsDeleted.get() - recordsRelocated.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int recordsAllocated() {
|
||||
public int recordsAllocated() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return recordsAllocated.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int recordsRelocated() {
|
||||
public int recordsRelocated() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return recordsRelocated.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int recordsDeleted() {
|
||||
public int recordsDeleted() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return recordsDeleted.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalLiveRecordsPayloadBytes() {
|
||||
public long totalLiveRecordsPayloadBytes() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return totalLiveRecordsPayloadBytes.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long totalLiveRecordsCapacityBytes() {
|
||||
public long totalLiveRecordsCapacityBytes() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return totalLiveRecordsCapacityBytes.get();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "[" + storagePath() + "]{nextRecordId: " + nextRecordId() + '}';
|
||||
try {
|
||||
return getClass().getSimpleName() + "[" + storagePath() + "]{nextRecordId: " + nextRecordId() + '}';
|
||||
}
|
||||
catch (IOException e) {
|
||||
return getClass().getSimpleName() + "[" + storagePath() + "]{closed}";
|
||||
}
|
||||
}
|
||||
|
||||
//==================== implementation: ==========================================================================
|
||||
@@ -314,14 +326,15 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
return Math.toIntExact(offsetInFile % pageSize);
|
||||
}
|
||||
|
||||
/** Field could be read as volatile, but writes are protected with this intrinsic lock */
|
||||
protected int nextRecordId() {
|
||||
/** @return first un-allocated id, next-to-be-allocated. Read operation: i.e. doesn't change anything. */
|
||||
protected int nextRecordId() throws IOException {
|
||||
//Field could be read as volatile, but writes are protected with this intrinsic lock
|
||||
return nextRecordId;
|
||||
}
|
||||
|
||||
/** Must be called under 'this' lock */
|
||||
//@GuardedBy(this)
|
||||
protected void updateNextRecordId(int nextRecordId) {
|
||||
protected void updateNextRecordId(int nextRecordId) throws IOException {
|
||||
if (nextRecordId <= NULL_ID) {
|
||||
throw new IllegalArgumentException("nextRecordId(=" + nextRecordId + ") must be >0");
|
||||
}
|
||||
@@ -374,7 +387,7 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
int pageSize) throws IOException;
|
||||
|
||||
|
||||
protected void checkRecordIdExists(int recordId) throws IllegalArgumentException {
|
||||
protected void checkRecordIdExists(int recordId) throws IllegalArgumentException, IOException {
|
||||
if (!isExistingRecordId(recordId)) {
|
||||
throw new IllegalArgumentException("recordId(" + recordId + ") is not valid: allocated ids are in (0, " + nextRecordId() + ")");
|
||||
}
|
||||
@@ -382,7 +395,7 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
|
||||
protected void checkRedirectToId(int startingRecordId,
|
||||
int currentRecordId,
|
||||
int redirectToId) throws RecordAlreadyDeletedException, CorruptedException {
|
||||
int redirectToId) throws RecordAlreadyDeletedException, IOException {
|
||||
if (redirectToId == NULL_ID) { //!actual && redirectTo = NULL
|
||||
throw new RecordAlreadyDeletedException("Can't access record[" + startingRecordId + "/" + currentRecordId + "]: it was deleted");
|
||||
}
|
||||
@@ -397,7 +410,7 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
* @return true if record with recordId is already allocated.
|
||||
* It doesn't mean the recordId is valid, though -- it could point to the middle of some record.
|
||||
*/
|
||||
protected boolean isRecordIdAllocated(int recordId) {
|
||||
protected boolean isRecordIdAllocated(int recordId) throws IOException {
|
||||
return recordId < nextRecordId();
|
||||
}
|
||||
|
||||
@@ -405,7 +418,7 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
* @return true if record with recordId is in the range of existing record ids.
|
||||
* It doesn't mean the recordId is valid, though -- it could point to the middle of some record.
|
||||
*/
|
||||
protected boolean isExistingRecordId(int recordId) {
|
||||
protected boolean isExistingRecordId(int recordId) throws IOException {
|
||||
return isValidRecordId(recordId) && isRecordIdAllocated(recordId);
|
||||
}
|
||||
|
||||
@@ -517,11 +530,16 @@ public abstract class StreamlinedBlobStorageHelper implements StreamlinedBlobSto
|
||||
.build();
|
||||
return meter.batchCallback(
|
||||
() -> {
|
||||
recordsAllocated.record(storage.recordsAllocated(), attributes);
|
||||
recordsRelocated.record(storage.recordsRelocated(), attributes);
|
||||
recordsDeleted.record(storage.recordsDeleted(), attributes);
|
||||
totalLiveRecordsPayloadBytes.record(storage.totalLiveRecordsPayloadBytes(), attributes);
|
||||
totalLiveRecordsCapacityBytes.record(storage.totalLiveRecordsCapacityBytes(), attributes);
|
||||
try {
|
||||
recordsAllocated.record(storage.recordsAllocated(), attributes);
|
||||
recordsRelocated.record(storage.recordsRelocated(), attributes);
|
||||
recordsDeleted.record(storage.recordsDeleted(), attributes);
|
||||
totalLiveRecordsPayloadBytes.record(storage.totalLiveRecordsPayloadBytes(), attributes);
|
||||
totalLiveRecordsCapacityBytes.record(storage.totalLiveRecordsCapacityBytes(), attributes);
|
||||
}
|
||||
catch (ClosedStorageException e) {
|
||||
//just skip
|
||||
}
|
||||
},
|
||||
recordsAllocated, recordsRelocated, recordsDeleted,
|
||||
totalLiveRecordsPayloadBytes, totalLiveRecordsCapacityBytes
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.openapi.vfs.newvfs.persistent.dev.blobstorage;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.IntRef;
|
||||
import com.intellij.util.io.ClosedStorageException;
|
||||
import com.intellij.util.io.PagedFileStorageWithRWLockedPageContent;
|
||||
import com.intellij.util.io.blobstorage.ByteBufferReader;
|
||||
import com.intellij.util.io.blobstorage.ByteBufferWriter;
|
||||
@@ -541,7 +542,8 @@ public final class StreamlinedBlobStorageOverLockFreePagedStorage extends Stream
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
public long sizeInBytes() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return pagedStorage.length();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package com.intellij.openapi.vfs.newvfs.persistent.dev.blobstorage;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.IntRef;
|
||||
import com.intellij.serviceContainer.AlreadyDisposedException;
|
||||
import com.intellij.platform.util.io.storages.mmapped.MMappedFileStorage;
|
||||
import com.intellij.platform.util.io.storages.mmapped.MMappedFileStorage.Page;
|
||||
import com.intellij.util.io.ClosedStorageException;
|
||||
import com.intellij.util.io.blobstorage.ByteBufferReader;
|
||||
import com.intellij.util.io.blobstorage.ByteBufferWriter;
|
||||
import com.intellij.util.io.blobstorage.SpaceAllocationStrategy;
|
||||
@@ -123,17 +123,17 @@ public final class StreamlinedBlobStorageOverMMappedFile extends StreamlinedBlob
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStorageVersion() {
|
||||
public int getStorageVersion() throws IOException {
|
||||
return readHeaderInt(HeaderLayout.STORAGE_VERSION_OFFSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDataFormatVersion() {
|
||||
public int getDataFormatVersion() throws IOException {
|
||||
return readHeaderInt(HeaderLayout.DATA_FORMAT_VERSION_OFFSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataFormatVersion(int expectedVersion) {
|
||||
public void setDataFormatVersion(int expectedVersion) throws IOException {
|
||||
putHeaderInt(HeaderLayout.DATA_FORMAT_VERSION_OFFSET, expectedVersion);
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ public final class StreamlinedBlobStorageOverMMappedFile extends StreamlinedBlob
|
||||
}
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
public long sizeInBytes() throws IOException {
|
||||
return actualLength();
|
||||
}
|
||||
|
||||
@@ -556,30 +556,30 @@ public final class StreamlinedBlobStorageOverMMappedFile extends StreamlinedBlob
|
||||
|
||||
// === storage header accessors: ===
|
||||
|
||||
private int readHeaderInt(int offset) {
|
||||
private int readHeaderInt(int offset) throws IOException {
|
||||
assert (0 <= offset && offset <= HeaderLayout.HEADER_SIZE - Integer.BYTES)
|
||||
: "header offset(=" + offset + ") must be in [0," + (HeaderLayout.HEADER_SIZE - Integer.BYTES) + "]";
|
||||
return headerPage.rawPageBuffer().getInt(offset);
|
||||
return headerPage().rawPageBuffer().getInt(offset);
|
||||
}
|
||||
|
||||
private void putHeaderInt(int offset,
|
||||
int value) {
|
||||
int value) throws IOException {
|
||||
assert (0 <= offset && offset <= HeaderLayout.HEADER_SIZE - Integer.BYTES)
|
||||
: "header offset(=" + offset + ") must be in [0," + (HeaderLayout.HEADER_SIZE - Integer.BYTES) + "]";
|
||||
headerPage.rawPageBuffer().putInt(offset, value);
|
||||
headerPage().rawPageBuffer().putInt(offset, value);
|
||||
}
|
||||
|
||||
private long readHeaderLong(int offset) {
|
||||
private long readHeaderLong(int offset) throws IOException {
|
||||
assert (0 <= offset && offset <= HeaderLayout.HEADER_SIZE - Long.BYTES)
|
||||
: "header offset(=" + offset + ") must be in [0," + (HeaderLayout.HEADER_SIZE - Long.BYTES) + "]";
|
||||
return headerPage.rawPageBuffer().getLong(offset);
|
||||
return headerPage().rawPageBuffer().getLong(offset);
|
||||
}
|
||||
|
||||
private void putHeaderLong(int offset,
|
||||
long value) {
|
||||
long value) throws IOException {
|
||||
assert (0 <= offset && offset <= HeaderLayout.HEADER_SIZE - Long.BYTES)
|
||||
: "header offset(=" + offset + ") must be in [0," + (HeaderLayout.HEADER_SIZE - Long.BYTES) + "]";
|
||||
headerPage.rawPageBuffer().putLong(offset, value);
|
||||
headerPage().rawPageBuffer().putLong(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -587,33 +587,36 @@ public final class StreamlinedBlobStorageOverMMappedFile extends StreamlinedBlob
|
||||
* File size is almost always larger than that because {@link MMappedFileStorage} pre-allocates each page
|
||||
* in advance.
|
||||
*/
|
||||
private long actualLength() {
|
||||
private long actualLength() throws IOException {
|
||||
return idToOffset(nextRecordId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int nextRecordId() {
|
||||
if (headerPage == null) {
|
||||
throw new AlreadyDisposedException("Storage is closed");
|
||||
}
|
||||
protected int nextRecordId() throws IOException {
|
||||
Page headerPage = headerPage();
|
||||
ByteBuffer headerBuffer = headerPage.rawPageBuffer();
|
||||
return (int)INT_HANDLE.getVolatile(headerBuffer, HeaderLayout.NEXT_RECORD_ID_OFFSET);
|
||||
}
|
||||
|
||||
//@GuardedBy(this)
|
||||
@Override
|
||||
protected void updateNextRecordId(int nextRecordId) {
|
||||
Page _headerPage = headerPage;
|
||||
if (_headerPage == null) {
|
||||
throw new AlreadyDisposedException("Storage is closed");
|
||||
}
|
||||
protected void updateNextRecordId(int nextRecordId) throws IOException {
|
||||
if (nextRecordId <= NULL_ID) {
|
||||
throw new IllegalArgumentException("nextRecordId(=" + nextRecordId + ") must be >0");
|
||||
}
|
||||
ByteBuffer headerBuffer = _headerPage.rawPageBuffer();
|
||||
Page headerPage = headerPage();
|
||||
ByteBuffer headerBuffer = headerPage.rawPageBuffer();
|
||||
INT_HANDLE.setVolatile(headerBuffer, HeaderLayout.NEXT_RECORD_ID_OFFSET, nextRecordId);
|
||||
}
|
||||
|
||||
private @NotNull Page headerPage() throws ClosedStorageException {
|
||||
Page _headerPage = this.headerPage;
|
||||
if (_headerPage == null) {
|
||||
throw new ClosedStorageException("Storage is closed");
|
||||
}
|
||||
return _headerPage;
|
||||
}
|
||||
|
||||
// === storage records accessors: ===
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.openapi.vfs.newvfs.persistent.dev.blobstorage;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.IntRef;
|
||||
import com.intellij.util.io.ClosedStorageException;
|
||||
import com.intellij.util.io.DirectBufferWrapper;
|
||||
import com.intellij.util.io.PagedFileStorage;
|
||||
import com.intellij.util.io.blobstorage.ByteBufferReader;
|
||||
@@ -583,7 +584,8 @@ public final class StreamlinedBlobStorageOverPagedStorage extends StreamlinedBlo
|
||||
|
||||
|
||||
@Override
|
||||
public long sizeInBytes() {
|
||||
public long sizeInBytes() throws ClosedStorageException {
|
||||
checkNotClosed();
|
||||
return pagedStorage.length();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.openapi.vfs.newvfs.impl.heavy.blobstorage;
|
||||
|
||||
import com.intellij.openapi.util.IntRef;
|
||||
import com.intellij.openapi.vfs.newvfs.persistent.dev.blobstorage.StreamlinedBlobStorageHelper;
|
||||
import com.intellij.util.io.ClosedStorageException;
|
||||
import com.intellij.util.io.blobstorage.SpaceAllocationStrategy;
|
||||
import com.intellij.util.io.blobstorage.SpaceAllocationStrategy.DataLengthPlusFixedPercentStrategy;
|
||||
import com.intellij.util.io.blobstorage.SpaceAllocationStrategy.WriterDecidesStrategy;
|
||||
@@ -369,11 +370,39 @@ public abstract class StreamlinedBlobStorageTestBase<S extends StreamlinedBlobSt
|
||||
storage.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterClose_storageAccessorsMustThrowException() throws IOException {
|
||||
storage.close();
|
||||
assertThrows("Storage must throw exception after close()",
|
||||
ClosedStorageException.class,
|
||||
() -> storage.sizeInBytes()
|
||||
);
|
||||
assertThrows("Storage must throw exception after close()",
|
||||
ClosedStorageException.class,
|
||||
() -> storage.getStorageVersion()
|
||||
);
|
||||
assertThrows("Storage must throw exception after close()",
|
||||
ClosedStorageException.class,
|
||||
() -> storage.getDataFormatVersion()
|
||||
);
|
||||
assertThrows("Storage must throw exception after close()",
|
||||
ClosedStorageException.class,
|
||||
() -> storage.liveRecordsCount()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterClose_toStringIsStillSafeToCall() throws IOException {
|
||||
storage.close();
|
||||
assertNotNull("Ensure .toString() could be called after .close()",
|
||||
storage.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void afterCloseAndClean_noFilesRemain() throws IOException {
|
||||
storage.closeAndClean();
|
||||
assertFalse(
|
||||
"No ["+storagePath+"] must remain after .closeAndClean()",
|
||||
"No [" + storagePath + "] must remain after .closeAndClean()",
|
||||
Files.exists(storagePath)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.intellij.util.io.blobstorage;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides access to storage internal statistics.
|
||||
* If storage provides access to such a statistics -- it must implement this interface
|
||||
@@ -11,16 +13,16 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
public interface BlobStorageStatistics {
|
||||
|
||||
/** records currently alive (=allocated-relocated-deleted) */
|
||||
int liveRecordsCount();
|
||||
int liveRecordsCount() throws IOException;
|
||||
|
||||
/** records allocated since storage creation (including deleted/relocated) */
|
||||
int recordsAllocated();
|
||||
int recordsAllocated() throws IOException;
|
||||
|
||||
/** records re-allocated -- i.e. recordId is accessible, but the access redirects to another (actual) record */
|
||||
int recordsRelocated();
|
||||
int recordsRelocated() throws IOException;
|
||||
|
||||
/** records deleted, i.e. un-accessible anymore */
|
||||
int recordsDeleted();
|
||||
int recordsDeleted() throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
@@ -28,11 +30,11 @@ public interface BlobStorageStatistics {
|
||||
* Includes all the overhead of file/records header, alignment, etc.
|
||||
* Could be <= file size, if e.g. file is expanded in advance.
|
||||
*/
|
||||
long sizeInBytes();
|
||||
long sizeInBytes() throws IOException;
|
||||
|
||||
/** Total size of all alive records' payload, i.e. 'useful size', without any overhead */
|
||||
long totalLiveRecordsPayloadBytes();
|
||||
long totalLiveRecordsPayloadBytes() throws IOException;
|
||||
|
||||
/** Total size of all alive records' capacity */
|
||||
long totalLiveRecordsCapacityBytes();
|
||||
long totalLiveRecordsCapacityBytes() throws IOException;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public interface StreamlinedBlobStorage extends Closeable, AutoCloseable, Forcea
|
||||
|
||||
boolean isRecordActual(int recordActualLength);
|
||||
|
||||
int liveRecordsCount();
|
||||
int liveRecordsCount() throws IOException;
|
||||
|
||||
/**
|
||||
* Total size of data in a storage -- including metadata, reserved and deleted
|
||||
@@ -161,7 +161,7 @@ public interface StreamlinedBlobStorage extends Closeable, AutoCloseable, Forcea
|
||||
* Not guaranteed to be == actual file size on disk -- disk file could be pre-allocated
|
||||
* in advance.
|
||||
*/
|
||||
long sizeInBytes();
|
||||
long sizeInBytes() throws IOException;
|
||||
|
||||
@Override
|
||||
boolean isDirty();
|
||||
|
||||
Reference in New Issue
Block a user