diff src/org/dancres/blitz/EntryTxnOp.java @ 0:3dc0c5604566

Initial checkin of blitz 2.0 fcs - no installer yet.
author Dan Creswell <dan.creswell@gmail.com>
date Sat, 21 Mar 2009 11:00:06 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/dancres/blitz/EntryTxnOp.java	Sat Mar 21 11:00:06 2009 +0000
@@ -0,0 +1,151 @@
+package org.dancres.blitz;
+
+import java.io.IOException;
+
+import org.dancres.blitz.mangler.MangledEntry;
+
+import org.dancres.blitz.entry.OpInfo;
+
+import org.dancres.blitz.txn.TxnOp;
+import org.dancres.blitz.txn.TxnState;
+
+import org.dancres.blitz.txnlock.*;
+
+import org.dancres.blitz.notify.QueueEvent;
+import org.dancres.blitz.notify.EventQueue;
+
+/**
+   Each operation against an Entry is represented by an instance of this class.
+   These are stored in <code>TxnState</code> instances and constitute part of
+   the record of operations performed by a particular transaction.
+
+   @see org.dancres.blitz.txn.TxnState
+
+   @todo As postEvent happens before txnlock is released one can get
+   temporary conflicts which could be alleviated if txnlock was released first
+   but to do this would mean needing a snapshot of txnlock state from before
+   the release was performed.
+ */
+class EntryTxnOp implements TxnOp {
+    static final long serialVersionUID = -347168809827671347L;
+
+    /**
+       Required when we figure out what lock type to assert.
+       Lock is transient because it's a memory only structure so we will
+       have to recover it if necessary.
+    */
+    private int theOp;
+    private OpInfo theInfo;
+
+    private transient TxnLock theTxnLock;
+
+    EntryTxnOp(OpInfo anInfo) {
+        theInfo = anInfo;
+    }
+
+    EntryTxnOp(int anOp, OpInfo anInfo, TxnLock aLock) {
+        theOp = anOp;
+        theInfo = anInfo;
+        theTxnLock = aLock;
+    }
+
+    public void restore(TxnState aState) throws IOException {
+        if (theInfo.isDebugOp())
+            return;
+
+        theInfo.restore();
+
+        LockMgr myMgr = TxnLocks.getLockMgr(theInfo.getType());
+        theTxnLock = myMgr.getLock(theInfo.getOID());
+
+        synchronized(theTxnLock) {
+            theTxnLock.acquire(aState, theOp, null, null, true);
+        }
+    }
+
+    public void commit(TxnState aState) throws IOException {
+        if (theInfo.isDebugOp())
+            return;
+
+        MangledEntry myEntry = theInfo.commit(aState);
+
+        postEvent(aState, myEntry, true);
+
+        theTxnLock.release(aState, theOp);
+    }
+
+    public void abort(TxnState aState) throws IOException {
+        if (theInfo.isDebugOp())
+            return;
+
+        MangledEntry myEntry = theInfo.abort(aState);
+
+        postEvent(aState, myEntry, false);
+
+        theTxnLock.release(aState, theOp);
+    }
+
+    public String toString() {
+        return theInfo.toString();
+    }
+
+    private void postEvent(TxnState aState, MangledEntry anEntry,
+                           boolean isCommit) {
+
+        // No Entry means no Event - if Entry is present, we then need to
+        // figure out whether or not we generate an event based on commit/abort
+        // and the kind of operation
+        if (anEntry == null)
+            return;
+
+        switch (theOp) {
+            case TxnLock.READ : {
+                if (theTxnLock.hasOnly(aState.getId(), TxnLock.READ)) {
+                    // Entry still exists and we are the last outstanding
+                    // read - which means we resolve a conflict
+                    QueueEvent myEvent =
+                        new QueueEvent(QueueEvent.ENTRY_NOT_CONFLICTED, aState,
+                                       new QueueEvent.Context(anEntry,
+                                           theInfo.getOID()));
+            
+                    EventQueue.get().add(myEvent);        
+                }
+
+                break;
+            }
+
+            case TxnLock.DELETE : {
+                if (!isCommit) {
+                    // We're aborting a take, if we wrote this Entry
+                    // we shouldn't generate an Event
+                    if (!theTxnLock.hasWriter(aState.getId())) {
+                        QueueEvent myEvent =
+                            new QueueEvent(QueueEvent.ENTRY_VISIBLE, aState,
+                                new QueueEvent.Context(anEntry,
+                                    theInfo.getOID()));
+
+                        EventQueue.get().add(myEvent);
+                    }
+                }
+                break;
+            }
+
+            /**
+             * If we've been doing a write, the Entry is pinned and cannot
+             * be flushed from cache thus even if it becomes lease expired
+             * it will be present in cache when we query it.
+             */
+            case TxnLock.WRITE : {
+                if (isCommit) {
+                    // If we're commiting a write, an Entry became visible
+                    QueueEvent myEvent =
+                        new QueueEvent(QueueEvent.ENTRY_WRITTEN,
+                            aState,
+                            new QueueEvent.Context(anEntry, theInfo.getOID()));
+                    EventQueue.get().add(myEvent);
+                }
+                break;
+            }
+        }
+    }
+}