org.lmdbjava.Txn Maven / Gradle / Ivy
/*-
* #%L
* LmdbJava
* %%
* Copyright (C) 2016 - 2018 The LmdbJava Open Source Project
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.lmdbjava;
import java.util.Comparator;
import static jnr.ffi.Memory.allocateDirect;
import static jnr.ffi.NativeType.ADDRESS;
import jnr.ffi.Pointer;
import static org.lmdbjava.Library.LIB;
import static org.lmdbjava.Library.RUNTIME;
import static org.lmdbjava.MaskedFlag.isSet;
import static org.lmdbjava.MaskedFlag.mask;
import static org.lmdbjava.ResultCodeMapper.checkRc;
import static org.lmdbjava.Txn.State.DONE;
import static org.lmdbjava.Txn.State.READY;
import static org.lmdbjava.Txn.State.RELEASED;
import static org.lmdbjava.Txn.State.RESET;
import static org.lmdbjava.TxnFlags.MDB_RDONLY_TXN;
/**
* LMDB transaction.
*
* @param buffer type
*/
public final class Txn implements AutoCloseable {
private final KeyVal keyVal;
private final Txn parent;
private final BufferProxy proxy;
private final Pointer ptr;
private final boolean readOnly;
private State state;
Txn(final Env env, final Txn parent, final BufferProxy proxy,
final TxnFlags... flags) {
this.proxy = proxy;
this.keyVal = proxy.keyVal();
final int flagsMask = mask(flags);
this.readOnly = isSet(flagsMask, MDB_RDONLY_TXN);
if (env.isReadOnly() && !this.readOnly) {
throw new EnvIsReadOnly();
}
this.parent = parent;
if (parent != null && parent.isReadOnly() != this.readOnly) {
throw new IncompatibleParent();
}
final Pointer txnPtr = allocateDirect(RUNTIME, ADDRESS);
final Pointer txnParentPtr = parent == null ? null : parent.ptr;
checkRc(LIB.mdb_txn_begin(env.pointer(), txnParentPtr, flagsMask, txnPtr));
ptr = txnPtr.getPointer(0);
state = READY;
}
/**
* Aborts this transaction.
*/
public void abort() {
checkReady();
state = DONE;
LIB.mdb_txn_abort(ptr);
}
/**
* Closes this transaction by aborting if not already committed.
*
*
* Closing the transaction will invoke
* {@link BufferProxy#deallocate(java.lang.Object)} for each read-only buffer
* (ie the key and value).
*/
@Override
public void close() {
if (state == RELEASED) {
return;
}
if (state == READY) {
LIB.mdb_txn_abort(ptr);
}
keyVal.close();
state = RELEASED;
}
/**
* Commits this transaction.
*/
public void commit() {
checkReady();
state = DONE;
checkRc(LIB.mdb_txn_commit(ptr));
}
/**
* Return the transaction's ID.
*
* @return A transaction ID, valid if input is an active transaction
*/
public long getId() {
return LIB.mdb_txn_id(ptr);
}
/**
* Obtains this transaction's parent.
*
* @return the parent transaction (may be null)
*/
public Txn getParent() {
return parent;
}
/**
* Whether this transaction is read-only.
*
* @return if read-only
*/
public boolean isReadOnly() {
return readOnly;
}
/**
* Fetch the buffer which holds a read-only view of the LMDI allocated memory.
* Any use of this buffer must comply with the standard LMDB C "mdb_get"
* contract (ie do not modify, do not attempt to release the memory, do not
* use once the transaction or cursor closes, do not use after a write etc).
*
* @return the key buffer (never null)
*/
public T key() {
return keyVal.key();
}
/**
* Renews a read-only transaction previously released by {@link #reset()}.
*/
public void renew() {
if (state != RESET) {
throw new NotResetException();
}
state = DONE;
checkRc(LIB.mdb_txn_renew(ptr));
state = READY;
}
/**
* Aborts this read-only transaction and resets the transaction handle so it
* can be reused upon calling {@link #renew()}.
*/
public void reset() {
checkReadOnly();
if (state != READY && state != DONE) {
throw new ResetException();
}
state = RESET;
LIB.mdb_txn_reset(ptr);
}
/**
* Fetch the buffer which holds a read-only view of the LMDI allocated memory.
* Any use of this buffer must comply with the standard LMDB C "mdb_get"
* contract (ie do not modify, do not attempt to release the memory, do not
* use once the transaction or cursor closes, do not use after a write etc).
*
* @return the value buffer (never null)
*/
public T val() {
return keyVal.val();
}
void checkReadOnly() throws ReadOnlyRequiredException {
if (!readOnly) {
throw new ReadOnlyRequiredException();
}
}
void checkReady() throws NotReadyException {
if (state != READY) {
throw new NotReadyException();
}
}
void checkWritesAllowed() throws ReadWriteRequiredException {
if (readOnly) {
throw new ReadWriteRequiredException();
}
}
Comparator comparator() {
return proxy::compare;
}
/**
* Obtain the buffer proxy.
*
* @return proxy (never null)
*/
BufferProxy getProxy() {
return proxy;
}
/**
* Return the state of the transaction.
*
* @return the state
*/
State getState() {
return state;
}
KeyVal kv() {
return keyVal;
}
KeyVal newKeyVal() {
return proxy.keyVal();
}
Pointer pointer() {
return ptr;
}
/**
* Transaction must abort, has a child, or is invalid.
*/
public static final class BadException extends LmdbNativeException {
static final int MDB_BAD_TXN = -30_782;
private static final long serialVersionUID = 1L;
BadException() {
super(MDB_BAD_TXN, "Transaction must abort, has a child, or is invalid");
}
}
/**
* Invalid reuse of reader locktable slot.
*/
public static final class BadReaderLockException extends LmdbNativeException {
static final int MDB_BAD_RSLOT = -30_783;
private static final long serialVersionUID = 1L;
BadReaderLockException() {
super(MDB_BAD_RSLOT, "Invalid reuse of reader locktable slot");
}
}
/**
* The proposed R-W transaction is incompatible with a R-O Env.
*/
public static class EnvIsReadOnly extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public EnvIsReadOnly() {
super("Read-write Txn incompatible with read-only Env");
}
}
/**
* The proposed transaction is incompatible with its parent transaction.
*/
public static class IncompatibleParent extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public IncompatibleParent() {
super("Transaction incompatible with its parent transaction");
}
}
/**
* Transaction is not in a READY state.
*/
public static final class NotReadyException extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public NotReadyException() {
super("Transaction is not in ready state");
}
}
/**
* The current transaction has not been reset.
*/
public static class NotResetException extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public NotResetException() {
super("Transaction has not been reset");
}
}
/**
* The current transaction is not a read-only transaction.
*/
public static class ReadOnlyRequiredException extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public ReadOnlyRequiredException() {
super("Not a read-only transaction");
}
}
/**
* The current transaction is not a read-write transaction.
*/
public static class ReadWriteRequiredException extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public ReadWriteRequiredException() {
super("Not a read-write transaction");
}
}
/**
* The current transaction has already been reset.
*/
public static class ResetException extends LmdbException {
private static final long serialVersionUID = 1L;
/**
* Creates a new instance.
*/
public ResetException() {
super("Transaction has already been reset");
}
}
/**
* Transaction has too many dirty pages.
*/
public static final class TxFullException extends LmdbNativeException {
static final int MDB_TXN_FULL = -30_788;
private static final long serialVersionUID = 1L;
TxFullException() {
super(MDB_TXN_FULL, "Transaction has too many dirty pages");
}
}
/**
* Transaction states.
*/
enum State {
READY, DONE, RESET, RELEASED
}
}