mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-4341 Force retained checkout if file is in-use during checkin or add-to-version-control. Deprecated upgrade concept during checkin. Revised manner in which file open for update is updated following a version control operation (perform DBHandle update).
This commit is contained in:
parent
74a5b6f0e1
commit
2dff876f0f
46 changed files with 695 additions and 852 deletions
|
@ -18,7 +18,6 @@ package db;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Iterator;
|
||||
|
||||
import db.buffers.*;
|
||||
import db.util.ErrorHandler;
|
||||
|
@ -439,8 +438,7 @@ public class DBHandle {
|
|||
* @throws IllegalStateException if transaction is already active or this {@link DBHandle} has
|
||||
* already been closed.
|
||||
*/
|
||||
public Transaction openTransaction(ErrorHandler errorHandler)
|
||||
throws IllegalStateException {
|
||||
public Transaction openTransaction(ErrorHandler errorHandler) throws IllegalStateException {
|
||||
return new Transaction() {
|
||||
|
||||
long txId = startTransaction();
|
||||
|
@ -542,6 +540,33 @@ public class DBHandle {
|
|||
return (bufferMgr != null && !bufferMgr.atCheckpoint());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB source buffer file with a newer local buffer file version.
|
||||
* Intended for use following a merge or commit operation only where a local checkout has been
|
||||
* retained.
|
||||
* @param versionedSourceBufferFile updated local DB source buffer file opened for versioning
|
||||
* update (NOTE: file itself is read-only). File must be an instance of
|
||||
* {@link LocalManagedBufferFile}.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void setDBVersionedSourceFile(BufferFile versionedSourceBufferFile) throws IOException {
|
||||
if (!(versionedSourceBufferFile instanceof LocalManagedBufferFile bf) ||
|
||||
!versionedSourceBufferFile.isReadOnly()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Requires local versioned buffer file opened for versioning update");
|
||||
}
|
||||
synchronized (this) {
|
||||
if (isTransactionActive()) {
|
||||
throw new IOException("transaction is active");
|
||||
}
|
||||
bufferMgr.clearRecoveryFiles();
|
||||
bufferMgr.setDBVersionedSourceFile(bf);
|
||||
++checkpointNum;
|
||||
reloadTables();
|
||||
}
|
||||
notifyDbRestored();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate current transaction. If commit is false a rollback may occur followed by
|
||||
* {@link DBListener#dbRestored(DBHandle)} notification to listeners. This method is very
|
||||
|
@ -1022,10 +1047,9 @@ public class DBHandle {
|
|||
public Table[] getTables() {
|
||||
Table[] t = new Table[tables.size()];
|
||||
|
||||
Iterator<Table> it = tables.values().iterator();
|
||||
int i = 0;
|
||||
while (it.hasNext()) {
|
||||
t[i++] = it.next();
|
||||
for (Table element : tables.values()) {
|
||||
t[i++] = element;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
@ -1050,8 +1074,7 @@ public class DBHandle {
|
|||
* @return new table instance
|
||||
* @throws IOException if IO error occurs during table creation
|
||||
*/
|
||||
public Table createTable(String name, Schema schema, int[] indexedColumns)
|
||||
throws IOException {
|
||||
public Table createTable(String name, Schema schema, int[] indexedColumns) throws IOException {
|
||||
Table table;
|
||||
synchronized (this) {
|
||||
if (tables.containsKey(name)) {
|
||||
|
|
|
@ -223,6 +223,22 @@ public class BufferMgr {
|
|||
approxCacheSize < MINIMUM_CACHE_SIZE ? MINIMUM_CACHE_SIZE : approxCacheSize;
|
||||
maxCacheSize = (int) (approxCacheSize / bufferSize);
|
||||
|
||||
// Setup baseline - checkpoint 0
|
||||
startCheckpoint();
|
||||
baselineCheckpointHead = currentCheckpointHead;
|
||||
currentCheckpointHead = null;
|
||||
|
||||
initializeCache();
|
||||
|
||||
addInstance(this);
|
||||
}
|
||||
|
||||
private void initializeCache() throws IOException {
|
||||
|
||||
if (cacheFile != null) {
|
||||
cacheFile.delete();
|
||||
}
|
||||
|
||||
// Setup memory cache list
|
||||
cacheHead = new BufferNode(HEAD, -1);
|
||||
cacheTail = new BufferNode(TAIL, -1);
|
||||
|
@ -234,11 +250,6 @@ public class BufferMgr {
|
|||
|
||||
cacheIndexProvider = new IndexProvider();
|
||||
|
||||
// Setup baseline - checkpoint 0
|
||||
startCheckpoint();
|
||||
baselineCheckpointHead = currentCheckpointHead;
|
||||
currentCheckpointHead = null;
|
||||
|
||||
// Copy file parameters into cache file
|
||||
if (sourceFile != null) {
|
||||
String[] parmNames = sourceFile.getParameterNames();
|
||||
|
@ -247,8 +258,6 @@ public class BufferMgr {
|
|||
}
|
||||
}
|
||||
|
||||
addInstance(this);
|
||||
|
||||
if (alwaysPreCache) {
|
||||
startPreCacheIfNeeded();
|
||||
}
|
||||
|
@ -417,9 +426,7 @@ public class BufferMgr {
|
|||
|
||||
// Dispose all buffer nodes - speeds up garbage collection
|
||||
if (checkpointHeads != null) {
|
||||
Iterator<BufferNode> iter = checkpointHeads.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BufferNode node = iter.next();
|
||||
for (BufferNode node : checkpointHeads) {
|
||||
while (node != null) {
|
||||
BufferNode next = node.nextInCheckpoint;
|
||||
node.buffer = null;
|
||||
|
@ -435,9 +442,7 @@ public class BufferMgr {
|
|||
checkpointHeads = null;
|
||||
}
|
||||
if (redoCheckpointHeads != null) {
|
||||
Iterator<BufferNode> iter = redoCheckpointHeads.iterator();
|
||||
while (iter.hasNext()) {
|
||||
BufferNode node = iter.next();
|
||||
for (BufferNode node : redoCheckpointHeads) {
|
||||
while (node != null) {
|
||||
BufferNode next = node.nextInCheckpoint;
|
||||
node.buffer = null;
|
||||
|
@ -702,24 +707,7 @@ public class BufferMgr {
|
|||
return; // only pre-cache remote buffer files
|
||||
}
|
||||
synchronized (preCacheLock) {
|
||||
preCacheThread = new Thread(() -> {
|
||||
try {
|
||||
preCacheSourceFile();
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
synchronized (preCacheLock) {
|
||||
preCacheStatus = PreCacheStatus.STOPPED;
|
||||
preCacheThread = null;
|
||||
preCacheLock.notifyAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
preCacheThread = new Thread(() -> preCacheSourceFile());
|
||||
preCacheThread.setName("Pre-Cache");
|
||||
preCacheThread.setPriority(Thread.MIN_PRIORITY);
|
||||
preCacheThread.start();
|
||||
|
@ -731,23 +719,38 @@ public class BufferMgr {
|
|||
* Pre-cache source file into cache file. This is intended to be run in a
|
||||
* dedicated thread when the source file is remote.
|
||||
*/
|
||||
private void preCacheSourceFile() throws IOException {
|
||||
if (!(sourceFile instanceof BufferFileAdapter)) {
|
||||
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache started...");
|
||||
int cacheCount = 0;
|
||||
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
|
||||
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
|
||||
BufferFileBlock block;
|
||||
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
|
||||
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
|
||||
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
|
||||
++cacheCount;
|
||||
}
|
||||
private void preCacheSourceFile() {
|
||||
try {
|
||||
if (!(sourceFile instanceof BufferFileAdapter)) {
|
||||
throw new UnsupportedOperationException("unsupported use of preCacheSourceFile");
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache started...");
|
||||
int cacheCount = 0;
|
||||
BufferFileAdapter sourceAdapter = (BufferFileAdapter) sourceFile;
|
||||
try (InputBlockStream inputBlockStream = sourceAdapter.getInputBlockStream()) {
|
||||
BufferFileBlock block;
|
||||
while (!Thread.interrupted() && (block = inputBlockStream.readBlock()) != null) {
|
||||
DataBuffer buf = LocalBufferFile.getDataBuffer(block);
|
||||
if (buf != null && !buf.isEmpty() && preCacheBuffer(buf)) { // skip head block and empty blocks
|
||||
++cacheCount;
|
||||
}
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
|
||||
sourceFile.getIndexCount() + " buffers to cache");
|
||||
}
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(BufferMgr.this, "pre-cache failure: " + e.getMessage(), e);
|
||||
}
|
||||
finally {
|
||||
synchronized (preCacheLock) {
|
||||
preCacheStatus = PreCacheStatus.STOPPED;
|
||||
preCacheThread = null;
|
||||
preCacheLock.notifyAll();
|
||||
}
|
||||
Msg.trace(BufferMgr.this, "Pre-cache added " + cacheCount + " of " +
|
||||
sourceFile.getIndexCount() + " buffers to cache");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1744,6 +1747,51 @@ public class BufferMgr {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source buffer file with a newer local buffer file version.
|
||||
* Intended for use following a merge or commit operation only where a local checkout has been
|
||||
* retained.
|
||||
* @param versionedSourceBufferFile updated local source buffer file opened for versioning
|
||||
* update (NOTE: file itself is read-only).
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public void setDBVersionedSourceFile(LocalManagedBufferFile versionedSourceBufferFile)
|
||||
throws IOException {
|
||||
|
||||
synchronized (snapshotLock) {
|
||||
synchronized (this) {
|
||||
if (!(sourceFile instanceof LocalManagedBufferFile)) {
|
||||
throw new UnsupportedOperationException(getClass().getSimpleName() +
|
||||
".setDBSourceFile not allowed: " + sourceFile.getClass());
|
||||
}
|
||||
if (bufferSize != sourceFile.getBufferSize()) {
|
||||
throw new IllegalArgumentException("Buffer size mismatch");
|
||||
}
|
||||
|
||||
if (corruptedState) {
|
||||
throw new IOException("Corrupted BufferMgr state");
|
||||
}
|
||||
|
||||
if (lockCount != 0) {
|
||||
throw new IOException("Attempted checkout update while buffers are locked");
|
||||
}
|
||||
|
||||
stopPreCache();
|
||||
|
||||
clearCheckpoints();
|
||||
|
||||
doSetSourceFile(versionedSourceBufferFile);
|
||||
|
||||
// re-initialize cached file data
|
||||
int cnt = sourceFile.getIndexCount();
|
||||
indexProvider = new IndexProvider(cnt, sourceFile.getFreeIndexes());
|
||||
bufferTable = new ObjectArray(cnt + INITIAL_BUFFER_TABLE_SIZE);
|
||||
|
||||
initializeCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current set of buffers to a new version of the source buffer file.
|
||||
* If the buffer manager was not instantiated with a source file an
|
||||
|
@ -1825,7 +1873,7 @@ public class BufferMgr {
|
|||
monitor.setCancelEnabled(oldCancelState & !monitor.isCancelled());
|
||||
}
|
||||
|
||||
setSourceFile(outFile);
|
||||
doSetSourceFile(outFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1881,7 +1929,7 @@ public class BufferMgr {
|
|||
monitor.setCancelEnabled(true);
|
||||
}
|
||||
if (associateWithNewFile) {
|
||||
setSourceFile(outFile);
|
||||
doSetSourceFile(outFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1965,7 +2013,7 @@ public class BufferMgr {
|
|||
}
|
||||
}
|
||||
|
||||
private void setSourceFile(BufferFile newFile) {
|
||||
private void doSetSourceFile(BufferFile newFile) {
|
||||
|
||||
// Close buffer file
|
||||
if (sourceFile != null) {
|
||||
|
|
|
@ -157,7 +157,6 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
|||
* <code>preSaveThread</code> corresponds to the PreSaveTask which creates the
|
||||
* preSaveFile when this buffer file is updateable.
|
||||
*/
|
||||
//private Thread preSaveThread;
|
||||
private PreSaveTask preSaveTask;
|
||||
|
||||
/**
|
||||
|
@ -228,8 +227,8 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
|||
setFreeIndexes(versionFileHandler.getFreeIndexList());
|
||||
String[] names = versionFileHandler.getOldParameterNames();
|
||||
clearParameters();
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
setParameter(names[i], versionFileHandler.getOldParameter(names[i]));
|
||||
for (String name : names) {
|
||||
setParameter(name, versionFileHandler.getOldParameter(name));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue