All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mapdb.TxEngine Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
package org.mapdb;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Naive implementation of Snapshots on top of StorageEngine.
 * On update it takes old value and stores it aside.
 * 

* TODO merge snapshots down with Storage for best performance * * @author Jan Kotek */ public class TxEngine extends EngineWrapper { protected static final Object TOMBSTONE = new Object(); protected final ReentrantReadWriteLock commitLock = new ReentrantReadWriteLock(CC.FAIR_LOCKS); protected final ReentrantReadWriteLock[] locks = new ReentrantReadWriteLock[CC.CONCURRENCY]; { for(int i=0;i> txs = new LinkedHashSet>(); protected ReferenceQueue txQueue = new ReferenceQueue(); protected final boolean fullTx; protected final Queue preallocRecids; protected final int PREALLOC_RECID_SIZE = 128; protected TxEngine(Engine engine, boolean fullTx) { super(engine); this.fullTx = fullTx; this.preallocRecids = fullTx ? new ArrayBlockingQueue(PREALLOC_RECID_SIZE) : null; } protected Long preallocRecidTake() { assert(commitLock.isWriteLockedByCurrentThread()); Long recid = preallocRecids.poll(); if(recid!=null) return recid; if(uncommitedData) throw new IllegalAccessError("uncommited data"); for(int i=0;i ref = txQueue.poll(); ref!=null; ref=txQueue.poll()){ txs.remove(ref); } } @Override public long preallocate() { commitLock.writeLock().lock(); try { uncommitedData = true; long recid = super.preallocate(); Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,TOMBSTONE); } }finally { lock.unlock(); } return recid; } finally { commitLock.writeLock().unlock(); } } @Override public void preallocate(long[] recids) { commitLock.writeLock().lock(); try { uncommitedData = true; super.preallocate(recids); for(long recid:recids){ Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,TOMBSTONE); } }finally { lock.unlock(); } } } finally { commitLock.writeLock().unlock(); } } @Override public long put(A value, Serializer serializer) { commitLock.readLock().lock(); try { uncommitedData = true; long recid = super.put(value, serializer); Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,TOMBSTONE); } }finally { lock.unlock(); } return recid; } finally { commitLock.readLock().unlock(); } } @Override public A get(long recid, Serializer serializer) { commitLock.readLock().lock(); try { return super.get(recid, serializer); } finally { commitLock.readLock().unlock(); } } @Override public void update(long recid, A value, Serializer serializer) { commitLock.readLock().lock(); try { uncommitedData = true; Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ Object old = get(recid,serializer); for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,old); } super.update(recid, value, serializer); }finally { lock.unlock(); } } finally { commitLock.readLock().unlock(); } } @Override public boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer) { commitLock.readLock().lock(); try { uncommitedData = true; Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ boolean ret = super.compareAndSwap(recid, expectedOldValue, newValue, serializer); if(ret){ for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,expectedOldValue); } } return ret; }finally { lock.unlock(); } } finally { commitLock.readLock().unlock(); } } @Override public void delete(long recid, Serializer serializer) { commitLock.readLock().lock(); try { uncommitedData = true; Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ Object old = get(recid,serializer); for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null) continue; tx.old.putIfAbsent(recid,old); } super.delete(recid, serializer); }finally { lock.unlock(); } } finally { commitLock.readLock().unlock(); } } @Override public void close() { commitLock.writeLock().lock(); try { super.close(); } finally { commitLock.writeLock().unlock(); } } @Override public void commit() { commitLock.writeLock().lock(); try { cleanTxQueue(); super.commit(); uncommitedData = false; } finally { commitLock.writeLock().unlock(); } } @Override public void rollback() { commitLock.writeLock().lock(); try { cleanTxQueue(); super.rollback(); uncommitedData = false; } finally { commitLock.writeLock().unlock(); } } protected void superCommit() { assert(commitLock.isWriteLockedByCurrentThread()); super.commit(); } protected void superUpdate(long recid, A value, Serializer serializer) { assert(commitLock.isWriteLockedByCurrentThread()); super.update(recid,value,serializer); } protected void superDelete(long recid, Serializer serializer) { assert(commitLock.isWriteLockedByCurrentThread()); super.delete(recid,serializer); } protected A superGet(long recid, Serializer serializer) { assert(commitLock.isWriteLockedByCurrentThread()); return super.get(recid,serializer); } public class Tx implements Engine{ protected LongConcurrentHashMap old = new LongConcurrentHashMap(); protected LongConcurrentHashMap mod = fullTx ? new LongConcurrentHashMap() : null; protected Collection usedPreallocatedRecids = fullTx ? new ArrayList() : null; protected final Reference ref = new WeakReference(this,txQueue); protected boolean closed = false; private Store parentEngine; public Tx(){ assert(commitLock.isWriteLockedByCurrentThread()); txs.add(ref); } @Override public long preallocate() { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.writeLock().lock(); try{ Long recid = preallocRecidTake(); usedPreallocatedRecids.add(recid); return recid; }finally { commitLock.writeLock().unlock(); } } @Override public void preallocate(long[] recids) { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.writeLock().lock(); try{ for(int i=0;i long put(A value, Serializer serializer) { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.writeLock().lock(); try{ Long recid = preallocRecidTake(); usedPreallocatedRecids.add(recid); mod.put(recid, Fun.t2(value,serializer)); return recid; }finally { commitLock.writeLock().unlock(); } } @Override public A get(long recid, Serializer serializer) { commitLock.readLock().lock(); try{ if(closed) throw new IllegalAccessError("closed"); Lock lock = locks[Store.lockPos(recid)].readLock(); lock.lock(); try{ return getNoLock(recid, serializer); }finally { lock.unlock(); } }finally { commitLock.readLock().unlock(); } } private A getNoLock(long recid, Serializer serializer) { if(fullTx){ Fun.Tuple2 tu = mod.get(recid); if(tu!=null){ if(tu.a==TOMBSTONE) return null; return (A) tu.a; } } Object oldVal = old.get(recid); if(oldVal!=null){ if(oldVal==TOMBSTONE) return null; return (A) oldVal; } return TxEngine.this.get(recid, serializer); } @Override public void update(long recid, A value, Serializer serializer) { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.readLock().lock(); try{ mod.put(recid, Fun.t2(value,serializer)); }finally { commitLock.readLock().unlock(); } } @Override public boolean compareAndSwap(long recid, A expectedOldValue, A newValue, Serializer serializer) { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.readLock().lock(); try{ Lock lock = locks[Store.lockPos(recid)].writeLock(); lock.lock(); try{ A oldVal = getNoLock(recid, serializer); boolean ret = oldVal!=null && oldVal.equals(expectedOldValue); if(ret){ mod.put(recid,Fun.t2(newValue,serializer)); } return ret; }finally { lock.unlock(); } }finally { commitLock.readLock().unlock(); } } @Override public void delete(long recid, Serializer serializer) { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.readLock().lock(); try{ mod.put(recid,Fun.t2(TOMBSTONE,serializer)); }finally { commitLock.readLock().unlock(); } } @Override public void close() { closed = true; old.clear(); ref.clear(); } @Override public boolean isClosed() { return closed; } @Override public void commit() { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.writeLock().lock(); try{ if(closed) return; if(uncommitedData) throw new IllegalAccessError("uncomitted data"); txs.remove(ref); cleanTxQueue(); if(pojo.hasUnsavedChanges()) pojo.save(this); //check no other TX has modified our data LongMap.LongMapIterator oldIter = old.longMapIterator(); while(oldIter.moveToNext()){ long recid = oldIter.key(); for(Reference ref2:txs){ Tx tx = ref2.get(); if(tx==this||tx==null) continue; if(tx.mod.containsKey(recid)){ close(); throw new TxRollbackException(); } } } LongMap.LongMapIterator iter = mod.longMapIterator(); while(iter.moveToNext()){ long recid = iter.key(); if(old.containsKey(recid)){ close(); throw new TxRollbackException(); } } iter = mod.longMapIterator(); while(iter.moveToNext()){ long recid = iter.key(); Fun.Tuple2 val = iter.value(); Serializer ser = (Serializer) val.b; Object old = superGet(recid,ser); if(old==null) old = TOMBSTONE; for(Reference txr:txs){ Tx tx = txr.get(); if(tx==null||tx==this) continue; tx.old.putIfAbsent(recid,old); } if(val.a==TOMBSTONE){ superDelete(recid, ser); }else { superUpdate(recid, val.a, ser); } } //there are no conflicts, so update the POJO in parent //TODO sort of hack, is it thread safe? getWrappedEngine().getSerializerPojo().registered = pojo.registered; superCommit(); close(); }finally { commitLock.writeLock().unlock(); } } @Override public void rollback() throws UnsupportedOperationException { if(!fullTx) throw new UnsupportedOperationException("read-only"); commitLock.writeLock().lock(); try{ if(closed) return; if(uncommitedData) throw new IllegalAccessError("uncomitted data"); txs.remove(ref); cleanTxQueue(); for(Long prealloc:usedPreallocatedRecids){ TxEngine.this.superDelete(prealloc,null); } TxEngine.this.superCommit(); close(); }finally { commitLock.writeLock().unlock(); } } @Override public boolean isReadOnly() { return !fullTx; } @Override public boolean canRollback() { return fullTx; } @Override public void clearCache() { } @Override public void compact() { } SerializerPojo pojo = new SerializerPojo((CopyOnWriteArrayList) TxEngine.this.getSerializerPojo().registered.clone()); @Override public SerializerPojo getSerializerPojo() { return pojo; } public Engine getWrappedEngine() { return TxEngine.this.getWrappedEngine(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy