Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaspar De Groot2011-05-11 09:34:39 +0000
committerCaspar De Groot2011-05-11 09:34:39 +0000
commitb1fb06ac3a16d1f51ac6b986bc8e07602b180534 (patch)
tree9197cebe017a90f4f52fca3c811497be070fee32 /plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
parent057648203be746491fcca57f5760039458656b5c (diff)
downloadcdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.tar.gz
cdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.tar.xz
cdo-b1fb06ac3a16d1f51ac6b986bc8e07602b180534.zip
[Bug 297940] CommitTimestamps are not guaranteed to be strictly ordered
https://bugs.eclipse.org/bugs/show_bug.cgi?id=297940
Diffstat (limited to 'plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java')
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java218
1 files changed, 218 insertions, 0 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
new file mode 100644
index 0000000000..d9a9a0ab0c
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TimeStampAuthority.java
@@ -0,0 +1,218 @@
+/**
+ * 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;
+ }
+
+ long previousIssuedTimeStamp = lastIssuedTimeStamp;
+ lastIssuedTimeStamp = now;
+
+ runningTransactions.add(lastIssuedTimeStamp);
+ return new long[] { lastIssuedTimeStamp, previousIssuedTimeStamp };
+ }
+ 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;
+ while (!finishedTransactions.isEmpty() && (oldestFinished = finishedTransactions.first()) < oldestRunning)
+ {
+ finishedTransactions.remove(oldestFinished);
+ lastFinishedTimeStamp = oldestFinished;
+ }
+
+ if (strictOrdering)
+ {
+ strictOrderingLock.unlock();
+ }
+ }
+
+ synchronized void failCommit(long timeStamp)
+ {
+ 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