Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java')
-rw-r--r--org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java230
1 files changed, 230 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
new file mode 100644
index 0000000000..eebbbfbc73
--- /dev/null
+++ b/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (c) 2004 - 2009 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.cdo.internal.server;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.spi.server.InternalRepository;
+
+import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
+import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
+import org.eclipse.net4j.util.om.monitor.OMMonitor;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Bugzilla 297940, 290032
+ *
+ * @author Caspar De Groot
+ */
+class TimeStampAuthority
+{
+ private InternalRepository repository;
+
+ /**
+ * Holds the timestamp that was issued in response to the last call to {@link #createTimestamp()}
+ */
+ @ExcludeFromDump
+ private transient long lastIssuedTimeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
+
+ /**
+ * Holds the timestamp that was last reported finished by a call to {@link #endCommit(long)}
+ */
+ private long lastFinishedTimeStamp;
+
+ private LastCommitTimeStampLock lastCommitTimeStampLock = new LastCommitTimeStampLock();
+
+ private boolean strictOrdering; // TODO (CD) Should be a repo property
+
+ /**
+ * A lock to block on if strict commit ordering is enabled
+ */
+ private StrictOrderingLock strictOrderingLock = new StrictOrderingLock();
+
+ /**
+ * An ordered list of timestamps that have been issued but have not (yet) been reported finished. (It is ordered
+ * because the timestamps are added sequentially.)
+ */
+ private List<Long> runningTransactions = new LinkedList<Long>();
+
+ /**
+ * A set of timestamps that have been reported finished but have not yet been
+ */
+ private SortedSet<Long> finishedTransactions = new TreeSet<Long>();
+
+ TimeStampAuthority(InternalRepository repository)
+ {
+ this.repository = repository;
+ }
+
+ synchronized long[] startCommit(OMMonitor monitor)
+ {
+ return startCommit(CDOBranchPoint.UNSPECIFIED_DATE, monitor);
+ }
+
+ synchronized long[] startCommit(long timeStampOverride, OMMonitor monitor)
+ {
+ monitor.begin();
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.lock();
+ }
+
+ try
+ {
+ long now = repository.getTimeStamp();
+ if (lastIssuedTimeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ while (lastIssuedTimeStamp == now)
+ {
+ ConcurrencyUtil.sleep(1);
+ now = repository.getTimeStamp();
+ monitor.checkCanceled();
+ }
+ }
+
+ if (timeStampOverride != CDOBranchPoint.UNSPECIFIED_DATE)
+ {
+ now = timeStampOverride;
+ }
+
+ lastIssuedTimeStamp = now;
+
+ runningTransactions.add(lastIssuedTimeStamp);
+ return new long[] { lastIssuedTimeStamp, getLastFinishedTimeStamp() };
+ }
+ finally
+ {
+ monitor.done();
+ }
+ }
+
+ synchronized void endCommit(long timeStamp)
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot end transaction with unknown timestamp " + timeStamp);
+ }
+
+ finishedTransactions.add(timeStamp);
+
+ // We can remove a timestamp from finishedTransactions if it is smaller (i.e. older) than any
+ // of the runningTransactions. Since both sets are sorted, we only need to compare the heads.
+ long oldestRunning = runningTransactions.isEmpty() ? Long.MAX_VALUE : runningTransactions.get(0);
+ long oldestFinished;
+ synchronized (lastCommitTimeStampLock)
+ {
+ long oldValue = lastFinishedTimeStamp;
+ while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning)
+ {
+ finishedTransactions.remove(oldestFinished);
+ lastFinishedTimeStamp = oldestFinished;
+ }
+
+ // If we actually changed the lastFinishedTimeStamp, we need to notify waiting threads
+ if (lastFinishedTimeStamp != oldValue)
+ {
+ lastCommitTimeStampLock.notifyAll();
+ }
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized void failCommit(long timeStamp)
+ {
+ if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) // Exclude problems before TransactionCommitContext.setTimeStamp()
+ {
+ if (!runningTransactions.remove(timeStamp))
+ {
+ throw new IllegalArgumentException("Cannot fail transaction with unknown timestamp " + timeStamp);
+ }
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized long getLastFinishedTimeStamp()
+ {
+ if (lastFinishedTimeStamp != 0)
+ {
+ return lastFinishedTimeStamp;
+ }
+
+ // If we get here, no commit has finished since the server was started
+ if (lastIssuedTimeStamp == 0) // No commit has started either
+ {
+ // We can safely return the current system time minus one milli.
+ return repository.getTimeStamp() - 1;
+ }
+
+ // If we get here, one or more commits are running
+ // We can safely return the start time of the longest-running, minus one milli.
+ return runningTransactions.get(0) - 1;
+ }
+
+ long waitForCommit(long timeout)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ try
+ {
+ lastCommitTimeStampLock.wait(timeout);
+ }
+ catch (Exception ignore)
+ {
+ }
+
+ return lastFinishedTimeStamp;
+ }
+ }
+
+ void setLastFinishedTimeStamp(long lastCommitTimeStamp)
+ {
+ synchronized (lastCommitTimeStampLock)
+ {
+ if (lastFinishedTimeStamp < lastCommitTimeStamp)
+ {
+ lastFinishedTimeStamp = lastCommitTimeStamp;
+ lastCommitTimeStampLock.notifyAll();
+ }
+ }
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class LastCommitTimeStampLock
+ {
+ }
+
+ /**
+ * A separate class for better monitor debugging.
+ *
+ * @author Eike Stepper
+ */
+ private static final class StrictOrderingLock extends ReentrantLock
+ {
+ private static final long serialVersionUID = 1L;
+ }
+}

Back to the top