org.sqlite.core.SafeStmtPtr Maven / Gradle / Ivy
package org.sqlite.core;
import java.sql.SQLException;
/**
* A class for safely wrapping calls to a native pointer to a statement, ensuring no other thread
* has access to the pointer while it is run
*/
public class SafeStmtPtr {
// store a reference to the DB, to lock it before any safe function is called. This avoids
// deadlocking by locking the DB. All calls with the raw pointer are synchronized with the DB
// anyways, so making a separate lock would be pointless
private final DB db;
private final long ptr;
private volatile boolean closed = false;
// to return on subsequent calls to close() after this ptr has been closed
private int closedRC;
// to throw on subsequent calls to close after this ptr has been closed, if the close function
// threw an exception
private SQLException closeException;
/**
* Construct a new Safe Pointer Wrapper to ensure a pointer is properly handled
*
* @param db the database that made this pointer. Always locked before any safe run function is
* executed to avoid deadlocks
* @param ptr the raw pointer
*/
public SafeStmtPtr(DB db, long ptr) {
this.db = db;
this.ptr = ptr;
}
/**
* Check whether this pointer has been closed
*
* @return whether this pointer has been closed
*/
public boolean isClosed() {
return closed;
}
/**
* Close this pointer
*
* @return the return code of the close callback function
* @throws SQLException if the close callback throws an SQLException, or the pointer is locked
* elsewhere
*/
public int close() throws SQLException {
synchronized (db) {
return internalClose();
}
}
private int internalClose() throws SQLException {
try {
// if this is already closed, return or throw the previous result
if (closed) {
if (closeException != null) throw closeException;
return closedRC;
}
closedRC = db.finalize(this, ptr);
return closedRC;
} catch (SQLException ex) {
this.closeException = ex;
throw ex;
} finally {
this.closed = true;
}
}
/**
* Run a callback with the wrapped pointer safely.
*
* @param run the function to run
* @return the return of the passed in function
* @throws SQLException if the pointer is utilized elsewhere
*/
public int safeRunInt(SafePtrIntFunction run) throws SQLException, E {
synchronized (db) {
this.ensureOpen();
return run.run(db, ptr);
}
}
/**
* Run a callback with the wrapped pointer safely.
*
* @param run the function to run
* @return the return of the passed in function
* @throws SQLException if the pointer is utilized elsewhere
*/
public long safeRunLong(SafePtrLongFunction run)
throws SQLException, E {
synchronized (db) {
this.ensureOpen();
return run.run(db, ptr);
}
}
/**
* Run a callback with the wrapped pointer safely.
*
* @param run the function to run
* @return the return of the passed in function
* @throws SQLException if the pointer is utilized elsewhere
*/
public double safeRunDouble(SafePtrDoubleFunction run)
throws SQLException, E {
synchronized (db) {
this.ensureOpen();
return run.run(db, ptr);
}
}
/**
* Run a callback with the wrapped pointer safely.
*
* @param run the function to run
* @return the return code of the function
* @throws SQLException if the pointer is utilized elsewhere
*/
public T safeRun(SafePtrFunction run) throws SQLException, E {
synchronized (db) {
this.ensureOpen();
return run.run(db, ptr);
}
}
/**
* Run a callback with the wrapped pointer safely.
*
* @param run the function to run
* @throws SQLException if the pointer is utilized elsewhere
*/
public void safeRunConsume(SafePtrConsumer run)
throws SQLException, E {
synchronized (db) {
this.ensureOpen();
run.run(db, ptr);
}
}
private void ensureOpen() throws SQLException {
if (this.closed) {
throw new SQLException("stmt pointer is closed");
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SafeStmtPtr that = (SafeStmtPtr) o;
return ptr == that.ptr;
}
@Override
public int hashCode() {
return Long.hashCode(ptr);
}
@FunctionalInterface
public interface SafePtrIntFunction {
int run(DB db, long ptr) throws E;
}
@FunctionalInterface
public interface SafePtrLongFunction {
long run(DB db, long ptr) throws E;
}
@FunctionalInterface
public interface SafePtrDoubleFunction {
double run(DB db, long ptr) throws E;
}
@FunctionalInterface
public interface SafePtrFunction {
T run(DB db, long ptr) throws E;
}
@FunctionalInterface
public interface SafePtrConsumer {
void run(DB db, long ptr) throws E;
}
}