From 084375a3c9dd01126d4773c2f1711ec5553487f2 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 4 Oct 2014 19:29:40 -0400 Subject: [PATCH 001/100] bump version to 0.4.3 --- .version | 2 +- README.md | 2 +- concourse/README.md | 4 ++-- concourse/build.gradle | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.version b/.version index 2b7c5ae018..17b2ccd9bf 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.4.2 +0.4.3 diff --git a/README.md b/README.md index 3ae352103a..6d62ee868d 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Concourse runs on Java 1.7. #### Versioning -This is version 0.4.2 of Concourse. +This is version 0.4.3 of Concourse. Concourse will be maintained under the [Semantic Versioning](http://semver.org) guidelines such that release versions will be formatted as `..` diff --git a/concourse/README.md b/concourse/README.md index 334a4677f0..a5bb28915d 100644 --- a/concourse/README.md +++ b/concourse/README.md @@ -12,14 +12,14 @@ The concourse jar is available at [Maven Central](http://search.maven.org/#searc } dependencies { - compile 'org.cinchapi:concourse:0.4.2+' + compile 'org.cinchapi:concourse:0.4.3+' } If you prefer to use another dependency manager like Maven or Ivy, then use the following project information when declaring the dependency: GroupId: org.cinchapi ArtifactId: concourse - Version: 0.4.2+ + Version: 0.4.3+ Alternatively, you can [download](http://cinchapi.org/concourse/download-api) the latest jar and manually add it to your project's classpath. diff --git a/concourse/build.gradle b/concourse/build.gradle index 2aae933725..9553bd0e24 100644 --- a/concourse/build.gradle +++ b/concourse/build.gradle @@ -34,7 +34,7 @@ uploadArchives { authentication(userName: sonatypeUsername, password: sonatypePassword) } - pom.version = '0.4.2' + pom.version = '0.4.3' pom.project { name 'Concourse' From 86a8f8490fade991398416272d57a7466b642c20 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 5 Oct 2014 09:06:00 -0400 Subject: [PATCH 002/100] change VersionExpectation to LockIntention and stop checking for versions on each read or write which should speed things up marginally Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java --- .../server/storage/AtomicOperation.java | 139 +++++++----------- 1 file changed, 55 insertions(+), 84 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index b61ba6dcaf..949061c0d0 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -93,9 +93,9 @@ protected static AtomicOperation start(Compoundable store) { * @param locks */ protected static void prepareLockForPossibleUpgrade( - VersionExpectation expectation, Map locks) { // visible - // for - // testing + LockIntention expectation, Map locks) { // visible + // for + // testing LockType type = expectation.getLockType(); Token token = expectation.getToken(); if(token instanceof RangeToken && type == LockType.RANGE_WRITE) { @@ -144,18 +144,6 @@ else if(token instanceof Token && type == LockType.WRITE } } - /** - * A flag to distinguish the case where we should ignore the version when - * checking that expectations are met (i.e when performing a historical - * read). - *

- * --- We must use a value other than {@link Versioned#NO_VERSION} so that - * we can distinguish the cases where we legitimately need to check that - * there is no version for atomic safety. - *

- */ - private static final long IGNORE_VERSION = Versioned.NO_VERSION - 1; - /** * The initial capacity */ @@ -184,10 +172,10 @@ else if(token instanceof Token && type == LockType.WRITE protected AtomicBoolean open = new AtomicBoolean(true); /** - * The sequence of VersionExpectations that were generated from the sequence + * The sequence of LockIntentions that were generated from the sequence * of operations. */ - private final List expectations = Lists + private final List intentions = Lists .newArrayListWithExpectedSize(INITIAL_CAPACITY); /** @@ -219,10 +207,10 @@ public boolean add(String key, TObject value, long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(key, record), this); - expectations.add(new KeyInRecordVersionExpectation(key, record, - LockType.WRITE)); - expectations.add(new RangeVersionExpectation(Text.wrap(key), Value - .wrap(value))); + intentions + .add(new KeyInRecordLockIntention(key, record, LockType.WRITE)); + intentions + .add(new RangeLockIntention(Text.wrap(key), Value.wrap(value))); return super.add(key, value, record); } @@ -231,7 +219,7 @@ public Map audit(long record) throws AtomicStateException { checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(record), this); - expectations.add(new RecordVersionExpectation(record)); + intentions.add(new RecordLockIntention(record)); return super.audit(record); } @@ -241,8 +229,8 @@ public Map audit(String key, long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(key, record), this); - expectations.add(new KeyInRecordVersionExpectation(key, record, - LockType.READ)); + intentions + .add(new KeyInRecordLockIntention(key, record, LockType.READ)); return super.audit(key, record); } @@ -252,7 +240,7 @@ public Map> browse(long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(record), this); - expectations.add(new RecordVersionExpectation(record)); + intentions.add(new RecordLockIntention(record)); return super.browse(record); } @@ -269,7 +257,7 @@ public Map> browse(String key) checkState(); ((Compoundable) destination).addVersionChangeListener(Token.wrap(key), this); - expectations.add(new KeyVersionExcpectation(key)); + intentions.add(new KeyLockIntention(key)); return super.browse(key); } @@ -322,8 +310,8 @@ public Set fetch(String key, long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(key, record), this); - expectations.add(new KeyInRecordVersionExpectation(key, record, - LockType.READ)); + intentions + .add(new KeyInRecordLockIntention(key, record, LockType.READ)); return super.fetch(key, record); } @@ -348,10 +336,10 @@ public boolean remove(String key, TObject value, long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(key, record), this); - expectations.add(new KeyInRecordVersionExpectation(key, record, - LockType.WRITE)); - expectations.add(new RangeVersionExpectation(Text.wrap(key), Value - .wrap(value))); + intentions + .add(new KeyInRecordLockIntention(key, record, LockType.WRITE)); + intentions + .add(new RangeLockIntention(Text.wrap(key), Value.wrap(value))); return super.remove(key, value, record); } @@ -362,7 +350,6 @@ public Set search(String key, String query) return super.search(key, query); } - @Override public final void start() {} @Override @@ -374,8 +361,8 @@ public boolean verify(String key, TObject value, long record) checkState(); ((Compoundable) destination).addVersionChangeListener( Token.wrap(key, record), this); - expectations.add(new KeyInRecordVersionExpectation(key, record, - LockType.READ)); + intentions + .add(new KeyInRecordLockIntention(key, record, LockType.READ)); return super.verify(key, value, record); } @@ -432,14 +419,14 @@ protected Map> doExplore(String key, Operator operator, .forReading(Text.wrap(key), operator, Transformers .transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class)), this); - expectations.add(new RangeVersionExpectation(Text.wrap(key), operator, + intentions.add(new RangeLockIntention(Text.wrap(key), operator, Transformers.transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class))); return super.doExplore(key, operator, values); } /** - * Check each one of the {@link #expectations} against the + * Check each one of the {@link #intentions} against the * {@link #destination} and grab the appropriate locks along the way. This * method will return {@code true} if all expectations are met and all * necessary locks are grabbed. Otherwise it will return {@code false}, in @@ -451,29 +438,29 @@ protected Map> doExplore(String key, Operator operator, private boolean grabLocks() { locks = Maps.newHashMap(); try { - search: for (VersionExpectation expectation : expectations) { - prepareLockForPossibleUpgrade(expectation, locks); - if(expectation.getLockType() == LockType.RANGE_READ) { + search: for (LockIntention intention : intentions) { + prepareLockForPossibleUpgrade(intention, locks); + if(intention.getLockType() == LockType.RANGE_READ) { // CON-72: Check to see if we already have a RANGE_WRITE // that covers one of the values in this RANGE_READ. If so // skip this lock since the write will adequately block any // conflicting reads - for (Value value : ((RangeToken) expectation.getToken()) + for (Value value : ((RangeToken) intention.getToken()) .getValues()) { RangeToken rt = RangeToken.forWriting( - ((RangeToken) expectation.getToken()).getKey(), + ((RangeToken) intention.getToken()).getKey(), value); if(locks.containsKey(rt)) { continue search; } } } - if(!locks.containsKey(expectation.getToken())) { + if(!locks.containsKey(intention.getToken())) { LockDescription description = LockDescription - .forVersionExpectation(expectation, lockService, + .forVersionExpectation(intention, lockService, rangeLockService); if(description.getLock().tryLock()) { - locks.put(expectation.getToken(), description); + locks.put(intention.getToken(), description); } else { // If we can't grab the lock immediately because it is @@ -532,7 +519,7 @@ protected static final class LockDescription implements Byteable { * @return the LockDescription */ public static LockDescription forVersionExpectation( - VersionExpectation expectation, LockService lockService, + LockIntention expectation, LockService lockService, RangeLockService rangeLockService) { switch (expectation.getLockType()) { case RANGE_READ: @@ -682,13 +669,12 @@ public int size() { } /** - * A VersionExpectation for a read or write that touches a key IN a record + * A LockIntention for a read or write that touches a key IN a record * (i.e. fetch, verify, etc). * * @author jnelson */ - private final class KeyInRecordVersionExpectation extends - VersionExpectation { + private final class KeyInRecordLockIntention extends LockIntention { private final String key; @@ -702,10 +688,9 @@ private final class KeyInRecordVersionExpectation extends * @param record * @param lockType */ - protected KeyInRecordVersionExpectation(String key, long record, + protected KeyInRecordLockIntention(String key, long record, LockType lockType) { - super(Token.wrap(key, record), ((Compoundable) destination) - .getVersion(key, record)); + super(Token.wrap(key, record)); this.key = key; this.record = record; this.lockType = lockType; @@ -729,12 +714,12 @@ public long getRecord() throws UnsupportedOperationException { } /** - * A VersionExpectation for a read that touches an entire key (i.e. + * A LockIntention for a read that touches an entire key (i.e. * browse(key)). * * @author jnelson */ - private final class KeyVersionExcpectation extends VersionExpectation { + private final class KeyLockIntention extends LockIntention { private final String key; @@ -744,8 +729,8 @@ private final class KeyVersionExcpectation extends VersionExpectation { * @param token * @param expectedVersion */ - protected KeyVersionExcpectation(String key) { - super(Token.wrap(key), ((Compoundable) destination).getVersion(key)); + protected KeyLockIntention(String key) { + super(Token.wrap(key)); this.key = key; } @@ -767,13 +752,13 @@ public long getRecord() throws UnsupportedOperationException { } /** - * A VersionExpectation for a range read/write. No version is actually + * A LockIntention for a range read/write. No version is actually * expected, but this is a placeholder so that we know to grab to * appropriate range lock. * * @author jnelson */ - private final class RangeVersionExpectation extends VersionExpectation { + private final class RangeLockIntention extends LockIntention { private final Text key; @@ -786,11 +771,10 @@ private final class RangeVersionExpectation extends VersionExpectation { * @param operator * @param values */ - protected RangeVersionExpectation(Text key, Operator operator, + protected RangeLockIntention(Text key, Operator operator, Value... values) { super(operator == null ? RangeToken.forWriting(key, values[0]) - : RangeToken.forReading(key, operator, values), - IGNORE_VERSION); + : RangeToken.forReading(key, operator, values)); this.key = key; this.operator = operator; } @@ -801,7 +785,7 @@ protected RangeVersionExpectation(Text key, Operator operator, * @param key * @param value */ - protected RangeVersionExpectation(Text key, Value value) { + protected RangeLockIntention(Text key, Value value) { this(key, null, value); } @@ -823,12 +807,12 @@ public long getRecord() throws UnsupportedOperationException { } /** - * A VersionExpectation for a read that touches an entire record (i.e. + * A LockIntention for a read that touches an entire record (i.e. * browse(record), audit, etc). * * @author jnelson */ - private final class RecordVersionExpectation extends VersionExpectation { + private final class RecordLockIntention extends LockIntention { private final long record; @@ -837,9 +821,8 @@ private final class RecordVersionExpectation extends VersionExpectation { * * @param record */ - public RecordVersionExpectation(long record) { - super(Token.wrap(record), ((Compoundable) destination) - .getVersion(record)); + public RecordLockIntention(long record) { + super(Token.wrap(record)); this.record = record; } @@ -861,25 +844,15 @@ public long getRecord() throws UnsupportedOperationException { } /** - * The base class for those that determine and stores the expected version - * of a record and and/or key and/or timestamp in {@link #destination}. A - * VersionExpectation should be stored whenever a read/write occurs in the - * AtomicOperation, so that we can check to see if any versions have changed - * when we go to commit. + * The base class for those that record an atomic operation's intention to + * grab a particular lock identified by a certain token at commit time. * * @author jnelson */ - private abstract class VersionExpectation { + private abstract class LockIntention { // NOTE: This class does not define hashCode() or equals() because the // defaults are the desired behaviour. - /** - * OPTINAL parameter that exists iff {@link #timestamp} == - * {@link Versioned#NO_VERSION} since since data returned from a - * historical read won't change with additional writes. - */ - private final long expectedVersion; - /** * The Token that corresponds to the data components that were used to * generate this VersionExpectation. @@ -890,11 +863,9 @@ private abstract class VersionExpectation { * Construct a new instance. * * @param token - * @param expectedVersion */ - protected VersionExpectation(Token token, long expectedVersion) { + protected LockIntention(Token token) { this.token = token; - this.expectedVersion = expectedVersion; } /** @@ -935,7 +906,7 @@ public Token getToken() { public String toString() { StringBuilder sb = new StringBuilder(); boolean replaceInClause = false; - sb.append("Expecting version " + expectedVersion + " for '"); + sb.append("Intention to lock "); try { sb.append(getKey() + " IN "); } From 1d0f991101708c7b2e8bfaf4871aa563be4b83da Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 5 Oct 2014 19:17:52 -0400 Subject: [PATCH 003/100] backport jsr166e --- .../vendor/jsr166e/CompletableFuture.java | 3510 +++++++++ .../vendor/jsr166e/CompletionException.java | 63 + .../vendor/jsr166e/ConcurrentHashMapV8.java | 6565 +++++++++++++++++ .../vendor/jsr166e/CountedCompleter.java | 786 ++ .../cinchapi/vendor/jsr166e/DoubleAdder.java | 210 + .../vendor/jsr166e/DoubleMaxUpdater.java | 205 + .../cinchapi/vendor/jsr166e/ForkJoinPool.java | 3456 +++++++++ .../cinchapi/vendor/jsr166e/ForkJoinTask.java | 1633 ++++ .../vendor/jsr166e/ForkJoinWorkerThread.java | 127 + .../cinchapi/vendor/jsr166e/LongAdder.java | 210 + .../vendor/jsr166e/LongAdderTable.java | 203 + .../vendor/jsr166e/LongMaxUpdater.java | 193 + .../vendor/jsr166e/RecursiveAction.java | 194 + .../vendor/jsr166e/RecursiveTask.java | 80 + .../cinchapi/vendor/jsr166e/StampedLock.java | 1462 ++++ .../cinchapi/vendor/jsr166e/Striped64.java | 348 + .../vendor/jsr166e/ThreadLocalRandom.java | 199 + .../vendor/jsr166e/extra/AtomicDouble.java | 294 + .../jsr166e/extra/AtomicDoubleArray.java | 339 + .../jsr166e/extra/ReadMostlyVector.java | 1633 ++++ .../vendor/jsr166e/extra/SequenceLock.java | 688 ++ licenses/jsr166e | 121 + 22 files changed, 22519 insertions(+) create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletableFuture.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletionException.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ConcurrentHashMapV8.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CountedCompleter.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleAdder.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleMaxUpdater.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinWorkerThread.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdder.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongMaxUpdater.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveAction.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveTask.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/StampedLock.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/Striped64.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDouble.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDoubleArray.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java create mode 100644 concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/SequenceLock.java create mode 100644 licenses/jsr166e diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletableFuture.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletableFuture.java new file mode 100644 index 0000000000..b0dd3fc65f --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletableFuture.java @@ -0,0 +1,3510 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; + +/** + * A {@link Future} that may be explicitly completed (setting its + * value and status), and may include dependent functions and actions + * that trigger upon its completion. + * + *

+ * When two or more threads attempt to {@link #complete complete}, + * {@link #completeExceptionally completeExceptionally}, or {@link #cancel + * cancel} a CompletableFuture, only one of them succeeds. + * + *

+ * Methods are available for adding dependents based on user-provided Functions, + * Actions, or Runnables. The appropriate form to use depends on whether actions + * require arguments and/or produce results. Completion of a dependent action + * will trigger the completion of another CompletableFuture. Actions may also be + * triggered after either or both the current and another CompletableFuture + * complete. Multiple CompletableFutures may also be grouped as one using + * {@link #anyOf(CompletableFuture...)} and {@link #allOf(CompletableFuture...)}. + * + *

+ * CompletableFutures themselves do not execute asynchronously. However, actions + * supplied for dependent completions of another CompletableFuture may do so, + * depending on whether they are provided via one of the async methods + * (that is, methods with names of the form xxxAsync). The + * async methods provide a way to commence asynchronous processing of + * an action using either a given {@link Executor} or by default the + * {@link ForkJoinPool#commonPool()}. To simplify monitoring, debugging, and + * tracking, all generated asynchronous tasks are instances of the marker + * interface {@link AsynchronousCompletionTask}. + * + *

+ * Actions supplied for dependent completions of non-async methods may + * be performed by the thread that completes the current CompletableFuture, or + * by any other caller of these methods. There are no guarantees about the order + * of processing completions unless constrained by these methods. + * + *

+ * Since (unlike {@link FutureTask}) this class has no direct control over the + * computation that causes it to be completed, cancellation is treated as just + * another form of exceptional completion. Method {@link #cancel cancel} has the + * same effect as {@code completeExceptionally(new CancellationException())}. + * + *

+ * Upon exceptional completion (including cancellation), or when a completion + * entails an additional computation which terminates abruptly with an + * (unchecked) exception or error, then all of their dependent completions (and + * their dependents in turn) generally act as {@code completeExceptionally} with + * a {@link CompletionException} holding that exception as its cause. However, + * the {@link #exceptionally exceptionally} and {@link #handle handle} + * completions are able to handle exceptional completions of the + * CompletableFutures they depend on. + * + *

+ * In case of exceptional completion with a CompletionException, methods + * {@link #get()} and {@link #get(long, TimeUnit)} throw an + * {@link ExecutionException} with the same cause as held in the corresponding + * CompletionException. However, in these cases, methods {@link #join()} and + * {@link #getNow} throw the CompletionException, which simplifies usage. + * + *

+ * Arguments used to pass a completion result (that is, for parameters of type + * {@code T}) may be null, but passing a null value for any other parameter will + * result in a {@link NullPointerException} being thrown. + * + * @author Doug Lea + */ +public class CompletableFuture implements Future { + // jsr166e nested interfaces + + /** Interface describing a void action of one argument */ + public interface Action { + void accept(A a); + } + + /** Interface describing a void action of two arguments */ + public interface BiAction { + void accept(A a, B b); + } + + /** Interface describing a function of one argument */ + public interface Fun { + T apply(A a); + } + + /** Interface describing a function of two arguments */ + public interface BiFun { + T apply(A a, B b); + } + + /** Interface describing a function of no arguments */ + public interface Generator { + T get(); + } + + /* + * Overview: + * + * 1. Non-nullness of field result (set via CAS) indicates done. + * An AltResult is used to box null as a result, as well as to + * hold exceptions. Using a single field makes completion fast + * and simple to detect and trigger, at the expense of a lot of + * encoding and decoding that infiltrates many methods. One minor + * simplification relies on the (static) NIL (to box null results) + * being the only AltResult with a null exception field, so we + * don't usually need explicit comparisons with NIL. The CF + * exception propagation mechanics surrounding decoding rely on + * unchecked casts of decoded results really being unchecked, + * where user type errors are caught at point of use, as is + * currently the case in Java. These are highlighted by using + * SuppressWarnings-annotated temporaries. + * + * 2. Waiters are held in a Treiber stack similar to the one used + * in FutureTask, Phaser, and SynchronousQueue. See their + * internal documentation for algorithmic details. + * + * 3. Completions are also kept in a list/stack, and pulled off + * and run when completion is triggered. (We could even use the + * same stack as for waiters, but would give up the potential + * parallelism obtained because woken waiters help release/run + * others -- see method postComplete). Because post-processing + * may race with direct calls, class Completion opportunistically + * extends AtomicInteger so callers can claim the action via + * compareAndSet(0, 1). The Completion.run methods are all + * written a boringly similar uniform way (that sometimes includes + * unnecessary-looking checks, kept to maintain uniformity). + * There are enough dimensions upon which they differ that + * attempts to factor commonalities while maintaining efficiency + * require more lines of code than they would save. + * + * 4. The exported then/and/or methods do support a bit of + * factoring (see doThenApply etc). They must cope with the + * intrinsic races surrounding addition of a dependent action + * versus performing the action directly because the task is + * already complete. For example, a CF may not be complete upon + * entry, so a dependent completion is added, but by the time it + * is added, the target CF is complete, so must be directly + * executed. This is all done while avoiding unnecessary object + * construction in safe-bypass cases. + */ + + // preliminaries + + static final class AltResult { + final Throwable ex; // null only for NIL + + AltResult(Throwable ex) { + this.ex = ex; + } + } + + static final AltResult NIL = new AltResult(null); + + // Fields + + volatile Object result; // Either the result or boxed AltResult + volatile WaitNode waiters; // Treiber stack of threads blocked on get() + volatile CompletionNode completions; // list (Treiber stack) of completions + + // Basic utilities for triggering and processing completions + + /** + * Removes and signals all waiting threads and runs all completions. + */ + final void postComplete() { + WaitNode q; + Thread t; + while ((q = waiters) != null) { + if(UNSAFE.compareAndSwapObject(this, WAITERS, q, q.next) + && (t = q.thread) != null) { + q.thread = null; + LockSupport.unpark(t); + } + } + + CompletionNode h; + Completion c; + while ((h = completions) != null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, h, h.next) + && (c = h.completion) != null) + c.run(); + } + } + + /** + * Triggers completion with the encoding of the given arguments: + * if the exception is non-null, encodes it as a wrapped + * CompletionException unless it is one already. Otherwise uses + * the given result, boxed as NIL if null. + */ + final void internalComplete(T v, Throwable ex) { + if(result == null) + UNSAFE.compareAndSwapObject(this, RESULT, null, + (ex == null) ? (v == null) ? NIL : v : new AltResult( + (ex instanceof CompletionException) ? ex + : new CompletionException(ex))); + postComplete(); // help out even if not triggered + } + + /** + * If triggered, helps release and/or process completions. + */ + final void helpPostComplete() { + if(result != null) + postComplete(); + } + + /* ------------- waiting for completions -------------- */ + + /** Number of processors, for spin control */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Heuristic spin value for waitingGet() before blocking on + * multiprocessors + */ + static final int SPINS = (NCPU > 1) ? 1 << 8 : 0; + + /** + * Linked nodes to record waiting threads in a Treiber stack. See + * other classes such as Phaser and SynchronousQueue for more + * detailed explanation. This class implements ManagedBlocker to + * avoid starvation when blocking actions pile up in + * ForkJoinPools. + */ + static final class WaitNode implements ForkJoinPool.ManagedBlocker { + long nanos; // wait time if timed + final long deadline; // non-zero if timed + volatile int interruptControl; // > 0: interruptible, < 0: interrupted + volatile Thread thread; + volatile WaitNode next; + + WaitNode(boolean interruptible, long nanos, long deadline) { + this.thread = Thread.currentThread(); + this.interruptControl = interruptible ? 1 : 0; + this.nanos = nanos; + this.deadline = deadline; + } + + public boolean isReleasable() { + if(thread == null) + return true; + if(Thread.interrupted()) { + int i = interruptControl; + interruptControl = -1; + if(i > 0) + return true; + } + if(deadline != 0L + && (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) { + thread = null; + return true; + } + return false; + } + + public boolean block() { + if(isReleasable()) + return true; + else if(deadline == 0L) + LockSupport.park(this); + else if(nanos > 0L) + LockSupport.parkNanos(this, nanos); + return isReleasable(); + } + } + + /** + * Returns raw result after waiting, or null if interruptible and + * interrupted. + */ + private Object waitingGet(boolean interruptible) { + WaitNode q = null; + boolean queued = false; + int spins = SPINS; + for (Object r;;) { + if((r = result) != null) { + if(q != null) { // suppress unpark + q.thread = null; + if(q.interruptControl < 0) { + if(interruptible) { + removeWaiter(q); + return null; + } + Thread.currentThread().interrupt(); + } + } + postComplete(); // help release others + return r; + } + else if(spins > 0) { + int rnd = ThreadLocalRandom.current().nextInt(); + if(rnd >= 0) + --spins; + } + else if(q == null) + q = new WaitNode(interruptible, 0L, 0L); + else if(!queued) + queued = UNSAFE.compareAndSwapObject(this, WAITERS, + q.next = waiters, q); + else if(interruptible && q.interruptControl < 0) { + removeWaiter(q); + return null; + } + else if(q.thread != null && result == null) { + try { + ForkJoinPool.managedBlock(q); + } + catch (InterruptedException ex) { + q.interruptControl = -1; + } + } + } + } + + /** + * Awaits completion or aborts on interrupt or timeout. + * + * @param nanos time to wait + * @return raw result + */ + private Object timedAwaitDone(long nanos) throws InterruptedException, + TimeoutException { + WaitNode q = null; + boolean queued = false; + for (Object r;;) { + if((r = result) != null) { + if(q != null) { + q.thread = null; + if(q.interruptControl < 0) { + removeWaiter(q); + throw new InterruptedException(); + } + } + postComplete(); + return r; + } + else if(q == null) { + if(nanos <= 0L) + throw new TimeoutException(); + long d = System.nanoTime() + nanos; + q = new WaitNode(true, nanos, d == 0L ? 1L : d); // avoid 0 + } + else if(!queued) + queued = UNSAFE.compareAndSwapObject(this, WAITERS, + q.next = waiters, q); + else if(q.interruptControl < 0) { + removeWaiter(q); + throw new InterruptedException(); + } + else if(q.nanos <= 0L) { + if(result == null) { + removeWaiter(q); + throw new TimeoutException(); + } + } + else if(q.thread != null && result == null) { + try { + ForkJoinPool.managedBlock(q); + } + catch (InterruptedException ex) { + q.interruptControl = -1; + } + } + } + } + + /** + * Tries to unlink a timed-out or interrupted wait node to avoid + * accumulating garbage. Internal nodes are simply unspliced + * without CAS since it is harmless if they are traversed anyway + * by releasers. To avoid effects of unsplicing from already + * removed nodes, the list is retraversed in case of an apparent + * race. This is slow when there are a lot of nodes, but we don't + * expect lists to be long enough to outweigh higher-overhead + * schemes. + */ + private void removeWaiter(WaitNode node) { + if(node != null) { + node.thread = null; + retry: for (;;) { // restart on removeWaiter race + for (WaitNode pred = null, q = waiters, s; q != null; q = s) { + s = q.next; + if(q.thread != null) + pred = q; + else if(pred != null) { + pred.next = s; + if(pred.thread == null) // check for race + continue retry; + } + else if(!UNSAFE.compareAndSwapObject(this, WAITERS, q, s)) + continue retry; + } + break; + } + } + } + + /* ------------- Async tasks -------------- */ + + /** + * A marker interface identifying asynchronous tasks produced by + * {@code async} methods. This may be useful for monitoring, + * debugging, and tracking asynchronous activities. + * + * @since 1.8 + */ + public static interface AsynchronousCompletionTask {} + + /** Base class can act as either FJ or plain Runnable */ + @SuppressWarnings("serial") + abstract static class Async extends ForkJoinTask implements + Runnable, + AsynchronousCompletionTask { + public final Void getRawResult() { + return null; + } + + public final void setRawResult(Void v) {} + + public final void run() { + exec(); + } + } + + static final class AsyncRun extends Async { + final Runnable fn; + final CompletableFuture dst; + + AsyncRun(Runnable fn, CompletableFuture dst) { + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + fn.run(); + ex = null; + } + catch (Throwable rex) { + ex = rex; + } + d.internalComplete(null, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncSupply extends Async { + final Generator fn; + final CompletableFuture dst; + + AsyncSupply(Generator fn, CompletableFuture dst) { + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + U u; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + u = fn.get(); + ex = null; + } + catch (Throwable rex) { + ex = rex; + u = null; + } + d.internalComplete(u, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncApply extends Async { + final T arg; + final Fun fn; + final CompletableFuture dst; + + AsyncApply(T arg, Fun fn, + CompletableFuture dst) { + this.arg = arg; + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + U u; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + u = fn.apply(arg); + ex = null; + } + catch (Throwable rex) { + ex = rex; + u = null; + } + d.internalComplete(u, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncCombine extends Async { + final T arg1; + final U arg2; + final BiFun fn; + final CompletableFuture dst; + + AsyncCombine(T arg1, U arg2, + BiFun fn, + CompletableFuture dst) { + this.arg1 = arg1; + this.arg2 = arg2; + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + V v; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + v = fn.apply(arg1, arg2); + ex = null; + } + catch (Throwable rex) { + ex = rex; + v = null; + } + d.internalComplete(v, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncAccept extends Async { + final T arg; + final Action fn; + final CompletableFuture dst; + + AsyncAccept(T arg, Action fn, CompletableFuture dst) { + this.arg = arg; + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + fn.accept(arg); + ex = null; + } + catch (Throwable rex) { + ex = rex; + } + d.internalComplete(null, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncAcceptBoth extends Async { + final T arg1; + final U arg2; + final BiAction fn; + final CompletableFuture dst; + + AsyncAcceptBoth(T arg1, U arg2, BiAction fn, + CompletableFuture dst) { + this.arg1 = arg1; + this.arg2 = arg2; + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + fn.accept(arg1, arg2); + ex = null; + } + catch (Throwable rex) { + ex = rex; + } + d.internalComplete(null, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AsyncCompose extends Async { + final T arg; + final Fun> fn; + final CompletableFuture dst; + + AsyncCompose(T arg, Fun> fn, + CompletableFuture dst) { + this.arg = arg; + this.fn = fn; + this.dst = dst; + } + + public final boolean exec() { + CompletableFuture d, fr; + U u; + Throwable ex; + if((d = this.dst) != null && d.result == null) { + try { + fr = fn.apply(arg); + ex = (fr == null) ? new NullPointerException() : null; + } + catch (Throwable rex) { + ex = rex; + fr = null; + } + if(ex != null) + u = null; + else { + Object r = fr.result; + if(r == null) + r = fr.waitingGet(false); + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + u = null; + } + else { + @SuppressWarnings("unchecked") U ur = (U) r; + u = ur; + } + } + d.internalComplete(u, ex); + } + return true; + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + /* ------------- Completions -------------- */ + + /** + * Simple linked list nodes to record completions, used in + * basically the same way as WaitNodes. (We separate nodes from + * the Completions themselves mainly because for the And and Or + * methods, the same Completion object resides in two lists.) + */ + static final class CompletionNode { + final Completion completion; + volatile CompletionNode next; + + CompletionNode(Completion completion) { + this.completion = completion; + } + } + + // Opportunistically subclass AtomicInteger to use compareAndSet to claim. + @SuppressWarnings("serial") + abstract static class Completion extends AtomicInteger implements Runnable {} + + static final class ThenApply extends Completion { + final CompletableFuture src; + final Fun fn; + final CompletableFuture dst; + final Executor executor; + + ThenApply(CompletableFuture src, + Fun fn, CompletableFuture dst, + Executor executor) { + this.src = src; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final Fun fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + Executor e = executor; + U u = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncApply(t, fn, dst)); + else + u = fn.apply(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(u, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenAccept extends Completion { + final CompletableFuture src; + final Action fn; + final CompletableFuture dst; + final Executor executor; + + ThenAccept(CompletableFuture src, Action fn, + CompletableFuture dst, Executor executor) { + this.src = src; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final Action fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAccept(t, fn, dst)); + else + fn.accept(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenRun extends Completion { + final CompletableFuture src; + final Runnable fn; + final CompletableFuture dst; + final Executor executor; + + ThenRun(CompletableFuture src, Runnable fn, + CompletableFuture dst, Executor executor) { + this.src = src; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final Runnable fn; + final CompletableFuture dst; + Object r; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(fn, dst)); + else + fn.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenCombine extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final BiFun fn; + final CompletableFuture dst; + final Executor executor; + + ThenCombine(CompletableFuture src, + CompletableFuture snd, + BiFun fn, + CompletableFuture dst, Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final BiFun fn; + final CompletableFuture dst; + Object r, s; + T t; + U u; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && (b = this.snd) != null && (s = b.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex != null) + u = null; + else if(s instanceof AltResult) { + ex = ((AltResult) s).ex; + u = null; + } + else { + @SuppressWarnings("unchecked") U us = (U) s; + u = us; + } + Executor e = executor; + V v = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncCombine(t, u, fn, dst)); + else + v = fn.apply(t, u); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(v, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenAcceptBoth extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final BiAction fn; + final CompletableFuture dst; + final Executor executor; + + ThenAcceptBoth(CompletableFuture src, + CompletableFuture snd, + BiAction fn, CompletableFuture dst, + Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final BiAction fn; + final CompletableFuture dst; + Object r, s; + T t; + U u; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && (b = this.snd) != null && (s = b.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex != null) + u = null; + else if(s instanceof AltResult) { + ex = ((AltResult) s).ex; + u = null; + } + else { + @SuppressWarnings("unchecked") U us = (U) s; + u = us; + } + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAcceptBoth(t, u, fn, dst)); + else + fn.accept(t, u); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class RunAfterBoth extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final Runnable fn; + final CompletableFuture dst; + final Executor executor; + + RunAfterBoth(CompletableFuture src, CompletableFuture snd, + Runnable fn, CompletableFuture dst, Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final Runnable fn; + final CompletableFuture dst; + Object r, s; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && (b = this.snd) != null && (s = b.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null && (s instanceof AltResult)) + ex = ((AltResult) s).ex; + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(fn, dst)); + else + fn.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AndCompletion extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final CompletableFuture dst; + + AndCompletion(CompletableFuture src, CompletableFuture snd, + CompletableFuture dst) { + this.src = src; + this.snd = snd; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final CompletableFuture dst; + Object r, s; + Throwable ex; + if((dst = this.dst) != null && (a = this.src) != null + && (r = a.result) != null && (b = this.snd) != null + && (s = b.result) != null && compareAndSet(0, 1)) { + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null && (s instanceof AltResult)) + ex = ((AltResult) s).ex; + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ApplyToEither extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final Fun fn; + final CompletableFuture dst; + final Executor executor; + + ApplyToEither(CompletableFuture src, + CompletableFuture snd, + Fun fn, CompletableFuture dst, + Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final Fun fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null + && (fn = this.fn) != null + && (((a = this.src) != null && (r = a.result) != null) || ((b = this.snd) != null && (r = b.result) != null)) + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + Executor e = executor; + U u = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncApply(t, fn, dst)); + else + u = fn.apply(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(u, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class AcceptEither extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final Action fn; + final CompletableFuture dst; + final Executor executor; + + AcceptEither(CompletableFuture src, + CompletableFuture snd, Action fn, + CompletableFuture dst, Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final Action fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null + && (fn = this.fn) != null + && (((a = this.src) != null && (r = a.result) != null) || ((b = this.snd) != null && (r = b.result) != null)) + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAccept(t, fn, dst)); + else + fn.accept(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class RunAfterEither extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final Runnable fn; + final CompletableFuture dst; + final Executor executor; + + RunAfterEither(CompletableFuture src, CompletableFuture snd, + Runnable fn, CompletableFuture dst, Executor executor) { + this.src = src; + this.snd = snd; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final Runnable fn; + final CompletableFuture dst; + Object r; + Throwable ex; + if((dst = this.dst) != null + && (fn = this.fn) != null + && (((a = this.src) != null && (r = a.result) != null) || ((b = this.snd) != null && (r = b.result) != null)) + && compareAndSet(0, 1)) { + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + Executor e = executor; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(fn, dst)); + else + fn.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class OrCompletion extends Completion { + final CompletableFuture src; + final CompletableFuture snd; + final CompletableFuture dst; + + OrCompletion(CompletableFuture src, CompletableFuture snd, + CompletableFuture dst) { + this.src = src; + this.snd = snd; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture b; + final CompletableFuture dst; + Object r, t; + Throwable ex; + if((dst = this.dst) != null + && (((a = this.src) != null && (r = a.result) != null) || ((b = this.snd) != null && (r = b.result) != null)) + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + t = r; + } + dst.internalComplete(t, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ExceptionCompletion extends Completion { + final CompletableFuture src; + final Fun fn; + final CompletableFuture dst; + + ExceptionCompletion(CompletableFuture src, + Fun fn, CompletableFuture dst) { + this.src = src; + this.fn = fn; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final Fun fn; + final CompletableFuture dst; + Object r; + T t = null; + Throwable ex, dx = null; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if((r instanceof AltResult) + && (ex = ((AltResult) r).ex) != null) { + try { + t = fn.apply(ex); + } + catch (Throwable rex) { + dx = rex; + } + } + else { + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + dst.internalComplete(t, dx); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenCopy extends Completion { + final CompletableFuture src; + final CompletableFuture dst; + + ThenCopy(CompletableFuture src, CompletableFuture dst) { + this.src = src; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null && (a = this.src) != null + && (r = a.result) != null && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + dst.internalComplete(t, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + // version of ThenCopy for CompletableFuture dst + static final class ThenPropagate extends Completion { + final CompletableFuture src; + final CompletableFuture dst; + + ThenPropagate(CompletableFuture src, CompletableFuture dst) { + this.src = src; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final CompletableFuture dst; + Object r; + Throwable ex; + if((dst = this.dst) != null && (a = this.src) != null + && (r = a.result) != null && compareAndSet(0, 1)) { + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + dst.internalComplete(null, ex); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class HandleCompletion extends Completion { + final CompletableFuture src; + final BiFun fn; + final CompletableFuture dst; + + HandleCompletion(CompletableFuture src, + BiFun fn, + CompletableFuture dst) { + this.src = src; + this.fn = fn; + this.dst = dst; + } + + public final void run() { + final CompletableFuture a; + final BiFun fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + U u = null; + Throwable dx = null; + try { + u = fn.apply(t, ex); + } + catch (Throwable rex) { + dx = rex; + } + dst.internalComplete(u, dx); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + static final class ThenCompose extends Completion { + final CompletableFuture src; + final Fun> fn; + final CompletableFuture dst; + final Executor executor; + + ThenCompose(CompletableFuture src, + Fun> fn, + CompletableFuture dst, Executor executor) { + this.src = src; + this.fn = fn; + this.dst = dst; + this.executor = executor; + } + + public final void run() { + final CompletableFuture a; + final Fun> fn; + final CompletableFuture dst; + Object r; + T t; + Throwable ex; + Executor e; + if((dst = this.dst) != null && (fn = this.fn) != null + && (a = this.src) != null && (r = a.result) != null + && compareAndSet(0, 1)) { + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + CompletableFuture c = null; + U u = null; + boolean complete = false; + if(ex == null) { + if((e = executor) != null) + e.execute(new AsyncCompose(t, fn, dst)); + else { + try { + if((c = fn.apply(t)) == null) + ex = new NullPointerException(); + } + catch (Throwable rex) { + ex = rex; + } + } + } + if(c != null) { + ThenCopy d = null; + Object s; + if((s = c.result) == null) { + CompletionNode p = new CompletionNode( + d = new ThenCopy(c, dst)); + while ((s = c.result) == null) { + if(UNSAFE.compareAndSwapObject(c, COMPLETIONS, + p.next = c.completions, p)) + break; + } + } + if(s != null && (d == null || d.compareAndSet(0, 1))) { + complete = true; + if(s instanceof AltResult) { + ex = ((AltResult) s).ex; // no rewrap + u = null; + } + else { + @SuppressWarnings("unchecked") U us = (U) s; + u = us; + } + } + } + if(complete || ex != null) + dst.internalComplete(u, ex); + if(c != null) + c.helpPostComplete(); + } + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + // public methods + + /** + * Creates a new incomplete CompletableFuture. + */ + public CompletableFuture() {} + + /** + * Returns a new CompletableFuture that is asynchronously completed + * by a task running in the {@link ForkJoinPool#commonPool()} with + * the value obtained by calling the given Generator. + * + * @param supplier a function returning the value to be used + * to complete the returned CompletableFuture + * @param the function's return type + * @return the new CompletableFuture + */ + public static CompletableFuture supplyAsync(Generator supplier) { + if(supplier == null) + throw new NullPointerException(); + CompletableFuture f = new CompletableFuture(); + ForkJoinPool.commonPool().execute( + (ForkJoinTask) new AsyncSupply(supplier, f)); + return f; + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * by a task running in the given executor with the value obtained + * by calling the given Generator. + * + * @param supplier a function returning the value to be used + * to complete the returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @param the function's return type + * @return the new CompletableFuture + */ + public static CompletableFuture supplyAsync(Generator supplier, + Executor executor) { + if(executor == null || supplier == null) + throw new NullPointerException(); + CompletableFuture f = new CompletableFuture(); + executor.execute(new AsyncSupply(supplier, f)); + return f; + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * by a task running in the {@link ForkJoinPool#commonPool()} after + * it runs the given action. + * + * @param runnable the action to run before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public static CompletableFuture runAsync(Runnable runnable) { + if(runnable == null) + throw new NullPointerException(); + CompletableFuture f = new CompletableFuture(); + ForkJoinPool.commonPool().execute( + (ForkJoinTask) new AsyncRun(runnable, f)); + return f; + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * by a task running in the given executor after it runs the given + * action. + * + * @param runnable the action to run before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public static CompletableFuture runAsync(Runnable runnable, + Executor executor) { + if(executor == null || runnable == null) + throw new NullPointerException(); + CompletableFuture f = new CompletableFuture(); + executor.execute(new AsyncRun(runnable, f)); + return f; + } + + /** + * Returns a new CompletableFuture that is already completed with + * the given value. + * + * @param value the value + * @param the type of the value + * @return the completed CompletableFuture + */ + public static CompletableFuture completedFuture(U value) { + CompletableFuture f = new CompletableFuture(); + f.result = (value == null) ? NIL : value; + return f; + } + + /** + * Returns {@code true} if completed in any fashion: normally, + * exceptionally, or via cancellation. + * + * @return {@code true} if completed + */ + public boolean isDone() { + return result != null; + } + + /** + * Waits if necessary for this future to complete, and then + * returns its result. + * + * @return the result value + * @throws CancellationException if this future was cancelled + * @throws ExecutionException if this future completed exceptionally + * @throws InterruptedException if the current thread was interrupted + * while waiting + */ + public T get() throws InterruptedException, ExecutionException { + Object r; + Throwable ex, cause; + if((r = result) == null && (r = waitingGet(true)) == null) + throw new InterruptedException(); + if(!(r instanceof AltResult)) { + @SuppressWarnings("unchecked") T tr = (T) r; + return tr; + } + if((ex = ((AltResult) r).ex) == null) + return null; + if(ex instanceof CancellationException) + throw (CancellationException) ex; + if((ex instanceof CompletionException) + && (cause = ex.getCause()) != null) + ex = cause; + throw new ExecutionException(ex); + } + + /** + * Waits if necessary for at most the given time for this future + * to complete, and then returns its result, if available. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return the result value + * @throws CancellationException if this future was cancelled + * @throws ExecutionException if this future completed exceptionally + * @throws InterruptedException if the current thread was interrupted + * while waiting + * @throws TimeoutException if the wait timed out + */ + public T get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + Object r; + Throwable ex, cause; + long nanos = unit.toNanos(timeout); + if(Thread.interrupted()) + throw new InterruptedException(); + if((r = result) == null) + r = timedAwaitDone(nanos); + if(!(r instanceof AltResult)) { + @SuppressWarnings("unchecked") T tr = (T) r; + return tr; + } + if((ex = ((AltResult) r).ex) == null) + return null; + if(ex instanceof CancellationException) + throw (CancellationException) ex; + if((ex instanceof CompletionException) + && (cause = ex.getCause()) != null) + ex = cause; + throw new ExecutionException(ex); + } + + /** + * Returns the result value when complete, or throws an + * (unchecked) exception if completed exceptionally. To better + * conform with the use of common functional forms, if a + * computation involved in the completion of this + * CompletableFuture threw an exception, this method throws an + * (unchecked) {@link CompletionException} with the underlying + * exception as its cause. + * + * @return the result value + * @throws CancellationException if the computation was cancelled + * @throws CompletionException if this future completed + * exceptionally or a completion computation threw an exception + */ + public T join() { + Object r; + Throwable ex; + if((r = result) == null) + r = waitingGet(false); + if(!(r instanceof AltResult)) { + @SuppressWarnings("unchecked") T tr = (T) r; + return tr; + } + if((ex = ((AltResult) r).ex) == null) + return null; + if(ex instanceof CancellationException) + throw (CancellationException) ex; + if(ex instanceof CompletionException) + throw (CompletionException) ex; + throw new CompletionException(ex); + } + + /** + * Returns the result value (or throws any encountered exception) + * if completed, else returns the given valueIfAbsent. + * + * @param valueIfAbsent the value to return if not completed + * @return the result value, if completed, else the given valueIfAbsent + * @throws CancellationException if the computation was cancelled + * @throws CompletionException if this future completed + * exceptionally or a completion computation threw an exception + */ + public T getNow(T valueIfAbsent) { + Object r; + Throwable ex; + if((r = result) == null) + return valueIfAbsent; + if(!(r instanceof AltResult)) { + @SuppressWarnings("unchecked") T tr = (T) r; + return tr; + } + if((ex = ((AltResult) r).ex) == null) + return null; + if(ex instanceof CancellationException) + throw (CancellationException) ex; + if(ex instanceof CompletionException) + throw (CompletionException) ex; + throw new CompletionException(ex); + } + + /** + * If not already completed, sets the value returned by {@link #get()} and + * related methods to the given value. + * + * @param value the result value + * @return {@code true} if this invocation caused this CompletableFuture + * to transition to a completed state, else {@code false} + */ + public boolean complete(T value) { + boolean triggered = result == null + && UNSAFE.compareAndSwapObject(this, RESULT, null, + value == null ? NIL : value); + postComplete(); + return triggered; + } + + /** + * If not already completed, causes invocations of {@link #get()} and + * related methods to throw the given exception. + * + * @param ex the exception + * @return {@code true} if this invocation caused this CompletableFuture + * to transition to a completed state, else {@code false} + */ + public boolean completeExceptionally(Throwable ex) { + if(ex == null) + throw new NullPointerException(); + boolean triggered = result == null + && UNSAFE.compareAndSwapObject(this, RESULT, null, + new AltResult(ex)); + postComplete(); + return triggered; + } + + /** + * Returns a new CompletableFuture that is completed + * when this CompletableFuture completes, with the result of the + * given function of this CompletableFuture's result. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied + * function throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenApply(Fun fn) { + return doThenApply(fn, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, with the result of the + * given function of this CompletableFuture's result from a + * task running in the {@link ForkJoinPool#commonPool()}. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied + * function throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenApplyAsync( + Fun fn) { + return doThenApply(fn, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, with the result of the + * given function of this CompletableFuture's result from a + * task running in the given executor. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied + * function throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture thenApplyAsync( + Fun fn, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenApply(fn, executor); + } + + private CompletableFuture doThenApply( + Fun fn, Executor e) { + if(fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ThenApply d = null; + Object r; + if((r = result) == null) { + CompletionNode p = new CompletionNode(d = new ThenApply(this, + fn, dst, e)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + U u = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncApply(t, fn, dst)); + else + u = fn.apply(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(u, ex); + } + helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when this CompletableFuture completes, after performing the given + * action with this CompletableFuture's result. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenAccept(Action block) { + return doThenAccept(block, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, after performing the given + * action with this CompletableFuture's result from a task running + * in the {@link ForkJoinPool#commonPool()}. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenAcceptAsync(Action block) { + return doThenAccept(block, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, after performing the given + * action with this CompletableFuture's result from a task running + * in the given executor. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param block the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture thenAcceptAsync(Action block, + Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenAccept(block, executor); + } + + private CompletableFuture doThenAccept(Action fn, + Executor e) { + if(fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ThenAccept d = null; + Object r; + if((r = result) == null) { + CompletionNode p = new CompletionNode(d = new ThenAccept(this, + fn, dst, e)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAccept(t, fn, dst)); + else + fn.accept(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when this CompletableFuture completes, after performing the given + * action. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenRun(Runnable action) { + return doThenRun(action, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, after performing the given + * action from a task running in the {@link ForkJoinPool#commonPool()}. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenRunAsync(Runnable action) { + return doThenRun(action, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when this CompletableFuture completes, after performing the given + * action from a task running in the given executor. + * + *

+ * If this CompletableFuture completes exceptionally, or the supplied action + * throws an exception, then the returned CompletableFuture completes + * exceptionally with a CompletionException holding the exception as its + * cause. + * + * @param action the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture thenRunAsync(Runnable action, + Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenRun(action, executor); + } + + private CompletableFuture doThenRun(Runnable action, Executor e) { + if(action == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ThenRun d = null; + Object r; + if((r = result) == null) { + CompletionNode p = new CompletionNode(d = new ThenRun(this, action, + dst, e)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(action, dst)); + else + action.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when both this and the other given CompletableFuture complete, + * with the result of the given function of the results of the two + * CompletableFutures. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied function throws an exception, then the returned + * CompletableFuture completes exceptionally with a CompletionException + * holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenCombine( + CompletableFuture other, + BiFun fn) { + return doThenCombine(other, fn, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * with the result of the given function of the results of the two + * CompletableFutures from a task running in the + * {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied function throws an exception, then the returned + * CompletableFuture completes exceptionally with a CompletionException + * holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenCombineAsync( + CompletableFuture other, + BiFun fn) { + return doThenCombine(other, fn, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * with the result of the given function of the results of the two + * CompletableFutures from a task running in the given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied function throws an exception, then the returned + * CompletableFuture completes exceptionally with a CompletionException + * holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture thenCombineAsync( + CompletableFuture other, + BiFun fn, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenCombine(other, fn, executor); + } + + private CompletableFuture doThenCombine( + CompletableFuture other, + BiFun fn, Executor e) { + if(other == null || fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ThenCombine d = null; + Object r, s = null; + if((r = result) == null || (s = other.result) == null) { + d = new ThenCombine(this, other, fn, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r == null && (r = result) == null) + || (s == null && (s = other.result) == null)) { + if(q != null) { + if(s != null + || UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(r != null + || UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) { + if(s != null) + break; + q = new CompletionNode(d); + } + } + } + if(r != null && s != null && (d == null || d.compareAndSet(0, 1))) { + T t; + U u; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex != null) + u = null; + else if(s instanceof AltResult) { + ex = ((AltResult) s).ex; + u = null; + } + else { + @SuppressWarnings("unchecked") U us = (U) s; + u = us; + } + V v = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncCombine(t, u, fn, dst)); + else + v = fn.apply(t, u); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(v, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when both this and the other given CompletableFuture complete, + * after performing the given action with the results of the two + * CompletableFutures. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenAcceptBoth( + CompletableFuture other, + BiAction block) { + return doThenAcceptBoth(other, block, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * after performing the given action with the results of the two + * CompletableFutures from a task running in the + * {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture thenAcceptBothAsync( + CompletableFuture other, + BiAction block) { + return doThenAcceptBoth(other, block, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * after performing the given action with the results of the two + * CompletableFutures from a task running in the given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture thenAcceptBothAsync( + CompletableFuture other, + BiAction block, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenAcceptBoth(other, block, executor); + } + + private CompletableFuture doThenAcceptBoth( + CompletableFuture other, + BiAction fn, Executor e) { + if(other == null || fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ThenAcceptBoth d = null; + Object r, s = null; + if((r = result) == null || (s = other.result) == null) { + d = new ThenAcceptBoth(this, other, fn, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r == null && (r = result) == null) + || (s == null && (s = other.result) == null)) { + if(q != null) { + if(s != null + || UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(r != null + || UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) { + if(s != null) + break; + q = new CompletionNode(d); + } + } + } + if(r != null && s != null && (d == null || d.compareAndSet(0, 1))) { + T t; + U u; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex != null) + u = null; + else if(s instanceof AltResult) { + ex = ((AltResult) s).ex; + u = null; + } + else { + @SuppressWarnings("unchecked") U us = (U) s; + u = us; + } + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAcceptBoth(t, u, fn, dst)); + else + fn.accept(t, u); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when both this and the other given CompletableFuture complete, + * after performing the given action. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture runAfterBoth(CompletableFuture other, + Runnable action) { + return doRunAfterBoth(other, action, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * after performing the given action from a task running in the + * {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture runAfterBothAsync( + CompletableFuture other, Runnable action) { + return doRunAfterBoth(other, action, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when both this and the other given CompletableFuture complete, + * after performing the given action from a task running in the + * given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, or the + * supplied action throws an exception, then the returned CompletableFuture + * completes exceptionally with a CompletionException holding the exception + * as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture runAfterBothAsync( + CompletableFuture other, Runnable action, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doRunAfterBoth(other, action, executor); + } + + private CompletableFuture doRunAfterBoth(CompletableFuture other, + Runnable action, Executor e) { + if(other == null || action == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + RunAfterBoth d = null; + Object r, s = null; + if((r = result) == null || (s = other.result) == null) { + d = new RunAfterBoth(this, other, action, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r == null && (r = result) == null) + || (s == null && (s = other.result) == null)) { + if(q != null) { + if(s != null + || UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(r != null + || UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) { + if(s != null) + break; + q = new CompletionNode(d); + } + } + } + if(r != null && s != null && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null && (s instanceof AltResult)) + ex = ((AltResult) s).ex; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(action, dst)); + else + action.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when either this or the other given CompletableFuture completes, + * with the result of the given function of either this or the other + * CompletableFuture's result. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied function throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture applyToEither( + CompletableFuture other, Fun fn) { + return doApplyToEither(other, fn, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * with the result of the given function of either this or the other + * CompletableFuture's result from a task running in the + * {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied function throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture applyToEitherAsync( + CompletableFuture other, Fun fn) { + return doApplyToEither(other, fn, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * with the result of the given function of either this or the other + * CompletableFuture's result from a task running in the + * given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied function throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param fn the function to use to compute the value of + * the returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture applyToEitherAsync( + CompletableFuture other, Fun fn, + Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doApplyToEither(other, fn, executor); + } + + private CompletableFuture doApplyToEither( + CompletableFuture other, Fun fn, + Executor e) { + if(other == null || fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ApplyToEither d = null; + Object r; + if((r = result) == null && (r = other.result) == null) { + d = new ApplyToEither(this, other, fn, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r = result) == null && (r = other.result) == null) { + if(q != null) { + if(UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + q = new CompletionNode(d); + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + U u = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncApply(t, fn, dst)); + else + u = fn.apply(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(u, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when either this or the other given CompletableFuture completes, + * after performing the given action with the result of either this + * or the other CompletableFuture's result. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture acceptEither( + CompletableFuture other, Action block) { + return doAcceptEither(other, block, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * after performing the given action with the result of either this + * or the other CompletableFuture's result from a task running in + * the {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture acceptEitherAsync( + CompletableFuture other, Action block) { + return doAcceptEither(other, block, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * after performing the given action with the result of either this + * or the other CompletableFuture's result from a task running in + * the given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param block the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture acceptEitherAsync( + CompletableFuture other, Action block, + Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doAcceptEither(other, block, executor); + } + + private CompletableFuture doAcceptEither( + CompletableFuture other, Action fn, + Executor e) { + if(other == null || fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + AcceptEither d = null; + Object r; + if((r = result) == null && (r = other.result) == null) { + d = new AcceptEither(this, other, fn, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r = result) == null && (r = other.result) == null) { + if(q != null) { + if(UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + q = new CompletionNode(d); + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncAccept(t, fn, dst)); + else + fn.accept(t); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed + * when either this or the other given CompletableFuture completes, + * after performing the given action. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture runAfterEither(CompletableFuture other, + Runnable action) { + return doRunAfterEither(other, action, null); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * after performing the given action from a task running in the + * {@link ForkJoinPool#commonPool()}. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture runAfterEitherAsync( + CompletableFuture other, Runnable action) { + return doRunAfterEither(other, action, ForkJoinPool.commonPool()); + } + + /** + * Returns a new CompletableFuture that is asynchronously completed + * when either this or the other given CompletableFuture completes, + * after performing the given action from a task running in the + * given executor. + * + *

+ * If this and/or the other CompletableFuture complete exceptionally, then + * the returned CompletableFuture may also do so, with a CompletionException + * holding one of these exceptions as its cause. No guarantees are made + * about which result or exception is used in the returned + * CompletableFuture. If the supplied action throws an exception, then the + * returned CompletableFuture completes exceptionally with a + * CompletionException holding the exception as its cause. + * + * @param other the other CompletableFuture + * @param action the action to perform before completing the + * returned CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the new CompletableFuture + */ + public CompletableFuture runAfterEitherAsync( + CompletableFuture other, Runnable action, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doRunAfterEither(other, action, executor); + } + + private CompletableFuture doRunAfterEither( + CompletableFuture other, Runnable action, Executor e) { + if(other == null || action == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + RunAfterEither d = null; + Object r; + if((r = result) == null && (r = other.result) == null) { + d = new RunAfterEither(this, other, action, dst, e); + CompletionNode q = null, p = new CompletionNode(d); + while ((r = result) == null && (r = other.result) == null) { + if(q != null) { + if(UNSAFE.compareAndSwapObject(other, COMPLETIONS, + q.next = other.completions, q)) + break; + } + else if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + q = new CompletionNode(d); + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null) { + try { + if(e != null) + e.execute(new AsyncRun(action, dst)); + else + action.run(); + } + catch (Throwable rex) { + ex = rex; + } + } + if(e == null || ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + other.helpPostComplete(); + return dst; + } + + /** + * Returns a CompletableFuture that upon completion, has the same + * value as produced by the given function of the result of this + * CompletableFuture. + * + *

+ * If this CompletableFuture completes exceptionally, then the returned + * CompletableFuture also does so, with a CompletionException holding this + * exception as its cause. Similarly, if the computed CompletableFuture + * completes exceptionally, then so does the returned CompletableFuture. + * + * @param fn the function returning a new CompletableFuture + * @return the CompletableFuture + */ + public CompletableFuture thenCompose( + Fun> fn) { + return doThenCompose(fn, null); + } + + /** + * Returns a CompletableFuture that upon completion, has the same + * value as that produced asynchronously using the + * {@link ForkJoinPool#commonPool()} by the given function of the result + * of this CompletableFuture. + * + *

+ * If this CompletableFuture completes exceptionally, then the returned + * CompletableFuture also does so, with a CompletionException holding this + * exception as its cause. Similarly, if the computed CompletableFuture + * completes exceptionally, then so does the returned CompletableFuture. + * + * @param fn the function returning a new CompletableFuture + * @return the CompletableFuture + */ + public CompletableFuture thenComposeAsync( + Fun> fn) { + return doThenCompose(fn, ForkJoinPool.commonPool()); + } + + /** + * Returns a CompletableFuture that upon completion, has the same + * value as that produced asynchronously using the given executor + * by the given function of this CompletableFuture. + * + *

+ * If this CompletableFuture completes exceptionally, then the returned + * CompletableFuture also does so, with a CompletionException holding this + * exception as its cause. Similarly, if the computed CompletableFuture + * completes exceptionally, then so does the returned CompletableFuture. + * + * @param fn the function returning a new CompletableFuture + * @param executor the executor to use for asynchronous execution + * @return the CompletableFuture + */ + public CompletableFuture thenComposeAsync( + Fun> fn, Executor executor) { + if(executor == null) + throw new NullPointerException(); + return doThenCompose(fn, executor); + } + + private CompletableFuture doThenCompose( + Fun> fn, Executor e) { + if(fn == null) + throw new NullPointerException(); + CompletableFuture dst = null; + ThenCompose d = null; + Object r; + if((r = result) == null) { + dst = new CompletableFuture(); + CompletionNode p = new CompletionNode(d = new ThenCompose( + this, fn, dst, e)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + if(ex == null) { + if(e != null) { + if(dst == null) + dst = new CompletableFuture(); + e.execute(new AsyncCompose(t, fn, dst)); + } + else { + try { + if((dst = fn.apply(t)) == null) + ex = new NullPointerException(); + } + catch (Throwable rex) { + ex = rex; + } + } + } + if(dst == null) + dst = new CompletableFuture(); + if(ex != null) + dst.internalComplete(null, ex); + } + helpPostComplete(); + dst.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed when this + * CompletableFuture completes, with the result of the given + * function of the exception triggering this CompletableFuture's + * completion when it completes exceptionally; otherwise, if this + * CompletableFuture completes normally, then the returned + * CompletableFuture also completes normally with the same value. + * + * @param fn the function to use to compute the value of the + * returned CompletableFuture if this CompletableFuture completed + * exceptionally + * @return the new CompletableFuture + */ + public CompletableFuture exceptionally(Fun fn) { + if(fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + ExceptionCompletion d = null; + Object r; + if((r = result) == null) { + CompletionNode p = new CompletionNode( + d = new ExceptionCompletion(this, fn, dst)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t = null; + Throwable ex, dx = null; + if(r instanceof AltResult) { + if((ex = ((AltResult) r).ex) != null) { + try { + t = fn.apply(ex); + } + catch (Throwable rex) { + dx = rex; + } + } + } + else { + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + dst.internalComplete(t, dx); + } + helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed when this + * CompletableFuture completes, with the result of the given + * function of the result and exception of this CompletableFuture's + * completion. The given function is invoked with the result (or + * {@code null} if none) and the exception (or {@code null} if none) + * of this CompletableFuture when complete. + * + * @param fn the function to use to compute the value of the + * returned CompletableFuture + * @return the new CompletableFuture + */ + public CompletableFuture handle( + BiFun fn) { + if(fn == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + HandleCompletion d = null; + Object r; + if((r = result) == null) { + CompletionNode p = new CompletionNode( + d = new HandleCompletion(this, fn, dst)); + while ((r = result) == null) { + if(UNSAFE.compareAndSwapObject(this, COMPLETIONS, + p.next = completions, p)) + break; + } + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + T t; + Throwable ex; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + U u; + Throwable dx; + try { + u = fn.apply(t, ex); + dx = null; + } + catch (Throwable rex) { + dx = rex; + u = null; + } + dst.internalComplete(u, dx); + } + helpPostComplete(); + return dst; + } + + /* ------------- Arbitrary-arity constructions -------------- */ + + /* + * The basic plan of attack is to recursively form binary + * completion trees of elements. This can be overkill for small + * sets, but scales nicely. The And/All vs Or/Any forms use the + * same idea, but details differ. + */ + + /** + * Returns a new CompletableFuture that is completed when all of + * the given CompletableFutures complete. If any of the given + * CompletableFutures complete exceptionally, then the returned + * CompletableFuture also does so, with a CompletionException + * holding this exception as its cause. Otherwise, the results, + * if any, of the given CompletableFutures are not reflected in + * the returned CompletableFuture, but may be obtained by + * inspecting them individually. If no CompletableFutures are + * provided, returns a CompletableFuture completed with the value + * {@code null}. + * + *

+ * Among the applications of this method is to await completion of a set of + * independent CompletableFutures before continuing a program, as in: + * {@code CompletableFuture.allOf(c1, c2, + * c3).join();}. + * + * @param cfs the CompletableFutures + * @return a new CompletableFuture that is completed when all of the + * given CompletableFutures complete + * @throws NullPointerException if the array or any of its elements are + * {@code null} + */ + public static CompletableFuture allOf(CompletableFuture... cfs) { + int len = cfs.length; // Directly handle empty and singleton cases + if(len > 1) + return allTree(cfs, 0, len - 1); + else { + CompletableFuture dst = new CompletableFuture(); + CompletableFuture f; + if(len == 0) + dst.result = NIL; + else if((f = cfs[0]) == null) + throw new NullPointerException(); + else { + ThenPropagate d = null; + CompletionNode p = null; + Object r; + while ((r = f.result) == null) { + if(d == null) + d = new ThenPropagate(f, dst); + else if(p == null) + p = new CompletionNode(d); + else if(UNSAFE.compareAndSwapObject(f, COMPLETIONS, + p.next = f.completions, p)) + break; + } + if(r != null && (d == null || d.compareAndSet(0, 1))) + dst.internalComplete(null, + (r instanceof AltResult) ? ((AltResult) r).ex + : null); + f.helpPostComplete(); + } + return dst; + } + } + + /** + * Recursively constructs an And'ed tree of CompletableFutures. + * Called only when array known to have at least two elements. + */ + private static CompletableFuture allTree(CompletableFuture[] cfs, + int lo, int hi) { + CompletableFuture fst, snd; + int mid = (lo + hi) >>> 1; + if((fst = (lo == mid ? cfs[lo] : allTree(cfs, lo, mid))) == null + || (snd = (hi == mid + 1 ? cfs[hi] : allTree(cfs, mid + 1, hi))) == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + AndCompletion d = null; + CompletionNode p = null, q = null; + Object r = null, s = null; + while ((r = fst.result) == null || (s = snd.result) == null) { + if(d == null) + d = new AndCompletion(fst, snd, dst); + else if(p == null) + p = new CompletionNode(d); + else if(q == null) { + if(UNSAFE.compareAndSwapObject(fst, COMPLETIONS, + p.next = fst.completions, p)) + q = new CompletionNode(d); + } + else if(UNSAFE.compareAndSwapObject(snd, COMPLETIONS, + q.next = snd.completions, q)) + break; + } + if((r != null || (r = fst.result) != null) + && (s != null || (s = snd.result) != null) + && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + if(r instanceof AltResult) + ex = ((AltResult) r).ex; + else + ex = null; + if(ex == null && (s instanceof AltResult)) + ex = ((AltResult) s).ex; + dst.internalComplete(null, ex); + } + fst.helpPostComplete(); + snd.helpPostComplete(); + return dst; + } + + /** + * Returns a new CompletableFuture that is completed when any of + * the given CompletableFutures complete, with the same result. + * Otherwise, if it completed exceptionally, the returned + * CompletableFuture also does so, with a CompletionException + * holding this exception as its cause. If no CompletableFutures + * are provided, returns an incomplete CompletableFuture. + * + * @param cfs the CompletableFutures + * @return a new CompletableFuture that is completed with the + * result or exception of any of the given CompletableFutures when + * one completes + * @throws NullPointerException if the array or any of its elements are + * {@code null} + */ + public static CompletableFuture anyOf(CompletableFuture... cfs) { + int len = cfs.length; // Same idea as allOf + if(len > 1) + return anyTree(cfs, 0, len - 1); + else { + CompletableFuture dst = new CompletableFuture(); + CompletableFuture f; + if(len == 0) + ; // skip + else if((f = cfs[0]) == null) + throw new NullPointerException(); + else { + ThenCopy d = null; + CompletionNode p = null; + Object r; + while ((r = f.result) == null) { + if(d == null) + d = new ThenCopy(f, dst); + else if(p == null) + p = new CompletionNode(d); + else if(UNSAFE.compareAndSwapObject(f, COMPLETIONS, + p.next = f.completions, p)) + break; + } + if(r != null && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + Object t; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + t = r; + } + dst.internalComplete(t, ex); + } + f.helpPostComplete(); + } + return dst; + } + } + + /** + * Recursively constructs an Or'ed tree of CompletableFutures. + */ + private static CompletableFuture anyTree( + CompletableFuture[] cfs, int lo, int hi) { + CompletableFuture fst, snd; + int mid = (lo + hi) >>> 1; + if((fst = (lo == mid ? cfs[lo] : anyTree(cfs, lo, mid))) == null + || (snd = (hi == mid + 1 ? cfs[hi] : anyTree(cfs, mid + 1, hi))) == null) + throw new NullPointerException(); + CompletableFuture dst = new CompletableFuture(); + OrCompletion d = null; + CompletionNode p = null, q = null; + Object r; + while ((r = fst.result) == null && (r = snd.result) == null) { + if(d == null) + d = new OrCompletion(fst, snd, dst); + else if(p == null) + p = new CompletionNode(d); + else if(q == null) { + if(UNSAFE.compareAndSwapObject(fst, COMPLETIONS, + p.next = fst.completions, p)) + q = new CompletionNode(d); + } + else if(UNSAFE.compareAndSwapObject(snd, COMPLETIONS, + q.next = snd.completions, q)) + break; + } + if((r != null || (r = fst.result) != null || (r = snd.result) != null) + && (d == null || d.compareAndSet(0, 1))) { + Throwable ex; + Object t; + if(r instanceof AltResult) { + ex = ((AltResult) r).ex; + t = null; + } + else { + ex = null; + t = r; + } + dst.internalComplete(t, ex); + } + fst.helpPostComplete(); + snd.helpPostComplete(); + return dst; + } + + /* ------------- Control and status methods -------------- */ + + /** + * If not already completed, completes this CompletableFuture with + * a {@link CancellationException}. Dependent CompletableFutures + * that have not already completed will also complete + * exceptionally, with a {@link CompletionException} caused by + * this {@code CancellationException}. + * + * @param mayInterruptIfRunning this value has no effect in this + * implementation because interrupts are not used to control + * processing. + * + * @return {@code true} if this task is now cancelled + */ + public boolean cancel(boolean mayInterruptIfRunning) { + boolean cancelled = (result == null) + && UNSAFE.compareAndSwapObject(this, RESULT, null, + new AltResult(new CancellationException())); + postComplete(); + return cancelled || isCancelled(); + } + + /** + * Returns {@code true} if this CompletableFuture was cancelled + * before it completed normally. + * + * @return {@code true} if this CompletableFuture was cancelled + * before it completed normally + */ + public boolean isCancelled() { + Object r; + return ((r = result) instanceof AltResult) + && (((AltResult) r).ex instanceof CancellationException); + } + + /** + * Forcibly sets or resets the value subsequently returned by + * method {@link #get()} and related methods, whether or not + * already completed. This method is designed for use only in + * error recovery actions, and even in such situations may result + * in ongoing dependent completions using established versus + * overwritten outcomes. + * + * @param value the completion value + */ + public void obtrudeValue(T value) { + result = (value == null) ? NIL : value; + postComplete(); + } + + /** + * Forcibly causes subsequent invocations of method {@link #get()} and + * related methods to throw the given exception, whether or + * not already completed. This method is designed for use only in + * recovery actions, and even in such situations may result in + * ongoing dependent completions using established versus + * overwritten outcomes. + * + * @param ex the exception + */ + public void obtrudeException(Throwable ex) { + if(ex == null) + throw new NullPointerException(); + result = new AltResult(ex); + postComplete(); + } + + /** + * Returns the estimated number of CompletableFutures whose + * completions are awaiting completion of this CompletableFuture. + * This method is designed for use in monitoring system state, not + * for synchronization control. + * + * @return the number of dependent CompletableFutures + */ + public int getNumberOfDependents() { + int count = 0; + for (CompletionNode p = completions; p != null; p = p.next) + ++count; + return count; + } + + /** + * Returns a string identifying this CompletableFuture, as well as + * its completion state. The state, in brackets, contains the + * String {@code "Completed Normally"} or the String + * {@code "Completed Exceptionally"}, or the String {@code "Not + * completed"} followed by the number of CompletableFutures + * dependent upon its completion, if any. + * + * @return a string identifying this CompletableFuture, as well as its state + */ + public String toString() { + Object r = result; + int count; + return super.toString() + + ((r == null) ? (((count = getNumberOfDependents()) == 0) ? "[Not completed]" + : "[Not completed, " + count + " dependents]") + : (((r instanceof AltResult) && ((AltResult) r).ex != null) ? "[Completed exceptionally]" + : "[Completed normally]")); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long RESULT; + private static final long WAITERS; + private static final long COMPLETIONS; + static { + try { + UNSAFE = getUnsafe(); + Class k = CompletableFuture.class; + RESULT = UNSAFE.objectFieldOffset(k.getDeclaredField("result")); + WAITERS = UNSAFE.objectFieldOffset(k.getDeclaredField("waiters")); + COMPLETIONS = UNSAFE.objectFieldOffset(k + .getDeclaredField("completions")); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletionException.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletionException.java new file mode 100644 index 0000000000..6a5294178f --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CompletionException.java @@ -0,0 +1,63 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +/** + * Exception thrown when an error or other exception is encountered + * in the course of completing a result or task. + * + * @since 1.8 + * @author Doug Lea + */ +public class CompletionException extends RuntimeException { + private static final long serialVersionUID = 7830266012832686185L; + + /** + * Constructs a {@code CompletionException} with no detail message. + * The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause(Throwable) initCause}. + */ + protected CompletionException() {} + + /** + * Constructs a {@code CompletionException} with the specified detail + * message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause(Throwable) initCause}. + * + * @param message the detail message + */ + protected CompletionException(String message) { + super(message); + } + + /** + * Constructs a {@code CompletionException} with the specified detail + * message and cause. + * + * @param message the detail message + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method) + */ + public CompletionException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a {@code CompletionException} with the specified cause. + * The detail message is set to {@code (cause == null ? null : + * cause.toString())} (which typically contains the class and + * detail message of {@code cause}). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method) + */ + public CompletionException(Throwable cause) { + super(cause); + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ConcurrentHashMapV8.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ConcurrentHashMapV8.java new file mode 100644 index 0000000000..84b9244be7 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ConcurrentHashMapV8.java @@ -0,0 +1,6565 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import org.cinchapi.vendor.jsr166e.ForkJoinPool; + +import java.io.ObjectStreamField; +import java.io.Serializable; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.ConcurrentModificationException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.locks.ReentrantLock; + +/** + * A hash table supporting full concurrency of retrievals and + * high expected concurrency for updates. This class obeys the + * same functional specification as {@link java.util.Hashtable}, and + * includes versions of methods corresponding to each method of + * {@code Hashtable}. However, even though all operations are + * thread-safe, retrieval operations do not entail locking, + * and there is not any support for locking the entire table + * in a way that prevents all access. This class is fully + * interoperable with {@code Hashtable} in programs that rely on its + * thread safety but not on its synchronization details. + * + *

+ * Retrieval operations (including {@code get}) generally do not block, so may + * overlap with update operations (including {@code put} and {@code remove}). + * Retrievals reflect the results of the most recently completed update + * operations holding upon their onset. (More formally, an update operation for + * a given key bears a happens-before relation with any (non-null) + * retrieval for that key reporting the updated value.) For aggregate operations + * such as {@code putAll} and {@code clear}, concurrent retrievals may reflect + * insertion or removal of only some entries. Similarly, Iterators and + * Enumerations return elements reflecting the state of the hash table at some + * point at or since the creation of the iterator/enumeration. They do + * not throw {@link ConcurrentModificationException}. However, + * iterators are designed to be used by only one thread at a time. Bear in mind + * that the results of aggregate status methods including {@code size}, + * {@code isEmpty}, and {@code containsValue} are typically useful only when a + * map is not undergoing concurrent updates in other threads. Otherwise the + * results of these methods reflect transient states that may be adequate for + * monitoring or estimation purposes, but not for program control. + * + *

+ * The table is dynamically expanded when there are too many collisions (i.e., + * keys that have distinct hash codes but fall into the same slot modulo the + * table size), with the expected average effect of maintaining roughly two bins + * per mapping (corresponding to a 0.75 load factor threshold for resizing). + * There may be much variance around this average as mappings are added and + * removed, but overall, this maintains a commonly accepted time/space tradeoff + * for hash tables. However, resizing this or any other kind of hash table may + * be a relatively slow operation. When possible, it is a good idea to provide a + * size estimate as an optional {@code initialCapacity} constructor argument. An + * additional optional {@code loadFactor} constructor argument provides a + * further means of customizing initial table capacity by specifying the table + * density to be used in calculating the amount of space to allocate for the + * given number of elements. Also, for compatibility with previous versions of + * this class, constructors may optionally specify an expected + * {@code concurrencyLevel} as an additional hint for internal sizing. Note that + * using many keys with exactly the same {@code hashCode()} is a sure way to + * slow down performance of any hash table. To ameliorate impact, when keys are + * {@link Comparable}, this class may use comparison order among keys to help + * break ties. + * + *

+ * A {@link Set} projection of a ConcurrentHashMapV8 may be created (using + * {@link #newKeySet()} or {@link #newKeySet(int)}), or viewed (using + * {@link #keySet(Object)} when only keys are of interest, and the mapped values + * are (perhaps transiently) not used or all take the same mapping value. + * + *

+ * This class and its views and iterators implement all of the optional + * methods of the {@link Map} and {@link Iterator} interfaces. + * + *

+ * Like {@link Hashtable} but unlike {@link HashMap}, this class does + * not allow {@code null} to be used as a key or value. + * + *

+ * ConcurrentHashMapV8s support a set of sequential and parallel bulk operations + * that are designed to be safely, and often sensibly, applied even with maps + * that are being concurrently updated by other threads; for example, when + * computing a snapshot summary of the values in a shared registry. There are + * three kinds of operation, each with four forms, accepting functions with + * Keys, Values, Entries, and (Key, Value) arguments and/or return values. + * Because the elements of a ConcurrentHashMapV8 are not ordered in any + * particular way, and may be processed in different orders in different + * parallel executions, the correctness of supplied functions should not depend + * on any ordering, or on any other objects or values that may transiently + * change while computation is in progress; and except for forEach actions, + * should ideally be side-effect-free. Bulk operations on + * {@link java.util.Map.Entry} objects do not support method {@code setValue}. + * + *

    + *
  • forEach: Perform a given action on each element. A variant form applies a + * given transformation on each element before performing the action.
  • + * + *
  • search: Return the first available non-null result of applying a given + * function on each element; skipping further search when a result is found.
  • + * + *
  • reduce: Accumulate each element. The supplied reduction function cannot + * rely on ordering (more formally, it should be both associative and + * commutative). There are five variants: + * + *
      + * + *
    • Plain reductions. (There is not a form of this method for (key, value) + * function arguments since there is no corresponding return type.)
    • + * + *
    • Mapped reductions that accumulate the results of a given function applied + * to each element.
    • + * + *
    • Reductions to scalar doubles, longs, and ints, using a given basis value. + *
    • + * + *
    + *
  • + *
+ * + *

+ * These bulk operations accept a {@code parallelismThreshold} argument. Methods + * proceed sequentially if the current map size is estimated to be less than the + * given threshold. Using a value of {@code Long.MAX_VALUE} suppresses all + * parallelism. Using a value of {@code 1} results in maximal parallelism by + * partitioning into enough subtasks to fully utilize the + * {@link ForkJoinPool#commonPool()} that is used for all parallel computations. + * Normally, you would initially choose one of these extreme values, and then + * measure performance of using in-between values that trade off overhead versus + * throughput. + * + *

+ * The concurrency properties of bulk operations follow from those of + * ConcurrentHashMapV8: Any non-null result returned from {@code get(key)} and + * related access methods bears a happens-before relation with the associated + * insertion or update. The result of any bulk operation reflects the + * composition of these per-element relations (but is not necessarily atomic + * with respect to the map as a whole unless it is somehow known to be + * quiescent). Conversely, because keys and values in the map are never null, + * null serves as a reliable atomic indicator of the current lack of any result. + * To maintain this property, null serves as an implicit basis for all + * non-scalar reduction operations. For the double, long, and int versions, the + * basis should be one that, when combined with any other value, returns that + * other value (more formally, it should be the identity element for the + * reduction). Most common reductions have these properties; for example, + * computing a sum with basis 0 or a minimum with basis MAX_VALUE. + * + *

+ * Search and transformation functions provided as arguments should similarly + * return null to indicate the lack of any result (in which case it is not + * used). In the case of mapped reductions, this also enables transformations to + * serve as filters, returning null (or, in the case of primitive + * specializations, the identity basis) if the element should not be combined. + * You can create compound transformations and filterings by composing them + * yourself under this "null means there is nothing there now" rule before using + * them in search or reduce operations. + * + *

+ * Methods accepting and/or returning Entry arguments maintain key-value + * associations. They may be useful for example when finding the key for the + * greatest value. Note that "plain" Entry arguments can be supplied using + * {@code new + * AbstractMap.SimpleEntry(k,v)}. + * + *

+ * Bulk operations may complete abruptly, throwing an exception encountered in + * the application of a supplied function. Bear in mind when handling such + * exceptions that other concurrently executing functions could also have thrown + * exceptions, or would have done so if the first exception had not occurred. + * + *

+ * Speedups for parallel compared to sequential forms are common but not + * guaranteed. Parallel operations involving brief functions on small maps may + * execute more slowly than sequential forms if the underlying work to + * parallelize the computation is more expensive than the computation itself. + * Similarly, parallelization may not lead to much actual parallelism if all + * processors are busy performing unrelated tasks. + * + *

+ * All arguments to all task methods must be non-null. + * + *

+ * org.cinchapi.vendor.jsr166e note: During transition, this class + * uses nested functional interfaces with different names but the + * same forms as those expected for JDK8. + * + *

+ * This class is a member of the Java Collections Framework. + * + * @since 1.5 + * @author Doug Lea + * @param the type of keys maintained by this map + * @param the type of mapped values + */ +@SuppressWarnings("unused") +public class ConcurrentHashMapV8 extends AbstractMap implements + ConcurrentMap, + Serializable { + private static final long serialVersionUID = 7249069246763182397L; + + /** + * An object for traversing and partitioning elements of a source. + * This interface provides a subset of the functionality of JDK8 + * java.util.Spliterator. + */ + public static interface ConcurrentHashMapSpliterator { + /** + * If possible, returns a new spliterator covering + * approximately one half of the elements, which will not be + * covered by this spliterator. Returns null if cannot be + * split. + */ + ConcurrentHashMapSpliterator trySplit(); + + /** + * Returns an estimate of the number of elements covered by + * this Spliterator. + */ + long estimateSize(); + + /** Applies the action to each untraversed element */ + void forEachRemaining(Action action); + + /** If an element remains, applies the action and returns true. */ + boolean tryAdvance(Action action); + } + + // Sams + /** Interface describing a void action of one argument */ + public interface Action { + void apply(A a); + } + + /** Interface describing a void action of two arguments */ + public interface BiAction { + void apply(A a, B b); + } + + /** Interface describing a function of one argument */ + public interface Fun { + T apply(A a); + } + + /** Interface describing a function of two arguments */ + public interface BiFun { + T apply(A a, B b); + } + + /** Interface describing a function mapping its argument to a double */ + public interface ObjectToDouble { + double apply(A a); + } + + /** Interface describing a function mapping its argument to a long */ + public interface ObjectToLong { + long apply(A a); + } + + /** Interface describing a function mapping its argument to an int */ + public interface ObjectToInt { + int apply(A a); + } + + /** Interface describing a function mapping two arguments to a double */ + public interface ObjectByObjectToDouble { + double apply(A a, B b); + } + + /** Interface describing a function mapping two arguments to a long */ + public interface ObjectByObjectToLong { + long apply(A a, B b); + } + + /** Interface describing a function mapping two arguments to an int */ + public interface ObjectByObjectToInt { + int apply(A a, B b); + } + + /** Interface describing a function mapping two doubles to a double */ + public interface DoubleByDoubleToDouble { + double apply(double a, double b); + } + + /** Interface describing a function mapping two longs to a long */ + public interface LongByLongToLong { + long apply(long a, long b); + } + + /** Interface describing a function mapping two ints to an int */ + public interface IntByIntToInt { + int apply(int a, int b); + } + + /* + * Overview: + * + * The primary design goal of this hash table is to maintain + * concurrent readability (typically method get(), but also + * iterators and related methods) while minimizing update + * contention. Secondary goals are to keep space consumption about + * the same or better than java.util.HashMap, and to support high + * initial insertion rates on an empty table by many threads. + * + * This map usually acts as a binned (bucketed) hash table. Each + * key-value mapping is held in a Node. Most nodes are instances + * of the basic Node class with hash, key, value, and next + * fields. However, various subclasses exist: TreeNodes are + * arranged in balanced trees, not lists. TreeBins hold the roots + * of sets of TreeNodes. ForwardingNodes are placed at the heads + * of bins during resizing. ReservationNodes are used as + * placeholders while establishing values in computeIfAbsent and + * related methods. The types TreeBin, ForwardingNode, and + * ReservationNode do not hold normal user keys, values, or + * hashes, and are readily distinguishable during search etc + * because they have negative hash fields and null key and value + * fields. (These special nodes are either uncommon or transient, + * so the impact of carrying around some unused fields is + * insignificant.) + * + * The table is lazily initialized to a power-of-two size upon the + * first insertion. Each bin in the table normally contains a + * list of Nodes (most often, the list has only zero or one Node). + * Table accesses require volatile/atomic reads, writes, and + * CASes. Because there is no other way to arrange this without + * adding further indirections, we use intrinsics + * (sun.misc.Unsafe) operations. + * + * We use the top (sign) bit of Node hash fields for control + * purposes -- it is available anyway because of addressing + * constraints. Nodes with negative hash fields are specially + * handled or ignored in map methods. + * + * Insertion (via put or its variants) of the first node in an + * empty bin is performed by just CASing it to the bin. This is + * by far the most common case for put operations under most + * key/hash distributions. Other update operations (insert, + * delete, and replace) require locks. We do not want to waste + * the space required to associate a distinct lock object with + * each bin, so instead use the first node of a bin list itself as + * a lock. Locking support for these locks relies on builtin + * "synchronized" monitors. + * + * Using the first node of a list as a lock does not by itself + * suffice though: When a node is locked, any update must first + * validate that it is still the first node after locking it, and + * retry if not. Because new nodes are always appended to lists, + * once a node is first in a bin, it remains first until deleted + * or the bin becomes invalidated (upon resizing). + * + * The main disadvantage of per-bin locks is that other update + * operations on other nodes in a bin list protected by the same + * lock can stall, for example when user equals() or mapping + * functions take a long time. However, statistically, under + * random hash codes, this is not a common problem. Ideally, the + * frequency of nodes in bins follows a Poisson distribution + * (http://en.wikipedia.org/wiki/Poisson_distribution) with a + * parameter of about 0.5 on average, given the resizing threshold + * of 0.75, although with a large variance because of resizing + * granularity. Ignoring variance, the expected occurrences of + * list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The + * first values are: + * + * 0: 0.60653066 + * 1: 0.30326533 + * 2: 0.07581633 + * 3: 0.01263606 + * 4: 0.00157952 + * 5: 0.00015795 + * 6: 0.00001316 + * 7: 0.00000094 + * 8: 0.00000006 + * more: less than 1 in ten million + * + * Lock contention probability for two threads accessing distinct + * elements is roughly 1 / (8 * #elements) under random hashes. + * + * Actual hash code distributions encountered in practice + * sometimes deviate significantly from uniform randomness. This + * includes the case when N > (1<<30), so some keys MUST collide. + * Similarly for dumb or hostile usages in which multiple keys are + * designed to have identical hash codes or ones that differs only + * in masked-out high bits. So we use a secondary strategy that + * applies when the number of nodes in a bin exceeds a + * threshold. These TreeBins use a balanced tree to hold nodes (a + * specialized form of red-black trees), bounding search time to + * O(log N). Each search step in a TreeBin is at least twice as + * slow as in a regular list, but given that N cannot exceed + * (1<<64) (before running out of addresses) this bounds search + * steps, lock hold times, etc, to reasonable constants (roughly + * 100 nodes inspected per operation worst case) so long as keys + * are Comparable (which is very common -- String, Long, etc). + * TreeBin nodes (TreeNodes) also maintain the same "next" + * traversal pointers as regular nodes, so can be traversed in + * iterators in the same way. + * + * The table is resized when occupancy exceeds a percentage + * threshold (nominally, 0.75, but see below). Any thread + * noticing an overfull bin may assist in resizing after the + * initiating thread allocates and sets up the replacement array. + * However, rather than stalling, these other threads may proceed + * with insertions etc. The use of TreeBins shields us from the + * worst case effects of overfilling while resizes are in + * progress. Resizing proceeds by transferring bins, one by one, + * from the table to the next table. However, threads claim small + * blocks of indices to transfer (via field transferIndex) before + * doing so, reducing contention. A generation stamp in field + * sizeCtl ensures that resizings do not overlap. Because we are + * using power-of-two expansion, the elements from each bin must + * either stay at same index, or move with a power of two + * offset. We eliminate unnecessary node creation by catching + * cases where old nodes can be reused because their next fields + * won't change. On average, only about one-sixth of them need + * cloning when a table doubles. The nodes they replace will be + * garbage collectable as soon as they are no longer referenced by + * any reader thread that may be in the midst of concurrently + * traversing table. Upon transfer, the old table bin contains + * only a special forwarding node (with hash field "MOVED") that + * contains the next table as its key. On encountering a + * forwarding node, access and update operations restart, using + * the new table. + * + * Each bin transfer requires its bin lock, which can stall + * waiting for locks while resizing. However, because other + * threads can join in and help resize rather than contend for + * locks, average aggregate waits become shorter as resizing + * progresses. The transfer operation must also ensure that all + * accessible bins in both the old and new table are usable by any + * traversal. This is arranged in part by proceeding from the + * last bin (table.length - 1) up towards the first. Upon seeing + * a forwarding node, traversals (see class Traverser) arrange to + * move to the new table without revisiting nodes. To ensure that + * no intervening nodes are skipped even when moved out of order, + * a stack (see class TableStack) is created on first encounter of + * a forwarding node during a traversal, to maintain its place if + * later processing the current table. The need for these + * save/restore mechanics is relatively rare, but when one + * forwarding node is encountered, typically many more will be. + * So Traversers use a simple caching scheme to avoid creating so + * many new TableStack nodes. (Thanks to Peter Levart for + * suggesting use of a stack here.) + * + * The traversal scheme also applies to partial traversals of + * ranges of bins (via an alternate Traverser constructor) + * to support partitioned aggregate operations. Also, read-only + * operations give up if ever forwarded to a null table, which + * provides support for shutdown-style clearing, which is also not + * currently implemented. + * + * Lazy table initialization minimizes footprint until first use, + * and also avoids resizings when the first operation is from a + * putAll, constructor with map argument, or deserialization. + * These cases attempt to override the initial capacity settings, + * but harmlessly fail to take effect in cases of races. + * + * The element count is maintained using a specialization of + * LongAdder. We need to incorporate a specialization rather than + * just use a LongAdder in order to access implicit + * contention-sensing that leads to creation of multiple + * CounterCells. The counter mechanics avoid contention on + * updates but can encounter cache thrashing if read too + * frequently during concurrent access. To avoid reading so often, + * resizing under contention is attempted only upon adding to a + * bin already holding two or more nodes. Under uniform hash + * distributions, the probability of this occurring at threshold + * is around 13%, meaning that only about 1 in 8 puts check + * threshold (and after resizing, many fewer do so). + * + * TreeBins use a special form of comparison for search and + * related operations (which is the main reason we cannot use + * existing collections such as TreeMaps). TreeBins contain + * Comparable elements, but may contain others, as well as + * elements that are Comparable but not necessarily Comparable for + * the same T, so we cannot invoke compareTo among them. To handle + * this, the tree is ordered primarily by hash value, then by + * Comparable.compareTo order if applicable. On lookup at a node, + * if elements are not comparable or compare as 0 then both left + * and right children may need to be searched in the case of tied + * hash values. (This corresponds to the full list search that + * would be necessary if all elements were non-Comparable and had + * tied hashes.) On insertion, to keep a total ordering (or as + * close as is required here) across rebalancings, we compare + * classes and identityHashCodes as tie-breakers. The red-black + * balancing code is updated from pre-jdk-collections + * (http://gee.cs.oswego.edu/dl/classes/collections/RBCell.java) + * based in turn on Cormen, Leiserson, and Rivest "Introduction to + * Algorithms" (CLR). + * + * TreeBins also require an additional locking mechanism. While + * list traversal is always possible by readers even during + * updates, tree traversal is not, mainly because of tree-rotations + * that may change the root node and/or its linkages. TreeBins + * include a simple read-write lock mechanism parasitic on the + * main bin-synchronization strategy: Structural adjustments + * associated with an insertion or removal are already bin-locked + * (and so cannot conflict with other writers) but must wait for + * ongoing readers to finish. Since there can be only one such + * waiter, we use a simple scheme using a single "waiter" field to + * block writers. However, readers need never block. If the root + * lock is held, they proceed along the slow traversal path (via + * next-pointers) until the lock becomes available or the list is + * exhausted, whichever comes first. These cases are not fast, but + * maximize aggregate expected throughput. + * + * Maintaining API and serialization compatibility with previous + * versions of this class introduces several oddities. Mainly: We + * leave untouched but unused constructor arguments refering to + * concurrencyLevel. We accept a loadFactor constructor argument, + * but apply it only to initial table capacity (which is the only + * time that we can guarantee to honor it.) We also declare an + * unused "Segment" class that is instantiated in minimal form + * only when serializing. + * + * Also, solely for compatibility with previous versions of this + * class, it extends AbstractMap, even though all of its methods + * are overridden, so it is just useless baggage. + * + * This file is organized to make things a little easier to follow + * while reading than they might otherwise: First the main static + * declarations and utilities, then fields, then main public + * methods (with a few factorings of multiple public methods into + * internal ones), then sizing methods, trees, traversers, and + * bulk operations. + */ + + /* ---------------- Constants -------------- */ + + /** + * The largest possible table capacity. This value must be + * exactly 1<<30 to stay within Java array allocation and indexing + * bounds for power of two table sizes, and is further required + * because the top two bits of 32bit hash fields are used for + * control purposes. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; + + /** + * The default initial table capacity. Must be a power of 2 + * (i.e., at least 1) and at most MAXIMUM_CAPACITY. + */ + private static final int DEFAULT_CAPACITY = 16; + + /** + * The largest possible (non-power of two) array size. + * Needed by toArray and related methods. + */ + static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * The default concurrency level for this table. Unused but + * defined for compatibility with previous versions of this class. + */ + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + /** + * The load factor for this table. Overrides of this value in + * constructors affect only the initial table capacity. The + * actual floating point value isn't normally used -- it is + * simpler to use expressions such as {@code n - (n >>> 2)} for + * the associated resizing threshold. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The bin count threshold for using a tree rather than list for a + * bin. Bins are converted to trees when adding an element to a + * bin with at least this many nodes. The value must be greater + * than 2, and should be at least 8 to mesh with assumptions in + * tree removal about conversion back to plain bins upon + * shrinkage. + */ + static final int TREEIFY_THRESHOLD = 8; + + /** + * The bin count threshold for untreeifying a (split) bin during a + * resize operation. Should be less than TREEIFY_THRESHOLD, and at + * most 6 to mesh with shrinkage detection under removal. + */ + static final int UNTREEIFY_THRESHOLD = 6; + + /** + * The smallest table capacity for which bins may be treeified. + * (Otherwise the table is resized if too many nodes in a bin.) + * The value should be at least 4 * TREEIFY_THRESHOLD to avoid + * conflicts between resizing and treeification thresholds. + */ + static final int MIN_TREEIFY_CAPACITY = 64; + + /** + * Minimum number of rebinnings per transfer step. Ranges are + * subdivided to allow multiple resizer threads. This value + * serves as a lower bound to avoid resizers encountering + * excessive memory contention. The value should be at least + * DEFAULT_CAPACITY. + */ + private static final int MIN_TRANSFER_STRIDE = 16; + + /** + * The number of bits used for generation stamp in sizeCtl. + * Must be at least 6 for 32bit arrays. + */ + private static int RESIZE_STAMP_BITS = 16; + + /** + * The maximum number of threads that can help resize. + * Must fit in 32 - RESIZE_STAMP_BITS bits. + */ + private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1; + + /** + * The bit shift for recording size stamp in sizeCtl. + */ + private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS; + + /* + * Encodings for Node hash fields. See above for explanation. + */ + static final int MOVED = -1; // hash for forwarding nodes + static final int TREEBIN = -2; // hash for roots of trees + static final int RESERVED = -3; // hash for transient reservations + static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash + + /** Number of CPUS, to place bounds on some sizings */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** For serialization compatibility. */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("segments", Segment[].class), + new ObjectStreamField("segmentMask", Integer.TYPE), + new ObjectStreamField("segmentShift", Integer.TYPE) }; + + /* ---------------- Nodes -------------- */ + + /** + * Key-value entry. This class is never exported out as a + * user-mutable Map.Entry (i.e., one supporting setValue; see + * MapEntry below), but can be used for read-only traversals used + * in bulk tasks. Subclasses of Node with a negative hash field + * are special, and contain null keys and values (but are never + * exported). Otherwise, keys and vals are never null. + */ + static class Node implements Map.Entry { + final int hash; + final K key; + volatile V val; + volatile Node next; + + Node(int hash, K key, V val, Node next) { + this.hash = hash; + this.key = key; + this.val = val; + this.next = next; + } + + public final K getKey() { + return key; + } + + public final V getValue() { + return val; + } + + public final int hashCode() { + return key.hashCode() ^ val.hashCode(); + } + + public final String toString() { + return key + "=" + val; + } + + public final V setValue(V value) { + throw new UnsupportedOperationException(); + } + + public final boolean equals(Object o) { + Object k, v, u; + Map.Entry e; + return ((o instanceof Map.Entry) + && (k = (e = (Map.Entry) o).getKey()) != null + && (v = e.getValue()) != null + && (k == key || k.equals(key)) && (v == (u = val) || v + .equals(u))); + } + + /** + * Virtualized support for map.get(); overridden in subclasses. + */ + Node find(int h, Object k) { + Node e = this; + if(k != null) { + do { + K ek; + if(e.hash == h + && ((ek = e.key) == k || (ek != null && k + .equals(ek)))) + return e; + } + while ((e = e.next) != null); + } + return null; + } + } + + /* ---------------- Static utilities -------------- */ + + /** + * Spreads (XORs) higher bits of hash to lower and also forces top + * bit to 0. Because the table uses power-of-two masking, sets of + * hashes that vary only in bits above the current mask will + * always collide. (Among known examples are sets of Float keys + * holding consecutive whole numbers in small tables.) So we + * apply a transform that spreads the impact of higher bits + * downward. There is a tradeoff between speed, utility, and + * quality of bit-spreading. Because many common sets of hashes + * are already reasonably distributed (so don't benefit from + * spreading), and because we use trees to handle large sets of + * collisions in bins, we just XOR some shifted bits in the + * cheapest possible way to reduce systematic lossage, as well as + * to incorporate impact of the highest bits that would otherwise + * never be used in index calculations because of table bounds. + */ + static final int spread(int h) { + return (h ^ (h >>> 16)) & HASH_BITS; + } + + /** + * Returns a power of two table size for the given desired capacity. + * See Hackers Delight, sec 3.2 + */ + private static final int tableSizeFor(int c) { + int n = c - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; + } + + /** + * Returns x's Class if it is of the form "class C implements + * Comparable", else null. + */ + static Class comparableClassFor(Object x) { + if(x instanceof Comparable) { + Class c; + Type[] ts, as; + Type t; + ParameterizedType p; + if((c = x.getClass()) == String.class) // bypass checks + return c; + if((ts = c.getGenericInterfaces()) != null) { + for (int i = 0; i < ts.length; ++i) { + if(((t = ts[i]) instanceof ParameterizedType) + && ((p = (ParameterizedType) t).getRawType() == Comparable.class) + && (as = p.getActualTypeArguments()) != null + && as.length == 1 && as[0] == c) // type arg is c + return c; + } + } + } + return null; + } + + /** + * Returns k.compareTo(x) if x matches kc (k's screened comparable + * class), else 0. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + // for cast to Comparable + static int compareComparables(Class kc, Object k, Object x) { + return (x == null || x.getClass() != kc ? 0 : ((Comparable) k) + .compareTo(x)); + } + + /* ---------------- Table element access -------------- */ + + /* + * Volatile access methods are used for table elements as well as + * elements of in-progress next table while resizing. All uses of + * the tab arguments must be null checked by callers. All callers + * also paranoically precheck that tab's length is not zero (or an + * equivalent check), thus ensuring that any index argument taking + * the form of a hash value anded with (length - 1) is a valid + * index. Note that, to be correct wrt arbitrary concurrency + * errors by users, these checks must operate on local variables, + * which accounts for some odd-looking inline assignments below. + * Note that calls to setTabAt always occur within locked regions, + * and so in principle require only release ordering, not + * full volatile semantics, but are currently coded as volatile + * writes to be conservative. + */ + + @SuppressWarnings("unchecked") + static final Node tabAt(Node[] tab, int i) { + return (Node) U.getObjectVolatile(tab, ((long) i << ASHIFT) + + ABASE); + } + + static final boolean casTabAt(Node[] tab, int i, Node c, + Node v) { + return U.compareAndSwapObject(tab, ((long) i << ASHIFT) + ABASE, c, v); + } + + static final void setTabAt(Node[] tab, int i, Node v) { + U.putObjectVolatile(tab, ((long) i << ASHIFT) + ABASE, v); + } + + /* ---------------- Fields -------------- */ + + /** + * The array of bins. Lazily initialized upon first insertion. + * Size is always a power of two. Accessed directly by iterators. + */ + transient volatile Node[] table; + + /** + * The next table to use; non-null only while resizing. + */ + private transient volatile Node[] nextTable; + + /** + * Base counter value, used mainly when there is no contention, + * but also as a fallback during table initialization + * races. Updated via CAS. + */ + private transient volatile long baseCount; + + /** + * Table initialization and resizing control. When negative, the + * table is being initialized or resized: -1 for initialization, + * else -(1 + the number of active resizing threads). Otherwise, + * when table is null, holds the initial table size to use upon + * creation, or 0 for default. After initialization, holds the + * next element count value upon which to resize the table. + */ + private transient volatile int sizeCtl; + + /** + * The next table index (plus one) to split while resizing. + */ + private transient volatile int transferIndex; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating + * CounterCells. + */ + private transient volatile int cellsBusy; + + /** + * Table of counter cells. When non-null, size is a power of 2. + */ + private transient volatile CounterCell[] counterCells; + + // views + private transient KeySetView keySet; + private transient ValuesView values; + private transient EntrySetView entrySet; + + /* ---------------- Public operations -------------- */ + + /** + * Creates a new, empty map with the default initial table size (16). + */ + public ConcurrentHashMapV8() {} + + /** + * Creates a new, empty map with an initial table size + * accommodating the specified number of elements without the need + * to dynamically resize. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + */ + public ConcurrentHashMapV8(int initialCapacity) { + if(initialCapacity < 0) + throw new IllegalArgumentException(); + int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY + : tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1)); + this.sizeCtl = cap; + } + + /** + * Creates a new map with the same mappings as the given map. + * + * @param m the map + */ + public ConcurrentHashMapV8(Map m) { + this.sizeCtl = DEFAULT_CAPACITY; + putAll(m); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}) and + * initial table density ({@code loadFactor}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @throws IllegalArgumentException if the initial capacity of + * elements is negative or the load factor is nonpositive + * + * @since 1.6 + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, 1); + } + + /** + * Creates a new, empty map with an initial table size based on + * the given number of elements ({@code initialCapacity}), table + * density ({@code loadFactor}), and number of concurrently + * updating threads ({@code concurrencyLevel}). + * + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements, + * given the specified load factor. + * @param loadFactor the load factor (table density) for + * establishing the initial table size + * @param concurrencyLevel the estimated number of concurrently + * updating threads. The implementation may use this value as + * a sizing hint. + * @throws IllegalArgumentException if the initial capacity is + * negative or the load factor or concurrencyLevel are + * nonpositive + */ + public ConcurrentHashMapV8(int initialCapacity, float loadFactor, + int concurrencyLevel) { + if(!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0) + throw new IllegalArgumentException(); + if(initialCapacity < concurrencyLevel) // Use at least as many bins + initialCapacity = concurrencyLevel; // as estimated threads + long size = (long) (1.0 + (long) initialCapacity / loadFactor); + int cap = (size >= (long) MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY + : tableSizeFor((int) size); + this.sizeCtl = cap; + } + + // Original (since JDK1.2) Map methods + + /** + * {@inheritDoc} + */ + public int size() { + long n = sumCount(); + return ((n < 0L) ? 0 + : (n > (long) Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) n); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return sumCount() <= 0L; // ignore transient negative values + } + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

+ * More formally, if this map contains a mapping from a key {@code k} to a + * value {@code v} such that {@code key.equals(k)}, then this method returns + * {@code v}; otherwise it returns {@code null}. (There can be at most one + * such mapping.) + * + * @throws NullPointerException if the specified key is null + */ + public V get(Object key) { + Node[] tab; + Node e, p; + int n, eh; + K ek; + int h = spread(key.hashCode()); + if((tab = table) != null && (n = tab.length) > 0 + && (e = tabAt(tab, (n - 1) & h)) != null) { + if((eh = e.hash) == h) { + if((ek = e.key) == key || (ek != null && key.equals(ek))) + return e.val; + } + else if(eh < 0) + return (p = e.find(h, key)) != null ? p.val : null; + while ((e = e.next) != null) { + if(e.hash == h + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) + return e.val; + } + } + return null; + } + + /** + * Tests if the specified object is a key in this table. + * + * @param key possible key + * @return {@code true} if and only if the specified object + * is a key in this table, as determined by the {@code equals} + * method; {@code false} otherwise + * @throws NullPointerException if the specified key is null + */ + public boolean containsKey(Object key) { + return get(key) != null; + } + + /** + * Returns {@code true} if this map maps one or more keys to the + * specified value. Note: This method may require a full traversal + * of the map, and is much slower than method {@code containsKey}. + * + * @param value value whose presence in this map is to be tested + * @return {@code true} if this map maps one or more keys to the + * specified value + * @throws NullPointerException if the specified value is null + */ + public boolean containsValue(Object value) { + if(value == null) + throw new NullPointerException(); + Node[] t; + if((t = table) != null) { + Traverser it = new Traverser(t, t.length, 0, t.length); + for (Node p; (p = it.advance()) != null;) { + V v; + if((v = p.val) == value || (v != null && value.equals(v))) + return true; + } + } + return false; + } + + /** + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. + * + *

+ * The value can be retrieved by calling the {@code get} method with a key + * that is equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with {@code key}, or {@code null} + * if there was no mapping for {@code key} + * @throws NullPointerException if the specified key or value is null + */ + public V put(K key, V value) { + return putVal(key, value, false); + } + + /** Implementation for put and putIfAbsent */ + final V putVal(K key, V value, boolean onlyIfAbsent) { + if(key == null || value == null) + throw new NullPointerException(); + int hash = spread(key.hashCode()); + int binCount = 0; + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0) + tab = initTable(); + else if((f = tabAt(tab, i = (n - 1) & hash)) == null) { + if(casTabAt(tab, i, null, + new Node(hash, key, value, null))) + break; // no lock when adding to empty bin + } + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + V oldVal = null; + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + binCount = 1; + for (Node e = f;; ++binCount) { + K ek; + if(e.hash == hash + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + oldVal = e.val; + if(!onlyIfAbsent) + e.val = value; + break; + } + Node pred = e; + if((e = e.next) == null) { + pred.next = new Node(hash, key, + value, null); + break; + } + } + } + else if(f instanceof TreeBin) { + Node p; + binCount = 2; + if((p = ((TreeBin) f).putTreeVal(hash, key, + value)) != null) { + oldVal = p.val; + if(!onlyIfAbsent) + p.val = value; + } + } + } + } + if(binCount != 0) { + if(binCount >= TREEIFY_THRESHOLD) + treeifyBin(tab, i); + if(oldVal != null) + return oldVal; + break; + } + } + } + addCount(1L, binCount); + return null; + } + + /** + * Copies all of the mappings from the specified map to this one. + * These mappings replace any mappings that this map had for any of the + * keys currently in the specified map. + * + * @param m mappings to be stored in this map + */ + public void putAll(Map m) { + tryPresize(m.size()); + for (Map.Entry e : m.entrySet()) + putVal(e.getKey(), e.getValue(), false); + } + + /** + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @param key the key that needs to be removed + * @return the previous value associated with {@code key}, or {@code null} + * if there was no mapping for {@code key} + * @throws NullPointerException if the specified key is null + */ + public V remove(Object key) { + return replaceNode(key, null, null); + } + + /** + * Implementation for the four public remove/replace methods: + * Replaces node value with v, conditional upon match of cv if + * non-null. If resulting value is null, delete. + */ + final V replaceNode(Object key, V value, Object cv) { + int hash = spread(key.hashCode()); + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0 + || (f = tabAt(tab, i = (n - 1) & hash)) == null) + break; + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + V oldVal = null; + boolean validated = false; + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + validated = true; + for (Node e = f, pred = null;;) { + K ek; + if(e.hash == hash + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + V ev = e.val; + if(cv == null || cv == ev + || (ev != null && cv.equals(ev))) { + oldVal = ev; + if(value != null) + e.val = value; + else if(pred != null) + pred.next = e.next; + else + setTabAt(tab, i, e.next); + } + break; + } + pred = e; + if((e = e.next) == null) + break; + } + } + else if(f instanceof TreeBin) { + validated = true; + TreeBin t = (TreeBin) f; + TreeNode r, p; + if((r = t.root) != null + && (p = r.findTreeNode(hash, key, null)) != null) { + V pv = p.val; + if(cv == null || cv == pv + || (pv != null && cv.equals(pv))) { + oldVal = pv; + if(value != null) + p.val = value; + else if(t.removeTreeNode(p)) + setTabAt(tab, i, untreeify(t.first)); + } + } + } + } + } + if(validated) { + if(oldVal != null) { + if(value == null) + addCount(-1L, -1); + return oldVal; + } + break; + } + } + } + return null; + } + + /** + * Removes all of the mappings from this map. + */ + public void clear() { + long delta = 0L; // negative number of deletions + int i = 0; + Node[] tab = table; + while (tab != null && i < tab.length) { + int fh; + Node f = tabAt(tab, i); + if(f == null) + ++i; + else if((fh = f.hash) == MOVED) { + tab = helpTransfer(tab, f); + i = 0; // restart + } + else { + synchronized (f) { + if(tabAt(tab, i) == f) { + Node p = (fh >= 0 ? f + : (f instanceof TreeBin) ? ((TreeBin) f).first + : null); + while (p != null) { + --delta; + p = p.next; + } + setTabAt(tab, i++, null); + } + } + } + } + if(delta != 0L) + addCount(delta, -1); + } + + /** + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from this map, + * via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, + * {@code retainAll}, and {@code clear} operations. It does not support the + * {@code add} or {@code addAll} operations. + * + *

+ * The view's {@code iterator} is a "weakly consistent" iterator that will + * never throw {@link ConcurrentModificationException}, and guarantees to + * traverse elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return the set view + */ + public KeySetView keySet() { + KeySetView ks; + return (ks = keySet) != null ? ks : (keySet = new KeySetView( + this, null)); + } + + /** + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. The collection + * supports element removal, which removes the corresponding + * mapping from this map, via the {@code Iterator.remove}, + * {@code Collection.remove}, {@code removeAll}, {@code retainAll}, and + * {@code clear} operations. It does not + * support the {@code add} or {@code addAll} operations. + * + *

+ * The view's {@code iterator} is a "weakly consistent" iterator that will + * never throw {@link ConcurrentModificationException}, and guarantees to + * traverse elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return the collection view + */ + public Collection values() { + ValuesView vs; + return (vs = values) != null ? vs + : (values = new ValuesView(this)); + } + + /** + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, + * {@code retainAll}, and {@code clear} operations. + * + *

+ * The view's {@code iterator} is a "weakly consistent" iterator that will + * never throw {@link ConcurrentModificationException}, and guarantees to + * traverse elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return the set view + */ + public Set> entrySet() { + EntrySetView es; + return (es = entrySet) != null ? es + : (entrySet = new EntrySetView(this)); + } + + /** + * Returns the hash code value for this {@link Map}, i.e., + * the sum of, for each key-value pair in the map, + * {@code key.hashCode() ^ value.hashCode()}. + * + * @return the hash code value for this map + */ + public int hashCode() { + int h = 0; + Node[] t; + if((t = table) != null) { + Traverser it = new Traverser(t, t.length, 0, t.length); + for (Node p; (p = it.advance()) != null;) + h += p.key.hashCode() ^ p.val.hashCode(); + } + return h; + } + + /** + * Returns a string representation of this map. The string + * representation consists of a list of key-value mappings (in no + * particular order) enclosed in braces ("{@code "). Adjacent + * mappings are separated by the characters {@code ", "} (comma + * and space). Each key-value mapping is rendered as the key + * followed by an equals sign ("{@code =}") followed by the + * associated value. + * + * @return a string representation of this map + */ + public String toString() { + Node[] t; + int f = (t = table) == null ? 0 : t.length; + Traverser it = new Traverser(t, f, 0, f); + StringBuilder sb = new StringBuilder(); + sb.append('{'); + Node p; + if((p = it.advance()) != null) { + for (;;) { + K k = p.key; + V v = p.val; + sb.append(k == this ? "(this Map)" : k); + sb.append('='); + sb.append(v == this ? "(this Map)" : v); + if((p = it.advance()) == null) + break; + sb.append(',').append(' '); + } + } + return sb.append('}').toString(); + } + + /** + * Compares the specified object with this map for equality. + * Returns {@code true} if the given object is a map with the same + * mappings as this map. This operation may return misleading + * results if either map is concurrently modified during execution + * of this method. + * + * @param o object to be compared for equality with this map + * @return {@code true} if the specified object is equal to this map + */ + public boolean equals(Object o) { + if(o != this) { + if(!(o instanceof Map)) + return false; + Map m = (Map) o; + Node[] t; + int f = (t = table) == null ? 0 : t.length; + Traverser it = new Traverser(t, f, 0, f); + for (Node p; (p = it.advance()) != null;) { + V val = p.val; + Object v = m.get(p.key); + if(v == null || (v != val && !v.equals(val))) + return false; + } + for (Map.Entry e : m.entrySet()) { + Object mk, mv, v; + if((mk = e.getKey()) == null || (mv = e.getValue()) == null + || (v = get(mk)) == null || (mv != v && !mv.equals(v))) + return false; + } + } + return true; + } + + /** + * Stripped-down version of helper class used in previous version, + * declared for the sake of serialization compatibility + */ + static class Segment extends ReentrantLock implements Serializable { + private static final long serialVersionUID = 2249069246763182397L; + final float loadFactor; + + Segment(float lf) { + this.loadFactor = lf; + } + } + + /** + * Saves the state of the {@code ConcurrentHashMapV8} instance to a + * stream (i.e., serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @serialData + * the key (Object) and value (Object) + * for each key-value mapping, followed by a null pair. + * The key-value mappings are emitted in no particular order. + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + // For serialization compatibility + // Emulate segment calculation from previous version of this class + int sshift = 0; + int ssize = 1; + while (ssize < DEFAULT_CONCURRENCY_LEVEL) { + ++sshift; + ssize <<= 1; + } + int segmentShift = 32 - sshift; + int segmentMask = ssize - 1; + @SuppressWarnings("unchecked") Segment[] segments = (Segment[]) new Segment[DEFAULT_CONCURRENCY_LEVEL]; + for (int i = 0; i < segments.length; ++i) + segments[i] = new Segment(LOAD_FACTOR); + s.putFields().put("segments", segments); + s.putFields().put("segmentShift", segmentShift); + s.putFields().put("segmentMask", segmentMask); + s.writeFields(); + + Node[] t; + if((t = table) != null) { + Traverser it = new Traverser(t, t.length, 0, t.length); + for (Node p; (p = it.advance()) != null;) { + s.writeObject(p.key); + s.writeObject(p.val); + } + } + s.writeObject(null); + s.writeObject(null); + segments = null; // throw away + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + /* + * To improve performance in typical cases, we create nodes + * while reading, then place in table once size is known. + * However, we must also validate uniqueness and deal with + * overpopulated bins while doing so, which requires + * specialized versions of putVal mechanics. + */ + sizeCtl = -1; // force exclusion for table construction + s.defaultReadObject(); + long size = 0L; + Node p = null; + for (;;) { + @SuppressWarnings("unchecked") K k = (K) s.readObject(); + @SuppressWarnings("unchecked") V v = (V) s.readObject(); + if(k != null && v != null) { + p = new Node(spread(k.hashCode()), k, v, p); + ++size; + } + else + break; + } + if(size == 0L) + sizeCtl = 0; + else { + int n; + if(size >= (long) (MAXIMUM_CAPACITY >>> 1)) + n = MAXIMUM_CAPACITY; + else { + int sz = (int) size; + n = tableSizeFor(sz + (sz >>> 1) + 1); + } + @SuppressWarnings("unchecked") Node[] tab = (Node[]) new Node[n]; + int mask = n - 1; + long added = 0L; + while (p != null) { + boolean insertAtFront; + Node next = p.next, first; + int h = p.hash, j = h & mask; + if((first = tabAt(tab, j)) == null) + insertAtFront = true; + else { + K k = p.key; + if(first.hash < 0) { + TreeBin t = (TreeBin) first; + if(t.putTreeVal(h, k, p.val) == null) + ++added; + insertAtFront = false; + } + else { + int binCount = 0; + insertAtFront = true; + Node q; + K qk; + for (q = first; q != null; q = q.next) { + if(q.hash == h + && ((qk = q.key) == k || (qk != null && k + .equals(qk)))) { + insertAtFront = false; + break; + } + ++binCount; + } + if(insertAtFront && binCount >= TREEIFY_THRESHOLD) { + insertAtFront = false; + ++added; + p.next = first; + TreeNode hd = null, tl = null; + for (q = p; q != null; q = q.next) { + TreeNode t = new TreeNode(q.hash, + q.key, q.val, null, null); + if((t.prev = tl) == null) + hd = t; + else + tl.next = t; + tl = t; + } + setTabAt(tab, j, new TreeBin(hd)); + } + } + } + if(insertAtFront) { + ++added; + p.next = first; + setTabAt(tab, j, p); + } + p = next; + } + table = tab; + sizeCtl = n - (n >>> 2); + baseCount = added; + } + } + + // ConcurrentMap methods + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + public V putIfAbsent(K key, V value) { + return putVal(key, value, true); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object key, Object value) { + if(key == null) + throw new NullPointerException(); + return value != null && replaceNode(key, null, value) != null; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null + */ + public boolean replace(K key, V oldValue, V newValue) { + if(key == null || oldValue == null || newValue == null) + throw new NullPointerException(); + return replaceNode(key, newValue, oldValue) != null; + } + + /** + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or {@code null} if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ + public V replace(K key, V value) { + if(key == null || value == null) + throw new NullPointerException(); + return replaceNode(key, value, null); + } + + // Overrides of JDK8+ Map extension method defaults + + /** + * Returns the value to which the specified key is mapped, or the + * given default value if this map contains no mapping for the + * key. + * + * @param key the key whose associated value is to be returned + * @param defaultValue the value to return if this map contains + * no mapping for the given key + * @return the mapping for the key, if present; else the default value + * @throws NullPointerException if the specified key is null + */ + public V getOrDefault(Object key, V defaultValue) { + V v; + return (v = get(key)) == null ? defaultValue : v; + } + + public void forEach(BiAction action) { + if(action == null) + throw new NullPointerException(); + Node[] t; + if((t = table) != null) { + Traverser it = new Traverser(t, t.length, 0, t.length); + for (Node p; (p = it.advance()) != null;) { + action.apply(p.key, p.val); + } + } + } + + public void replaceAll(BiFun function) { + if(function == null) + throw new NullPointerException(); + Node[] t; + if((t = table) != null) { + Traverser it = new Traverser(t, t.length, 0, t.length); + for (Node p; (p = it.advance()) != null;) { + V oldValue = p.val; + for (K key = p.key;;) { + V newValue = function.apply(key, oldValue); + if(newValue == null) + throw new NullPointerException(); + if(replaceNode(key, newValue, oldValue) != null + || (oldValue = get(key)) == null) + break; + } + } + } + } + + /** + * If the specified key is not already associated with a value, + * attempts to compute its value using the given mapping function + * and enters it into this map unless {@code null}. The entire + * method invocation is performed atomically, so the function is + * applied at most once per key. Some attempted update operations + * on this map by other threads may be blocked while computation + * is in progress, so the computation should be short and simple, + * and must not attempt to update any other mappings of this map. + * + * @param key key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with + * the specified key, or null if the computed value is null + * @throws NullPointerException if the specified key or mappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the mappingFunction does so, + * in which case the mapping is left unestablished + */ + public V computeIfAbsent(K key, Fun mappingFunction) { + if(key == null || mappingFunction == null) + throw new NullPointerException(); + int h = spread(key.hashCode()); + V val = null; + int binCount = 0; + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0) + tab = initTable(); + else if((f = tabAt(tab, i = (n - 1) & h)) == null) { + Node r = new ReservationNode(); + synchronized (r) { + if(casTabAt(tab, i, null, r)) { + binCount = 1; + Node node = null; + try { + if((val = mappingFunction.apply(key)) != null) + node = new Node(h, key, val, null); + } + finally { + setTabAt(tab, i, node); + } + } + } + if(binCount != 0) + break; + } + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + boolean added = false; + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + binCount = 1; + for (Node e = f;; ++binCount) { + K ek; + V ev; + if(e.hash == h + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + val = e.val; + break; + } + Node pred = e; + if((e = e.next) == null) { + if((val = mappingFunction.apply(key)) != null) { + added = true; + pred.next = new Node(h, key, val, + null); + } + break; + } + } + } + else if(f instanceof TreeBin) { + binCount = 2; + TreeBin t = (TreeBin) f; + TreeNode r, p; + if((r = t.root) != null + && (p = r.findTreeNode(h, key, null)) != null) + val = p.val; + else if((val = mappingFunction.apply(key)) != null) { + added = true; + t.putTreeVal(h, key, val); + } + } + } + } + if(binCount != 0) { + if(binCount >= TREEIFY_THRESHOLD) + treeifyBin(tab, i); + if(!added) + return val; + break; + } + } + } + if(val != null) + addCount(1L, binCount); + return val; + } + + /** + * If the value for the specified key is present, attempts to + * compute a new mapping given the key and its current mapped + * value. The entire method invocation is performed atomically. + * Some attempted update operations on this map by other threads + * may be blocked while computation is in progress, so the + * computation should be short and simple, and must not attempt to + * update any other mappings of this map. + * + * @param key key with which a value may be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + public V computeIfPresent(K key, + BiFun remappingFunction) { + if(key == null || remappingFunction == null) + throw new NullPointerException(); + int h = spread(key.hashCode()); + V val = null; + int delta = 0; + int binCount = 0; + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0) + tab = initTable(); + else if((f = tabAt(tab, i = (n - 1) & h)) == null) + break; + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + binCount = 1; + for (Node e = f, pred = null;; ++binCount) { + K ek; + if(e.hash == h + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + val = remappingFunction.apply(key, e.val); + if(val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if(pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if((e = e.next) == null) + break; + } + } + else if(f instanceof TreeBin) { + binCount = 2; + TreeBin t = (TreeBin) f; + TreeNode r, p; + if((r = t.root) != null + && (p = r.findTreeNode(h, key, null)) != null) { + val = remappingFunction.apply(key, p.val); + if(val != null) + p.val = val; + else { + delta = -1; + if(t.removeTreeNode(p)) + setTabAt(tab, i, untreeify(t.first)); + } + } + } + } + } + if(binCount != 0) + break; + } + } + if(delta != 0) + addCount((long) delta, binCount); + return val; + } + + /** + * Attempts to compute a mapping for the specified key and its + * current mapped value (or {@code null} if there is no current + * mapping). The entire method invocation is performed atomically. + * Some attempted update operations on this map by other threads + * may be blocked while computation is in progress, so the + * computation should be short and simple, and must not attempt to + * update any other mappings of this Map. + * + * @param key key with which the specified value is to be associated + * @param remappingFunction the function to compute a value + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or remappingFunction + * is null + * @throws IllegalStateException if the computation detectably + * attempts a recursive update to this map that would + * otherwise never complete + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + public V compute(K key, + BiFun remappingFunction) { + if(key == null || remappingFunction == null) + throw new NullPointerException(); + int h = spread(key.hashCode()); + V val = null; + int delta = 0; + int binCount = 0; + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0) + tab = initTable(); + else if((f = tabAt(tab, i = (n - 1) & h)) == null) { + Node r = new ReservationNode(); + synchronized (r) { + if(casTabAt(tab, i, null, r)) { + binCount = 1; + Node node = null; + try { + if((val = remappingFunction.apply(key, null)) != null) { + delta = 1; + node = new Node(h, key, val, null); + } + } + finally { + setTabAt(tab, i, node); + } + } + } + if(binCount != 0) + break; + } + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + binCount = 1; + for (Node e = f, pred = null;; ++binCount) { + K ek; + if(e.hash == h + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + val = remappingFunction.apply(key, e.val); + if(val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if(pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if((e = e.next) == null) { + val = remappingFunction.apply(key, null); + if(val != null) { + delta = 1; + pred.next = new Node(h, key, val, + null); + } + break; + } + } + } + else if(f instanceof TreeBin) { + binCount = 1; + TreeBin t = (TreeBin) f; + TreeNode r, p; + if((r = t.root) != null) + p = r.findTreeNode(h, key, null); + else + p = null; + V pv = (p == null) ? null : p.val; + val = remappingFunction.apply(key, pv); + if(val != null) { + if(p != null) + p.val = val; + else { + delta = 1; + t.putTreeVal(h, key, val); + } + } + else if(p != null) { + delta = -1; + if(t.removeTreeNode(p)) + setTabAt(tab, i, untreeify(t.first)); + } + } + } + } + if(binCount != 0) { + if(binCount >= TREEIFY_THRESHOLD) + treeifyBin(tab, i); + break; + } + } + } + if(delta != 0) + addCount((long) delta, binCount); + return val; + } + + /** + * If the specified key is not already associated with a + * (non-null) value, associates it with the given value. + * Otherwise, replaces the value with the results of the given + * remapping function, or removes if {@code null}. The entire + * method invocation is performed atomically. Some attempted + * update operations on this map by other threads may be blocked + * while computation is in progress, so the computation should be + * short and simple, and must not attempt to update any other + * mappings of this Map. + * + * @param key key with which the specified value is to be associated + * @param value the value to use if absent + * @param remappingFunction the function to recompute a value if present + * @return the new value associated with the specified key, or null if none + * @throws NullPointerException if the specified key or the + * remappingFunction is null + * @throws RuntimeException or Error if the remappingFunction does so, + * in which case the mapping is unchanged + */ + public V merge(K key, V value, + BiFun remappingFunction) { + if(key == null || value == null || remappingFunction == null) + throw new NullPointerException(); + int h = spread(key.hashCode()); + V val = null; + int delta = 0; + int binCount = 0; + for (Node[] tab = table;;) { + Node f; + int n, i, fh; + if(tab == null || (n = tab.length) == 0) + tab = initTable(); + else if((f = tabAt(tab, i = (n - 1) & h)) == null) { + if(casTabAt(tab, i, null, new Node(h, key, value, null))) { + delta = 1; + val = value; + break; + } + } + else if((fh = f.hash) == MOVED) + tab = helpTransfer(tab, f); + else { + synchronized (f) { + if(tabAt(tab, i) == f) { + if(fh >= 0) { + binCount = 1; + for (Node e = f, pred = null;; ++binCount) { + K ek; + if(e.hash == h + && ((ek = e.key) == key || (ek != null && key + .equals(ek)))) { + val = remappingFunction.apply(e.val, value); + if(val != null) + e.val = val; + else { + delta = -1; + Node en = e.next; + if(pred != null) + pred.next = en; + else + setTabAt(tab, i, en); + } + break; + } + pred = e; + if((e = e.next) == null) { + delta = 1; + val = value; + pred.next = new Node(h, key, val, + null); + break; + } + } + } + else if(f instanceof TreeBin) { + binCount = 2; + TreeBin t = (TreeBin) f; + TreeNode r = t.root; + TreeNode p = (r == null) ? null : r + .findTreeNode(h, key, null); + val = (p == null) ? value : remappingFunction + .apply(p.val, value); + if(val != null) { + if(p != null) + p.val = val; + else { + delta = 1; + t.putTreeVal(h, key, val); + } + } + else if(p != null) { + delta = -1; + if(t.removeTreeNode(p)) + setTabAt(tab, i, untreeify(t.first)); + } + } + } + } + if(binCount != 0) { + if(binCount >= TREEIFY_THRESHOLD) + treeifyBin(tab, i); + break; + } + } + } + if(delta != 0) + addCount((long) delta, binCount); + return val; + } + + // Hashtable legacy methods + + /** + * Legacy method testing if some key maps into the specified value + * in this table. This method is identical in functionality to + * {@link #containsValue(Object)}, and exists solely to ensure + * full compatibility with class {@link java.util.Hashtable}, + * which supported this method prior to introduction of the + * Java Collections framework. + * + * @param value a value to search for + * @return {@code true} if and only if some key maps to the {@code value} + * argument in this table as + * determined by the {@code equals} method; {@code false} otherwise + * @throws NullPointerException if the specified value is null + */ + @Deprecated + public boolean contains(Object value) { + return containsValue(value); + } + + /** + * Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() + */ + public Enumeration keys() { + Node[] t; + int f = (t = table) == null ? 0 : t.length; + return new KeyIterator(t, f, 0, f, this); + } + + /** + * Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() + */ + public Enumeration elements() { + Node[] t; + int f = (t = table) == null ? 0 : t.length; + return new ValueIterator(t, f, 0, f, this); + } + + // ConcurrentHashMapV8-only methods + + /** + * Returns the number of mappings. This method should be used + * instead of {@link #size} because a ConcurrentHashMapV8 may + * contain more mappings than can be represented as an int. The + * value returned is an estimate; the actual count may differ if + * there are concurrent insertions or removals. + * + * @return the number of mappings + * @since 1.8 + */ + public long mappingCount() { + long n = sumCount(); + return (n < 0L) ? 0L : n; // ignore transient negative values + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @return the new set + * @since 1.8 + */ + public static KeySetView newKeySet() { + return new KeySetView( + new ConcurrentHashMapV8(), Boolean.TRUE); + } + + /** + * Creates a new {@link Set} backed by a ConcurrentHashMapV8 + * from the given type to {@code Boolean.TRUE}. + * + * @param initialCapacity The implementation performs internal + * sizing to accommodate this many elements. + * @return the new set + * @throws IllegalArgumentException if the initial capacity of + * elements is negative + * @since 1.8 + */ + public static KeySetView newKeySet(int initialCapacity) { + return new KeySetView(new ConcurrentHashMapV8( + initialCapacity), Boolean.TRUE); + } + + /** + * Returns a {@link Set} view of the keys in this map, using the + * given common mapped value for any additions (i.e., {@link Collection#add} + * and {@link Collection#addAll(Collection)}). + * This is of course only appropriate if it is acceptable to use + * the same value for all additions from this view. + * + * @param mappedValue the mapped value to use for any additions + * @return the set view + * @throws NullPointerException if the mappedValue is null + */ + public KeySetView keySet(V mappedValue) { + if(mappedValue == null) + throw new NullPointerException(); + return new KeySetView(this, mappedValue); + } + + /* ---------------- Special Nodes -------------- */ + + /** + * A node inserted at head of bins during transfer operations. + */ + static final class ForwardingNode extends Node { + final Node[] nextTable; + + ForwardingNode(Node[] tab) { + super(MOVED, null, null, null); + this.nextTable = tab; + } + + Node find(int h, Object k) { + // loop to avoid arbitrarily deep recursion on forwarding nodes + outer: for (Node[] tab = nextTable;;) { + Node e; + int n; + if(k == null || tab == null || (n = tab.length) == 0 + || (e = tabAt(tab, (n - 1) & h)) == null) + return null; + for (;;) { + int eh; + K ek; + if((eh = e.hash) == h + && ((ek = e.key) == k || (ek != null && k + .equals(ek)))) + return e; + if(eh < 0) { + if(e instanceof ForwardingNode) { + tab = ((ForwardingNode) e).nextTable; + continue outer; + } + else + return e.find(h, k); + } + if((e = e.next) == null) + return null; + } + } + } + } + + /** + * A place-holder node used in computeIfAbsent and compute + */ + static final class ReservationNode extends Node { + ReservationNode() { + super(RESERVED, null, null, null); + } + + Node find(int h, Object k) { + return null; + } + } + + /* ---------------- Table Initialization and Resizing -------------- */ + + /** + * Returns the stamp bits for resizing a table of size n. + * Must be negative when shifted left by RESIZE_STAMP_SHIFT. + */ + static final int resizeStamp(int n) { + return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); + } + + /** + * Initializes table, using the size recorded in sizeCtl. + */ + private final Node[] initTable() { + Node[] tab; + int sc; + while ((tab = table) == null || tab.length == 0) { + if((sc = sizeCtl) < 0) + Thread.yield(); // lost initialization race; just spin + else if(U.compareAndSwapInt(this, SIZECTL, sc, -1)) { + try { + if((tab = table) == null || tab.length == 0) { + int n = (sc > 0) ? sc : DEFAULT_CAPACITY; + @SuppressWarnings("unchecked") Node[] nt = (Node[]) new Node[n]; + table = tab = nt; + sc = n - (n >>> 2); + } + } + finally { + sizeCtl = sc; + } + break; + } + } + return tab; + } + + /** + * Adds to count, and if table is too small and not already + * resizing, initiates transfer. If already resizing, helps + * perform transfer if work is available. Rechecks occupancy + * after a transfer to see if another resize is already needed + * because resizings are lagging additions. + * + * @param x the count to add + * @param check if <0, don't check resize, if <= 1 only check if uncontended + */ + private final void addCount(long x, int check) { + CounterCell[] as; + long b, s; + if((as = counterCells) != null + || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + + x)) { + CounterHashCode hc; + CounterCell a; + long v; + int m; + boolean uncontended = true; + if((hc = threadCounterHashCode.get()) == null + || as == null + || (m = as.length - 1) < 0 + || (a = as[m & hc.code]) == null + || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, + v = a.value, v + x))) { + fullAddCount(x, hc, uncontended); + return; + } + if(check <= 1) + return; + s = sumCount(); + } + if(check >= 0) { + Node[] tab, nt; + int n, sc; + while (s >= (long) (sc = sizeCtl) && (tab = table) != null + && (n = tab.length) < MAXIMUM_CAPACITY) { + int rs = resizeStamp(n); + if(sc < 0) { + if((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 + || sc == rs + MAX_RESIZERS + || (nt = nextTable) == null || transferIndex <= 0) + break; + if(U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) + transfer(tab, nt); + } + else if(U.compareAndSwapInt(this, SIZECTL, sc, + (rs << RESIZE_STAMP_SHIFT) + 2)) + transfer(tab, null); + s = sumCount(); + } + } + } + + /** + * Helps transfer if a resize is in progress. + */ + final Node[] helpTransfer(Node[] tab, Node f) { + Node[] nextTab; + int sc; + if(tab != null && (f instanceof ForwardingNode) + && (nextTab = ((ForwardingNode) f).nextTable) != null) { + int rs = resizeStamp(tab.length); + while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) { + if((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 + || sc == rs + MAX_RESIZERS || transferIndex <= 0) + break; + if(U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) { + transfer(tab, nextTab); + break; + } + } + return nextTab; + } + return table; + } + + /** + * Tries to presize table to accommodate the given number of elements. + * + * @param size number of elements (doesn't need to be perfectly accurate) + */ + private final void tryPresize(int size) { + int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY + : tableSizeFor(size + (size >>> 1) + 1); + int sc; + while ((sc = sizeCtl) >= 0) { + Node[] tab = table; + int n; + if(tab == null || (n = tab.length) == 0) { + n = (sc > c) ? sc : c; + if(U.compareAndSwapInt(this, SIZECTL, sc, -1)) { + try { + if(table == tab) { + @SuppressWarnings("unchecked") Node[] nt = (Node[]) new Node[n]; + table = nt; + sc = n - (n >>> 2); + } + } + finally { + sizeCtl = sc; + } + } + } + else if(c <= sc || n >= MAXIMUM_CAPACITY) + break; + else if(tab == table) { + int rs = resizeStamp(n); + if(sc < 0) { + Node[] nt; + if((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 + || sc == rs + MAX_RESIZERS + || (nt = nextTable) == null || transferIndex <= 0) + break; + if(U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) + transfer(tab, nt); + } + else if(U.compareAndSwapInt(this, SIZECTL, sc, + (rs << RESIZE_STAMP_SHIFT) + 2)) + transfer(tab, null); + } + } + } + + /** + * Moves and/or copies the nodes in each bin to new table. See + * above for explanation. + */ + private final void transfer(Node[] tab, Node[] nextTab) { + int n = tab.length, stride; + if((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE) + stride = MIN_TRANSFER_STRIDE; // subdivide range + if(nextTab == null) { // initiating + try { + @SuppressWarnings("unchecked") Node[] nt = (Node[]) new Node[n << 1]; + nextTab = nt; + } + catch (Throwable ex) { // try to cope with OOME + sizeCtl = Integer.MAX_VALUE; + return; + } + nextTable = nextTab; + transferIndex = n; + } + int nextn = nextTab.length; + ForwardingNode fwd = new ForwardingNode(nextTab); + boolean advance = true; + boolean finishing = false; // to ensure sweep before committing nextTab + for (int i = 0, bound = 0;;) { + Node f; + int fh; + while (advance) { + int nextIndex, nextBound; + if(--i >= bound || finishing) + advance = false; + else if((nextIndex = transferIndex) <= 0) { + i = -1; + advance = false; + } + else if(U.compareAndSwapInt(this, TRANSFERINDEX, nextIndex, + nextBound = (nextIndex > stride ? nextIndex - stride + : 0))) { + bound = nextBound; + i = nextIndex - 1; + advance = false; + } + } + if(i < 0 || i >= n || i + n >= nextn) { + int sc; + if(finishing) { + nextTable = null; + table = nextTab; + sizeCtl = (n << 1) - (n >>> 1); + return; + } + if(U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) { + if((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT) + return; + finishing = advance = true; + i = n; // recheck before commit + } + } + else if((f = tabAt(tab, i)) == null) + advance = casTabAt(tab, i, null, fwd); + else if((fh = f.hash) == MOVED) + advance = true; // already processed + else { + synchronized (f) { + if(tabAt(tab, i) == f) { + Node ln, hn; + if(fh >= 0) { + int runBit = fh & n; + Node lastRun = f; + for (Node p = f.next; p != null; p = p.next) { + int b = p.hash & n; + if(b != runBit) { + runBit = b; + lastRun = p; + } + } + if(runBit == 0) { + ln = lastRun; + hn = null; + } + else { + hn = lastRun; + ln = null; + } + for (Node p = f; p != lastRun; p = p.next) { + int ph = p.hash; + K pk = p.key; + V pv = p.val; + if((ph & n) == 0) + ln = new Node(ph, pk, pv, ln); + else + hn = new Node(ph, pk, pv, hn); + } + setTabAt(nextTab, i, ln); + setTabAt(nextTab, i + n, hn); + setTabAt(tab, i, fwd); + advance = true; + } + else if(f instanceof TreeBin) { + TreeBin t = (TreeBin) f; + TreeNode lo = null, loTail = null; + TreeNode hi = null, hiTail = null; + int lc = 0, hc = 0; + for (Node e = t.first; e != null; e = e.next) { + int h = e.hash; + TreeNode p = new TreeNode(h, e.key, + e.val, null, null); + if((h & n) == 0) { + if((p.prev = loTail) == null) + lo = p; + else + loTail.next = p; + loTail = p; + ++lc; + } + else { + if((p.prev = hiTail) == null) + hi = p; + else + hiTail.next = p; + hiTail = p; + ++hc; + } + } + ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) + : (hc != 0) ? new TreeBin(lo) : t; + hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) + : (lc != 0) ? new TreeBin(hi) : t; + setTabAt(nextTab, i, ln); + setTabAt(nextTab, i + n, hn); + setTabAt(tab, i, fwd); + advance = true; + } + } + } + } + } + } + + /* ---------------- Conversion from/to TreeBins -------------- */ + + /** + * Replaces all linked nodes in bin at given index unless table is + * too small, in which case resizes instead. + */ + private final void treeifyBin(Node[] tab, int index) { + Node b; + int n, sc; + if(tab != null) { + if((n = tab.length) < MIN_TREEIFY_CAPACITY) + tryPresize(n << 1); + else if((b = tabAt(tab, index)) != null && b.hash >= 0) { + synchronized (b) { + if(tabAt(tab, index) == b) { + TreeNode hd = null, tl = null; + for (Node e = b; e != null; e = e.next) { + TreeNode p = new TreeNode(e.hash, + e.key, e.val, null, null); + if((p.prev = tl) == null) + hd = p; + else + tl.next = p; + tl = p; + } + setTabAt(tab, index, new TreeBin(hd)); + } + } + } + } + } + + /** + * Returns a list on non-TreeNodes replacing those in given list. + */ + static Node untreeify(Node b) { + Node hd = null, tl = null; + for (Node q = b; q != null; q = q.next) { + Node p = new Node(q.hash, q.key, q.val, null); + if(tl == null) + hd = p; + else + tl.next = p; + tl = p; + } + return hd; + } + + /* ---------------- TreeNodes -------------- */ + + /** + * Nodes for use in TreeBins + */ + static final class TreeNode extends Node { + TreeNode parent; // red-black tree links + TreeNode left; + TreeNode right; + TreeNode prev; // needed to unlink next upon deletion + boolean red; + + TreeNode(int hash, K key, V val, Node next, TreeNode parent) { + super(hash, key, val, next); + this.parent = parent; + } + + Node find(int h, Object k) { + return findTreeNode(h, k, null); + } + + /** + * Returns the TreeNode (or null if not found) for the given key + * starting at given root. + */ + final TreeNode findTreeNode(int h, Object k, Class kc) { + if(k != null) { + TreeNode p = this; + do { + int ph, dir; + K pk; + TreeNode q; + TreeNode pl = p.left, pr = p.right; + if((ph = p.hash) > h) + p = pl; + else if(ph < h) + p = pr; + else if((pk = p.key) == k || (pk != null && k.equals(pk))) + return p; + else if(pl == null) + p = pr; + else if(pr == null) + p = pl; + else if((kc != null || (kc = comparableClassFor(k)) != null) + && (dir = compareComparables(kc, k, pk)) != 0) + p = (dir < 0) ? pl : pr; + else if((q = pr.findTreeNode(h, k, kc)) != null) + return q; + else + p = pl; + } + while (p != null); + } + return null; + } + } + + /* ---------------- TreeBins -------------- */ + + /** + * TreeNodes used at the heads of bins. TreeBins do not hold user + * keys or values, but instead point to list of TreeNodes and + * their root. They also maintain a parasitic read-write lock + * forcing writers (who hold bin lock) to wait for readers (who do + * not) to complete before tree restructuring operations. + */ + static final class TreeBin extends Node { + TreeNode root; + volatile TreeNode first; + volatile Thread waiter; + volatile int lockState; + // values for lockState + static final int WRITER = 1; // set while holding write lock + static final int WAITER = 2; // set when waiting for write lock + static final int READER = 4; // increment value for setting read lock + + /** + * Tie-breaking utility for ordering insertions when equal + * hashCodes and non-comparable. We don't require a total + * order, just a consistent insertion rule to maintain + * equivalence across rebalancings. Tie-breaking further than + * necessary simplifies testing a bit. + */ + static int tieBreakOrder(Object a, Object b) { + int d; + if(a == null + || b == null + || (d = a.getClass().getName() + .compareTo(b.getClass().getName())) == 0) + d = (System.identityHashCode(a) <= System.identityHashCode(b) ? -1 + : 1); + return d; + } + + /** + * Creates bin with initial set of nodes headed by b. + */ + TreeBin(TreeNode b) { + super(TREEBIN, null, null, null); + this.first = b; + TreeNode r = null; + for (TreeNode x = b, next; x != null; x = next) { + next = (TreeNode) x.next; + x.left = x.right = null; + if(r == null) { + x.parent = null; + x.red = false; + r = x; + } + else { + K k = x.key; + int h = x.hash; + Class kc = null; + for (TreeNode p = r;;) { + int dir, ph; + K pk = p.key; + if((ph = p.hash) > h) + dir = -1; + else if(ph < h) + dir = 1; + else if((kc == null && (kc = comparableClassFor(k)) == null) + || (dir = compareComparables(kc, k, pk)) == 0) + dir = tieBreakOrder(k, pk); + TreeNode xp = p; + if((p = (dir <= 0) ? p.left : p.right) == null) { + x.parent = xp; + if(dir <= 0) + xp.left = x; + else + xp.right = x; + r = balanceInsertion(r, x); + break; + } + } + } + } + this.root = r; + assert checkInvariants(root); + } + + /** + * Acquires write lock for tree restructuring. + */ + private final void lockRoot() { + if(!U.compareAndSwapInt(this, LOCKSTATE, 0, WRITER)) + contendedLock(); // offload to separate method + } + + /** + * Releases write lock for tree restructuring. + */ + private final void unlockRoot() { + lockState = 0; + } + + /** + * Possibly blocks awaiting root lock. + */ + private final void contendedLock() { + boolean waiting = false; + for (int s;;) { + if(((s = lockState) & ~WAITER) == 0) { + if(U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) { + if(waiting) + waiter = null; + return; + } + } + else if((s & WAITER) == 0) { + if(U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) { + waiting = true; + waiter = Thread.currentThread(); + } + } + else if(waiting) + LockSupport.park(this); + } + } + + /** + * Returns matching node or null if none. Tries to search + * using tree comparisons from root, but continues linear + * search when lock not available. + */ + final Node find(int h, Object k) { + if(k != null) { + for (Node e = first; e != null;) { + int s; + K ek; + if(((s = lockState) & (WAITER | WRITER)) != 0) { + if(e.hash == h + && ((ek = e.key) == k || (ek != null && k + .equals(ek)))) + return e; + e = e.next; + } + else if(U.compareAndSwapInt(this, LOCKSTATE, s, s + READER)) { + TreeNode r, p; + try { + p = ((r = root) == null ? null : r.findTreeNode(h, + k, null)); + } + finally { + Thread w; + int ls; + do {} + while (!U.compareAndSwapInt(this, LOCKSTATE, + ls = lockState, ls - READER)); + if(ls == (READER | WAITER) && (w = waiter) != null) + LockSupport.unpark(w); + } + return p; + } + } + } + return null; + } + + /** + * Finds or adds a node. + * + * @return null if added + */ + final TreeNode putTreeVal(int h, K k, V v) { + Class kc = null; + boolean searched = false; + for (TreeNode p = root;;) { + int dir, ph; + K pk; + if(p == null) { + first = root = new TreeNode(h, k, v, null, null); + break; + } + else if((ph = p.hash) > h) + dir = -1; + else if(ph < h) + dir = 1; + else if((pk = p.key) == k || (pk != null && k.equals(pk))) + return p; + else if((kc == null && (kc = comparableClassFor(k)) == null) + || (dir = compareComparables(kc, k, pk)) == 0) { + if(!searched) { + TreeNode q, ch; + searched = true; + if(((ch = p.left) != null && (q = ch.findTreeNode(h, k, + kc)) != null) + || ((ch = p.right) != null && (q = ch + .findTreeNode(h, k, kc)) != null)) + return q; + } + dir = tieBreakOrder(k, pk); + } + + TreeNode xp = p; + if((p = (dir <= 0) ? p.left : p.right) == null) { + TreeNode x, f = first; + first = x = new TreeNode(h, k, v, f, xp); + if(f != null) + f.prev = x; + if(dir <= 0) + xp.left = x; + else + xp.right = x; + if(!xp.red) + x.red = true; + else { + lockRoot(); + try { + root = balanceInsertion(root, x); + } + finally { + unlockRoot(); + } + } + break; + } + } + assert checkInvariants(root); + return null; + } + + /** + * Removes the given node, that must be present before this + * call. This is messier than typical red-black deletion code + * because we cannot swap the contents of an interior node + * with a leaf successor that is pinned by "next" pointers + * that are accessible independently of lock. So instead we + * swap the tree linkages. + * + * @return true if now too small, so should be untreeified + */ + final boolean removeTreeNode(TreeNode p) { + TreeNode next = (TreeNode) p.next; + TreeNode pred = p.prev; // unlink traversal pointers + TreeNode r, rl; + if(pred == null) + first = next; + else + pred.next = next; + if(next != null) + next.prev = pred; + if(first == null) { + root = null; + return true; + } + if((r = root) == null || r.right == null || // too small + (rl = r.left) == null || rl.left == null) + return true; + lockRoot(); + try { + TreeNode replacement; + TreeNode pl = p.left; + TreeNode pr = p.right; + if(pl != null && pr != null) { + TreeNode s = pr, sl; + while ((sl = s.left) != null) + // find successor + s = sl; + boolean c = s.red; + s.red = p.red; + p.red = c; // swap colors + TreeNode sr = s.right; + TreeNode pp = p.parent; + if(s == pr) { // p was s's direct parent + p.parent = s; + s.right = p; + } + else { + TreeNode sp = s.parent; + if((p.parent = sp) != null) { + if(s == sp.left) + sp.left = p; + else + sp.right = p; + } + if((s.right = pr) != null) + pr.parent = s; + } + p.left = null; + if((p.right = sr) != null) + sr.parent = p; + if((s.left = pl) != null) + pl.parent = s; + if((s.parent = pp) == null) + r = s; + else if(p == pp.left) + pp.left = s; + else + pp.right = s; + if(sr != null) + replacement = sr; + else + replacement = p; + } + else if(pl != null) + replacement = pl; + else if(pr != null) + replacement = pr; + else + replacement = p; + if(replacement != p) { + TreeNode pp = replacement.parent = p.parent; + if(pp == null) + r = replacement; + else if(p == pp.left) + pp.left = replacement; + else + pp.right = replacement; + p.left = p.right = p.parent = null; + } + + root = (p.red) ? r : balanceDeletion(r, replacement); + + if(p == replacement) { // detach pointers + TreeNode pp; + if((pp = p.parent) != null) { + if(p == pp.left) + pp.left = null; + else if(p == pp.right) + pp.right = null; + p.parent = null; + } + } + } + finally { + unlockRoot(); + } + assert checkInvariants(root); + return false; + } + + /* ------------------------------------------------------------ */ + // Red-black tree methods, all adapted from CLR + + static TreeNode rotateLeft(TreeNode root, + TreeNode p) { + TreeNode r, pp, rl; + if(p != null && (r = p.right) != null) { + if((rl = p.right = r.left) != null) + rl.parent = p; + if((pp = r.parent = p.parent) == null) + (root = r).red = false; + else if(pp.left == p) + pp.left = r; + else + pp.right = r; + r.left = p; + p.parent = r; + } + return root; + } + + static TreeNode rotateRight(TreeNode root, + TreeNode p) { + TreeNode l, pp, lr; + if(p != null && (l = p.left) != null) { + if((lr = p.left = l.right) != null) + lr.parent = p; + if((pp = l.parent = p.parent) == null) + (root = l).red = false; + else if(pp.right == p) + pp.right = l; + else + pp.left = l; + l.right = p; + p.parent = l; + } + return root; + } + + static TreeNode balanceInsertion(TreeNode root, + TreeNode x) { + x.red = true; + for (TreeNode xp, xpp, xppl, xppr;;) { + if((xp = x.parent) == null) { + x.red = false; + return x; + } + else if(!xp.red || (xpp = xp.parent) == null) + return root; + if(xp == (xppl = xpp.left)) { + if((xppr = xpp.right) != null && xppr.red) { + xppr.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if(x == xp.right) { + root = rotateLeft(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if(xp != null) { + xp.red = false; + if(xpp != null) { + xpp.red = true; + root = rotateRight(root, xpp); + } + } + } + } + else { + if(xppl != null && xppl.red) { + xppl.red = false; + xp.red = false; + xpp.red = true; + x = xpp; + } + else { + if(x == xp.left) { + root = rotateRight(root, x = xp); + xpp = (xp = x.parent) == null ? null : xp.parent; + } + if(xp != null) { + xp.red = false; + if(xpp != null) { + xpp.red = true; + root = rotateLeft(root, xpp); + } + } + } + } + } + } + + static TreeNode balanceDeletion(TreeNode root, + TreeNode x) { + for (TreeNode xp, xpl, xpr;;) { + if(x == null || x == root) + return root; + else if((xp = x.parent) == null) { + x.red = false; + return x; + } + else if(x.red) { + x.red = false; + return root; + } + else if((xpl = xp.left) == x) { + if((xpr = xp.right) != null && xpr.red) { + xpr.red = false; + xp.red = true; + root = rotateLeft(root, xp); + xpr = (xp = x.parent) == null ? null : xp.right; + } + if(xpr == null) + x = xp; + else { + TreeNode sl = xpr.left, sr = xpr.right; + if((sr == null || !sr.red) && (sl == null || !sl.red)) { + xpr.red = true; + x = xp; + } + else { + if(sr == null || !sr.red) { + if(sl != null) + sl.red = false; + xpr.red = true; + root = rotateRight(root, xpr); + xpr = (xp = x.parent) == null ? null : xp.right; + } + if(xpr != null) { + xpr.red = (xp == null) ? false : xp.red; + if((sr = xpr.right) != null) + sr.red = false; + } + if(xp != null) { + xp.red = false; + root = rotateLeft(root, xp); + } + x = root; + } + } + } + else { // symmetric + if(xpl != null && xpl.red) { + xpl.red = false; + xp.red = true; + root = rotateRight(root, xp); + xpl = (xp = x.parent) == null ? null : xp.left; + } + if(xpl == null) + x = xp; + else { + TreeNode sl = xpl.left, sr = xpl.right; + if((sl == null || !sl.red) && (sr == null || !sr.red)) { + xpl.red = true; + x = xp; + } + else { + if(sl == null || !sl.red) { + if(sr != null) + sr.red = false; + xpl.red = true; + root = rotateLeft(root, xpl); + xpl = (xp = x.parent) == null ? null : xp.left; + } + if(xpl != null) { + xpl.red = (xp == null) ? false : xp.red; + if((sl = xpl.left) != null) + sl.red = false; + } + if(xp != null) { + xp.red = false; + root = rotateRight(root, xp); + } + x = root; + } + } + } + } + } + + /** + * Recursive invariant check + */ + static boolean checkInvariants(TreeNode t) { + TreeNode tp = t.parent, tl = t.left, tr = t.right, tb = t.prev, tn = (TreeNode) t.next; + if(tb != null && tb.next != t) + return false; + if(tn != null && tn.prev != t) + return false; + if(tp != null && t != tp.left && t != tp.right) + return false; + if(tl != null && (tl.parent != t || tl.hash > t.hash)) + return false; + if(tr != null && (tr.parent != t || tr.hash < t.hash)) + return false; + if(t.red && tl != null && tl.red && tr != null && tr.red) + return false; + if(tl != null && !checkInvariants(tl)) + return false; + if(tr != null && !checkInvariants(tr)) + return false; + return true; + } + + private static final sun.misc.Unsafe U; + private static final long LOCKSTATE; + static { + try { + U = getUnsafe(); + Class k = TreeBin.class; + LOCKSTATE = U + .objectFieldOffset(k.getDeclaredField("lockState")); + } + catch (Exception e) { + throw new Error(e); + } + } + } + + /* ----------------Table Traversal -------------- */ + + /** + * Records the table, its length, and current traversal index for a + * traverser that must process a region of a forwarded table before + * proceeding with current table. + */ + static final class TableStack { + int length; + int index; + Node[] tab; + TableStack next; + } + + /** + * Encapsulates traversal for methods such as containsValue; also + * serves as a base class for other iterators and spliterators. + * + * Method advance visits once each still-valid node that was + * reachable upon iterator construction. It might miss some that + * were added to a bin after the bin was visited, which is OK wrt + * consistency guarantees. Maintaining this property in the face + * of possible ongoing resizes requires a fair amount of + * bookkeeping state that is difficult to optimize away amidst + * volatile accesses. Even so, traversal maintains reasonable + * throughput. + * + * Normally, iteration proceeds bin-by-bin traversing lists. + * However, if the table has been resized, then all future steps + * must traverse both the bin at the current index as well as at + * (index + baseSize); and so on for further resizings. To + * paranoically cope with potential sharing by users of iterators + * across threads, iteration terminates if a bounds checks fails + * for a table read. + */ + static class Traverser { + Node[] tab; // current table; updated if resized + Node next; // the next entry to use + TableStack stack, spare; // to save/restore on ForwardingNodes + int index; // index of bin to use next + int baseIndex; // current index of initial table + int baseLimit; // index bound for initial table + final int baseSize; // initial table size + + Traverser(Node[] tab, int size, int index, int limit) { + this.tab = tab; + this.baseSize = size; + this.baseIndex = this.index = index; + this.baseLimit = limit; + this.next = null; + } + + /** + * Advances if possible, returning next valid node, or null if none. + */ + final Node advance() { + Node e; + if((e = next) != null) + e = e.next; + for (;;) { + Node[] t; + int i, n; // must use locals in checks + if(e != null) + return next = e; + if(baseIndex >= baseLimit || (t = tab) == null + || (n = t.length) <= (i = index) || i < 0) + return next = null; + if((e = tabAt(t, i)) != null && e.hash < 0) { + if(e instanceof ForwardingNode) { + tab = ((ForwardingNode) e).nextTable; + e = null; + pushState(t, i, n); + continue; + } + else if(e instanceof TreeBin) + e = ((TreeBin) e).first; + else + e = null; + } + if(stack != null) + recoverState(n); + else if((index = i + baseSize) >= n) + index = ++baseIndex; // visit upper slots if present + } + } + + /** + * Saves traversal state upon encountering a forwarding node. + */ + private void pushState(Node[] t, int i, int n) { + TableStack s = spare; // reuse if possible + if(s != null) + spare = s.next; + else + s = new TableStack(); + s.tab = t; + s.length = n; + s.index = i; + s.next = stack; + stack = s; + } + + /** + * Possibly pops traversal state. + * + * @param n length of current table + */ + private void recoverState(int n) { + TableStack s; + int len; + while ((s = stack) != null && (index += (len = s.length)) >= n) { + n = len; + index = s.index; + tab = s.tab; + s.tab = null; + TableStack next = s.next; + s.next = spare; // save for reuse + stack = next; + spare = s; + } + if(s == null && (index += baseSize) >= n) + index = ++baseIndex; + } + } + + /** + * Base of key, value, and entry Iterators. Adds fields to + * Traverser to support iterator.remove. + */ + static class BaseIterator extends Traverser { + final ConcurrentHashMapV8 map; + Node lastReturned; + + BaseIterator(Node[] tab, int size, int index, int limit, + ConcurrentHashMapV8 map) { + super(tab, size, index, limit); + this.map = map; + advance(); + } + + public final boolean hasNext() { + return next != null; + } + + public final boolean hasMoreElements() { + return next != null; + } + + public final void remove() { + Node p; + if((p = lastReturned) == null) + throw new IllegalStateException(); + lastReturned = null; + map.replaceNode(p.key, null, null); + } + } + + static final class KeyIterator extends BaseIterator implements + Iterator, + Enumeration { + KeyIterator(Node[] tab, int index, int size, int limit, + ConcurrentHashMapV8 map) { + super(tab, index, size, limit, map); + } + + public final K next() { + Node p; + if((p = next) == null) + throw new NoSuchElementException(); + K k = p.key; + lastReturned = p; + advance(); + return k; + } + + public final K nextElement() { + return next(); + } + } + + static final class ValueIterator extends BaseIterator implements + Iterator, + Enumeration { + ValueIterator(Node[] tab, int index, int size, int limit, + ConcurrentHashMapV8 map) { + super(tab, index, size, limit, map); + } + + public final V next() { + Node p; + if((p = next) == null) + throw new NoSuchElementException(); + V v = p.val; + lastReturned = p; + advance(); + return v; + } + + public final V nextElement() { + return next(); + } + } + + static final class EntryIterator extends BaseIterator implements + Iterator> { + EntryIterator(Node[] tab, int index, int size, int limit, + ConcurrentHashMapV8 map) { + super(tab, index, size, limit, map); + } + + public final Map.Entry next() { + Node p; + if((p = next) == null) + throw new NoSuchElementException(); + K k = p.key; + V v = p.val; + lastReturned = p; + advance(); + return new MapEntry(k, v, map); + } + } + + /** + * Exported Entry for EntryIterator + */ + static final class MapEntry implements Map.Entry { + final K key; // non-null + V val; // non-null + final ConcurrentHashMapV8 map; + + MapEntry(K key, V val, ConcurrentHashMapV8 map) { + this.key = key; + this.val = val; + this.map = map; + } + + public K getKey() { + return key; + } + + public V getValue() { + return val; + } + + public int hashCode() { + return key.hashCode() ^ val.hashCode(); + } + + public String toString() { + return key + "=" + val; + } + + public boolean equals(Object o) { + Object k, v; + Map.Entry e; + return ((o instanceof Map.Entry) + && (k = (e = (Map.Entry) o).getKey()) != null + && (v = e.getValue()) != null + && (k == key || k.equals(key)) && (v == val || v + .equals(val))); + } + + /** + * Sets our entry's value and writes through to the map. The + * value to return is somewhat arbitrary here. Since we do not + * necessarily track asynchronous changes, the most recent + * "previous" value could be different from what we return (or + * could even have been removed, in which case the put will + * re-establish). We do not and cannot guarantee more. + */ + public V setValue(V value) { + if(value == null) + throw new NullPointerException(); + V v = val; + val = value; + map.put(key, value); + return v; + } + } + + static final class KeySpliterator extends Traverser implements + ConcurrentHashMapSpliterator { + long est; // size estimate + + KeySpliterator(Node[] tab, int size, int index, int limit, + long est) { + super(tab, size, index, limit); + this.est = est; + } + + public ConcurrentHashMapSpliterator trySplit() { + int i, f, h; + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null + : new KeySpliterator(tab, baseSize, baseLimit = h, f, + est >>>= 1); + } + + public void forEachRemaining(Action action) { + if(action == null) + throw new NullPointerException(); + for (Node p; (p = advance()) != null;) + action.apply(p.key); + } + + public boolean tryAdvance(Action action) { + if(action == null) + throw new NullPointerException(); + Node p; + if((p = advance()) == null) + return false; + action.apply(p.key); + return true; + } + + public long estimateSize() { + return est; + } + + } + + static final class ValueSpliterator extends Traverser implements + ConcurrentHashMapSpliterator { + long est; // size estimate + + ValueSpliterator(Node[] tab, int size, int index, int limit, + long est) { + super(tab, size, index, limit); + this.est = est; + } + + public ConcurrentHashMapSpliterator trySplit() { + int i, f, h; + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null + : new ValueSpliterator(tab, baseSize, baseLimit = h, + f, est >>>= 1); + } + + public void forEachRemaining(Action action) { + if(action == null) + throw new NullPointerException(); + for (Node p; (p = advance()) != null;) + action.apply(p.val); + } + + public boolean tryAdvance(Action action) { + if(action == null) + throw new NullPointerException(); + Node p; + if((p = advance()) == null) + return false; + action.apply(p.val); + return true; + } + + public long estimateSize() { + return est; + } + + } + + static final class EntrySpliterator extends Traverser implements + ConcurrentHashMapSpliterator> { + final ConcurrentHashMapV8 map; // To export MapEntry + long est; // size estimate + + EntrySpliterator(Node[] tab, int size, int index, int limit, + long est, ConcurrentHashMapV8 map) { + super(tab, size, index, limit); + this.map = map; + this.est = est; + } + + public ConcurrentHashMapSpliterator> trySplit() { + int i, f, h; + return (h = ((i = baseIndex) + (f = baseLimit)) >>> 1) <= i ? null + : new EntrySpliterator(tab, baseSize, baseLimit = h, + f, est >>>= 1, map); + } + + public void forEachRemaining(Action> action) { + if(action == null) + throw new NullPointerException(); + for (Node p; (p = advance()) != null;) + action.apply(new MapEntry(p.key, p.val, map)); + } + + public boolean tryAdvance(Action> action) { + if(action == null) + throw new NullPointerException(); + Node p; + if((p = advance()) == null) + return false; + action.apply(new MapEntry(p.key, p.val, map)); + return true; + } + + public long estimateSize() { + return est; + } + + } + + // Parallel bulk operations + + /** + * Computes initial batch value for bulk tasks. The returned value + * is approximately exp2 of the number of times (minus one) to + * split task by two before executing leaf action. This value is + * faster to compute and more convenient to use as a guide to + * splitting than is the depth, since it is used while dividing by + * two anyway. + */ + final int batchFor(long b) { + long n; + if(b == Long.MAX_VALUE || (n = sumCount()) <= 1L || n < b) + return 0; + int sp = ForkJoinPool.getCommonPoolParallelism() << 2; // slack of 4 + return (b <= 0L || (n /= b) >= sp) ? sp : (int) n; + } + + /** + * Performs the given action for each (key, value). + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param action the action + * @since 1.8 + */ + public void forEach(long parallelismThreshold, + BiAction action) { + if(action == null) + throw new NullPointerException(); + new ForEachMappingTask(null, batchFor(parallelismThreshold), 0, + 0, table, action).invoke(); + } + + /** + * Performs the given action for each non-null transformation + * of each (key, value). + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case the action is not applied) + * @param action the action + * @since 1.8 + */ + public void forEach(long parallelismThreshold, + BiFun transformer, + Action action) { + if(transformer == null || action == null) + throw new NullPointerException(); + new ForEachTransformedMappingTask(null, + batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); + } + + /** + * Returns a non-null result from applying the given search + * function on each (key, value), or null if none. Upon + * success, further element processing is suppressed and the + * results of any other parallel invocations of the search + * function are ignored. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param searchFunction a function returning a non-null + * result on success, else null + * @return a non-null result from applying the given search + * function on each (key, value), or null if none + * @since 1.8 + */ + public U search(long parallelismThreshold, + BiFun searchFunction) { + if(searchFunction == null) + throw new NullPointerException(); + return new SearchMappingsTask(null, + batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all (key, value) pairs using the given reducer to + * combine values, or null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case it is not combined) + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all (key, value) pairs + * @since 1.8 + */ + public U reduce(long parallelismThreshold, + BiFun transformer, + BiFun reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceMappingsTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all (key, value) pairs using the given reducer to + * combine values, and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all (key, value) pairs + * @since 1.8 + */ + public double reduceToDouble(long parallelismThreshold, + ObjectByObjectToDouble transformer, + double basis, DoubleByDoubleToDouble reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceMappingsToDoubleTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all (key, value) pairs using the given reducer to + * combine values, and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all (key, value) pairs + * @since 1.8 + */ + public long reduceToLong(long parallelismThreshold, + ObjectByObjectToLong transformer, long basis, + LongByLongToLong reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceMappingsToLongTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all (key, value) pairs using the given reducer to + * combine values, and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all (key, value) pairs + * @since 1.8 + */ + public int reduceToInt(long parallelismThreshold, + ObjectByObjectToInt transformer, int basis, + IntByIntToInt reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceMappingsToIntTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Performs the given action for each key. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param action the action + * @since 1.8 + */ + public void forEachKey(long parallelismThreshold, Action action) { + if(action == null) + throw new NullPointerException(); + new ForEachKeyTask(null, batchFor(parallelismThreshold), 0, 0, + table, action).invoke(); + } + + /** + * Performs the given action for each non-null transformation + * of each key. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case the action is not applied) + * @param action the action + * @since 1.8 + */ + public void forEachKey(long parallelismThreshold, + Fun transformer, Action action) { + if(transformer == null || action == null) + throw new NullPointerException(); + new ForEachTransformedKeyTask(null, + batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); + } + + /** + * Returns a non-null result from applying the given search + * function on each key, or null if none. Upon success, + * further element processing is suppressed and the results of + * any other parallel invocations of the search function are + * ignored. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param searchFunction a function returning a non-null + * result on success, else null + * @return a non-null result from applying the given search + * function on each key, or null if none + * @since 1.8 + */ + public U searchKeys(long parallelismThreshold, + Fun searchFunction) { + if(searchFunction == null) + throw new NullPointerException(); + return new SearchKeysTask(null, + batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); + } + + /** + * Returns the result of accumulating all keys using the given + * reducer to combine values, or null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param reducer a commutative associative combining function + * @return the result of accumulating all keys using the given + * reducer to combine values, or null if none + * @since 1.8 + */ + public K reduceKeys(long parallelismThreshold, + BiFun reducer) { + if(reducer == null) + throw new NullPointerException(); + return new ReduceKeysTask(null, batchFor(parallelismThreshold), + 0, 0, table, null, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all keys using the given reducer to combine values, or + * null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case it is not combined) + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all keys + * @since 1.8 + */ + public U reduceKeys(long parallelismThreshold, + Fun transformer, + BiFun reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceKeysTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all keys using the given reducer to combine values, and + * the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all keys + * @since 1.8 + */ + public double reduceKeysToDouble(long parallelismThreshold, + ObjectToDouble transformer, double basis, + DoubleByDoubleToDouble reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceKeysToDoubleTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all keys using the given reducer to combine values, and + * the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all keys + * @since 1.8 + */ + public long reduceKeysToLong(long parallelismThreshold, + ObjectToLong transformer, long basis, + LongByLongToLong reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceKeysToLongTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all keys using the given reducer to combine values, and + * the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all keys + * @since 1.8 + */ + public int reduceKeysToInt(long parallelismThreshold, + ObjectToInt transformer, int basis, IntByIntToInt reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceKeysToIntTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Performs the given action for each value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param action the action + * @since 1.8 + */ + public void forEachValue(long parallelismThreshold, Action action) { + if(action == null) + throw new NullPointerException(); + new ForEachValueTask(null, batchFor(parallelismThreshold), 0, 0, + table, action).invoke(); + } + + /** + * Performs the given action for each non-null transformation + * of each value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case the action is not applied) + * @param action the action + * @since 1.8 + */ + public void forEachValue(long parallelismThreshold, + Fun transformer, Action action) { + if(transformer == null || action == null) + throw new NullPointerException(); + new ForEachTransformedValueTask(null, + batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); + } + + /** + * Returns a non-null result from applying the given search + * function on each value, or null if none. Upon success, + * further element processing is suppressed and the results of + * any other parallel invocations of the search function are + * ignored. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param searchFunction a function returning a non-null + * result on success, else null + * @return a non-null result from applying the given search + * function on each value, or null if none + * @since 1.8 + */ + public U searchValues(long parallelismThreshold, + Fun searchFunction) { + if(searchFunction == null) + throw new NullPointerException(); + return new SearchValuesTask(null, + batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); + } + + /** + * Returns the result of accumulating all values using the + * given reducer to combine values, or null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param reducer a commutative associative combining function + * @return the result of accumulating all values + * @since 1.8 + */ + public V reduceValues(long parallelismThreshold, + BiFun reducer) { + if(reducer == null) + throw new NullPointerException(); + return new ReduceValuesTask(null, batchFor(parallelismThreshold), + 0, 0, table, null, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all values using the given reducer to combine values, or + * null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case it is not combined) + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all values + * @since 1.8 + */ + public U reduceValues(long parallelismThreshold, + Fun transformer, + BiFun reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceValuesTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all values using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all values + * @since 1.8 + */ + public double reduceValuesToDouble(long parallelismThreshold, + ObjectToDouble transformer, double basis, + DoubleByDoubleToDouble reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceValuesToDoubleTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all values using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all values + * @since 1.8 + */ + public long reduceValuesToLong(long parallelismThreshold, + ObjectToLong transformer, long basis, + LongByLongToLong reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceValuesToLongTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all values using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all values + * @since 1.8 + */ + public int reduceValuesToInt(long parallelismThreshold, + ObjectToInt transformer, int basis, IntByIntToInt reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceValuesToIntTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Performs the given action for each entry. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param action the action + * @since 1.8 + */ + public void forEachEntry(long parallelismThreshold, + Action> action) { + if(action == null) + throw new NullPointerException(); + new ForEachEntryTask(null, batchFor(parallelismThreshold), 0, 0, + table, action).invoke(); + } + + /** + * Performs the given action for each non-null transformation + * of each entry. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case the action is not applied) + * @param action the action + * @since 1.8 + */ + public void forEachEntry(long parallelismThreshold, + Fun, ? extends U> transformer, + Action action) { + if(transformer == null || action == null) + throw new NullPointerException(); + new ForEachTransformedEntryTask(null, + batchFor(parallelismThreshold), 0, 0, table, transformer, + action).invoke(); + } + + /** + * Returns a non-null result from applying the given search + * function on each entry, or null if none. Upon success, + * further element processing is suppressed and the results of + * any other parallel invocations of the search function are + * ignored. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param searchFunction a function returning a non-null + * result on success, else null + * @return a non-null result from applying the given search + * function on each entry, or null if none + * @since 1.8 + */ + public U searchEntries(long parallelismThreshold, + Fun, ? extends U> searchFunction) { + if(searchFunction == null) + throw new NullPointerException(); + return new SearchEntriesTask(null, + batchFor(parallelismThreshold), 0, 0, table, searchFunction, + new AtomicReference()).invoke(); + } + + /** + * Returns the result of accumulating all entries using the + * given reducer to combine values, or null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param reducer a commutative associative combining function + * @return the result of accumulating all entries + * @since 1.8 + */ + public Map.Entry reduceEntries( + long parallelismThreshold, + BiFun, Map.Entry, ? extends Map.Entry> reducer) { + if(reducer == null) + throw new NullPointerException(); + return new ReduceEntriesTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, reducer) + .invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all entries using the given reducer to combine values, + * or null if none. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element, or null if there is no transformation (in + * which case it is not combined) + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all entries + * @since 1.8 + */ + public U reduceEntries(long parallelismThreshold, + Fun, ? extends U> transformer, + BiFun reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceEntriesTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all entries using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all entries + * @since 1.8 + */ + public double reduceEntriesToDouble(long parallelismThreshold, + ObjectToDouble> transformer, double basis, + DoubleByDoubleToDouble reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceEntriesToDoubleTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all entries using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all entries + * @since 1.8 + */ + public long reduceEntriesToLong(long parallelismThreshold, + ObjectToLong> transformer, long basis, + LongByLongToLong reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceEntriesToLongTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /** + * Returns the result of accumulating the given transformation + * of all entries using the given reducer to combine values, + * and the given basis as an identity value. + * + * @param parallelismThreshold the (estimated) number of elements + * needed for this operation to be executed in parallel + * @param transformer a function returning the transformation + * for an element + * @param basis the identity (initial default value) for the reduction + * @param reducer a commutative associative combining function + * @return the result of accumulating the given transformation + * of all entries + * @since 1.8 + */ + public int reduceEntriesToInt(long parallelismThreshold, + ObjectToInt> transformer, int basis, + IntByIntToInt reducer) { + if(transformer == null || reducer == null) + throw new NullPointerException(); + return new MapReduceEntriesToIntTask(null, + batchFor(parallelismThreshold), 0, 0, table, null, transformer, + basis, reducer).invoke(); + } + + /* ----------------Views -------------- */ + + /** + * Base class for views. + */ + abstract static class CollectionView implements + Collection, + java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + final ConcurrentHashMapV8 map; + + CollectionView(ConcurrentHashMapV8 map) { + this.map = map; + } + + /** + * Returns the map backing this view. + * + * @return the map backing this view + */ + public ConcurrentHashMapV8 getMap() { + return map; + } + + /** + * Removes all of the elements from this view, by removing all + * the mappings from the map backing this view. + */ + public final void clear() { + map.clear(); + } + + public final int size() { + return map.size(); + } + + public final boolean isEmpty() { + return map.isEmpty(); + } + + // implementations below rely on concrete classes supplying these + // abstract methods + /** + * Returns a "weakly consistent" iterator that will never + * throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not + * guaranteed to) reflect any modifications subsequent to + * construction. + */ + public abstract Iterator iterator(); + + public abstract boolean contains(Object o); + + public abstract boolean remove(Object o); + + private static final String oomeMsg = "Required array size too large"; + + public final Object[] toArray() { + long sz = map.mappingCount(); + if(sz > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + int n = (int) sz; + Object[] r = new Object[n]; + int i = 0; + for (E e : this) { + if(i == n) { + if(n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if(n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = e; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + @SuppressWarnings("unchecked") + public final T[] toArray(T[] a) { + long sz = map.mappingCount(); + if(sz > MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + int m = (int) sz; + T[] r = (a.length >= m) ? a : (T[]) java.lang.reflect.Array + .newInstance(a.getClass().getComponentType(), m); + int n = r.length; + int i = 0; + for (E e : this) { + if(i == n) { + if(n >= MAX_ARRAY_SIZE) + throw new OutOfMemoryError(oomeMsg); + if(n >= MAX_ARRAY_SIZE - (MAX_ARRAY_SIZE >>> 1) - 1) + n = MAX_ARRAY_SIZE; + else + n += (n >>> 1) + 1; + r = Arrays.copyOf(r, n); + } + r[i++] = (T) e; + } + if(a == r && i < n) { + r[i] = null; // null-terminate + return r; + } + return (i == n) ? r : Arrays.copyOf(r, i); + } + + /** + * Returns a string representation of this collection. + * The string representation consists of the string representations + * of the collection's elements in the order they are returned by + * its iterator, enclosed in square brackets ({@code "[]"}). + * Adjacent elements are separated by the characters {@code ", "} (comma + * and space). Elements are converted to strings as by + * {@link String#valueOf(Object)}. + * + * @return a string representation of this collection + */ + public final String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + Iterator it = iterator(); + if(it.hasNext()) { + for (;;) { + Object e = it.next(); + sb.append(e == this ? "(this Collection)" : e); + if(!it.hasNext()) + break; + sb.append(',').append(' '); + } + } + return sb.append(']').toString(); + } + + public final boolean containsAll(Collection c) { + if(c != this) { + for (Object e : c) { + if(e == null || !contains(e)) + return false; + } + } + return true; + } + + public final boolean removeAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if(c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + public final boolean retainAll(Collection c) { + boolean modified = false; + for (Iterator it = iterator(); it.hasNext();) { + if(!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of keys, in + * which additions may optionally be enabled by mapping to a + * common value. This class cannot be directly instantiated. + * See {@link #keySet() keySet()}, {@link #keySet(Object) keySet(V)}, + * {@link #newKeySet() newKeySet()}, {@link #newKeySet(int) newKeySet(int)}. + * + * @since 1.8 + */ + public static class KeySetView extends CollectionView implements + Set, + java.io.Serializable { + private static final long serialVersionUID = 7249069246763182397L; + private final V value; + + KeySetView(ConcurrentHashMapV8 map, V value) { // non-public + super(map); + this.value = value; + } + + /** + * Returns the default mapped value for additions, + * or {@code null} if additions are not supported. + * + * @return the default mapped value for additions, or {@code null} if + * not supported + */ + public V getMappedValue() { + return value; + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null + */ + public boolean contains(Object o) { + return map.containsKey(o); + } + + /** + * Removes the key from this map view, by removing the key (and its + * corresponding value) from the backing map. This method does + * nothing if the key is not in the map. + * + * @param o the key to be removed from the backing map + * @return {@code true} if the backing map contained the specified key + * @throws NullPointerException if the specified key is null + */ + public boolean remove(Object o) { + return map.remove(o) != null; + } + + /** + * @return an iterator over the keys of the backing map + */ + public Iterator iterator() { + Node[] t; + ConcurrentHashMapV8 m = map; + int f = (t = m.table) == null ? 0 : t.length; + return new KeyIterator(t, f, 0, f, m); + } + + /** + * Adds the specified key to this set view by mapping the key to + * the default mapped value in the backing map, if defined. + * + * @param e key to be added + * @return {@code true} if this set changed as a result of the call + * @throws NullPointerException if the specified key is null + * @throws UnsupportedOperationException if no default mapped value + * for additions was provided + */ + public boolean add(K e) { + V v; + if((v = value) == null) + throw new UnsupportedOperationException(); + return map.putVal(e, v, true) == null; + } + + /** + * Adds all of the elements in the specified collection to this set, + * as if by calling {@link #add} on each one. + * + * @param c the elements to be inserted into this set + * @return {@code true} if this set changed as a result of the call + * @throws NullPointerException if the collection or any of its + * elements are {@code null} + * @throws UnsupportedOperationException if no default mapped value + * for additions was provided + */ + public boolean addAll(Collection c) { + boolean added = false; + V v; + if((v = value) == null) + throw new UnsupportedOperationException(); + for (K e : c) { + if(map.putVal(e, v, true) == null) + added = true; + } + return added; + } + + public int hashCode() { + int h = 0; + for (K e : this) + h += e.hashCode(); + return h; + } + + public boolean equals(Object o) { + Set c; + return ((o instanceof Set) && ((c = (Set) o) == this || (containsAll(c) && c + .containsAll(this)))); + } + + public ConcurrentHashMapSpliterator spliterator() { + Node[] t; + ConcurrentHashMapV8 m = map; + long n = m.sumCount(); + int f = (t = m.table) == null ? 0 : t.length; + return new KeySpliterator(t, f, 0, f, n < 0L ? 0L : n); + } + + public void forEach(Action action) { + if(action == null) + throw new NullPointerException(); + Node[] t; + if((t = map.table) != null) { + Traverser it = new Traverser(t, t.length, 0, + t.length); + for (Node p; (p = it.advance()) != null;) + action.apply(p.key); + } + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Collection} of + * values, in which additions are disabled. This class cannot be + * directly instantiated. See {@link #values()}. + */ + static final class ValuesView extends CollectionView implements + Collection, + java.io.Serializable { + private static final long serialVersionUID = 2249069246763182397L; + + ValuesView(ConcurrentHashMapV8 map) { + super(map); + } + + public final boolean contains(Object o) { + return map.containsValue(o); + } + + public final boolean remove(Object o) { + if(o != null) { + for (Iterator it = iterator(); it.hasNext();) { + if(o.equals(it.next())) { + it.remove(); + return true; + } + } + } + return false; + } + + public final Iterator iterator() { + ConcurrentHashMapV8 m = map; + Node[] t; + int f = (t = m.table) == null ? 0 : t.length; + return new ValueIterator(t, f, 0, f, m); + } + + public final boolean add(V e) { + throw new UnsupportedOperationException(); + } + + public final boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public ConcurrentHashMapSpliterator spliterator() { + Node[] t; + ConcurrentHashMapV8 m = map; + long n = m.sumCount(); + int f = (t = m.table) == null ? 0 : t.length; + return new ValueSpliterator(t, f, 0, f, n < 0L ? 0L : n); + } + + public void forEach(Action action) { + if(action == null) + throw new NullPointerException(); + Node[] t; + if((t = map.table) != null) { + Traverser it = new Traverser(t, t.length, 0, + t.length); + for (Node p; (p = it.advance()) != null;) + action.apply(p.val); + } + } + } + + /** + * A view of a ConcurrentHashMapV8 as a {@link Set} of (key, value) + * entries. This class cannot be directly instantiated. See + * {@link #entrySet()}. + */ + static final class EntrySetView extends + CollectionView> implements + Set>, + java.io.Serializable { + private static final long serialVersionUID = 2249069246763182397L; + + EntrySetView(ConcurrentHashMapV8 map) { + super(map); + } + + public boolean contains(Object o) { + Object k, v, r; + Map.Entry e; + return ((o instanceof Map.Entry) + && (k = (e = (Map.Entry) o).getKey()) != null + && (r = map.get(k)) != null && (v = e.getValue()) != null && (v == r || v + .equals(r))); + } + + public boolean remove(Object o) { + Object k, v; + Map.Entry e; + return ((o instanceof Map.Entry) + && (k = (e = (Map.Entry) o).getKey()) != null + && (v = e.getValue()) != null && map.remove(k, v)); + } + + /** + * @return an iterator over the entries of the backing map + */ + public Iterator> iterator() { + ConcurrentHashMapV8 m = map; + Node[] t; + int f = (t = m.table) == null ? 0 : t.length; + return new EntryIterator(t, f, 0, f, m); + } + + public boolean add(Entry e) { + return map.putVal(e.getKey(), e.getValue(), false) == null; + } + + public boolean addAll(Collection> c) { + boolean added = false; + for (Entry e : c) { + if(add(e)) + added = true; + } + return added; + } + + public final int hashCode() { + int h = 0; + Node[] t; + if((t = map.table) != null) { + Traverser it = new Traverser(t, t.length, 0, + t.length); + for (Node p; (p = it.advance()) != null;) { + h += p.hashCode(); + } + } + return h; + } + + public final boolean equals(Object o) { + Set c; + return ((o instanceof Set) && ((c = (Set) o) == this || (containsAll(c) && c + .containsAll(this)))); + } + + public ConcurrentHashMapSpliterator> spliterator() { + Node[] t; + ConcurrentHashMapV8 m = map; + long n = m.sumCount(); + int f = (t = m.table) == null ? 0 : t.length; + return new EntrySpliterator(t, f, 0, f, n < 0L ? 0L : n, m); + } + + public void forEach(Action> action) { + if(action == null) + throw new NullPointerException(); + Node[] t; + if((t = map.table) != null) { + Traverser it = new Traverser(t, t.length, 0, + t.length); + for (Node p; (p = it.advance()) != null;) + action.apply(new MapEntry(p.key, p.val, map)); + } + } + + } + + // ------------------------------------------------------- + + /** + * Base class for bulk tasks. Repeats some fields and code from + * class Traverser, because we need to subclass CountedCompleter. + */ + @SuppressWarnings("serial") + abstract static class BulkTask extends CountedCompleter { + Node[] tab; // same as Traverser + Node next; + int index; + int baseIndex; + int baseLimit; + final int baseSize; + int batch; // split control + + BulkTask(BulkTask par, int b, int i, int f, Node[] t) { + super(par); + this.batch = b; + this.index = this.baseIndex = i; + if((this.tab = t) == null) + this.baseSize = this.baseLimit = 0; + else if(par == null) + this.baseSize = this.baseLimit = t.length; + else { + this.baseLimit = f; + this.baseSize = par.baseSize; + } + } + + /** + * Same as Traverser version + */ + final Node advance() { + Node e; + if((e = next) != null) + e = e.next; + for (;;) { + Node[] t; + int i, n; + K ek; // must use locals in checks + if(e != null) + return next = e; + if(baseIndex >= baseLimit || (t = tab) == null + || (n = t.length) <= (i = index) || i < 0) + return next = null; + if((e = tabAt(t, index)) != null && e.hash < 0) { + if(e instanceof ForwardingNode) { + tab = ((ForwardingNode) e).nextTable; + e = null; + continue; + } + else if(e instanceof TreeBin) + e = ((TreeBin) e).first; + else + e = null; + } + if((index += baseSize) >= n) + index = ++baseIndex; // visit upper slots if present + } + } + } + + /* + * Task classes. Coded in a regular but ugly format/style to + * simplify checks that each variant differs in the right way from + * others. The null screenings exist because compilers cannot tell + * that we've already null-checked task arguments, so we force + * simplest hoisted bypass to help avoid convoluted traps. + */ + @SuppressWarnings("serial") + static final class ForEachKeyTask extends BulkTask { + final Action action; + + ForEachKeyTask(BulkTask p, int b, int i, int f, + Node[] t, Action action) { + super(p, b, i, f, t); + this.action = action; + } + + public final void compute() { + final Action action; + if((action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachKeyTask(this, batch >>>= 1, baseLimit = h, + f, tab, action).fork(); + } + for (Node p; (p = advance()) != null;) + action.apply(p.key); + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachValueTask extends BulkTask { + final Action action; + + ForEachValueTask(BulkTask p, int b, int i, int f, + Node[] t, Action action) { + super(p, b, i, f, t); + this.action = action; + } + + public final void compute() { + final Action action; + if((action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachValueTask(this, batch >>>= 1, + baseLimit = h, f, tab, action).fork(); + } + for (Node p; (p = advance()) != null;) + action.apply(p.val); + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachEntryTask extends BulkTask { + final Action> action; + + ForEachEntryTask(BulkTask p, int b, int i, int f, + Node[] t, Action> action) { + super(p, b, i, f, t); + this.action = action; + } + + public final void compute() { + final Action> action; + if((action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachEntryTask(this, batch >>>= 1, + baseLimit = h, f, tab, action).fork(); + } + for (Node p; (p = advance()) != null;) + action.apply(p); + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachMappingTask extends BulkTask { + final BiAction action; + + ForEachMappingTask(BulkTask p, int b, int i, int f, + Node[] t, BiAction action) { + super(p, b, i, f, t); + this.action = action; + } + + public final void compute() { + final BiAction action; + if((action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachMappingTask(this, batch >>>= 1, + baseLimit = h, f, tab, action).fork(); + } + for (Node p; (p = advance()) != null;) + action.apply(p.key, p.val); + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachTransformedKeyTask extends + BulkTask { + final Fun transformer; + final Action action; + + ForEachTransformedKeyTask(BulkTask p, int b, int i, int f, + Node[] t, Fun transformer, + Action action) { + super(p, b, i, f, t); + this.transformer = transformer; + this.action = action; + } + + public final void compute() { + final Fun transformer; + final Action action; + if((transformer = this.transformer) != null + && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachTransformedKeyTask(this, batch >>>= 1, + baseLimit = h, f, tab, transformer, action).fork(); + } + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.key)) != null) + action.apply(u); + } + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachTransformedValueTask extends + BulkTask { + final Fun transformer; + final Action action; + + ForEachTransformedValueTask(BulkTask p, int b, int i, int f, + Node[] t, Fun transformer, + Action action) { + super(p, b, i, f, t); + this.transformer = transformer; + this.action = action; + } + + public final void compute() { + final Fun transformer; + final Action action; + if((transformer = this.transformer) != null + && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachTransformedValueTask(this, + batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); + } + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.val)) != null) + action.apply(u); + } + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachTransformedEntryTask extends + BulkTask { + final Fun, ? extends U> transformer; + final Action action; + + ForEachTransformedEntryTask(BulkTask p, int b, int i, int f, + Node[] t, Fun, ? extends U> transformer, + Action action) { + super(p, b, i, f, t); + this.transformer = transformer; + this.action = action; + } + + public final void compute() { + final Fun, ? extends U> transformer; + final Action action; + if((transformer = this.transformer) != null + && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachTransformedEntryTask(this, + batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); + } + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p)) != null) + action.apply(u); + } + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class ForEachTransformedMappingTask extends + BulkTask { + final BiFun transformer; + final Action action; + + ForEachTransformedMappingTask(BulkTask p, int b, int i, int f, + Node[] t, + BiFun transformer, + Action action) { + super(p, b, i, f, t); + this.transformer = transformer; + this.action = action; + } + + public final void compute() { + final BiFun transformer; + final Action action; + if((transformer = this.transformer) != null + && (action = this.action) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + new ForEachTransformedMappingTask(this, + batch >>>= 1, baseLimit = h, f, tab, transformer, + action).fork(); + } + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.key, p.val)) != null) + action.apply(u); + } + propagateCompletion(); + } + } + } + + @SuppressWarnings("serial") + static final class SearchKeysTask extends BulkTask { + final Fun searchFunction; + final AtomicReference result; + + SearchKeysTask(BulkTask p, int b, int i, int f, + Node[] t, Fun searchFunction, + AtomicReference result) { + super(p, b, i, f, t); + this.searchFunction = searchFunction; + this.result = result; + } + + public final U getRawResult() { + return result.get(); + } + + public final void compute() { + final Fun searchFunction; + final AtomicReference result; + if((searchFunction = this.searchFunction) != null + && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + if(result.get() != null) + return; + addToPendingCount(1); + new SearchKeysTask(this, batch >>>= 1, + baseLimit = h, f, tab, searchFunction, result) + .fork(); + } + while (result.get() == null) { + U u; + Node p; + if((p = advance()) == null) { + propagateCompletion(); + break; + } + if((u = searchFunction.apply(p.key)) != null) { + if(result.compareAndSet(null, u)) + quietlyCompleteRoot(); + break; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class SearchValuesTask extends BulkTask { + final Fun searchFunction; + final AtomicReference result; + + SearchValuesTask(BulkTask p, int b, int i, int f, + Node[] t, Fun searchFunction, + AtomicReference result) { + super(p, b, i, f, t); + this.searchFunction = searchFunction; + this.result = result; + } + + public final U getRawResult() { + return result.get(); + } + + public final void compute() { + final Fun searchFunction; + final AtomicReference result; + if((searchFunction = this.searchFunction) != null + && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + if(result.get() != null) + return; + addToPendingCount(1); + new SearchValuesTask(this, batch >>>= 1, + baseLimit = h, f, tab, searchFunction, result) + .fork(); + } + while (result.get() == null) { + U u; + Node p; + if((p = advance()) == null) { + propagateCompletion(); + break; + } + if((u = searchFunction.apply(p.val)) != null) { + if(result.compareAndSet(null, u)) + quietlyCompleteRoot(); + break; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class SearchEntriesTask extends BulkTask { + final Fun, ? extends U> searchFunction; + final AtomicReference result; + + SearchEntriesTask(BulkTask p, int b, int i, int f, + Node[] t, Fun, ? extends U> searchFunction, + AtomicReference result) { + super(p, b, i, f, t); + this.searchFunction = searchFunction; + this.result = result; + } + + public final U getRawResult() { + return result.get(); + } + + public final void compute() { + final Fun, ? extends U> searchFunction; + final AtomicReference result; + if((searchFunction = this.searchFunction) != null + && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + if(result.get() != null) + return; + addToPendingCount(1); + new SearchEntriesTask(this, batch >>>= 1, + baseLimit = h, f, tab, searchFunction, result) + .fork(); + } + while (result.get() == null) { + U u; + Node p; + if((p = advance()) == null) { + propagateCompletion(); + break; + } + if((u = searchFunction.apply(p)) != null) { + if(result.compareAndSet(null, u)) + quietlyCompleteRoot(); + return; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class SearchMappingsTask extends BulkTask { + final BiFun searchFunction; + final AtomicReference result; + + SearchMappingsTask(BulkTask p, int b, int i, int f, + Node[] t, + BiFun searchFunction, + AtomicReference result) { + super(p, b, i, f, t); + this.searchFunction = searchFunction; + this.result = result; + } + + public final U getRawResult() { + return result.get(); + } + + public final void compute() { + final BiFun searchFunction; + final AtomicReference result; + if((searchFunction = this.searchFunction) != null + && (result = this.result) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + if(result.get() != null) + return; + addToPendingCount(1); + new SearchMappingsTask(this, batch >>>= 1, + baseLimit = h, f, tab, searchFunction, result) + .fork(); + } + while (result.get() == null) { + U u; + Node p; + if((p = advance()) == null) { + propagateCompletion(); + break; + } + if((u = searchFunction.apply(p.key, p.val)) != null) { + if(result.compareAndSet(null, u)) + quietlyCompleteRoot(); + break; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class ReduceKeysTask extends BulkTask { + final BiFun reducer; + K result; + ReduceKeysTask rights, nextRight; + + ReduceKeysTask(BulkTask p, int b, int i, int f, + Node[] t, ReduceKeysTask nextRight, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.reducer = reducer; + } + + public final K getRawResult() { + return result; + } + + public final void compute() { + final BiFun reducer; + if((reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new ReduceKeysTask(this, batch >>>= 1, + baseLimit = h, f, tab, rights, reducer)).fork(); + } + K r = null; + for (Node p; (p = advance()) != null;) { + K u = p.key; + r = (r == null) ? u : u == null ? r : reducer.apply(r, u); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") ReduceKeysTask t = (ReduceKeysTask) c, s = t.rights; + while (s != null) { + K tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class ReduceValuesTask extends BulkTask { + final BiFun reducer; + V result; + ReduceValuesTask rights, nextRight; + + ReduceValuesTask(BulkTask p, int b, int i, int f, + Node[] t, ReduceValuesTask nextRight, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.reducer = reducer; + } + + public final V getRawResult() { + return result; + } + + public final void compute() { + final BiFun reducer; + if((reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new ReduceValuesTask(this, batch >>>= 1, + baseLimit = h, f, tab, rights, reducer)).fork(); + } + V r = null; + for (Node p; (p = advance()) != null;) { + V v = p.val; + r = (r == null) ? v : reducer.apply(r, v); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") ReduceValuesTask t = (ReduceValuesTask) c, s = t.rights; + while (s != null) { + V tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class ReduceEntriesTask extends + BulkTask> { + final BiFun, Map.Entry, ? extends Map.Entry> reducer; + Map.Entry result; + ReduceEntriesTask rights, nextRight; + + ReduceEntriesTask( + BulkTask p, + int b, + int i, + int f, + Node[] t, + ReduceEntriesTask nextRight, + BiFun, Map.Entry, ? extends Map.Entry> reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.reducer = reducer; + } + + public final Map.Entry getRawResult() { + return result; + } + + public final void compute() { + final BiFun, Map.Entry, ? extends Map.Entry> reducer; + if((reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new ReduceEntriesTask(this, batch >>>= 1, + baseLimit = h, f, tab, rights, reducer)).fork(); + } + Map.Entry r = null; + for (Node p; (p = advance()) != null;) + r = (r == null) ? p : reducer.apply(r, p); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") ReduceEntriesTask t = (ReduceEntriesTask) c, s = t.rights; + while (s != null) { + Map.Entry tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceKeysTask extends BulkTask { + final Fun transformer; + final BiFun reducer; + U result; + MapReduceKeysTask rights, nextRight; + + MapReduceKeysTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceKeysTask nextRight, + Fun transformer, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.reducer = reducer; + } + + public final U getRawResult() { + return result; + } + + public final void compute() { + final Fun transformer; + final BiFun reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceKeysTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); + } + U r = null; + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.key)) != null) + r = (r == null) ? u : reducer.apply(r, u); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceKeysTask t = (MapReduceKeysTask) c, s = t.rights; + while (s != null) { + U tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceValuesTask extends BulkTask { + final Fun transformer; + final BiFun reducer; + U result; + MapReduceValuesTask rights, nextRight; + + MapReduceValuesTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceValuesTask nextRight, + Fun transformer, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.reducer = reducer; + } + + public final U getRawResult() { + return result; + } + + public final void compute() { + final Fun transformer; + final BiFun reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceValuesTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); + } + U r = null; + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.val)) != null) + r = (r == null) ? u : reducer.apply(r, u); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceValuesTask t = (MapReduceValuesTask) c, s = t.rights; + while (s != null) { + U tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceEntriesTask extends BulkTask { + final Fun, ? extends U> transformer; + final BiFun reducer; + U result; + MapReduceEntriesTask rights, nextRight; + + MapReduceEntriesTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceEntriesTask nextRight, + Fun, ? extends U> transformer, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.reducer = reducer; + } + + public final U getRawResult() { + return result; + } + + public final void compute() { + final Fun, ? extends U> transformer; + final BiFun reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceEntriesTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); + } + U r = null; + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p)) != null) + r = (r == null) ? u : reducer.apply(r, u); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceEntriesTask t = (MapReduceEntriesTask) c, s = t.rights; + while (s != null) { + U tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceMappingsTask extends BulkTask { + final BiFun transformer; + final BiFun reducer; + U result; + MapReduceMappingsTask rights, nextRight; + + MapReduceMappingsTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceMappingsTask nextRight, + BiFun transformer, + BiFun reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.reducer = reducer; + } + + public final U getRawResult() { + return result; + } + + public final void compute() { + final BiFun transformer; + final BiFun reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceMappingsTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, reducer)).fork(); + } + U r = null; + for (Node p; (p = advance()) != null;) { + U u; + if((u = transformer.apply(p.key, p.val)) != null) + r = (r == null) ? u : reducer.apply(r, u); + } + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceMappingsTask t = (MapReduceMappingsTask) c, s = t.rights; + while (s != null) { + U tr, sr; + if((sr = s.result) != null) + t.result = (((tr = t.result) == null) ? sr + : reducer.apply(tr, sr)); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceKeysToDoubleTask extends + BulkTask { + final ObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + final double basis; + double result; + MapReduceKeysToDoubleTask rights, nextRight; + + MapReduceKeysToDoubleTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceKeysToDoubleTask nextRight, + ObjectToDouble transformer, double basis, + DoubleByDoubleToDouble reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Double getRawResult() { + return result; + } + + public final void compute() { + final ObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + double r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceKeysToDoubleTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceKeysToDoubleTask t = (MapReduceKeysToDoubleTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceValuesToDoubleTask extends + BulkTask { + final ObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + final double basis; + double result; + MapReduceValuesToDoubleTask rights, nextRight; + + MapReduceValuesToDoubleTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceValuesToDoubleTask nextRight, + ObjectToDouble transformer, double basis, + DoubleByDoubleToDouble reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Double getRawResult() { + return result; + } + + public final void compute() { + final ObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + double r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceValuesToDoubleTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceValuesToDoubleTask t = (MapReduceValuesToDoubleTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceEntriesToDoubleTask extends + BulkTask { + final ObjectToDouble> transformer; + final DoubleByDoubleToDouble reducer; + final double basis; + double result; + MapReduceEntriesToDoubleTask rights, nextRight; + + MapReduceEntriesToDoubleTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceEntriesToDoubleTask nextRight, + ObjectToDouble> transformer, double basis, + DoubleByDoubleToDouble reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Double getRawResult() { + return result; + } + + public final void compute() { + final ObjectToDouble> transformer; + final DoubleByDoubleToDouble reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + double r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceEntriesToDoubleTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceEntriesToDoubleTask t = (MapReduceEntriesToDoubleTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceMappingsToDoubleTask extends + BulkTask { + final ObjectByObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + final double basis; + double result; + MapReduceMappingsToDoubleTask rights, nextRight; + + MapReduceMappingsToDoubleTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceMappingsToDoubleTask nextRight, + ObjectByObjectToDouble transformer, + double basis, DoubleByDoubleToDouble reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Double getRawResult() { + return result; + } + + public final void compute() { + final ObjectByObjectToDouble transformer; + final DoubleByDoubleToDouble reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + double r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceMappingsToDoubleTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key, p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceMappingsToDoubleTask t = (MapReduceMappingsToDoubleTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceKeysToLongTask extends + BulkTask { + final ObjectToLong transformer; + final LongByLongToLong reducer; + final long basis; + long result; + MapReduceKeysToLongTask rights, nextRight; + + MapReduceKeysToLongTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceKeysToLongTask nextRight, + ObjectToLong transformer, long basis, + LongByLongToLong reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Long getRawResult() { + return result; + } + + public final void compute() { + final ObjectToLong transformer; + final LongByLongToLong reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + long r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceKeysToLongTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceKeysToLongTask t = (MapReduceKeysToLongTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceValuesToLongTask extends + BulkTask { + final ObjectToLong transformer; + final LongByLongToLong reducer; + final long basis; + long result; + MapReduceValuesToLongTask rights, nextRight; + + MapReduceValuesToLongTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceValuesToLongTask nextRight, + ObjectToLong transformer, long basis, + LongByLongToLong reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Long getRawResult() { + return result; + } + + public final void compute() { + final ObjectToLong transformer; + final LongByLongToLong reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + long r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceValuesToLongTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceValuesToLongTask t = (MapReduceValuesToLongTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceEntriesToLongTask extends + BulkTask { + final ObjectToLong> transformer; + final LongByLongToLong reducer; + final long basis; + long result; + MapReduceEntriesToLongTask rights, nextRight; + + MapReduceEntriesToLongTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceEntriesToLongTask nextRight, + ObjectToLong> transformer, long basis, + LongByLongToLong reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Long getRawResult() { + return result; + } + + public final void compute() { + final ObjectToLong> transformer; + final LongByLongToLong reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + long r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceEntriesToLongTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceEntriesToLongTask t = (MapReduceEntriesToLongTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceMappingsToLongTask extends + BulkTask { + final ObjectByObjectToLong transformer; + final LongByLongToLong reducer; + final long basis; + long result; + MapReduceMappingsToLongTask rights, nextRight; + + MapReduceMappingsToLongTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceMappingsToLongTask nextRight, + ObjectByObjectToLong transformer, + long basis, LongByLongToLong reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Long getRawResult() { + return result; + } + + public final void compute() { + final ObjectByObjectToLong transformer; + final LongByLongToLong reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + long r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceMappingsToLongTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key, p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceMappingsToLongTask t = (MapReduceMappingsToLongTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceKeysToIntTask extends + BulkTask { + final ObjectToInt transformer; + final IntByIntToInt reducer; + final int basis; + int result; + MapReduceKeysToIntTask rights, nextRight; + + MapReduceKeysToIntTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceKeysToIntTask nextRight, + ObjectToInt transformer, int basis, + IntByIntToInt reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Integer getRawResult() { + return result; + } + + public final void compute() { + final ObjectToInt transformer; + final IntByIntToInt reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + int r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceKeysToIntTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceKeysToIntTask t = (MapReduceKeysToIntTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceValuesToIntTask extends + BulkTask { + final ObjectToInt transformer; + final IntByIntToInt reducer; + final int basis; + int result; + MapReduceValuesToIntTask rights, nextRight; + + MapReduceValuesToIntTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceValuesToIntTask nextRight, + ObjectToInt transformer, int basis, + IntByIntToInt reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Integer getRawResult() { + return result; + } + + public final void compute() { + final ObjectToInt transformer; + final IntByIntToInt reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + int r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceValuesToIntTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceValuesToIntTask t = (MapReduceValuesToIntTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceEntriesToIntTask extends + BulkTask { + final ObjectToInt> transformer; + final IntByIntToInt reducer; + final int basis; + int result; + MapReduceEntriesToIntTask rights, nextRight; + + MapReduceEntriesToIntTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceEntriesToIntTask nextRight, + ObjectToInt> transformer, int basis, + IntByIntToInt reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Integer getRawResult() { + return result; + } + + public final void compute() { + final ObjectToInt> transformer; + final IntByIntToInt reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + int r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceEntriesToIntTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceEntriesToIntTask t = (MapReduceEntriesToIntTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + @SuppressWarnings("serial") + static final class MapReduceMappingsToIntTask extends + BulkTask { + final ObjectByObjectToInt transformer; + final IntByIntToInt reducer; + final int basis; + int result; + MapReduceMappingsToIntTask rights, nextRight; + + MapReduceMappingsToIntTask(BulkTask p, int b, int i, int f, + Node[] t, MapReduceMappingsToIntTask nextRight, + ObjectByObjectToInt transformer, + int basis, IntByIntToInt reducer) { + super(p, b, i, f, t); + this.nextRight = nextRight; + this.transformer = transformer; + this.basis = basis; + this.reducer = reducer; + } + + public final Integer getRawResult() { + return result; + } + + public final void compute() { + final ObjectByObjectToInt transformer; + final IntByIntToInt reducer; + if((transformer = this.transformer) != null + && (reducer = this.reducer) != null) { + int r = this.basis; + for (int i = baseIndex, f, h; batch > 0 + && (h = ((f = baseLimit) + i) >>> 1) > i;) { + addToPendingCount(1); + (rights = new MapReduceMappingsToIntTask(this, + batch >>>= 1, baseLimit = h, f, tab, rights, + transformer, r, reducer)).fork(); + } + for (Node p; (p = advance()) != null;) + r = reducer.apply(r, transformer.apply(p.key, p.val)); + result = r; + CountedCompleter c; + for (c = firstComplete(); c != null; c = c.nextComplete()) { + @SuppressWarnings("unchecked") MapReduceMappingsToIntTask t = (MapReduceMappingsToIntTask) c, s = t.rights; + while (s != null) { + t.result = reducer.apply(t.result, s.result); + s = t.rights = s.nextRight; + } + } + } + } + } + + /* ---------------- Counters -------------- */ + + // Adapted from LongAdder and Striped64. + // See their internal docs for explanation. + + // A padded cell for distributing counts + static final class CounterCell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + CounterCell(long x) { + value = x; + } + } + + /** + * Holder for the thread-local hash code determining which + * CounterCell to use. The code is initialized via the + * counterHashCodeGenerator, but may be moved upon collisions. + */ + static final class CounterHashCode { + int code; + } + + /** + * Generates initial value for per-thread CounterHashCodes. + */ + static final AtomicInteger counterHashCodeGenerator = new AtomicInteger(); + + /** + * Increment for counterHashCodeGenerator. See class ThreadLocal + * for explanation. + */ + static final int SEED_INCREMENT = 0x61c88647; + + /** + * Per-thread counter hash codes. Shared across all instances. + */ + static final ThreadLocal threadCounterHashCode = new ThreadLocal(); + + final long sumCount() { + CounterCell[] as = counterCells; + CounterCell a; + long sum = baseCount; + if(as != null) { + for (int i = 0; i < as.length; ++i) { + if((a = as[i]) != null) + sum += a.value; + } + } + return sum; + } + + // See LongAdder version for explanation + private final void fullAddCount(long x, CounterHashCode hc, + boolean wasUncontended) { + int h; + if(hc == null) { + hc = new CounterHashCode(); + int s = counterHashCodeGenerator.addAndGet(SEED_INCREMENT); + h = hc.code = (s == 0) ? 1 : s; // Avoid zero + threadCounterHashCode.set(hc); + } + else + h = hc.code; + boolean collide = false; // True if last slot nonempty + for (;;) { + CounterCell[] as; + CounterCell a; + int n; + long v; + if((as = counterCells) != null && (n = as.length) > 0) { + if((a = as[(n - 1) & h]) == null) { + if(cellsBusy == 0) { // Try to attach new Cell + CounterCell r = new CounterCell(x); // Optimistic create + if(cellsBusy == 0 + && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + boolean created = false; + try { // Recheck under lock + CounterCell[] rs; + int m, j; + if((rs = counterCells) != null + && (m = rs.length) > 0 + && rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } + finally { + cellsBusy = 0; + } + if(created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if(!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if(U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x)) + break; + else if(counterCells != as || n >= NCPU) + collide = false; // At max size or stale + else if(!collide) + collide = true; + else if(cellsBusy == 0 + && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + try { + if(counterCells == as) {// Expand table unless stale + CounterCell[] rs = new CounterCell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + counterCells = rs; + } + } + finally { + cellsBusy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + } + else if(cellsBusy == 0 && counterCells == as + && U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) { + boolean init = false; + try { // Initialize table + if(counterCells == as) { + CounterCell[] rs = new CounterCell[2]; + rs[h & 1] = new CounterCell(x); + counterCells = rs; + init = true; + } + } + finally { + cellsBusy = 0; + } + if(init) + break; + } + else if(U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x)) + break; // Fall back on using base + } + hc.code = h; // Record index for next time + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long SIZECTL; + private static final long TRANSFERINDEX; + private static final long BASECOUNT; + private static final long CELLSBUSY; + private static final long CELLVALUE; + private static final long ABASE; + private static final int ASHIFT; + + static { + try { + U = getUnsafe(); + Class k = ConcurrentHashMapV8.class; + SIZECTL = U.objectFieldOffset(k.getDeclaredField("sizeCtl")); + TRANSFERINDEX = U.objectFieldOffset(k + .getDeclaredField("transferIndex")); + BASECOUNT = U.objectFieldOffset(k.getDeclaredField("baseCount")); + CELLSBUSY = U.objectFieldOffset(k.getDeclaredField("cellsBusy")); + Class ck = CounterCell.class; + CELLVALUE = U.objectFieldOffset(ck.getDeclaredField("value")); + Class ak = Node[].class; + ABASE = U.arrayBaseOffset(ak); + int scale = U.arrayIndexScale(ak); + if((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CountedCompleter.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CountedCompleter.java new file mode 100644 index 0000000000..4134d1cb51 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/CountedCompleter.java @@ -0,0 +1,786 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +/** + * A {@link ForkJoinTask} with a completion action performed when + * triggered and there are no remaining pending actions. + * CountedCompleters are in general more robust in the + * presence of subtask stalls and blockage than are other forms of + * ForkJoinTasks, but are less intuitive to program. Uses of + * CountedCompleter are similar to those of other completion based + * components (such as {@link java.nio.channels.CompletionHandler}) + * except that multiple pending completions may be necessary + * to trigger the completion action {@link #onCompletion(CountedCompleter)}, + * not just one. + * Unless initialized otherwise, the {@linkplain #getPendingCount pending + * count} starts at zero, but may be (atomically) changed using + * methods {@link #setPendingCount}, {@link #addToPendingCount}, and + * {@link #compareAndSetPendingCount}. Upon invocation of {@link #tryComplete}, + * if the pending action count is nonzero, it is + * decremented; otherwise, the completion action is performed, and if + * this completer itself has a completer, the process is continued + * with its completer. As is the case with related synchronization + * components such as {@link java.util.concurrent.Phaser Phaser} and + * {@link java.util.concurrent.Semaphore Semaphore}, these methods + * affect only internal counts; they do not establish any further + * internal bookkeeping. In particular, the identities of pending + * tasks are not maintained. As illustrated below, you can create + * subclasses that do record some or all pending tasks or their + * results when needed. As illustrated below, utility methods + * supporting customization of completion traversals are also + * provided. However, because CountedCompleters provide only basic + * synchronization mechanisms, it may be useful to create further + * abstract subclasses that maintain linkages, fields, and additional + * support methods appropriate for a set of related usages. + * + *

+ * A concrete CountedCompleter class must define method {@link #compute}, that + * should in most cases (as illustrated below), invoke {@code tryComplete()} + * once before returning. The class may also optionally override method + * {@link #onCompletion(CountedCompleter)} to perform an action upon normal + * completion, and method + * {@link #onExceptionalCompletion(Throwable, CountedCompleter)} to perform an + * action upon any exception. + * + *

+ * CountedCompleters most often do not bear results, in which case they are + * normally declared as {@code CountedCompleter}, and will always return + * {@code null} as a result value. In other cases, you should override method + * {@link #getRawResult} to provide a result from {@code join(), invoke()}, and + * related methods. In general, this method should return the value of a field + * (or a function of one or more fields) of the CountedCompleter object that + * holds the result upon completion. Method {@link #setRawResult} by default + * plays no role in CountedCompleters. It is possible, but rarely applicable, to + * override this method to maintain other objects or fields holding result data. + * + *

+ * A CountedCompleter that does not itself have a completer (i.e., one for which + * {@link #getCompleter} returns {@code null}) can be used as a regular + * ForkJoinTask with this added functionality. However, any completer that in + * turn has another completer serves only as an internal helper for other + * computations, so its own task status (as reported in methods such as + * {@link ForkJoinTask#isDone}) is arbitrary; this status changes only upon + * explicit invocations of {@link #complete}, {@link ForkJoinTask#cancel}, + * {@link ForkJoinTask#completeExceptionally(Throwable)} or upon exceptional + * completion of method {@code compute}. Upon any exceptional completion, the + * exception may be relayed to a task's completer (and its completer, and so + * on), if one exists and it has not otherwise already completed. Similarly, + * cancelling an internal CountedCompleter has only a local effect on that + * completer, so is not often useful. + * + *

+ * Sample Usages. + * + *

+ * Parallel recursive decomposition. CountedCompleters may be arranged in + * trees similar to those often used with {@link RecursiveAction}s, although the + * constructions involved in setting them up typically vary. Here, the completer + * of each task is its parent in the computation tree. Even though they entail a + * bit more bookkeeping, CountedCompleters may be better choices when applying a + * possibly time-consuming operation (that cannot be further subdivided) to each + * element of an array or collection; especially when the operation takes a + * significantly different amount of time to complete for some elements than + * others, either because of intrinsic variation (for example I/O) or auxiliary + * effects such as garbage collection. Because CountedCompleters provide their + * own continuations, other threads need not block waiting to perform them. + * + *

+ * For example, here is an initial version of a class that uses divide-by-two + * recursive decomposition to divide work into single pieces (leaf tasks). Even + * when work is split into individual calls, tree-based techniques are usually + * preferable to directly forking leaf tasks, because they reduce inter-thread + * communication and improve load balancing. In the recursive case, the second + * of each pair of subtasks to finish triggers completion of its parent (because + * no result combination is performed, the default no-op implementation of + * method {@code onCompletion} is not overridden). A static utility method sets + * up the base task and invokes it (here, implicitly using the + * {@link ForkJoinPool#commonPool()}). + * + *

+ * {@code
+ * class MyOperation { void apply(E e) { ... }  }
+ * 
+ * class ForEach extends CountedCompleter {
+ * 
+ *   public static  void forEach(E[] array, MyOperation op) {
+ *     new ForEach(null, array, op, 0, array.length).invoke();
+ *   }
+ * 
+ *   final E[] array; final MyOperation op; final int lo, hi;
+ *   ForEach(CountedCompleter p, E[] array, MyOperation op, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.op = op; this.lo = lo; this.hi = hi;
+ *   }
+ * 
+ *   public void compute() { // version 1
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       setPendingCount(2); // must set pending count before fork
+ *       new ForEach(this, array, op, mid, hi).fork(); // right child
+ *       new ForEach(this, array, op, lo, mid).fork(); // left child
+ *     }
+ *     else if (hi > lo)
+ *       op.apply(array[lo]);
+ *     tryComplete();
+ *   }
+ * }}
+ * 
+ * + * This design can be improved by noticing that in the recursive case, the task + * has nothing to do after forking its right task, so can directly invoke its + * left task before returning. (This is an analog of tail recursion removal.) + * Also, because the task returns upon executing its left task (rather than + * falling through to invoke {@code tryComplete}) the pending count is set to + * one: + * + *
+ * {@code
+ * class ForEach ...
+ *   public void compute() { // version 2
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       setPendingCount(1); // only one pending
+ *       new ForEach(this, array, op, mid, hi).fork(); // right child
+ *       new ForEach(this, array, op, lo, mid).compute(); // direct invoke
+ *     }
+ *     else {
+ *       if (hi > lo)
+ *         op.apply(array[lo]);
+ *       tryComplete();
+ *     }
+ *   }
+ * }
+ * 
+ * + * As a further improvement, notice that the left task need not even exist. + * Instead of creating a new one, we can iterate using the original task, and + * add a pending count for each fork. Additionally, because no task in this tree + * implements an {@link #onCompletion(CountedCompleter)} method, + * {@code tryComplete()} can be replaced with {@link #propagateCompletion}. + * + *
+ * {@code
+ * class ForEach ...
+ *   public void compute() { // version 3
+ *     int l = lo,  h = hi;
+ *     while (h - l >= 2) {
+ *       int mid = (l + h) >>> 1;
+ *       addToPendingCount(1);
+ *       new ForEach(this, array, op, mid, h).fork(); // right child
+ *       h = mid;
+ *     }
+ *     if (h > l)
+ *       op.apply(array[l]);
+ *     propagateCompletion();
+ *   }
+ * }
+ * 
+ * + * Additional improvements of such classes might entail precomputing pending + * counts so that they can be established in constructors, specializing classes + * for leaf steps, subdividing by say, four, instead of two per iteration, and + * using an adaptive threshold instead of always subdividing down to single + * elements. + * + *

+ * Searching. A tree of CountedCompleters can search for a value or + * property in different parts of a data structure, and report a result in an + * {@link java.util.concurrent.atomic.AtomicReference AtomicReference} as soon + * as one is found. The others can poll the result to avoid unnecessary work. + * (You could additionally {@linkplain #cancel + * cancel} other tasks, but it is usually simpler and more efficient to just let + * them notice that the result is set and if so skip further processing.) + * Illustrating again with an array using full partitioning (again, in practice, + * leaf tasks will almost always process more than one element): + * + *

+ * {@code
+ * class Searcher extends CountedCompleter {
+ *   final E[] array; final AtomicReference result; final int lo, hi;
+ *   Searcher(CountedCompleter p, E[] array, AtomicReference result, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.result = result; this.lo = lo; this.hi = hi;
+ *   }
+ *   public E getRawResult() { return result.get(); }
+ *   public void compute() { // similar to ForEach version 3
+ *     int l = lo,  h = hi;
+ *     while (result.get() == null && h >= l) {
+ *       if (h - l >= 2) {
+ *         int mid = (l + h) >>> 1;
+ *         addToPendingCount(1);
+ *         new Searcher(this, array, result, mid, h).fork();
+ *         h = mid;
+ *       }
+ *       else {
+ *         E x = array[l];
+ *         if (matches(x) && result.compareAndSet(null, x))
+ *           quietlyCompleteRoot(); // root task is now joinable
+ *         break;
+ *       }
+ *     }
+ *     tryComplete(); // normally complete whether or not found
+ *   }
+ *   boolean matches(E e) { ... } // return true if found
+ * 
+ *   public static  E search(E[] array) {
+ *       return new Searcher(null, array, new AtomicReference(), 0, array.length).invoke();
+ *   }
+ * }}
+ * 
+ * + * In this example, as well as others in which tasks have no other effects + * except to compareAndSet a common result, the trailing unconditional + * invocation of {@code tryComplete} could be made conditional ( + * {@code if (result.get() == null) tryComplete();}) because no further + * bookkeeping is required to manage completions once the root task completes. + * + *

+ * Recording subtasks. CountedCompleter tasks that combine results of + * multiple subtasks usually need to access these results in method + * {@link #onCompletion(CountedCompleter)}. As illustrated in the following + * class (that performs a simplified form of map-reduce where mappings and + * reductions are all of type {@code E}), one way to do this in divide and + * conquer designs is to have each subtask record its sibling, so that it can be + * accessed in method {@code onCompletion}. This technique applies to reductions + * in which the order of combining left and right results does not matter; + * ordered reductions require explicit left/right designations. Variants of + * other streamlinings seen in the above examples may also apply. + * + *

+ * {@code
+ * class MyMapper { E apply(E v) {  ...  } }
+ * class MyReducer { E apply(E x, E y) {  ...  } }
+ * class MapReducer extends CountedCompleter {
+ *   final E[] array; final MyMapper mapper;
+ *   final MyReducer reducer; final int lo, hi;
+ *   MapReducer sibling;
+ *   E result;
+ *   MapReducer(CountedCompleter p, E[] array, MyMapper mapper,
+ *              MyReducer reducer, int lo, int hi) {
+ *     super(p);
+ *     this.array = array; this.mapper = mapper;
+ *     this.reducer = reducer; this.lo = lo; this.hi = hi;
+ *   }
+ *   public void compute() {
+ *     if (hi - lo >= 2) {
+ *       int mid = (lo + hi) >>> 1;
+ *       MapReducer left = new MapReducer(this, array, mapper, reducer, lo, mid);
+ *       MapReducer right = new MapReducer(this, array, mapper, reducer, mid, hi);
+ *       left.sibling = right;
+ *       right.sibling = left;
+ *       setPendingCount(1); // only right is pending
+ *       right.fork();
+ *       left.compute();     // directly execute left
+ *     }
+ *     else {
+ *       if (hi > lo)
+ *           result = mapper.apply(array[lo]);
+ *       tryComplete();
+ *     }
+ *   }
+ *   public void onCompletion(CountedCompleter caller) {
+ *     if (caller != this) {
+ *       MapReducer child = (MapReducer)caller;
+ *       MapReducer sib = child.sibling;
+ *       if (sib == null || sib.result == null)
+ *         result = child.result;
+ *       else
+ *         result = reducer.apply(child.result, sib.result);
+ *     }
+ *   }
+ *   public E getRawResult() { return result; }
+ * 
+ *   public static  E mapReduce(E[] array, MyMapper mapper, MyReducer reducer) {
+ *     return new MapReducer(null, array, mapper, reducer,
+ *                              0, array.length).invoke();
+ *   }
+ * }}
+ * 
+ * + * Here, method {@code onCompletion} takes a form common to many completion + * designs that combine results. This callback-style method is triggered once + * per task, in either of the two different contexts in which the pending count + * is, or becomes, zero: (1) by a task itself, if its pending count is zero upon + * invocation of {@code tryComplete}, or (2) by any of its subtasks when they + * complete and decrement the pending count to zero. The {@code caller} argument + * distinguishes cases. Most often, when the caller is {@code this}, no action + * is necessary. Otherwise the caller argument can be used (usually via a cast) + * to supply a value (and/or links to other values) to be combined. Assuming + * proper use of pending counts, the actions inside {@code onCompletion} occur + * (once) upon completion of a task and its subtasks. No additional + * synchronization is required within this method to ensure thread safety of + * accesses to fields of this task or other completed tasks. + * + *

+ * Completion Traversals. If using {@code onCompletion} to process + * completions is inapplicable or inconvenient, you can use methods + * {@link #firstComplete} and {@link #nextComplete} to create custom traversals. + * For example, to define a MapReducer that only splits out right-hand tasks in + * the form of the third ForEach example, the completions must cooperatively + * reduce along unexhausted subtask links, which can be done as follows: + * + *

+ * {
+ *     @code
+ *     class MapReducer<E> extends CountedCompleter<E> { // version 2
+ *         final E[] array;
+ *         final MyMapper<E> mapper;
+ *         final MyReducer<E> reducer;
+ *         final int lo, hi;
+ *         MapReducer<E> forks, next; // record subtask forks in list
+ *         E result;
+ * 
+ *         MapReducer(CountedCompleter<?> p, E[] array, MyMapper<E> mapper,
+ *                 MyReducer<E> reducer, int lo, int hi, MapReducer<E> next) {
+ *             super(p);
+ *             this.array = array;
+ *             this.mapper = mapper;
+ *             this.reducer = reducer;
+ *             this.lo = lo;
+ *             this.hi = hi;
+ *             this.next = next;
+ *         }
+ * 
+ *         public void compute() {
+ *             int l = lo, h = hi;
+ *             while (h - l >= 2) {
+ *                 int mid = (l + h) >>> 1;
+ *                 addToPendingCount(1);
+ *                 (forks = new MapReducer(this, array, mapper, reducer, mid, h,
+ *                         forks)).fork();
+ *                 h = mid;
+ *             }
+ *             if(h > l)
+ *                 result = mapper.apply(array[l]);
+ *             // process completions by reducing along and advancing subtask links
+ *             for (CountedCompleter<?> c = firstComplete(); c != null; c = c
+ *                     .nextComplete()) {
+ *                 for (MapReducer t = (MapReducer) c, s = t.forks; s != null; s = t.forks = s.next)
+ *                     t.result = reducer.apply(t.result, s.result);
+ *             }
+ *         }
+ * 
+ *         public E getRawResult() {
+ *             return result;
+ *         }
+ * 
+ *         public static <E> E mapReduce(E[] array, MyMapper<E> mapper,
+ *                 MyReducer<E> reducer) {
+ *             return new MapReducer<E>(null, array, mapper, reducer, 0,
+ *                     array.length, null).invoke();
+ *         }
+ *     }
+ * }
+ * 
+ * + *

+ * Triggers. Some CountedCompleters are themselves never forked, but + * instead serve as bits of plumbing in other designs; including those in which + * the completion of one or more async tasks triggers another async task. For + * example: + * + *

+ * {@code
+ * class HeaderBuilder extends CountedCompleter<...> { ... }
+ * class BodyBuilder extends CountedCompleter<...> { ... }
+ * class PacketSender extends CountedCompleter<...> {
+ *   PacketSender(...) { super(null, 1); ... } // trigger on second completion
+ *   public void compute() { } // never called
+ *   public void onCompletion(CountedCompleter caller) { sendPacket(); }
+ * }
+ * // sample use:
+ * PacketSender p = new PacketSender();
+ * new HeaderBuilder(p, ...).fork();
+ * new BodyBuilder(p, ...).fork();
+ * }
+ * 
+ * + * @since 1.8 + * @author Doug Lea + */ +public abstract class CountedCompleter extends ForkJoinTask { + private static final long serialVersionUID = 5232453752276485070L; + + /** This task's completer, or null if none */ + final CountedCompleter completer; + /** The number of pending tasks until completion */ + volatile int pending; + + /** + * Creates a new CountedCompleter with the given completer + * and initial pending count. + * + * @param completer this task's completer, or {@code null} if none + * @param initialPendingCount the initial pending count + */ + protected CountedCompleter(CountedCompleter completer, + int initialPendingCount) { + this.completer = completer; + this.pending = initialPendingCount; + } + + /** + * Creates a new CountedCompleter with the given completer + * and an initial pending count of zero. + * + * @param completer this task's completer, or {@code null} if none + */ + protected CountedCompleter(CountedCompleter completer) { + this.completer = completer; + } + + /** + * Creates a new CountedCompleter with no completer + * and an initial pending count of zero. + */ + protected CountedCompleter() { + this.completer = null; + } + + /** + * The main computation performed by this task. + */ + public abstract void compute(); + + /** + * Performs an action when method {@link #tryComplete} is invoked + * and the pending count is zero, or when the unconditional + * method {@link #complete} is invoked. By default, this method + * does nothing. You can distinguish cases by checking the + * identity of the given caller argument. If not equal to {@code this}, then + * it is typically a subtask that may contain results + * (and/or links to other results) to combine. + * + * @param caller the task invoking this method (which may + * be this task itself) + */ + public void onCompletion(CountedCompleter caller) {} + + /** + * Performs an action when method {@link #completeExceptionally(Throwable)} + * is invoked or method {@link #compute} throws an exception, and this task + * has not already + * otherwise completed normally. On entry to this method, this task + * {@link ForkJoinTask#isCompletedAbnormally}. The return value + * of this method controls further propagation: If {@code true} and this + * task has a completer that has not completed, then that + * completer is also completed exceptionally, with the same + * exception as this completer. The default implementation of + * this method does nothing except return {@code true}. + * + * @param ex the exception + * @param caller the task invoking this method (which may + * be this task itself) + * @return {@code true} if this exception should be propagated to this + * task's completer, if one exists + */ + public boolean onExceptionalCompletion(Throwable ex, + CountedCompleter caller) { + return true; + } + + /** + * Returns the completer established in this task's constructor, + * or {@code null} if none. + * + * @return the completer + */ + public final CountedCompleter getCompleter() { + return completer; + } + + /** + * Returns the current pending count. + * + * @return the current pending count + */ + public final int getPendingCount() { + return pending; + } + + /** + * Sets the pending count to the given value. + * + * @param count the count + */ + public final void setPendingCount(int count) { + pending = count; + } + + /** + * Adds (atomically) the given value to the pending count. + * + * @param delta the value to add + */ + public final void addToPendingCount(int delta) { + int c; + do {} + while (!U.compareAndSwapInt(this, PENDING, c = pending, c + delta)); + } + + /** + * Sets (atomically) the pending count to the given count only if + * it currently holds the given expected value. + * + * @param expected the expected value + * @param count the new value + * @return {@code true} if successful + */ + public final boolean compareAndSetPendingCount(int expected, int count) { + return U.compareAndSwapInt(this, PENDING, expected, count); + } + + /** + * If the pending count is nonzero, (atomically) decrements it. + * + * @return the initial (undecremented) pending count holding on entry + * to this method + */ + public final int decrementPendingCountUnlessZero() { + int c; + do {} + while ((c = pending) != 0 + && !U.compareAndSwapInt(this, PENDING, c, c - 1)); + return c; + } + + /** + * Returns the root of the current computation; i.e., this + * task if it has no completer, else its completer's root. + * + * @return the root of the current computation + */ + public final CountedCompleter getRoot() { + CountedCompleter a = this, p; + while ((p = a.completer) != null) + a = p; + return a; + } + + /** + * If the pending count is nonzero, decrements the count; + * otherwise invokes {@link #onCompletion(CountedCompleter)} and then + * similarly tries to complete this task's completer, + * if one exists, else marks this task as complete. + */ + public final void tryComplete() { + CountedCompleter a = this, s = a; + for (int c;;) { + if((c = a.pending) == 0) { + a.onCompletion(s); + if((a = (s = a).completer) == null) { + s.quietlyComplete(); + return; + } + } + else if(U.compareAndSwapInt(a, PENDING, c, c - 1)) + return; + } + } + + /** + * Equivalent to {@link #tryComplete} but does not invoke + * {@link #onCompletion(CountedCompleter)} along the completion path: + * If the pending count is nonzero, decrements the count; + * otherwise, similarly tries to complete this task's completer, if + * one exists, else marks this task as complete. This method may be + * useful in cases where {@code onCompletion} should not, or need + * not, be invoked for each completer in a computation. + */ + public final void propagateCompletion() { + CountedCompleter a = this, s = a; + for (int c;;) { + if((c = a.pending) == 0) { + if((a = (s = a).completer) == null) { + s.quietlyComplete(); + return; + } + } + else if(U.compareAndSwapInt(a, PENDING, c, c - 1)) + return; + } + } + + /** + * Regardless of pending count, invokes + * {@link #onCompletion(CountedCompleter)}, marks this task as + * complete and further triggers {@link #tryComplete} on this + * task's completer, if one exists. The given rawResult is + * used as an argument to {@link #setRawResult} before invoking + * {@link #onCompletion(CountedCompleter)} or marking this task + * as complete; its value is meaningful only for classes + * overriding {@code setRawResult}. This method does not modify + * the pending count. + * + *

+ * This method may be useful when forcing completion as soon as any one + * (versus all) of several subtask results are obtained. However, in the + * common (and recommended) case in which {@code setRawResult} is not + * overridden, this effect can be obtained more simply using + * {@code quietlyCompleteRoot();}. + * + * @param rawResult the raw result + */ + public void complete(T rawResult) { + CountedCompleter p; + setRawResult(rawResult); + onCompletion(this); + quietlyComplete(); + if((p = completer) != null) + p.tryComplete(); + } + + /** + * If this task's pending count is zero, returns this task; + * otherwise decrements its pending count and returns {@code null}. This + * method is designed to be used with {@link #nextComplete} in completion + * traversal loops. + * + * @return this task, if pending count was zero, else {@code null} + */ + public final CountedCompleter firstComplete() { + for (int c;;) { + if((c = pending) == 0) + return this; + else if(U.compareAndSwapInt(this, PENDING, c, c - 1)) + return null; + } + } + + /** + * If this task does not have a completer, invokes + * {@link ForkJoinTask#quietlyComplete} and returns {@code null}. Or, if + * the completer's pending count is non-zero, decrements that + * pending count and returns {@code null}. Otherwise, returns the + * completer. This method can be used as part of a completion + * traversal loop for homogeneous task hierarchies: + * + *

+     * {@code
+     * for (CountedCompleter c = firstComplete();
+     *      c != null;
+     *      c = c.nextComplete()) {
+     *   // ... process c ...
+     * }}
+     * 
+ * + * @return the completer, or {@code null} if none + */ + public final CountedCompleter nextComplete() { + CountedCompleter p; + if((p = completer) != null) + return p.firstComplete(); + else { + quietlyComplete(); + return null; + } + } + + /** + * Equivalent to {@code getRoot().quietlyComplete()}. + */ + public final void quietlyCompleteRoot() { + for (CountedCompleter a = this, p;;) { + if((p = a.completer) == null) { + a.quietlyComplete(); + return; + } + a = p; + } + } + + /** + * Supports ForkJoinTask exception propagation. + */ + void internalPropagateException(Throwable ex) { + CountedCompleter a = this, s = a; + while (a.onExceptionalCompletion(ex, s) + && (a = (s = a).completer) != null && a.status >= 0 + && a.recordExceptionalCompletion(ex) == EXCEPTIONAL); + } + + /** + * Implements execution conventions for CountedCompleters. + */ + protected final boolean exec() { + compute(); + return false; + } + + /** + * Returns the result of the computation. By default, + * returns {@code null}, which is appropriate for {@code Void} actions, but + * in other cases should be overridden, almost + * always to return a field or function of a field that + * holds the result upon completion. + * + * @return the result of the computation + */ + public T getRawResult() { + return null; + } + + /** + * A method that result-bearing CountedCompleters may optionally + * use to help maintain result data. By default, does nothing. + * Overrides are not recommended. However, if this method is + * overridden to update existing objects or fields, then it must + * in general be defined to be thread-safe. + */ + protected void setRawResult(T t) {} + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long PENDING; + static { + try { + U = getUnsafe(); + PENDING = U.objectFieldOffset(CountedCompleter.class + .getDeclaredField("pending")); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleAdder.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleAdder.java new file mode 100644 index 0000000000..619458c921 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleAdder.java @@ -0,0 +1,210 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * One or more variables that together maintain an initially zero {@code double} + * sum. When updates (method {@link #add}) are + * contended across threads, the set of variables may grow dynamically + * to reduce contention. Method {@link #sum} (or, equivalently + * {@link #doubleValue}) returns the current total combined across the + * variables maintaining the sum. + * + *

+ * This class extends {@link Number}, but does not define methods such + * as {@code equals}, {@code hashCode} and {@code compareTo} because instances + * are expected to be mutated, and so are not useful as collection keys. + * + *

+ * org.cinchapi.vendor.jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class DoubleAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Update function. Note that we must use "long" for underlying + * representations, because there is no compareAndSet for double, + * due to the fact that the bitwise equals used in any CAS + * implementation is not the same as double-precision equals. + * However, we use CAS only to detect and alleviate contention, + * for which bitwise equals works best anyway. In principle, the + * long/double conversions used here should be essentially free on + * most platforms since they just re-interpret bits. + * + * Similar conversions are used in other methods. + */ + final long fn(long v, long x) { + return Double.doubleToRawLongBits(Double.longBitsToDouble(v) + + Double.longBitsToDouble(x)); + } + + /** + * Creates a new adder with initial sum of zero. + */ + public DoubleAdder() {} + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(double x) { + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if((as = cells) != null + || !casBase( + b = base, + Double.doubleToRawLongBits(Double.longBitsToDouble(b) + + x))) { + boolean uncontended = true; + if((hc = threadHashCode.get()) == null + || as == null + || (n = as.length) < 1 + || (a = as[(n - 1) & hc[0]]) == null + || !(uncontended = a.cas( + v = a.value, + Double.doubleToRawLongBits(Double + .longBitsToDouble(v) + x)))) + retryUpdate(Double.doubleToRawLongBits(x), hc, uncontended); + } + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot; invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. Also, because floating-point arithmetic is not + * strictly associative, the returned result need not be identical + * to the value that would be obtained in a sequential series of + * updates to a single variable. + * + * @return the sum + */ + public double sum() { + Cell[] as = cells; + double sum = Double.longBitsToDouble(base); + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) + sum += Double.longBitsToDouble(a.value); + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link #reset}. This + * method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is not + * guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public double sumThenReset() { + Cell[] as = cells; + double sum = Double.longBitsToDouble(base); + base = 0L; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) { + long v = a.value; + a.value = 0L; + sum += Double.longBitsToDouble(v); + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Double.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public double doubleValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as a {@code long} after a + * narrowing primitive conversion. + */ + public long longValue() { + return (long) sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a + * narrowing primitive conversion. + */ + public int intValue() { + return (int) sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} after a narrowing primitive + * conversion. + */ + public float floatValue() { + return (float) sum(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeDouble(sum()); + } + + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = Double.doubleToRawLongBits(s.readDouble()); + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleMaxUpdater.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleMaxUpdater.java new file mode 100644 index 0000000000..1e4c24cb04 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/DoubleMaxUpdater.java @@ -0,0 +1,205 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * One or more variables that together maintain a running {@code double} maximum + * with initial value {@code Double.NEGATIVE_INFINITY}. When + * updates (method {@link #update}) are contended across threads, the + * set of variables may grow dynamically to reduce contention. Method + * {@link #max} (or, equivalently, {@link #doubleValue}) returns the + * current maximum across the variables maintaining updates. + * + *

+ * This class extends {@link Number}, but does not define methods such + * as {@code equals}, {@code hashCode} and {@code compareTo} because instances + * are expected to be mutated, and so are not useful as collection keys. + * + *

+ * org.cinchapi.vendor.jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class DoubleMaxUpdater extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + /** + * Long representation of negative infinity. See class Double + * internal documentation for explanation. + */ + private static final long MIN_AS_LONG = 0xfff0000000000000L; + + /** + * Update function. See class DoubleAdder for rationale + * for using conversions from/to long. + */ + final long fn(long v, long x) { + return Double.longBitsToDouble(v) > Double.longBitsToDouble(x) ? v : x; + } + + /** + * Creates a new instance with initial value of + * {@code Double.NEGATIVE_INFINITY}. + */ + public DoubleMaxUpdater() { + base = MIN_AS_LONG; + } + + /** + * Updates the maximum to be at least the given value. + * + * @param x the value to update + */ + public void update(double x) { + long lx = Double.doubleToRawLongBits(x); + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if((as = cells) != null + || (Double.longBitsToDouble(b = base) < x && !casBase(b, lx))) { + boolean uncontended = true; + if((hc = threadHashCode.get()) == null + || as == null + || (n = as.length) < 1 + || (a = as[(n - 1) & hc[0]]) == null + || (Double.longBitsToDouble(v = a.value) < x && !(uncontended = a + .cas(v, lx)))) + retryUpdate(lx, hc, uncontended); + } + } + + /** + * Returns the current maximum. The returned value is NOT an atomic + * snapshot; invocation in the absence of + * concurrent updates returns an accurate result, but concurrent + * updates that occur while the value is being calculated might + * not be incorporated. + * + * @return the maximum + */ + public double max() { + Cell[] as = cells; + double max = Double.longBitsToDouble(base); + if(as != null) { + int n = as.length; + double v; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null && (v = Double.longBitsToDouble(a.value)) > max) + max = v; + } + } + return max; + } + + /** + * Resets variables maintaining updates to {@code Double.NEGATIVE_INFINITY}. + * This method may be a useful + * alternative to creating a new updater, but is only effective if + * there are no concurrent updates. Because this method is + * intrinsically racy, it should only be used when it is known + * that no threads are concurrently updating. + */ + public void reset() { + internalReset(MIN_AS_LONG); + } + + /** + * Equivalent in effect to {@link #max} followed by {@link #reset}. This + * method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is not + * guaranteed to be the final value occurring before + * the reset. + * + * @return the maximum + */ + public double maxThenReset() { + Cell[] as = cells; + double max = Double.longBitsToDouble(base); + base = MIN_AS_LONG; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) { + double v = Double.longBitsToDouble(a.value); + a.value = MIN_AS_LONG; + if(v > max) + max = v; + } + } + } + return max; + } + + /** + * Returns the String representation of the {@link #max}. + * + * @return the String representation of the {@link #max} + */ + public String toString() { + return Double.toString(max()); + } + + /** + * Equivalent to {@link #max}. + * + * @return the max + */ + public double doubleValue() { + return max(); + } + + /** + * Returns the {@link #max} as a {@code long} after a + * narrowing primitive conversion. + */ + public long longValue() { + return (long) max(); + } + + /** + * Returns the {@link #max} as an {@code int} after a + * narrowing primitive conversion. + */ + public int intValue() { + return (int) max(); + } + + /** + * Returns the {@link #max} as a {@code float} after a narrowing primitive + * conversion. + */ + public float floatValue() { + return (float) max(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeDouble(max()); + } + + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = Double.doubleToRawLongBits(s.readDouble()); + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java new file mode 100644 index 0000000000..f0dc0d0795 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java @@ -0,0 +1,3456 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; + +/** + * An {@link ExecutorService} for running {@link ForkJoinTask}s. + * A {@code ForkJoinPool} provides the entry point for submissions + * from non-{@code ForkJoinTask} clients, as well as management and + * monitoring operations. + * + *

+ * A {@code ForkJoinPool} differs from other kinds of {@link ExecutorService} + * mainly by virtue of employing work-stealing: all threads in the pool + * attempt to find and execute tasks submitted to the pool and/or created by + * other active tasks (eventually blocking waiting for work if none exist). This + * enables efficient processing when most tasks spawn other subtasks (as do most + * {@code ForkJoinTask}s), as well as when many small tasks are submitted to the + * pool from external clients. Especially when setting asyncMode to + * true in constructors, {@code ForkJoinPool}s may also be appropriate for use + * with event-style tasks that are never joined. + * + *

+ * A static {@link #commonPool()} is available and appropriate for most + * applications. The common pool is used by any ForkJoinTask that is not + * explicitly submitted to a specified pool. Using the common pool normally + * reduces resource usage (its threads are slowly reclaimed during periods of + * non-use, and reinstated upon subsequent use). + * + *

+ * For applications that require separate or custom pools, a + * {@code ForkJoinPool} may be constructed with a given target parallelism + * level; by default, equal to the number of available processors. The pool + * attempts to maintain enough active (or available) threads by dynamically + * adding, suspending, or resuming internal worker threads, even if some tasks + * are stalled waiting to join others. However, no such adjustments are + * guaranteed in the face of blocked I/O or other unmanaged synchronization. The + * nested {@link ManagedBlocker} interface enables extension of the kinds of + * synchronization accommodated. + * + *

+ * In addition to execution and lifecycle control methods, this class provides + * status check methods (for example {@link #getStealCount}) that are intended + * to aid in developing, tuning, and monitoring fork/join applications. Also, + * method {@link #toString} returns indications of pool state in a convenient + * form for informal monitoring. + * + *

+ * As is the case with other ExecutorServices, there are three main task + * execution methods summarized in the following table. These are designed to be + * used primarily by clients not already engaged in fork/join computations in + * the current pool. The main forms of these methods accept instances of + * {@code ForkJoinTask}, but overloaded forms also allow mixed execution of + * plain {@code Runnable}- or {@code Callable}- based activities as well. + * However, tasks that are already executing in a pool should normally instead + * use the within-computation forms listed in the table unless using async + * event-style tasks that are not usually joined, in which case there is little + * difference among choice of methods. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Summary of task execution methods
Call from non-fork/join clientsCall from within fork/join computations
Arrange async execution {@link #execute(ForkJoinTask)} {@link ForkJoinTask#fork}
Await and obtain result {@link #invoke(ForkJoinTask)} {@link ForkJoinTask#invoke}
Arrange exec and obtain Future {@link #submit(ForkJoinTask)} {@link ForkJoinTask#fork} (ForkJoinTasks are Futures)
+ * + *

+ * The common pool is by default constructed with default parameters, but these + * may be controlled by setting three {@linkplain System#getProperty system + * properties}: + *

    + *
  • {@code java.util.concurrent.ForkJoinPool.common.parallelism} - the + * parallelism level, a non-negative integer + *
  • {@code java.util.concurrent.ForkJoinPool.common.threadFactory} - the + * class name of a {@link ForkJoinWorkerThreadFactory} + *
  • {@code java.util.concurrent.ForkJoinPool.common.exceptionHandler} - the + * class name of a {@link UncaughtExceptionHandler} + *
+ * The system class loader is used to load these classes. Upon any error in + * establishing these settings, default parameters are used. It is possible to + * disable or limit the use of threads in the common pool by setting the + * parallelism property to zero, and/or using a factory that may return + * {@code null}. + * + *

+ * Implementation notes: This implementation restricts the maximum number + * of running threads to 32767. Attempts to create pools with greater than the + * maximum number result in {@code IllegalArgumentException}. + * + *

+ * This implementation rejects submitted tasks (that is, by throwing + * {@link RejectedExecutionException}) only when the pool is shut down or + * internal resources have been exhausted. + * + * @since 1.7 + * @author Doug Lea + */ +public class ForkJoinPool extends AbstractExecutorService { + + /* + * Implementation Overview + * + * This class and its nested classes provide the main + * functionality and control for a set of worker threads: + * Submissions from non-FJ threads enter into submission queues. + * Workers take these tasks and typically split them into subtasks + * that may be stolen by other workers. Preference rules give + * first priority to processing tasks from their own queues (LIFO + * or FIFO, depending on mode), then to randomized FIFO steals of + * tasks in other queues. + * + * WorkQueues + * ========== + * + * Most operations occur within work-stealing queues (in nested + * class WorkQueue). These are special forms of Deques that + * support only three of the four possible end-operations -- push, + * pop, and poll (aka steal), under the further constraints that + * push and pop are called only from the owning thread (or, as + * extended here, under a lock), while poll may be called from + * other threads. (If you are unfamiliar with them, you probably + * want to read Herlihy and Shavit's book "The Art of + * Multiprocessor programming", chapter 16 describing these in + * more detail before proceeding.) The main work-stealing queue + * design is roughly similar to those in the papers "Dynamic + * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005 + * (http://research.sun.com/scalable/pubs/index.html) and + * "Idempotent work stealing" by Michael, Saraswat, and Vechev, + * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186). + * See also "Correct and Efficient Work-Stealing for Weak Memory + * Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013 + * (http://www.di.ens.fr/~zappa/readings/ppopp13.pdf) for an + * analysis of memory ordering (atomic, volatile etc) issues. The + * main differences ultimately stem from GC requirements that we + * null out taken slots as soon as we can, to maintain as small a + * footprint as possible even in programs generating huge numbers + * of tasks. To accomplish this, we shift the CAS arbitrating pop + * vs poll (steal) from being on the indices ("base" and "top") to + * the slots themselves. So, both a successful pop and poll + * mainly entail a CAS of a slot from non-null to null. Because + * we rely on CASes of references, we do not need tag bits on base + * or top. They are simple ints as used in any circular + * array-based queue (see for example ArrayDeque). Updates to the + * indices must still be ordered in a way that guarantees that top + * == base means the queue is empty, but otherwise may err on the + * side of possibly making the queue appear nonempty when a push, + * pop, or poll have not fully committed. Note that this means + * that the poll operation, considered individually, is not + * wait-free. One thief cannot successfully continue until another + * in-progress one (or, if previously empty, a push) completes. + * However, in the aggregate, we ensure at least probabilistic + * non-blockingness. If an attempted steal fails, a thief always + * chooses a different random victim target to try next. So, in + * order for one thief to progress, it suffices for any + * in-progress poll or new push on any empty queue to + * complete. (This is why we normally use method pollAt and its + * variants that try once at the apparent base index, else + * consider alternative actions, rather than method poll.) + * + * This approach also enables support of a user mode in which local + * task processing is in FIFO, not LIFO order, simply by using + * poll rather than pop. This can be useful in message-passing + * frameworks in which tasks are never joined. However neither + * mode considers affinities, loads, cache localities, etc, so + * rarely provide the best possible performance on a given + * machine, but portably provide good throughput by averaging over + * these factors. (Further, even if we did try to use such + * information, we do not usually have a basis for exploiting it. + * For example, some sets of tasks profit from cache affinities, + * but others are harmed by cache pollution effects.) + * + * WorkQueues are also used in a similar way for tasks submitted + * to the pool. We cannot mix these tasks in the same queues used + * for work-stealing (this would contaminate lifo/fifo + * processing). Instead, we randomly associate submission queues + * with submitting threads, using a form of hashing. The + * Submitter probe value serves as a hash code for + * choosing existing queues, and may be randomly repositioned upon + * contention with other submitters. In essence, submitters act + * like workers except that they are restricted to executing local + * tasks that they submitted (or in the case of CountedCompleters, + * others with the same root task). However, because most + * shared/external queue operations are more expensive than + * internal, and because, at steady state, external submitters + * will compete for CPU with workers, ForkJoinTask.join and + * related methods disable them from repeatedly helping to process + * tasks if all workers are active. Insertion of tasks in shared + * mode requires a lock (mainly to protect in the case of + * resizing) but we use only a simple spinlock (using bits in + * field qlock), because submitters encountering a busy queue move + * on to try or create other queues -- they block only when + * creating and registering new queues. + * + * Management + * ========== + * + * The main throughput advantages of work-stealing stem from + * decentralized control -- workers mostly take tasks from + * themselves or each other. We cannot negate this in the + * implementation of other management responsibilities. The main + * tactic for avoiding bottlenecks is packing nearly all + * essentially atomic control state into two volatile variables + * that are by far most often read (not written) as status and + * consistency checks. + * + * Field "ctl" contains 64 bits holding all the information needed + * to atomically decide to add, inactivate, enqueue (on an event + * queue), dequeue, and/or re-activate workers. To enable this + * packing, we restrict maximum parallelism to (1<<15)-1 (which is + * far in excess of normal operating range) to allow ids, counts, + * and their negations (used for thresholding) to fit into 16bit + * fields. + * + * Field "plock" is a form of sequence lock with a saturating + * shutdown bit (similarly for per-queue "qlocks"), mainly + * protecting updates to the workQueues array, as well as to + * enable shutdown. When used as a lock, it is normally only very + * briefly held, so is nearly always available after at most a + * brief spin, but we use a monitor-based backup strategy to + * block when needed. + * + * Recording WorkQueues. WorkQueues are recorded in the + * "workQueues" array that is created upon first use and expanded + * if necessary. Updates to the array while recording new workers + * and unrecording terminated ones are protected from each other + * by a lock but the array is otherwise concurrently readable, and + * accessed directly. To simplify index-based operations, the + * array size is always a power of two, and all readers must + * tolerate null slots. Worker queues are at odd indices. Shared + * (submission) queues are at even indices, up to a maximum of 64 + * slots, to limit growth even if array needs to expand to add + * more workers. Grouping them together in this way simplifies and + * speeds up task scanning. + * + * All worker thread creation is on-demand, triggered by task + * submissions, replacement of terminated workers, and/or + * compensation for blocked workers. However, all other support + * code is set up to work with other policies. To ensure that we + * do not hold on to worker references that would prevent GC, ALL + * accesses to workQueues are via indices into the workQueues + * array (which is one source of some of the messy code + * constructions here). In essence, the workQueues array serves as + * a weak reference mechanism. Thus for example the wait queue + * field of ctl stores indices, not references. Access to the + * workQueues in associated methods (for example signalWork) must + * both index-check and null-check the IDs. All such accesses + * ignore bad IDs by returning out early from what they are doing, + * since this can only be associated with termination, in which + * case it is OK to give up. All uses of the workQueues array + * also check that it is non-null (even if previously + * non-null). This allows nulling during termination, which is + * currently not necessary, but remains an option for + * resource-revocation-based shutdown schemes. It also helps + * reduce JIT issuance of uncommon-trap code, which tends to + * unnecessarily complicate control flow in some methods. + * + * Event Queuing. Unlike HPC work-stealing frameworks, we cannot + * let workers spin indefinitely scanning for tasks when none can + * be found immediately, and we cannot start/resume workers unless + * there appear to be tasks available. On the other hand, we must + * quickly prod them into action when new tasks are submitted or + * generated. In many usages, ramp-up time to activate workers is + * the main limiting factor in overall performance (this is + * compounded at program start-up by JIT compilation and + * allocation). So we try to streamline this as much as possible. + * We park/unpark workers after placing in an event wait queue + * when they cannot find work. This "queue" is actually a simple + * Treiber stack, headed by the "id" field of ctl, plus a 15bit + * counter value (that reflects the number of times a worker has + * been inactivated) to avoid ABA effects (we need only as many + * version numbers as worker threads). Successors are held in + * field WorkQueue.nextWait. Queuing deals with several intrinsic + * races, mainly that a task-producing thread can miss seeing (and + * signalling) another thread that gave up looking for work but + * has not yet entered the wait queue. We solve this by requiring + * a full sweep of all workers (via repeated calls to method + * scan()) both before and after a newly waiting worker is added + * to the wait queue. Because enqueued workers may actually be + * rescanning rather than waiting, we set and clear the "parker" + * field of WorkQueues to reduce unnecessary calls to unpark. + * (This requires a secondary recheck to avoid missed signals.) + * Note the unusual conventions about Thread.interrupts + * surrounding parking and other blocking: Because interrupts are + * used solely to alert threads to check termination, which is + * checked anyway upon blocking, we clear status (using + * Thread.interrupted) before any call to park, so that park does + * not immediately return due to status being set via some other + * unrelated call to interrupt in user code. + * + * Signalling. We create or wake up workers only when there + * appears to be at least one task they might be able to find and + * execute. When a submission is added or another worker adds a + * task to a queue that has fewer than two tasks, they signal + * waiting workers (or trigger creation of new ones if fewer than + * the given parallelism level -- signalWork). These primary + * signals are buttressed by others whenever other threads remove + * a task from a queue and notice that there are other tasks there + * as well. So in general, pools will be over-signalled. On most + * platforms, signalling (unpark) overhead time is noticeably + * long, and the time between signalling a thread and it actually + * making progress can be very noticeably long, so it is worth + * offloading these delays from critical paths as much as + * possible. Additionally, workers spin-down gradually, by staying + * alive so long as they see the ctl state changing. Similar + * stability-sensing techniques are also used before blocking in + * awaitJoin and helpComplete. + * + * Trimming workers. To release resources after periods of lack of + * use, a worker starting to wait when the pool is quiescent will + * time out and terminate if the pool has remained quiescent for a + * given period -- a short period if there are more threads than + * parallelism, longer as the number of threads decreases. This + * will slowly propagate, eventually terminating all workers after + * periods of non-use. + * + * Shutdown and Termination. A call to shutdownNow atomically sets + * a plock bit and then (non-atomically) sets each worker's + * qlock status, cancels all unprocessed tasks, and wakes up + * all waiting workers. Detecting whether termination should + * commence after a non-abrupt shutdown() call requires more work + * and bookkeeping. We need consensus about quiescence (i.e., that + * there is no more work). The active count provides a primary + * indication but non-abrupt shutdown still requires a rechecking + * scan for any workers that are inactive but not queued. + * + * Joining Tasks + * ============= + * + * Any of several actions may be taken when one worker is waiting + * to join a task stolen (or always held) by another. Because we + * are multiplexing many tasks on to a pool of workers, we can't + * just let them block (as in Thread.join). We also cannot just + * reassign the joiner's run-time stack with another and replace + * it later, which would be a form of "continuation", that even if + * possible is not necessarily a good idea since we sometimes need + * both an unblocked task and its continuation to progress. + * Instead we combine two tactics: + * + * Helping: Arranging for the joiner to execute some task that it + * would be running if the steal had not occurred. + * + * Compensating: Unless there are already enough live threads, + * method tryCompensate() may create or re-activate a spare + * thread to compensate for blocked joiners until they unblock. + * + * A third form (implemented in tryRemoveAndExec) amounts to + * helping a hypothetical compensator: If we can readily tell that + * a possible action of a compensator is to steal and execute the + * task being joined, the joining thread can do so directly, + * without the need for a compensation thread (although at the + * expense of larger run-time stacks, but the tradeoff is + * typically worthwhile). + * + * The ManagedBlocker extension API can't use helping so relies + * only on compensation in method awaitBlocker. + * + * The algorithm in tryHelpStealer entails a form of "linear" + * helping: Each worker records (in field currentSteal) the most + * recent task it stole from some other worker. Plus, it records + * (in field currentJoin) the task it is currently actively + * joining. Method tryHelpStealer uses these markers to try to + * find a worker to help (i.e., steal back a task from and execute + * it) that could hasten completion of the actively joined task. + * In essence, the joiner executes a task that would be on its own + * local deque had the to-be-joined task not been stolen. This may + * be seen as a conservative variant of the approach in Wagner & + * Calder "Leapfrogging: a portable technique for implementing + * efficient futures" SIGPLAN Notices, 1993 + * (http://portal.acm.org/citation.cfm?id=155354). It differs in + * that: (1) We only maintain dependency links across workers upon + * steals, rather than use per-task bookkeeping. This sometimes + * requires a linear scan of workQueues array to locate stealers, + * but often doesn't because stealers leave hints (that may become + * stale/wrong) of where to locate them. It is only a hint + * because a worker might have had multiple steals and the hint + * records only one of them (usually the most current). Hinting + * isolates cost to when it is needed, rather than adding to + * per-task overhead. (2) It is "shallow", ignoring nesting and + * potentially cyclic mutual steals. (3) It is intentionally + * racy: field currentJoin is updated only while actively joining, + * which means that we miss links in the chain during long-lived + * tasks, GC stalls etc (which is OK since blocking in such cases + * is usually a good idea). (4) We bound the number of attempts + * to find work (see MAX_HELP) and fall back to suspending the + * worker and if necessary replacing it with another. + * + * Helping actions for CountedCompleters are much simpler: Method + * helpComplete can take and execute any task with the same root + * as the task being waited on. However, this still entails some + * traversal of completer chains, so is less efficient than using + * CountedCompleters without explicit joins. + * + * It is impossible to keep exactly the target parallelism number + * of threads running at any given time. Determining the + * existence of conservatively safe helping targets, the + * availability of already-created spares, and the apparent need + * to create new spares are all racy, so we rely on multiple + * retries of each. Compensation in the apparent absence of + * helping opportunities is challenging to control on JVMs, where + * GC and other activities can stall progress of tasks that in + * turn stall out many other dependent tasks, without us being + * able to determine whether they will ever require compensation. + * Even though work-stealing otherwise encounters little + * degradation in the presence of more threads than cores, + * aggressively adding new threads in such cases entails risk of + * unwanted positive feedback control loops in which more threads + * cause more dependent stalls (as well as delayed progress of + * unblocked threads to the point that we know they are available) + * leading to more situations requiring more threads, and so + * on. This aspect of control can be seen as an (analytically + * intractable) game with an opponent that may choose the worst + * (for us) active thread to stall at any time. We take several + * precautions to bound losses (and thus bound gains), mainly in + * methods tryCompensate and awaitJoin. + * + * Common Pool + * =========== + * + * The static common pool always exists after static + * initialization. Since it (or any other created pool) need + * never be used, we minimize initial construction overhead and + * footprint to the setup of about a dozen fields, with no nested + * allocation. Most bootstrapping occurs within method + * fullExternalPush during the first submission to the pool. + * + * When external threads submit to the common pool, they can + * perform subtask processing (see externalHelpJoin and related + * methods). This caller-helps policy makes it sensible to set + * common pool parallelism level to one (or more) less than the + * total number of available cores, or even zero for pure + * caller-runs. We do not need to record whether external + * submissions are to the common pool -- if not, externalHelpJoin + * returns quickly (at the most helping to signal some common pool + * workers). These submitters would otherwise be blocked waiting + * for completion, so the extra effort (with liberally sprinkled + * task status checks) in inapplicable cases amounts to an odd + * form of limited spin-wait before blocking in ForkJoinTask.join. + * + * Style notes + * =========== + * + * There is a lot of representation-level coupling among classes + * ForkJoinPool, ForkJoinWorkerThread, and ForkJoinTask. The + * fields of WorkQueue maintain data structures managed by + * ForkJoinPool, so are directly accessed. There is little point + * trying to reduce this, since any associated future changes in + * representations will need to be accompanied by algorithmic + * changes anyway. Several methods intrinsically sprawl because + * they must accumulate sets of consistent reads of volatiles held + * in local variables. Methods signalWork() and scan() are the + * main bottlenecks, so are especially heavily + * micro-optimized/mangled. There are lots of inline assignments + * (of form "while ((local = field) != 0)") which are usually the + * simplest way to ensure the required read orderings (which are + * sometimes critical). This leads to a "C"-like style of listing + * declarations of these locals at the heads of methods or blocks. + * There are several occurrences of the unusual "do {} while + * (!cas...)" which is the simplest way to force an update of a + * CAS'ed variable. There are also other coding oddities (including + * several unnecessary-looking hoisted null checks) that help + * some methods perform reasonably even when interpreted (not + * compiled). + * + * The order of declarations in this file is: + * (1) Static utility functions + * (2) Nested (static) classes + * (3) Static fields + * (4) Fields, along with constants used when unpacking some of them + * (5) Internal control methods + * (6) Callbacks and other support for ForkJoinTask methods + * (7) Exported methods + * (8) Static block initializing statics in minimally dependent order + */ + + // Static utilities + + /** + * If there is a security manager, makes sure caller has + * permission to modify threads. + */ + private static void checkPermission() { + SecurityManager security = System.getSecurityManager(); + if(security != null) + security.checkPermission(modifyThreadPermission); + } + + // Nested classes + + /** + * Factory for creating new {@link ForkJoinWorkerThread}s. + * A {@code ForkJoinWorkerThreadFactory} must be defined and used + * for {@code ForkJoinWorkerThread} subclasses that extend base + * functionality or initialize threads with different contexts. + */ + public static interface ForkJoinWorkerThreadFactory { + /** + * Returns a new worker thread operating in the given pool. + * + * @param pool the pool this thread works in + * @return the new worker thread + * @throws NullPointerException if the pool is null + */ + public ForkJoinWorkerThread newThread(ForkJoinPool pool); + } + + /** + * Default ForkJoinWorkerThreadFactory implementation; creates a + * new ForkJoinWorkerThread. + */ + static final class DefaultForkJoinWorkerThreadFactory implements + ForkJoinWorkerThreadFactory { + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + return new ForkJoinWorkerThread(pool); + } + } + + /** + * Class for artificial tasks that are used to replace the target + * of local joins if they are removed from an interior queue slot + * in WorkQueue.tryRemoveAndExec. We don't need the proxy to + * actually do anything beyond having a unique identity. + */ + static final class EmptyTask extends ForkJoinTask { + private static final long serialVersionUID = -7721805057305804111L; + + EmptyTask() { + status = ForkJoinTask.NORMAL; + } // force done + + public final Void getRawResult() { + return null; + } + + public final void setRawResult(Void x) {} + + public final boolean exec() { + return true; + } + } + + /** + * Queues supporting work-stealing as well as external task + * submission. See above for main rationale and algorithms. + * Implementation relies heavily on "Unsafe" intrinsics + * and selective use of "volatile": + * + * Field "base" is the index (mod array.length) of the least valid + * queue slot, which is always the next position to steal (poll) + * from if nonempty. Reads and writes require volatile orderings + * but not CAS, because updates are only performed after slot + * CASes. + * + * Field "top" is the index (mod array.length) of the next queue + * slot to push to or pop from. It is written only by owner thread + * for push, or under lock for external/shared push, and accessed + * by other threads only after reading (volatile) base. Both top + * and base are allowed to wrap around on overflow, but (top - + * base) (or more commonly -(base - top) to force volatile read of + * base before top) still estimates size. The lock ("qlock") is + * forced to -1 on termination, causing all further lock attempts + * to fail. (Note: we don't need CAS for termination state because + * upon pool shutdown, all shared-queues will stop being used + * anyway.) Nearly all lock bodies are set up so that exceptions + * within lock bodies are "impossible" (modulo JVM errors that + * would cause failure anyway.) + * + * The array slots are read and written using the emulation of + * volatiles/atomics provided by Unsafe. Insertions must in + * general use putOrderedObject as a form of releasing store to + * ensure that all writes to the task object are ordered before + * its publication in the queue. All removals entail a CAS to + * null. The array is always a power of two. To ensure safety of + * Unsafe array operations, all accesses perform explicit null + * checks and implicit bounds checks via power-of-two masking. + * + * In addition to basic queuing support, this class contains + * fields described elsewhere to control execution. It turns out + * to work better memory-layout-wise to include them in this class + * rather than a separate class. + * + * Performance on most platforms is very sensitive to placement of + * instances of both WorkQueues and their arrays -- we absolutely + * do not want multiple WorkQueue instances or multiple queue + * arrays sharing cache lines. (It would be best for queue objects + * and their arrays to share, but there is nothing available to + * help arrange that). The @Contended annotation alerts JVMs to + * try to keep instances apart. + */ + static final class WorkQueue { + /** + * Capacity of work-stealing queue array upon initialization. + * Must be a power of two; at least 4, but should be larger to + * reduce or eliminate cacheline sharing among queues. + * Currently, it is much larger, as a partial workaround for + * the fact that JVMs often place arrays in locations that + * share GC bookkeeping (especially cardmarks) such that + * per-write accesses encounter serious memory contention. + */ + static final int INITIAL_QUEUE_CAPACITY = 1 << 13; + + /** + * Maximum size for queue arrays. Must be a power of two less + * than or equal to 1 << (31 - width of array entry) to ensure + * lack of wraparound of index calculations, but defined to a + * value a bit less than this to help users trap runaway + * programs before saturating systems. + */ + static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M + + // Heuristic padding to ameliorate unfortunate memory placements + volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06; + + volatile int eventCount; // encoded inactivation count; < 0 if inactive + int nextWait; // encoded record of next event waiter + int nsteals; // number of steals + int hint; // steal index hint + short poolIndex; // index of this queue in pool + final short mode; // 0: lifo, > 0: fifo, < 0: shared + volatile int qlock; // 1: locked, -1: terminate; else 0 + volatile int base; // index of next slot for poll + int top; // index of next slot for push + ForkJoinTask[] array; // the elements (initially unallocated) + final ForkJoinPool pool; // the containing pool (may be null) + final ForkJoinWorkerThread owner; // owning thread or null if shared + volatile Thread parker; // == owner during call to park; else null + volatile ForkJoinTask currentJoin; // task being joined in awaitJoin + ForkJoinTask currentSteal; // current non-local task being executed + + volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17; + volatile Object pad18, pad19, pad1a, pad1b, pad1c, pad1d; + + WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner, int mode, + int seed) { + this.pool = pool; + this.owner = owner; + this.mode = (short) mode; + this.hint = seed; // store initial seed for runWorker + // Place indices in the center of array (that is not yet allocated) + base = top = INITIAL_QUEUE_CAPACITY >>> 1; + } + + /** + * Returns the approximate number of tasks in the queue. + */ + final int queueSize() { + int n = base - top; // non-owner callers must read base first + return (n >= 0) ? 0 : -n; // ignore transient negative + } + + /** + * Provides a more accurate estimate of whether this queue has + * any tasks than does queueSize, by checking whether a + * near-empty queue has at least one unclaimed task. + */ + final boolean isEmpty() { + ForkJoinTask[] a; + int m, s; + int n = base - (s = top); + return (n >= 0 || (n == -1 && ((a = array) == null + || (m = a.length - 1) < 0 || U.getObject(a, + (long) ((m & (s - 1)) << ASHIFT) + ABASE) == null))); + } + + /** + * Pushes a task. Call only by owner in unshared queues. (The + * shared-queue version is embedded in method externalPush.) + * + * @param task the task. Caller must ensure non-null. + * @throws RejectedExecutionException if array cannot be resized + */ + final void push(ForkJoinTask task) { + ForkJoinTask[] a; + ForkJoinPool p; + int s = top, n; + if((a = array) != null) { // ignore if queue removed + int m = a.length - 1; + U.putOrderedObject(a, ((m & s) << ASHIFT) + ABASE, task); + if((n = (top = s + 1) - base) <= 2) + (p = pool).signalWork(p.workQueues, this); + else if(n >= m) + growArray(); + } + } + + /** + * Initializes or doubles the capacity of array. Call either + * by owner or with lock held -- it is OK for base, but not + * top, to move while resizings are in progress. + */ + final ForkJoinTask[] growArray() { + ForkJoinTask[] oldA = array; + int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY; + if(size > MAXIMUM_QUEUE_CAPACITY) + throw new RejectedExecutionException("Queue capacity exceeded"); + int oldMask, t, b; + ForkJoinTask[] a = array = new ForkJoinTask[size]; + if(oldA != null && (oldMask = oldA.length - 1) >= 0 + && (t = top) - (b = base) > 0) { + int mask = size - 1; + do { + ForkJoinTask x; + int oldj = ((b & oldMask) << ASHIFT) + ABASE; + int j = ((b & mask) << ASHIFT) + ABASE; + x = (ForkJoinTask) U.getObjectVolatile(oldA, oldj); + if(x != null && U.compareAndSwapObject(oldA, oldj, x, null)) + U.putObjectVolatile(a, j, x); + } + while (++b != t); + } + return a; + } + + /** + * Takes next task, if one exists, in LIFO order. Call only + * by owner in unshared queues. + */ + final ForkJoinTask pop() { + ForkJoinTask[] a; + ForkJoinTask t; + int m; + if((a = array) != null && (m = a.length - 1) >= 0) { + for (int s; (s = top - 1) - base >= 0;) { + long j = ((m & s) << ASHIFT) + ABASE; + if((t = (ForkJoinTask) U.getObject(a, j)) == null) + break; + if(U.compareAndSwapObject(a, j, t, null)) { + top = s; + return t; + } + } + } + return null; + } + + /** + * Takes a task in FIFO order if b is base of queue and a task + * can be claimed without contention. Specialized versions + * appear in ForkJoinPool methods scan and tryHelpStealer. + */ + final ForkJoinTask pollAt(int b) { + ForkJoinTask t; + ForkJoinTask[] a; + if((a = array) != null) { + int j = (((a.length - 1) & b) << ASHIFT) + ABASE; + if((t = (ForkJoinTask) U.getObjectVolatile(a, j)) != null + && base == b && U.compareAndSwapObject(a, j, t, null)) { + U.putOrderedInt(this, QBASE, b + 1); + return t; + } + } + return null; + } + + /** + * Takes next task, if one exists, in FIFO order. + */ + final ForkJoinTask poll() { + ForkJoinTask[] a; + int b; + ForkJoinTask t; + while ((b = base) - top < 0 && (a = array) != null) { + int j = (((a.length - 1) & b) << ASHIFT) + ABASE; + t = (ForkJoinTask) U.getObjectVolatile(a, j); + if(t != null) { + if(U.compareAndSwapObject(a, j, t, null)) { + U.putOrderedInt(this, QBASE, b + 1); + return t; + } + } + else if(base == b) { + if(b + 1 == top) + break; + Thread.yield(); // wait for lagging update (very rare) + } + } + return null; + } + + /** + * Takes next task, if one exists, in order specified by mode. + */ + final ForkJoinTask nextLocalTask() { + return mode == 0 ? pop() : poll(); + } + + /** + * Returns next task, if one exists, in order specified by mode. + */ + final ForkJoinTask peek() { + ForkJoinTask[] a = array; + int m; + if(a == null || (m = a.length - 1) < 0) + return null; + int i = mode == 0 ? top - 1 : base; + int j = ((i & m) << ASHIFT) + ABASE; + return (ForkJoinTask) U.getObjectVolatile(a, j); + } + + /** + * Pops the given task only if it is at the current top. + * (A shared version is available only via FJP.tryExternalUnpush) + */ + final boolean tryUnpush(ForkJoinTask t) { + ForkJoinTask[] a; + int s; + if((a = array) != null + && (s = top) != base + && U.compareAndSwapObject(a, + (((a.length - 1) & --s) << ASHIFT) + ABASE, t, null)) { + top = s; + return true; + } + return false; + } + + /** + * Removes and cancels all known tasks, ignoring any exceptions. + */ + final void cancelAll() { + ForkJoinTask.cancelIgnoringExceptions(currentJoin); + ForkJoinTask.cancelIgnoringExceptions(currentSteal); + for (ForkJoinTask t; (t = poll()) != null;) + ForkJoinTask.cancelIgnoringExceptions(t); + } + + // Specialized execution methods + + /** + * Polls and runs tasks until empty. + */ + final void pollAndExecAll() { + for (ForkJoinTask t; (t = poll()) != null;) + t.doExec(); + } + + /** + * Executes a top-level task and any local tasks remaining + * after execution. + */ + final void runTask(ForkJoinTask task) { + if((currentSteal = task) != null) { + task.doExec(); + ForkJoinTask[] a = array; + int md = mode; + ++nsteals; + currentSteal = null; + if(md != 0) + pollAndExecAll(); + else if(a != null) { + int s, m = a.length - 1; + while ((s = top - 1) - base >= 0) { + long i = ((m & s) << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) U.getObject(a, i); + if(t == null) + break; + if(U.compareAndSwapObject(a, i, t, null)) { + top = s; + t.doExec(); + } + } + } + } + } + + /** + * If present, removes from queue and executes the given task, + * or any other cancelled task. Returns (true) on any CAS + * or consistency check failure so caller can retry. + * + * @return false if no progress can be made, else true + */ + final boolean tryRemoveAndExec(ForkJoinTask task) { + boolean stat; + ForkJoinTask[] a; + int m, s, b, n; + if(task != null && (a = array) != null && (m = a.length - 1) >= 0 + && (n = (s = top) - (b = base)) > 0) { + boolean removed = false, empty = true; + stat = true; + for (ForkJoinTask t;;) { // traverse from s to b + long j = ((--s & m) << ASHIFT) + ABASE; + t = (ForkJoinTask) U.getObject(a, j); + if(t == null) // inconsistent length + break; + else if(t == task) { + if(s + 1 == top) { // pop + if(!U.compareAndSwapObject(a, j, task, null)) + break; + top = s; + removed = true; + } + else if(base == b) // replace with proxy + removed = U.compareAndSwapObject(a, j, task, + new EmptyTask()); + break; + } + else if(t.status >= 0) + empty = false; + else if(s + 1 == top) { // pop and throw away + if(U.compareAndSwapObject(a, j, t, null)) + top = s; + break; + } + if(--n == 0) { + if(!empty && base == b) + stat = false; + break; + } + } + if(removed) + task.doExec(); + } + else + stat = false; + return stat; + } + + /** + * Tries to poll for and execute the given task or any other + * task in its CountedCompleter computation. + */ + final boolean pollAndExecCC(CountedCompleter root) { + ForkJoinTask[] a; + int b; + Object o; + CountedCompleter t, r; + if((b = base) - top < 0 && (a = array) != null) { + long j = (((a.length - 1) & b) << ASHIFT) + ABASE; + if((o = U.getObjectVolatile(a, j)) == null) + return true; // retry + if(o instanceof CountedCompleter) { + for (t = (CountedCompleter) o, r = t;;) { + if(r == root) { + if(base == b + && U.compareAndSwapObject(a, j, t, null)) { + U.putOrderedInt(this, QBASE, b + 1); + t.doExec(); + } + return true; + } + else if((r = r.completer) == null) + break; // not part of root computation + } + } + } + return false; + } + + /** + * Tries to pop and execute the given task or any other task + * in its CountedCompleter computation. + */ + final boolean externalPopAndExecCC(CountedCompleter root) { + ForkJoinTask[] a; + int s; + Object o; + CountedCompleter t, r; + if(base - (s = top) < 0 && (a = array) != null) { + long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE; + if((o = U.getObject(a, j)) instanceof CountedCompleter) { + for (t = (CountedCompleter) o, r = t;;) { + if(r == root) { + if(U.compareAndSwapInt(this, QLOCK, 0, 1)) { + if(top == s + && array == a + && U.compareAndSwapObject(a, j, t, null)) { + top = s - 1; + qlock = 0; + t.doExec(); + } + else + qlock = 0; + } + return true; + } + else if((r = r.completer) == null) + break; + } + } + } + return false; + } + + /** + * Internal version + */ + final boolean internalPopAndExecCC(CountedCompleter root) { + ForkJoinTask[] a; + int s; + Object o; + CountedCompleter t, r; + if(base - (s = top) < 0 && (a = array) != null) { + long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE; + if((o = U.getObject(a, j)) instanceof CountedCompleter) { + for (t = (CountedCompleter) o, r = t;;) { + if(r == root) { + if(U.compareAndSwapObject(a, j, t, null)) { + top = s - 1; + t.doExec(); + } + return true; + } + else if((r = r.completer) == null) + break; + } + } + } + return false; + } + + /** + * Returns true if owned and not known to be blocked. + */ + final boolean isApparentlyUnblocked() { + Thread wt; + Thread.State s; + return (eventCount >= 0 && (wt = owner) != null + && (s = wt.getState()) != Thread.State.BLOCKED + && s != Thread.State.WAITING && s != Thread.State.TIMED_WAITING); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long QBASE; + private static final long QLOCK; + private static final int ABASE; + private static final int ASHIFT; + static { + try { + U = getUnsafe(); + Class k = WorkQueue.class; + Class ak = ForkJoinTask[].class; + QBASE = U.objectFieldOffset(k.getDeclaredField("base")); + QLOCK = U.objectFieldOffset(k.getDeclaredField("qlock")); + ABASE = U.arrayBaseOffset(ak); + int scale = U.arrayIndexScale(ak); + if((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } + catch (Exception e) { + throw new Error(e); + } + } + } + + // static fields (initialized in static initializer below) + + /** + * Per-thread submission bookkeeping. Shared across all pools + * to reduce ThreadLocal pollution and because random motion + * to avoid contention in one pool is likely to hold for others. + * Lazily initialized on first submission (but null-checked + * in other contexts to avoid unnecessary initialization). + */ + static final ThreadLocal submitters; + + /** + * Creates a new ForkJoinWorkerThread. This factory is used unless + * overridden in ForkJoinPool constructors. + */ + public static final ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory; + + /** + * Permission required for callers of methods that may start or + * kill threads. + */ + private static final RuntimePermission modifyThreadPermission; + + /** + * Common (static) pool. Non-null for public use unless a static + * construction exception, but internal usages null-check on use + * to paranoically avoid potential initialization circularities + * as well as to simplify generated code. + */ + static final ForkJoinPool common; + + /** + * Common pool parallelism. To allow simpler use and management + * when common pool threads are disabled, we allow the underlying + * common.parallelism field to be zero, but in that case still report + * parallelism as 1 to reflect resulting caller-runs mechanics. + */ + static final int commonParallelism; + + /** + * Sequence number for creating workerNamePrefix. + */ + private static int poolNumberSequence; + + /** + * Returns the next sequence number. We don't expect this to + * ever contend, so use simple builtin sync. + */ + private static final synchronized int nextPoolId() { + return ++poolNumberSequence; + } + + // static constants + + /** + * Initial timeout value (in nanoseconds) for the thread + * triggering quiescence to park waiting for new work. On timeout, + * the thread will instead try to shrink the number of + * workers. The value should be large enough to avoid overly + * aggressive shrinkage during most transient stalls (long GCs + * etc). + */ + private static final long IDLE_TIMEOUT = 2000L * 1000L * 1000L; // 2sec + + /** + * Timeout value when there are more threads than parallelism level + */ + private static final long FAST_IDLE_TIMEOUT = 200L * 1000L * 1000L; + + /** + * Tolerance for idle timeouts, to cope with timer undershoots + */ + private static final long TIMEOUT_SLOP = 2000000L; + + /** + * The maximum stolen->joining link depth allowed in method + * tryHelpStealer. Must be a power of two. Depths for legitimate + * chains are unbounded, but we use a fixed constant to avoid + * (otherwise unchecked) cycles and to bound staleness of + * traversal parameters at the expense of sometimes blocking when + * we could be helping. + */ + private static final int MAX_HELP = 64; + + /** + * Increment for seed generators. See class ThreadLocal for + * explanation. + */ + private static final int SEED_INCREMENT = 0x61c88647; + + /* + * Bits and masks for control variables + * + * Field ctl is a long packed with: + * AC: Number of active running workers minus target parallelism (16 bits) + * TC: Number of total workers minus target parallelism (16 bits) + * ST: true if pool is terminating (1 bit) + * EC: the wait count of top waiting thread (15 bits) + * ID: poolIndex of top of Treiber stack of waiters (16 bits) + * + * When convenient, we can extract the upper 32 bits of counts and + * the lower 32 bits of queue state, u = (int)(ctl >>> 32) and e = + * (int)ctl. The ec field is never accessed alone, but always + * together with id and st. The offsets of counts by the target + * parallelism and the positionings of fields makes it possible to + * perform the most common checks via sign tests of fields: When + * ac is negative, there are not enough active workers, when tc is + * negative, there are not enough total workers, and when e is + * negative, the pool is terminating. To deal with these possibly + * negative fields, we use casts in and out of "short" and/or + * signed shifts to maintain signedness. + * + * When a thread is queued (inactivated), its eventCount field is + * set negative, which is the only way to tell if a worker is + * prevented from executing tasks, even though it must continue to + * scan for them to avoid queuing races. Note however that + * eventCount updates lag releases so usage requires care. + * + * Field plock is an int packed with: + * SHUTDOWN: true if shutdown is enabled (1 bit) + * SEQ: a sequence lock, with PL_LOCK bit set if locked (30 bits) + * SIGNAL: set when threads may be waiting on the lock (1 bit) + * + * The sequence number enables simple consistency checks: + * Staleness of read-only operations on the workQueues array can + * be checked by comparing plock before vs after the reads. + */ + + // bit positions/shifts for fields + private static final int AC_SHIFT = 48; + private static final int TC_SHIFT = 32; + private static final int ST_SHIFT = 31; + private static final int EC_SHIFT = 16; + + // bounds + private static final int SMASK = 0xffff; // short bits + private static final int MAX_CAP = 0x7fff; // max #workers - 1 + private static final int EVENMASK = 0xfffe; // even short bits + private static final int SQMASK = 0x007e; // max 64 (even) slots + private static final int SHORT_SIGN = 1 << 15; + private static final int INT_SIGN = 1 << 31; + + // masks + private static final long STOP_BIT = 0x0001L << ST_SHIFT; + private static final long AC_MASK = ((long) SMASK) << AC_SHIFT; + private static final long TC_MASK = ((long) SMASK) << TC_SHIFT; + + // units for incrementing and decrementing + private static final long TC_UNIT = 1L << TC_SHIFT; + private static final long AC_UNIT = 1L << AC_SHIFT; + + // masks and units for dealing with u = (int)(ctl >>> 32) + private static final int UAC_SHIFT = AC_SHIFT - 32; + private static final int UTC_SHIFT = TC_SHIFT - 32; + private static final int UAC_MASK = SMASK << UAC_SHIFT; + private static final int UTC_MASK = SMASK << UTC_SHIFT; + private static final int UAC_UNIT = 1 << UAC_SHIFT; + private static final int UTC_UNIT = 1 << UTC_SHIFT; + + // masks and units for dealing with e = (int)ctl + private static final int E_MASK = 0x7fffffff; // no STOP_BIT + private static final int E_SEQ = 1 << EC_SHIFT; + + // plock bits + private static final int SHUTDOWN = 1 << 31; + private static final int PL_LOCK = 2; + private static final int PL_SIGNAL = 1; + private static final int PL_SPINS = 1 << 8; + + // access mode for WorkQueue + static final int LIFO_QUEUE = 0; + static final int FIFO_QUEUE = 1; + static final int SHARED_QUEUE = -1; + + // Heuristic padding to ameliorate unfortunate memory placements + volatile long pad00, pad01, pad02, pad03, pad04, pad05, pad06; + + // Instance fields + volatile long stealCount; // collects worker counts + volatile long ctl; // main pool control + volatile int plock; // shutdown status and seqLock + volatile int indexSeed; // worker/submitter index seed + final short parallelism; // parallelism level + final short mode; // LIFO/FIFO + WorkQueue[] workQueues; // main registry + final ForkJoinWorkerThreadFactory factory; + final UncaughtExceptionHandler ueh; // per-worker UEH + final String workerNamePrefix; // to create worker name string + + volatile Object pad10, pad11, pad12, pad13, pad14, pad15, pad16, pad17; + volatile Object pad18, pad19, pad1a, pad1b; + + /** + * Acquires the plock lock to protect worker array and related + * updates. This method is called only if an initial CAS on plock + * fails. This acts as a spinlock for normal cases, but falls back + * to builtin monitor to block when (rarely) needed. This would be + * a terrible idea for a highly contended lock, but works fine as + * a more conservative alternative to a pure spinlock. + */ + private int acquirePlock() { + int spins = PL_SPINS, ps, nps; + for (;;) { + if(((ps = plock) & PL_LOCK) == 0 + && U.compareAndSwapInt(this, PLOCK, ps, nps = ps + PL_LOCK)) + return nps; + else if(spins >= 0) { + if(ThreadLocalRandom.current().nextInt() >= 0) + --spins; + } + else if(U.compareAndSwapInt(this, PLOCK, ps, ps | PL_SIGNAL)) { + synchronized (this) { + if((plock & PL_SIGNAL) != 0) { + try { + wait(); + } + catch (InterruptedException ie) { + try { + Thread.currentThread().interrupt(); + } + catch (SecurityException ignore) {} + } + } + else + notifyAll(); + } + } + } + } + + /** + * Unlocks and signals any thread waiting for plock. Called only + * when CAS of seq value for unlock fails. + */ + private void releasePlock(int ps) { + plock = ps; + synchronized (this) { + notifyAll(); + } + } + + /** + * Tries to create and start one worker if fewer than target + * parallelism level exist. Adjusts counts etc on failure. + */ + private void tryAddWorker() { + long c; + int u, e; + while ((u = (int) ((c = ctl) >>> 32)) < 0 && (u & SHORT_SIGN) != 0 + && (e = (int) c) >= 0) { + long nc = ((long) (((u + UTC_UNIT) & UTC_MASK) | ((u + UAC_UNIT) & UAC_MASK)) << 32) + | (long) e; + if(U.compareAndSwapLong(this, CTL, c, nc)) { + ForkJoinWorkerThreadFactory fac; + Throwable ex = null; + ForkJoinWorkerThread wt = null; + try { + if((fac = factory) != null + && (wt = fac.newThread(this)) != null) { + wt.start(); + break; + } + } + catch (Throwable rex) { + ex = rex; + } + deregisterWorker(wt, ex); + break; + } + } + } + + // Registering and deregistering workers + + /** + * Callback from ForkJoinWorkerThread to establish and record its + * WorkQueue. To avoid scanning bias due to packing entries in + * front of the workQueues array, we treat the array as a simple + * power-of-two hash table using per-thread seed as hash, + * expanding as needed. + * + * @param wt the worker thread + * @return the worker's queue + */ + final WorkQueue registerWorker(ForkJoinWorkerThread wt) { + UncaughtExceptionHandler handler; + WorkQueue[] ws; + int s, ps; + wt.setDaemon(true); + if((handler = ueh) != null) + wt.setUncaughtExceptionHandler(handler); + do {} + while (!U.compareAndSwapInt(this, INDEXSEED, s = indexSeed, + s += SEED_INCREMENT) || s == 0); // skip 0 + WorkQueue w = new WorkQueue(this, wt, mode, s); + if(((ps = plock) & PL_LOCK) != 0 + || !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK)) + ps = acquirePlock(); + int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN); + try { + if((ws = workQueues) != null) { // skip if shutting down + int n = ws.length, m = n - 1; + int r = (s << 1) | 1; // use odd-numbered indices + if(ws[r &= m] != null) { // collision + int probes = 0; // step by approx half size + int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2; + while (ws[r = (r + step) & m] != null) { + if(++probes >= n) { + workQueues = ws = Arrays.copyOf(ws, n <<= 1); + m = n - 1; + probes = 0; + } + } + } + w.poolIndex = (short) r; + w.eventCount = r; // volatile write orders + ws[r] = w; + } + } + finally { + if(!U.compareAndSwapInt(this, PLOCK, ps, nps)) + releasePlock(nps); + } + wt.setName(workerNamePrefix.concat(Integer.toString(w.poolIndex >>> 1))); + return w; + } + + /** + * Final callback from terminating worker, as well as upon failure + * to construct or start a worker. Removes record of worker from + * array, and adjusts counts. If pool is shutting down, tries to + * complete termination. + * + * @param wt the worker thread, or null if construction failed + * @param ex the exception causing failure, or null if none + */ + final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) { + WorkQueue w = null; + if(wt != null && (w = wt.workQueue) != null) { + int ps; + long sc; + w.qlock = -1; // ensure set + do {} + while (!U.compareAndSwapLong(this, STEALCOUNT, sc = stealCount, sc + + w.nsteals)); + if(((ps = plock) & PL_LOCK) != 0 + || !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK)) + ps = acquirePlock(); + int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN); + try { + int idx = w.poolIndex; + WorkQueue[] ws = workQueues; + if(ws != null && idx >= 0 && idx < ws.length && ws[idx] == w) + ws[idx] = null; + } + finally { + if(!U.compareAndSwapInt(this, PLOCK, ps, nps)) + releasePlock(nps); + } + } + + long c; // adjust ctl counts + do {} + while (!U + .compareAndSwapLong( + this, + CTL, + c = ctl, + (((c - AC_UNIT) & AC_MASK) | ((c - TC_UNIT) & TC_MASK) | (c & ~(AC_MASK | TC_MASK))))); + + if(!tryTerminate(false, false) && w != null && w.array != null) { + w.cancelAll(); // cancel remaining tasks + WorkQueue[] ws; + WorkQueue v; + Thread p; + int u, i, e; + while ((u = (int) ((c = ctl) >>> 32)) < 0 && (e = (int) c) >= 0) { + if(e > 0) { // activate or create replacement + if((ws = workQueues) == null + || (i = e & SMASK) >= ws.length + || (v = ws[i]) == null) + break; + long nc = (((long) (v.nextWait & E_MASK)) | ((long) (u + UAC_UNIT) << 32)); + if(v.eventCount != (e | INT_SIGN)) + break; + if(U.compareAndSwapLong(this, CTL, c, nc)) { + v.eventCount = (e + E_SEQ) & E_MASK; + if((p = v.parker) != null) + U.unpark(p); + break; + } + } + else { + if((short) u < 0) + tryAddWorker(); + break; + } + } + } + if(ex == null) // help clean refs on way out + ForkJoinTask.helpExpungeStaleExceptions(); + else + // rethrow + ForkJoinTask.rethrow(ex); + } + + // Submissions + + /** + * Per-thread records for threads that submit to pools. Currently + * holds only pseudo-random seed / index that is used to choose + * submission queues in method externalPush. In the future, this may + * also incorporate a means to implement different task rejection + * and resubmission policies. + * + * Seeds for submitters and workers/workQueues work in basically + * the same way but are initialized and updated using slightly + * different mechanics. Both are initialized using the same + * approach as in class ThreadLocal, where successive values are + * unlikely to collide with previous values. Seeds are then + * randomly modified upon collisions using xorshifts, which + * requires a non-zero seed. + */ + static final class Submitter { + int seed; + + Submitter(int s) { + seed = s; + } + } + + /** + * Unless shutting down, adds the given task to a submission queue + * at submitter's current queue index (modulo submission + * range). Only the most common path is directly handled in this + * method. All others are relayed to fullExternalPush. + * + * @param task the task. Caller must ensure non-null. + */ + final void externalPush(ForkJoinTask task) { + Submitter z = submitters.get(); + WorkQueue q; + int r, m, s, n, am; + ForkJoinTask[] a; + int ps = plock; + WorkQueue[] ws = workQueues; + if(z != null && ps > 0 && ws != null && (m = (ws.length - 1)) >= 0 + && (q = ws[m & (r = z.seed) & SQMASK]) != null && r != 0 + && U.compareAndSwapInt(q, QLOCK, 0, 1)) { // lock + if((a = q.array) != null + && (am = a.length - 1) > (n = (s = q.top) - q.base)) { + int j = ((am & s) << ASHIFT) + ABASE; + U.putOrderedObject(a, j, task); + q.top = s + 1; // push on to deque + q.qlock = 0; + if(n <= 1) + signalWork(ws, q); + return; + } + q.qlock = 0; + } + fullExternalPush(task); + } + + /** + * Full version of externalPush. This method is called, among + * other times, upon the first submission of the first task to the + * pool, so must perform secondary initialization. It also + * detects first submission by an external thread by looking up + * its ThreadLocal, and creates a new shared queue if the one at + * index if empty or contended. The plock lock body must be + * exception-free (so no try/finally) so we optimistically + * allocate new queues outside the lock and throw them away if + * (very rarely) not needed. + * + * Secondary initialization occurs when plock is zero, to create + * workQueue array and set plock to a valid value. This lock body + * must also be exception-free. Because the plock seq value can + * eventually wrap around zero, this method harmlessly fails to + * reinitialize if workQueues exists, while still advancing plock. + */ + private void fullExternalPush(ForkJoinTask task) { + int r = 0; // random index seed + for (Submitter z = submitters.get();;) { + WorkQueue[] ws; + WorkQueue q; + int ps, m, k; + if(z == null) { + if(U.compareAndSwapInt(this, INDEXSEED, r = indexSeed, + r += SEED_INCREMENT) && r != 0) + submitters.set(z = new Submitter(r)); + } + else if(r == 0) { // move to a different index + r = z.seed; + r ^= r << 13; // same xorshift as WorkQueues + r ^= r >>> 17; + z.seed = r ^= (r << 5); + } + if((ps = plock) < 0) + throw new RejectedExecutionException(); + else if(ps == 0 || (ws = workQueues) == null + || (m = ws.length - 1) < 0) { // initialize workQueues + int p = parallelism; // find power of two table size + int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + n = (n + 1) << 1; + WorkQueue[] nws = ((ws = workQueues) == null || ws.length == 0 ? new WorkQueue[n] + : null); + if(((ps = plock) & PL_LOCK) != 0 + || !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK)) + ps = acquirePlock(); + if(((ws = workQueues) == null || ws.length == 0) && nws != null) + workQueues = nws; + int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN); + if(!U.compareAndSwapInt(this, PLOCK, ps, nps)) + releasePlock(nps); + } + else if((q = ws[k = r & m & SQMASK]) != null) { + if(q.qlock == 0 && U.compareAndSwapInt(q, QLOCK, 0, 1)) { + ForkJoinTask[] a = q.array; + int s = q.top; + boolean submitted = false; + try { // locked version of push + if((a != null && a.length > s + 1 - q.base) + || (a = q.growArray()) != null) { // must + // presize + int j = (((a.length - 1) & s) << ASHIFT) + ABASE; + U.putOrderedObject(a, j, task); + q.top = s + 1; + submitted = true; + } + } + finally { + q.qlock = 0; // unlock + } + if(submitted) { + signalWork(ws, q); + return; + } + } + r = 0; // move on failure + } + else if(((ps = plock) & PL_LOCK) == 0) { // create new queue + q = new WorkQueue(this, null, SHARED_QUEUE, r); + q.poolIndex = (short) k; + if(((ps = plock) & PL_LOCK) != 0 + || !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK)) + ps = acquirePlock(); + if((ws = workQueues) != null && k < ws.length && ws[k] == null) + ws[k] = q; + int nps = (ps & SHUTDOWN) | ((ps + PL_LOCK) & ~SHUTDOWN); + if(!U.compareAndSwapInt(this, PLOCK, ps, nps)) + releasePlock(nps); + } + else + r = 0; + } + } + + // Maintaining ctl counts + + /** + * Increments active count; mainly called upon return from blocking. + */ + final void incrementActiveCount() { + long c; + do {} + while (!U.compareAndSwapLong(this, CTL, c = ctl, + ((c & ~AC_MASK) | ((c & AC_MASK) + AC_UNIT)))); + } + + /** + * Tries to create or activate a worker if too few are active. + * + * @param ws the worker array to use to find signallees + * @param q if non-null, the queue holding tasks to be processed + */ + final void signalWork(WorkQueue[] ws, WorkQueue q) { + for (;;) { + long c; + int e, u, i; + WorkQueue w; + Thread p; + if((u = (int) ((c = ctl) >>> 32)) >= 0) + break; + if((e = (int) c) <= 0) { + if((short) u < 0) + tryAddWorker(); + break; + } + if(ws == null || ws.length <= (i = e & SMASK) + || (w = ws[i]) == null) + break; + long nc = (((long) (w.nextWait & E_MASK)) | ((long) (u + UAC_UNIT)) << 32); + int ne = (e + E_SEQ) & E_MASK; + if(w.eventCount == (e | INT_SIGN) + && U.compareAndSwapLong(this, CTL, c, nc)) { + w.eventCount = ne; + if((p = w.parker) != null) + U.unpark(p); + break; + } + if(q != null && q.base >= q.top) + break; + } + } + + // Scanning for tasks + + /** + * Top-level runloop for workers, called by ForkJoinWorkerThread.run. + */ + final void runWorker(WorkQueue w) { + w.growArray(); // allocate queue + for (int r = w.hint; scan(w, r) == 0;) { + r ^= r << 13; + r ^= r >>> 17; + r ^= r << 5; // xorshift + } + } + + /** + * Scans for and, if found, runs one task, else possibly + * inactivates the worker. This method operates on single reads of + * volatile state and is designed to be re-invoked continuously, + * in part because it returns upon detecting inconsistencies, + * contention, or state changes that indicate possible success on + * re-invocation. + * + * The scan searches for tasks across queues starting at a random + * index, checking each at least twice. The scan terminates upon + * either finding a non-empty queue, or completing the sweep. If + * the worker is not inactivated, it takes and runs a task from + * this queue. Otherwise, if not activated, it tries to activate + * itself or some other worker by signalling. On failure to find a + * task, returns (for retry) if pool state may have changed during + * an empty scan, or tries to inactivate if active, else possibly + * blocks or terminates via method awaitWork. + * + * @param w the worker (via its WorkQueue) + * @param r a random seed + * @return worker qlock status if would have waited, else 0 + */ + private final int scan(WorkQueue w, int r) { + WorkQueue[] ws; + int m; + long c = ctl; // for consistency check + if((ws = workQueues) != null && (m = ws.length - 1) >= 0 && w != null) { + for (int j = m + m + 1, ec = w.eventCount;;) { + WorkQueue q; + int b, e; + ForkJoinTask[] a; + ForkJoinTask t; + if((q = ws[(r - j) & m]) != null && (b = q.base) - q.top < 0 + && (a = q.array) != null) { + long i = (((a.length - 1) & b) << ASHIFT) + ABASE; + if((t = ((ForkJoinTask) U.getObjectVolatile(a, i))) != null) { + if(ec < 0) + helpRelease(c, ws, w, q, b); + else if(q.base == b + && U.compareAndSwapObject(a, i, t, null)) { + U.putOrderedInt(q, QBASE, b + 1); + if((b + 1) - q.top < 0) + signalWork(ws, q); + w.runTask(t); + } + } + break; + } + else if(--j < 0) { + if((ec | (e = (int) c)) < 0) // inactive or terminating + return awaitWork(w, c, ec); + else if(ctl == c) { // try to inactivate and enqueue + long nc = (long) ec + | ((c - AC_UNIT) & (AC_MASK | TC_MASK)); + w.nextWait = e; + w.eventCount = ec | INT_SIGN; + if(!U.compareAndSwapLong(this, CTL, c, nc)) + w.eventCount = ec; // back out + } + break; + } + } + } + return 0; + } + + /** + * A continuation of scan(), possibly blocking or terminating + * worker w. Returns without blocking if pool state has apparently + * changed since last invocation. Also, if inactivating w has + * caused the pool to become quiescent, checks for pool + * termination, and, so long as this is not the only worker, waits + * for event for up to a given duration. On timeout, if ctl has + * not changed, terminates the worker, which will in turn wake up + * another worker to possibly repeat this process. + * + * @param w the calling worker + * @param c the ctl value on entry to scan + * @param ec the worker's eventCount on entry to scan + */ + private final int awaitWork(WorkQueue w, long c, int ec) { + int stat, ns; + long parkTime, deadline; + if((stat = w.qlock) >= 0 && w.eventCount == ec && ctl == c + && !Thread.interrupted()) { + int e = (int) c; + int u = (int) (c >>> 32); + int d = (u >> UAC_SHIFT) + parallelism; // active count + + if(e < 0 || (d <= 0 && tryTerminate(false, false))) + stat = w.qlock = -1; // pool is terminating + else if((ns = w.nsteals) != 0) { // collect steals and retry + long sc; + w.nsteals = 0; + do {} + while (!U.compareAndSwapLong(this, STEALCOUNT, sc = stealCount, + sc + ns)); + } + else { + long pc = ((d > 0 || ec != (e | INT_SIGN)) ? 0L + : ((long) (w.nextWait & E_MASK)) | // ctl to restore + ((long) (u + UAC_UNIT)) << 32); + if(pc != 0L) { // timed wait if last waiter + int dc = -(short) (c >>> TC_SHIFT); + parkTime = (dc < 0 ? FAST_IDLE_TIMEOUT : (dc + 1) + * IDLE_TIMEOUT); + deadline = System.nanoTime() + parkTime - TIMEOUT_SLOP; + } + else + parkTime = deadline = 0L; + if(w.eventCount == ec && ctl == c) { + Thread wt = Thread.currentThread(); + U.putObject(wt, PARKBLOCKER, this); + w.parker = wt; // emulate LockSupport.park + if(w.eventCount == ec && ctl == c) + U.park(false, parkTime); // must recheck before park + w.parker = null; + U.putObject(wt, PARKBLOCKER, null); + if(parkTime != 0L && ctl == c + && deadline - System.nanoTime() <= 0L + && U.compareAndSwapLong(this, CTL, c, pc)) + stat = w.qlock = -1; // shrink pool + } + } + } + return stat; + } + + /** + * Possibly releases (signals) a worker. Called only from scan() + * when a worker with apparently inactive status finds a non-empty + * queue. This requires revalidating all of the associated state + * from caller. + */ + private final void helpRelease(long c, WorkQueue[] ws, WorkQueue w, + WorkQueue q, int b) { + WorkQueue v; + int e, i; + Thread p; + if(w != null && w.eventCount < 0 && (e = (int) c) > 0 && ws != null + && ws.length > (i = e & SMASK) && (v = ws[i]) != null + && ctl == c) { + long nc = (((long) (v.nextWait & E_MASK)) | ((long) ((int) (c >>> 32) + UAC_UNIT)) << 32); + int ne = (e + E_SEQ) & E_MASK; + if(q != null && q.base == b && w.eventCount < 0 + && v.eventCount == (e | INT_SIGN) + && U.compareAndSwapLong(this, CTL, c, nc)) { + v.eventCount = ne; + if((p = v.parker) != null) + U.unpark(p); + } + } + } + + /** + * Tries to locate and execute tasks for a stealer of the given + * task, or in turn one of its stealers, Traces currentSteal -> + * currentJoin links looking for a thread working on a descendant + * of the given task and with a non-empty queue to steal back and + * execute tasks from. The first call to this method upon a + * waiting join will often entail scanning/search, (which is OK + * because the joiner has nothing better to do), but this method + * leaves hints in workers to speed up subsequent calls. The + * implementation is very branchy to cope with potential + * inconsistencies or loops encountering chains that are stale, + * unknown, or so long that they are likely cyclic. + * + * @param joiner the joining worker + * @param task the task to join + * @return 0 if no progress can be made, negative if task + * known complete, else positive + */ + private int tryHelpStealer(WorkQueue joiner, ForkJoinTask task) { + int stat = 0, steps = 0; // bound to avoid cycles + if(task != null && joiner != null && joiner.base - joiner.top >= 0) { // hoist + // checks + restart: for (;;) { + ForkJoinTask subtask = task; // current target + for (WorkQueue j = joiner, v;;) { // v is stealer of subtask + WorkQueue[] ws; + int m, s, h; + if((s = task.status) < 0) { + stat = s; + break restart; + } + if((ws = workQueues) == null || (m = ws.length - 1) <= 0) + break restart; // shutting down + if((v = ws[h = (j.hint | 1) & m]) == null + || v.currentSteal != subtask) { + for (int origin = h;;) { // find stealer + if(((h = (h + 2) & m) & 15) == 1 + && (subtask.status < 0 || j.currentJoin != subtask)) + continue restart; // occasional staleness check + if((v = ws[h]) != null && v.currentSteal == subtask) { + j.hint = h; // save hint + break; + } + if(h == origin) + break restart; // cannot find stealer + } + } + for (;;) { // help stealer or descend to its stealer + ForkJoinTask[] a; + int b; + if(subtask.status < 0) // surround probes with + continue restart; // consistency checks + if((b = v.base) - v.top < 0 && (a = v.array) != null) { + int i = (((a.length - 1) & b) << ASHIFT) + ABASE; + ForkJoinTask t = (ForkJoinTask) U + .getObjectVolatile(a, i); + if(subtask.status < 0 || j.currentJoin != subtask + || v.currentSteal != subtask) + continue restart; // stale + stat = 1; // apparent progress + if(v.base == b) { + if(t == null) + break restart; + if(U.compareAndSwapObject(a, i, t, null)) { + U.putOrderedInt(v, QBASE, b + 1); + ForkJoinTask ps = joiner.currentSteal; + int jt = joiner.top; + do { + joiner.currentSteal = t; + t.doExec(); // clear local tasks too + } + while (task.status >= 0 && joiner.top != jt + && (t = joiner.pop()) != null); + joiner.currentSteal = ps; + break restart; + } + } + } + else { // empty -- try to descend + ForkJoinTask next = v.currentJoin; + if(subtask.status < 0 || j.currentJoin != subtask + || v.currentSteal != subtask) + continue restart; // stale + else if(next == null || ++steps == MAX_HELP) + break restart; // dead-end or maybe cyclic + else { + subtask = next; + j = v; + break; + } + } + } + } + } + } + return stat; + } + + /** + * Analog of tryHelpStealer for CountedCompleters. Tries to steal + * and run tasks within the target's computation. + * + * @param task the task to join + */ + private int helpComplete(WorkQueue joiner, CountedCompleter task) { + WorkQueue[] ws; + int m; + int s = 0; + if((ws = workQueues) != null && (m = ws.length - 1) >= 0 + && joiner != null && task != null) { + int j = joiner.poolIndex; + int scans = m + m + 1; + long c = 0L; // for stability check + for (int k = scans;; j += 2) { + WorkQueue q; + if((s = task.status) < 0) + break; + else if(joiner.internalPopAndExecCC(task)) + k = scans; + else if((s = task.status) < 0) + break; + else if((q = ws[j & m]) != null && q.pollAndExecCC(task)) + k = scans; + else if(--k < 0) { + if(c == (c = ctl)) + break; + k = scans; + } + } + } + return s; + } + + /** + * Tries to decrement active count (sometimes implicitly) and + * possibly release or create a compensating worker in preparation + * for blocking. Fails on contention or termination. Otherwise, + * adds a new thread if no idle workers are available and pool + * may become starved. + * + * @param c the assumed ctl value + */ + final boolean tryCompensate(long c) { + WorkQueue[] ws = workQueues; + int pc = parallelism, e = (int) c, m, tc; + if(ws != null && (m = ws.length - 1) >= 0 && e >= 0 && ctl == c) { + WorkQueue w = ws[e & m]; + if(e != 0 && w != null) { + Thread p; + long nc = ((long) (w.nextWait & E_MASK) | (c & (AC_MASK | TC_MASK))); + int ne = (e + E_SEQ) & E_MASK; + if(w.eventCount == (e | INT_SIGN) + && U.compareAndSwapLong(this, CTL, c, nc)) { + w.eventCount = ne; + if((p = w.parker) != null) + U.unpark(p); + return true; // replace with idle worker + } + } + else if((tc = (short) (c >>> TC_SHIFT)) >= 0 + && (int) (c >> AC_SHIFT) + pc > 1) { + long nc = ((c - AC_UNIT) & AC_MASK) | (c & ~AC_MASK); + if(U.compareAndSwapLong(this, CTL, c, nc)) + return true; // no compensation + } + else if(tc + pc < MAX_CAP) { + long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK); + if(U.compareAndSwapLong(this, CTL, c, nc)) { + ForkJoinWorkerThreadFactory fac; + Throwable ex = null; + ForkJoinWorkerThread wt = null; + try { + if((fac = factory) != null + && (wt = fac.newThread(this)) != null) { + wt.start(); + return true; + } + } + catch (Throwable rex) { + ex = rex; + } + deregisterWorker(wt, ex); // clean up and return false + } + } + } + return false; + } + + /** + * Helps and/or blocks until the given task is done. + * + * @param joiner the joining worker + * @param task the task + * @return task status on exit + */ + final int awaitJoin(WorkQueue joiner, ForkJoinTask task) { + int s = 0; + if(task != null && (s = task.status) >= 0 && joiner != null) { + ForkJoinTask prevJoin = joiner.currentJoin; + joiner.currentJoin = task; + do {} + while (joiner.tryRemoveAndExec(task) && // process local tasks + (s = task.status) >= 0); + if(s >= 0 && (task instanceof CountedCompleter)) + s = helpComplete(joiner, (CountedCompleter) task); + long cc = 0; // for stability checks + while (s >= 0 && (s = task.status) >= 0) { + if((s = tryHelpStealer(joiner, task)) == 0 + && (s = task.status) >= 0) { + if(!tryCompensate(cc)) + cc = ctl; + else { + if(task.trySetSignal() && (s = task.status) >= 0) { + synchronized (task) { + if(task.status >= 0) { + try { // see ForkJoinTask + task.wait(); // for explanation + } + catch (InterruptedException ie) {} + } + else + task.notifyAll(); + } + } + long c; // reactivate + do {} + while (!U.compareAndSwapLong(this, CTL, c = ctl, + ((c & ~AC_MASK) | ((c & AC_MASK) + AC_UNIT)))); + } + } + } + joiner.currentJoin = prevJoin; + } + return s; + } + + /** + * Stripped-down variant of awaitJoin used by timed joins. Tries + * to help join only while there is continuous progress. (Caller + * will then enter a timed wait.) + * + * @param joiner the joining worker + * @param task the task + */ + final void helpJoinOnce(WorkQueue joiner, ForkJoinTask task) { + int s; + if(joiner != null && task != null && (s = task.status) >= 0) { + ForkJoinTask prevJoin = joiner.currentJoin; + joiner.currentJoin = task; + do {} + while (joiner.tryRemoveAndExec(task) && // process local tasks + (s = task.status) >= 0); + if(s >= 0) { + if(task instanceof CountedCompleter) + helpComplete(joiner, (CountedCompleter) task); + do {} + while (task.status >= 0 && tryHelpStealer(joiner, task) > 0); + } + joiner.currentJoin = prevJoin; + } + } + + /** + * Returns a (probably) non-empty steal queue, if one is found + * during a scan, else null. This method must be retried by + * caller if, by the time it tries to use the queue, it is empty. + */ + private WorkQueue findNonEmptyStealQueue() { + int r = ThreadLocalRandom.current().nextInt(); + for (;;) { + int ps = plock, m; + WorkQueue[] ws; + WorkQueue q; + if((ws = workQueues) != null && (m = ws.length - 1) >= 0) { + for (int j = (m + 1) << 2; j >= 0; --j) { + if((q = ws[(((r - j) << 1) | 1) & m]) != null + && q.base - q.top < 0) + return q; + } + } + if(plock == ps) + return null; + } + } + + /** + * Runs tasks until {@code isQuiescent()}. We piggyback on + * active count ctl maintenance, but rather than blocking + * when tasks cannot be found, we rescan until all others cannot + * find tasks either. + */ + final void helpQuiescePool(WorkQueue w) { + ForkJoinTask ps = w.currentSteal; + for (boolean active = true;;) { + long c; + WorkQueue q; + ForkJoinTask t; + int b; + while ((t = w.nextLocalTask()) != null) + t.doExec(); + if((q = findNonEmptyStealQueue()) != null) { + if(!active) { // re-establish active count + active = true; + do {} + while (!U.compareAndSwapLong(this, CTL, c = ctl, + ((c & ~AC_MASK) | ((c & AC_MASK) + AC_UNIT)))); + } + if((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) { + (w.currentSteal = t).doExec(); + w.currentSteal = ps; + } + } + else if(active) { // decrement active count without queuing + long nc = ((c = ctl) & ~AC_MASK) | ((c & AC_MASK) - AC_UNIT); + if((int) (nc >> AC_SHIFT) + parallelism == 0) + break; // bypass decrement-then-increment + if(U.compareAndSwapLong(this, CTL, c, nc)) + active = false; + } + else if((int) ((c = ctl) >> AC_SHIFT) + parallelism <= 0 + && U.compareAndSwapLong(this, CTL, c, + ((c & ~AC_MASK) | ((c & AC_MASK) + AC_UNIT)))) + break; + } + } + + /** + * Gets and removes a local or stolen task for the given worker. + * + * @return a task, if available + */ + final ForkJoinTask nextTaskFor(WorkQueue w) { + for (ForkJoinTask t;;) { + WorkQueue q; + int b; + if((t = w.nextLocalTask()) != null) + return t; + if((q = findNonEmptyStealQueue()) == null) + return null; + if((b = q.base) - q.top < 0 && (t = q.pollAt(b)) != null) + return t; + } + } + + /** + * Returns a cheap heuristic guide for task partitioning when + * programmers, frameworks, tools, or languages have little or no + * idea about task granularity. In essence by offering this + * method, we ask users only about tradeoffs in overhead vs + * expected throughput and its variance, rather than how finely to + * partition tasks. + * + * In a steady state strict (tree-structured) computation, each + * thread makes available for stealing enough tasks for other + * threads to remain active. Inductively, if all threads play by + * the same rules, each thread should make available only a + * constant number of tasks. + * + * The minimum useful constant is just 1. But using a value of 1 + * would require immediate replenishment upon each steal to + * maintain enough tasks, which is infeasible. Further, + * partitionings/granularities of offered tasks should minimize + * steal rates, which in general means that threads nearer the top + * of computation tree should generate more than those nearer the + * bottom. In perfect steady state, each thread is at + * approximately the same level of computation tree. However, + * producing extra tasks amortizes the uncertainty of progress and + * diffusion assumptions. + * + * So, users will want to use values larger (but not much larger) + * than 1 to both smooth over transient shortages and hedge + * against uneven progress; as traded off against the cost of + * extra task overhead. We leave the user to pick a threshold + * value to compare with the results of this call to guide + * decisions, but recommend values such as 3. + * + * When all threads are active, it is on average OK to estimate + * surplus strictly locally. In steady-state, if one thread is + * maintaining say 2 surplus tasks, then so are others. So we can + * just use estimated queue length. However, this strategy alone + * leads to serious mis-estimates in some non-steady-state + * conditions (ramp-up, ramp-down, other stalls). We can detect + * many of these by further considering the number of "idle" + * threads, that are known to have zero queued tasks, so + * compensate by a factor of (#idle/#active) threads. + * + * Note: The approximation of #busy workers as #active workers is + * not very good under current signalling scheme, and should be + * improved. + */ + static int getSurplusQueuedTaskCount() { + Thread t; + ForkJoinWorkerThread wt; + ForkJoinPool pool; + WorkQueue q; + if(((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)) { + int p = (pool = (wt = (ForkJoinWorkerThread) t).pool).parallelism; + int n = (q = wt.workQueue).top - q.base; + int a = (int) (pool.ctl >> AC_SHIFT) + p; + return n + - (a > (p >>>= 1) ? 0 : a > (p >>>= 1) ? 1 + : a > (p >>>= 1) ? 2 : a > (p >>>= 1) ? 4 : 8); + } + return 0; + } + + // Termination + + /** + * Possibly initiates and/or completes termination. The caller + * triggering termination runs three passes through workQueues: + * (0) Setting termination status, followed by wakeups of queued + * workers; (1) cancelling all tasks; (2) interrupting lagging + * threads (likely in external tasks, but possibly also blocked in + * joins). Each pass repeats previous steps because of potential + * lagging thread creation. + * + * @param now if true, unconditionally terminate, else only + * if no work and no active workers + * @param enable if true, enable shutdown when next possible + * @return true if now terminating or terminated + */ + private boolean tryTerminate(boolean now, boolean enable) { + int ps; + if(this == common) // cannot shut down + return false; + if((ps = plock) >= 0) { // enable by setting plock + if(!enable) + return false; + if((ps & PL_LOCK) != 0 + || !U.compareAndSwapInt(this, PLOCK, ps, ps += PL_LOCK)) + ps = acquirePlock(); + int nps = ((ps + PL_LOCK) & ~SHUTDOWN) | SHUTDOWN; + if(!U.compareAndSwapInt(this, PLOCK, ps, nps)) + releasePlock(nps); + } + for (long c;;) { + if(((c = ctl) & STOP_BIT) != 0) { // already terminating + if((short) (c >>> TC_SHIFT) + parallelism <= 0) { + synchronized (this) { + notifyAll(); // signal when 0 workers + } + } + return true; + } + if(!now) { // check if idle & no tasks + WorkQueue[] ws; + WorkQueue w; + if((int) (c >> AC_SHIFT) + parallelism > 0) + return false; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; ++i) { + if((w = ws[i]) != null + && (!w.isEmpty() || ((i & 1) != 0 && w.eventCount >= 0))) { + signalWork(ws, w); + return false; + } + } + } + } + if(U.compareAndSwapLong(this, CTL, c, c | STOP_BIT)) { + for (int pass = 0; pass < 3; ++pass) { + WorkQueue[] ws; + WorkQueue w; + Thread wt; + if((ws = workQueues) != null) { + int n = ws.length; + for (int i = 0; i < n; ++i) { + if((w = ws[i]) != null) { + w.qlock = -1; + if(pass > 0) { + w.cancelAll(); + if(pass > 1 && (wt = w.owner) != null) { + if(!wt.isInterrupted()) { + try { + wt.interrupt(); + } + catch (Throwable ignore) {} + } + U.unpark(wt); + } + } + } + } + // Wake up workers parked on event queue + int i, e; + long cc; + Thread p; + while ((e = (int) (cc = ctl) & E_MASK) != 0 + && (i = e & SMASK) < n && i >= 0 + && (w = ws[i]) != null) { + long nc = ((long) (w.nextWait & E_MASK) + | ((cc + AC_UNIT) & AC_MASK) | (cc & (TC_MASK | STOP_BIT))); + if(w.eventCount == (e | INT_SIGN) + && U.compareAndSwapLong(this, CTL, cc, nc)) { + w.eventCount = (e + E_SEQ) & E_MASK; + w.qlock = -1; + if((p = w.parker) != null) + U.unpark(p); + } + } + } + } + } + } + } + + // external operations on common pool + + /** + * Returns common pool queue for a thread that has submitted at + * least one task. + */ + static WorkQueue commonSubmitterQueue() { + Submitter z; + ForkJoinPool p; + WorkQueue[] ws; + int m, r; + return ((z = submitters.get()) != null && (p = common) != null + && (ws = p.workQueues) != null && (m = ws.length - 1) >= 0) ? ws[m + & z.seed & SQMASK] + : null; + } + + /** + * Tries to pop the given task from submitter's queue in common pool. + */ + final boolean tryExternalUnpush(ForkJoinTask task) { + WorkQueue joiner; + ForkJoinTask[] a; + int m, s; + Submitter z = submitters.get(); + WorkQueue[] ws = workQueues; + boolean popped = false; + if(z != null && ws != null && (m = ws.length - 1) >= 0 + && (joiner = ws[z.seed & m & SQMASK]) != null + && joiner.base != (s = joiner.top) + && (a = joiner.array) != null) { + long j = (((a.length - 1) & (s - 1)) << ASHIFT) + ABASE; + if(U.getObject(a, j) == task + && U.compareAndSwapInt(joiner, QLOCK, 0, 1)) { + if(joiner.top == s && joiner.array == a + && U.compareAndSwapObject(a, j, task, null)) { + joiner.top = s - 1; + popped = true; + } + joiner.qlock = 0; + } + } + return popped; + } + + final int externalHelpComplete(CountedCompleter task) { + WorkQueue joiner; + int m, j; + Submitter z = submitters.get(); + WorkQueue[] ws = workQueues; + int s = 0; + if(z != null && ws != null && (m = ws.length - 1) >= 0 + && (joiner = ws[(j = z.seed) & m & SQMASK]) != null + && task != null) { + int scans = m + m + 1; + long c = 0L; // for stability check + j |= 1; // poll odd queues + for (int k = scans;; j += 2) { + WorkQueue q; + if((s = task.status) < 0) + break; + else if(joiner.externalPopAndExecCC(task)) + k = scans; + else if((s = task.status) < 0) + break; + else if((q = ws[j & m]) != null && q.pollAndExecCC(task)) + k = scans; + else if(--k < 0) { + if(c == (c = ctl)) + break; + k = scans; + } + } + } + return s; + } + + // Exported methods + + // Constructors + + /** + * Creates a {@code ForkJoinPool} with parallelism equal to + * {@link java.lang.Runtime#availableProcessors}, using the + * {@linkplain #defaultForkJoinWorkerThreadFactory default thread factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link java.lang.RuntimePermission} + * {@code ("modifyThread")} + */ + public ForkJoinPool() { + this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()), + defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the indicated parallelism + * level, the {@linkplain #defaultForkJoinWorkerThreadFactory default thread + * factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * + * @param parallelism the parallelism level + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link java.lang.RuntimePermission} + * {@code ("modifyThread")} + */ + public ForkJoinPool(int parallelism) { + this(parallelism, defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the given parameters. + * + * @param parallelism the parallelism level. For default value, + * use {@link java.lang.Runtime#availableProcessors}. + * @param factory the factory for creating new threads. For default value, + * use {@link #defaultForkJoinWorkerThreadFactory}. + * @param handler the handler for internal worker threads that + * terminate due to unrecoverable errors encountered while + * executing + * tasks. For default value, use {@code null}. + * @param asyncMode if true, + * establishes local first-in-first-out scheduling mode for + * forked + * tasks that are never joined. This mode may be more appropriate + * than default locally stack-based mode in applications in which + * worker threads only process event-style asynchronous tasks. + * For default value, use {@code false}. + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + * @throws NullPointerException if the factory is null + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link java.lang.RuntimePermission} + * {@code ("modifyThread")} + */ + public ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, + UncaughtExceptionHandler handler, boolean asyncMode) { + this(checkParallelism(parallelism), checkFactory(factory), handler, + (asyncMode ? FIFO_QUEUE : LIFO_QUEUE), "ForkJoinPool-" + + nextPoolId() + "-worker-"); + checkPermission(); + } + + private static int checkParallelism(int parallelism) { + if(parallelism <= 0 || parallelism > MAX_CAP) + throw new IllegalArgumentException(); + return parallelism; + } + + private static ForkJoinWorkerThreadFactory checkFactory( + ForkJoinWorkerThreadFactory factory) { + if(factory == null) + throw new NullPointerException(); + return factory; + } + + /** + * Creates a {@code ForkJoinPool} with the given parameters, without + * any security checks or parameter validation. Invoked directly by + * makeCommonPool. + */ + private ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, + UncaughtExceptionHandler handler, int mode, String workerNamePrefix) { + this.workerNamePrefix = workerNamePrefix; + this.factory = factory; + this.ueh = handler; + this.mode = (short) mode; + this.parallelism = (short) parallelism; + long np = (long) (-parallelism); // offset ctl counts + this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK); + } + + /** + * Returns the common pool instance. This pool is statically + * constructed; its run state is unaffected by attempts to {@link #shutdown} + * or {@link #shutdownNow}. However this pool and any + * ongoing processing are automatically terminated upon program + * {@link System#exit}. Any program that relies on asynchronous + * task processing to complete before program termination should + * invoke {@code commonPool().}{@link #awaitQuiescence awaitQuiescence}, + * before exit. + * + * @return the common pool instance + * @since 1.8 + */ + public static ForkJoinPool commonPool() { + // assert common != null : "static init error"; + return common; + } + + // Execution methods + + /** + * Performs the given task, returning its result upon completion. + * If the computation encounters an unchecked Exception or Error, + * it is rethrown as the outcome of this invocation. Rethrown + * exceptions behave in the same way as regular exceptions, but, + * when possible, contain stack traces (as displayed for example + * using {@code ex.printStackTrace()}) of both the current thread + * as well as the thread actually encountering the exception; + * minimally only the latter. + * + * @param task the task + * @param the type of the task's result + * @return the task's result + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public T invoke(ForkJoinTask task) { + if(task == null) + throw new NullPointerException(); + externalPush(task); + return task.join(); + } + + /** + * Arranges for (asynchronous) execution of the given task. + * + * @param task the task + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public void execute(ForkJoinTask task) { + if(task == null) + throw new NullPointerException(); + externalPush(task); + } + + // AbstractExecutorService methods + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public void execute(Runnable task) { + if(task == null) + throw new NullPointerException(); + ForkJoinTask job; + if(task instanceof ForkJoinTask) // avoid re-wrap + job = (ForkJoinTask) task; + else + job = new ForkJoinTask.RunnableExecuteAction(task); + externalPush(job); + } + + /** + * Submits a ForkJoinTask for execution. + * + * @param task the task to submit + * @param the type of the task's result + * @return the task + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(ForkJoinTask task) { + if(task == null) + throw new NullPointerException(); + externalPush(task); + return task; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Callable task) { + ForkJoinTask job = new ForkJoinTask.AdaptedCallable(task); + externalPush(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Runnable task, T result) { + ForkJoinTask job = new ForkJoinTask.AdaptedRunnable(task, result); + externalPush(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Runnable task) { + if(task == null) + throw new NullPointerException(); + ForkJoinTask job; + if(task instanceof ForkJoinTask) // avoid re-wrap + job = (ForkJoinTask) task; + else + job = new ForkJoinTask.AdaptedRunnableAction(task); + externalPush(job); + return job; + } + + /** + * @throws NullPointerException {@inheritDoc} + * @throws RejectedExecutionException {@inheritDoc} + */ + public List> invokeAll(Collection> tasks) { + // In previous versions of this class, this method constructed + // a task to run ForkJoinTask.invokeAll, but now external + // invocation of multiple tasks is at least as efficient. + ArrayList> futures = new ArrayList>(tasks.size()); + + boolean done = false; + try { + for (Callable t : tasks) { + ForkJoinTask f = new ForkJoinTask.AdaptedCallable(t); + futures.add(f); + externalPush(f); + } + for (int i = 0, size = futures.size(); i < size; i++) + ((ForkJoinTask) futures.get(i)).quietlyJoin(); + done = true; + return futures; + } + finally { + if(!done) + for (int i = 0, size = futures.size(); i < size; i++) + futures.get(i).cancel(false); + } + } + + /** + * Returns the factory used for constructing new workers. + * + * @return the factory used for constructing new workers + */ + public ForkJoinWorkerThreadFactory getFactory() { + return factory; + } + + /** + * Returns the handler for internal worker threads that terminate + * due to unrecoverable errors encountered while executing tasks. + * + * @return the handler, or {@code null} if none + */ + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return ueh; + } + + /** + * Returns the targeted parallelism level of this pool. + * + * @return the targeted parallelism level of this pool + */ + public int getParallelism() { + int par; + return ((par = parallelism) > 0) ? par : 1; + } + + /** + * Returns the targeted parallelism level of the common pool. + * + * @return the targeted parallelism level of the common pool + * @since 1.8 + */ + public static int getCommonPoolParallelism() { + return commonParallelism; + } + + /** + * Returns the number of worker threads that have started but not + * yet terminated. The result returned by this method may differ + * from {@link #getParallelism} when threads are created to + * maintain parallelism when others are cooperatively blocked. + * + * @return the number of worker threads + */ + public int getPoolSize() { + return parallelism + (short) (ctl >>> TC_SHIFT); + } + + /** + * Returns {@code true} if this pool uses local first-in-first-out + * scheduling mode for forked tasks that are never joined. + * + * @return {@code true} if this pool uses async mode + */ + public boolean getAsyncMode() { + return mode == FIFO_QUEUE; + } + + /** + * Returns an estimate of the number of worker threads that are + * not blocked waiting to join tasks or for other managed + * synchronization. This method may overestimate the + * number of running threads. + * + * @return the number of worker threads + */ + public int getRunningThreadCount() { + int rc = 0; + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 1; i < ws.length; i += 2) { + if((w = ws[i]) != null && w.isApparentlyUnblocked()) + ++rc; + } + } + return rc; + } + + /** + * Returns an estimate of the number of threads that are currently + * stealing or executing tasks. This method may overestimate the + * number of active threads. + * + * @return the number of active threads + */ + public int getActiveThreadCount() { + int r = parallelism + (int) (ctl >> AC_SHIFT); + return (r <= 0) ? 0 : r; // suppress momentarily negative values + } + + /** + * Returns {@code true} if all worker threads are currently idle. + * An idle worker is one that cannot obtain a task to execute + * because none are available to steal from other threads, and + * there are no pending submissions to the pool. This method is + * conservative; it might not return {@code true} immediately upon + * idleness of all threads, but will eventually become true if + * threads remain inactive. + * + * @return {@code true} if all threads are currently idle + */ + public boolean isQuiescent() { + return parallelism + (int) (ctl >> AC_SHIFT) <= 0; + } + + /** + * Returns an estimate of the total number of tasks stolen from + * one thread's work queue by another. The reported value + * underestimates the actual total number of steals when the pool + * is not quiescent. This value may be useful for monitoring and + * tuning fork/join programs: in general, steal counts should be + * high enough to keep threads busy, but low enough to avoid + * overhead and contention across threads. + * + * @return the number of steals + */ + public long getStealCount() { + long count = stealCount; + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 1; i < ws.length; i += 2) { + if((w = ws[i]) != null) + count += w.nsteals; + } + } + return count; + } + + /** + * Returns an estimate of the total number of tasks currently held + * in queues by worker threads (but not including tasks submitted + * to the pool that have not begun executing). This value is only + * an approximation, obtained by iterating across all threads in + * the pool. This method may be useful for tuning task + * granularities. + * + * @return the number of queued tasks + */ + public long getQueuedTaskCount() { + long count = 0; + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 1; i < ws.length; i += 2) { + if((w = ws[i]) != null) + count += w.queueSize(); + } + } + return count; + } + + /** + * Returns an estimate of the number of tasks submitted to this + * pool that have not yet begun executing. This method may take + * time proportional to the number of submissions. + * + * @return the number of queued submissions + */ + public int getQueuedSubmissionCount() { + int count = 0; + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; i += 2) { + if((w = ws[i]) != null) + count += w.queueSize(); + } + } + return count; + } + + /** + * Returns {@code true} if there are any tasks submitted to this + * pool that have not yet begun executing. + * + * @return {@code true} if there are any queued submissions + */ + public boolean hasQueuedSubmissions() { + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; i += 2) { + if((w = ws[i]) != null && !w.isEmpty()) + return true; + } + } + return false; + } + + /** + * Removes and returns the next unexecuted submission if one is + * available. This method may be useful in extensions to this + * class that re-assign work in systems with multiple pools. + * + * @return the next submission, or {@code null} if none + */ + protected ForkJoinTask pollSubmission() { + WorkQueue[] ws; + WorkQueue w; + ForkJoinTask t; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; i += 2) { + if((w = ws[i]) != null && (t = w.poll()) != null) + return t; + } + } + return null; + } + + /** + * Removes all available unexecuted submitted and forked tasks + * from scheduling queues and adds them to the given collection, + * without altering their execution status. These may include + * artificially generated or wrapped tasks. This method is + * designed to be invoked only when the pool is known to be + * quiescent. Invocations at other times may not remove all + * tasks. A failure encountered while attempting to add elements + * to collection {@code c} may result in elements being in + * neither, either or both collections when the associated + * exception is thrown. The behavior of this operation is + * undefined if the specified collection is modified while the + * operation is in progress. + * + * @param c the collection to transfer elements into + * @return the number of elements transferred + */ + protected int drainTasksTo(Collection> c) { + int count = 0; + WorkQueue[] ws; + WorkQueue w; + ForkJoinTask t; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; ++i) { + if((w = ws[i]) != null) { + while ((t = w.poll()) != null) { + c.add(t); + ++count; + } + } + } + } + return count; + } + + /** + * Returns a string identifying this pool, as well as its state, + * including indications of run state, parallelism level, and + * worker and task counts. + * + * @return a string identifying this pool, as well as its state + */ + public String toString() { + // Use a single pass through workQueues to collect counts + long qt = 0L, qs = 0L; + int rc = 0; + long st = stealCount; + long c = ctl; + WorkQueue[] ws; + WorkQueue w; + if((ws = workQueues) != null) { + for (int i = 0; i < ws.length; ++i) { + if((w = ws[i]) != null) { + int size = w.queueSize(); + if((i & 1) == 0) + qs += size; + else { + qt += size; + st += w.nsteals; + if(w.isApparentlyUnblocked()) + ++rc; + } + } + } + } + int pc = parallelism; + int tc = pc + (short) (c >>> TC_SHIFT); + int ac = pc + (int) (c >> AC_SHIFT); + if(ac < 0) // ignore transient negative + ac = 0; + String level; + if((c & STOP_BIT) != 0) + level = (tc == 0) ? "Terminated" : "Terminating"; + else + level = plock < 0 ? "Shutting down" : "Running"; + return super.toString() + "[" + level + ", parallelism = " + pc + + ", size = " + tc + ", active = " + ac + ", running = " + rc + + ", steals = " + st + ", tasks = " + qt + ", submissions = " + + qs + "]"; + } + + /** + * Possibly initiates an orderly shutdown in which previously + * submitted tasks are executed, but no new tasks will be + * accepted. Invocation has no effect on execution state if this + * is the {@link #commonPool()}, and no additional effect if + * already shut down. Tasks that are in the process of being + * submitted concurrently during the course of this method may or + * may not be rejected. + * + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link java.lang.RuntimePermission} + * {@code ("modifyThread")} + */ + public void shutdown() { + checkPermission(); + tryTerminate(false, true); + } + + /** + * Possibly attempts to cancel and/or stop all tasks, and reject + * all subsequently submitted tasks. Invocation has no effect on + * execution state if this is the {@link #commonPool()}, and no + * additional effect if already shut down. Otherwise, tasks that + * are in the process of being submitted or executed concurrently + * during the course of this method may or may not be + * rejected. This method cancels both existing and unexecuted + * tasks, in order to permit termination in the presence of task + * dependencies. So the method always returns an empty list + * (unlike the case for some other Executors). + * + * @return an empty list + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link java.lang.RuntimePermission} + * {@code ("modifyThread")} + */ + public List shutdownNow() { + checkPermission(); + tryTerminate(true, true); + return Collections.emptyList(); + } + + /** + * Returns {@code true} if all tasks have completed following shut down. + * + * @return {@code true} if all tasks have completed following shut down + */ + public boolean isTerminated() { + long c = ctl; + return ((c & STOP_BIT) != 0L && (short) (c >>> TC_SHIFT) + parallelism <= 0); + } + + /** + * Returns {@code true} if the process of termination has + * commenced but not yet completed. This method may be useful for + * debugging. A return of {@code true} reported a sufficient + * period after shutdown may indicate that submitted tasks have + * ignored or suppressed interruption, or are waiting for I/O, + * causing this executor not to properly terminate. (See the + * advisory notes for class {@link ForkJoinTask} stating that + * tasks should not normally entail blocking operations. But if + * they do, they must abort them on interrupt.) + * + * @return {@code true} if terminating but not yet terminated + */ + public boolean isTerminating() { + long c = ctl; + return ((c & STOP_BIT) != 0L && (short) (c >>> TC_SHIFT) + parallelism > 0); + } + + /** + * Returns {@code true} if this pool has been shut down. + * + * @return {@code true} if this pool has been shut down + */ + public boolean isShutdown() { + return plock < 0; + } + + /** + * Blocks until all tasks have completed execution after a + * shutdown request, or the timeout occurs, or the current thread + * is interrupted, whichever happens first. Because the + * {@link #commonPool()} never terminates until program shutdown, when + * applied to the common pool, this method is equivalent to + * {@link #awaitQuiescence(long, TimeUnit)} but always returns {@code false} + * . + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return {@code true} if this executor terminated and {@code false} if the + * timeout elapsed before termination + * @throws InterruptedException if interrupted while waiting + */ + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + if(Thread.interrupted()) + throw new InterruptedException(); + if(this == common) { + awaitQuiescence(timeout, unit); + return false; + } + long nanos = unit.toNanos(timeout); + if(isTerminated()) + return true; + if(nanos <= 0L) + return false; + long deadline = System.nanoTime() + nanos; + synchronized (this) { + for (;;) { + if(isTerminated()) + return true; + if(nanos <= 0L) + return false; + long millis = TimeUnit.NANOSECONDS.toMillis(nanos); + wait(millis > 0L ? millis : 1L); + nanos = deadline - System.nanoTime(); + } + } + } + + /** + * If called by a ForkJoinTask operating in this pool, equivalent + * in effect to {@link ForkJoinTask#helpQuiesce}. Otherwise, + * waits and/or attempts to assist performing tasks until this + * pool {@link #isQuiescent} or the indicated timeout elapses. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return {@code true} if quiescent; {@code false} if the + * timeout elapsed. + */ + public boolean awaitQuiescence(long timeout, TimeUnit unit) { + long nanos = unit.toNanos(timeout); + ForkJoinWorkerThread wt; + Thread thread = Thread.currentThread(); + if((thread instanceof ForkJoinWorkerThread) + && (wt = (ForkJoinWorkerThread) thread).pool == this) { + helpQuiescePool(wt.workQueue); + return true; + } + long startTime = System.nanoTime(); + WorkQueue[] ws; + int r = 0, m; + boolean found = true; + while (!isQuiescent() && (ws = workQueues) != null + && (m = ws.length - 1) >= 0) { + if(!found) { + if((System.nanoTime() - startTime) > nanos) + return false; + Thread.yield(); // cannot block + } + found = false; + for (int j = (m + 1) << 2; j >= 0; --j) { + ForkJoinTask t; + WorkQueue q; + int b; + if((q = ws[r++ & m]) != null && (b = q.base) - q.top < 0) { + found = true; + if((t = q.pollAt(b)) != null) + t.doExec(); + break; + } + } + } + return true; + } + + /** + * Waits and/or attempts to assist performing tasks indefinitely + * until the {@link #commonPool()} {@link #isQuiescent}. + */ + static void quiesceCommonPool() { + common.awaitQuiescence(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + } + + /** + * Interface for extending managed parallelism for tasks running + * in {@link ForkJoinPool}s. + * + *

+ * A {@code ManagedBlocker} provides two methods. Method + * {@code isReleasable} must return {@code true} if blocking is not + * necessary. Method {@code block} blocks the current thread if necessary + * (perhaps internally invoking {@code isReleasable} before actually + * blocking). These actions are performed by any thread invoking + * {@link ForkJoinPool#managedBlock(ManagedBlocker)}. The unusual methods in + * this API accommodate synchronizers that may, but don't usually, block for + * long periods. Similarly, they allow more efficient internal handling of + * cases in which additional workers may be, but usually are not, needed to + * ensure sufficient parallelism. Toward this end, implementations of method + * {@code isReleasable} must be amenable to repeated invocation. + * + *

+ * For example, here is a ManagedBlocker based on a ReentrantLock: + * + *

+     * {
+     *     @code
+     *     class ManagedLocker implements ManagedBlocker {
+     *         final ReentrantLock lock;
+     *         boolean hasLock = false;
+     * 
+     *         ManagedLocker(ReentrantLock lock) {
+     *             this.lock = lock;
+     *         }
+     * 
+     *         public boolean block() {
+     *             if(!hasLock)
+     *                 lock.lock();
+     *             return true;
+     *         }
+     * 
+     *         public boolean isReleasable() {
+     *             return hasLock || (hasLock = lock.tryLock());
+     *         }
+     *     }
+     * }
+     * 
+ * + *

+ * Here is a class that possibly blocks waiting for an item on a given + * queue: + * + *

+     * {
+     *     @code
+     *     class QueueTaker<E> implements ManagedBlocker {
+     *         final BlockingQueue<E> queue;
+     *         volatile E item = null;
+     * 
+     *         QueueTaker(BlockingQueue<E> q) {
+     *             this.queue = q;
+     *         }
+     * 
+     *         public boolean block() throws InterruptedException {
+     *             if(item == null)
+     *                 item = queue.take();
+     *             return true;
+     *         }
+     * 
+     *         public boolean isReleasable() {
+     *             return item != null || (item = queue.poll()) != null;
+     *         }
+     * 
+     *         public E getItem() { // call after pool.managedBlock completes
+     *             return item;
+     *         }
+     *     }
+     * }
+     * 
+ */ + public static interface ManagedBlocker { + /** + * Possibly blocks the current thread, for example waiting for + * a lock or condition. + * + * @return {@code true} if no additional blocking is necessary + * (i.e., if isReleasable would return true) + * @throws InterruptedException if interrupted while waiting + * (the method is not required to do so, but is allowed to) + */ + boolean block() throws InterruptedException; + + /** + * Returns {@code true} if blocking is unnecessary. + * + * @return {@code true} if blocking is unnecessary + */ + boolean isReleasable(); + } + + /** + * Blocks in accord with the given blocker. If the current thread + * is a {@link ForkJoinWorkerThread}, this method possibly + * arranges for a spare thread to be activated if necessary to + * ensure sufficient parallelism while the current thread is blocked. + * + *

+ * If the caller is not a {@link ForkJoinTask}, this method is behaviorally + * equivalent to + * + *

+     * {@code
+     * while (!blocker.isReleasable())
+     *   if (blocker.block())
+     *     return;
+     * }
+     * 
+ * + * If the caller is a {@code ForkJoinTask}, then the pool may first be + * expanded to ensure parallelism, and later adjusted. + * + * @param blocker the blocker + * @throws InterruptedException if blocker.block did so + */ + public static void managedBlock(ManagedBlocker blocker) + throws InterruptedException { + Thread t = Thread.currentThread(); + if(t instanceof ForkJoinWorkerThread) { + ForkJoinPool p = ((ForkJoinWorkerThread) t).pool; + while (!blocker.isReleasable()) { + if(p.tryCompensate(p.ctl)) { + try { + do {} + while (!blocker.isReleasable() && !blocker.block()); + } + finally { + p.incrementActiveCount(); + } + break; + } + } + } + else { + do {} + while (!blocker.isReleasable() && !blocker.block()); + } + } + + // AbstractExecutorService overrides. These rely on undocumented + // fact that ForkJoinTask.adapt returns ForkJoinTasks that also + // implement RunnableFuture. + + protected RunnableFuture newTaskFor(Runnable runnable, T value) { + return new ForkJoinTask.AdaptedRunnable(runnable, value); + } + + protected RunnableFuture newTaskFor(Callable callable) { + return new ForkJoinTask.AdaptedCallable(callable); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long CTL; + private static final long PARKBLOCKER; + private static final int ABASE; + private static final int ASHIFT; + private static final long STEALCOUNT; + private static final long PLOCK; + private static final long INDEXSEED; + private static final long QBASE; + private static final long QLOCK; + + static { + // initialize field offsets for CAS etc + try { + U = getUnsafe(); + Class k = ForkJoinPool.class; + CTL = U.objectFieldOffset(k.getDeclaredField("ctl")); + STEALCOUNT = U.objectFieldOffset(k.getDeclaredField("stealCount")); + PLOCK = U.objectFieldOffset(k.getDeclaredField("plock")); + INDEXSEED = U.objectFieldOffset(k.getDeclaredField("indexSeed")); + Class tk = Thread.class; + PARKBLOCKER = U.objectFieldOffset(tk + .getDeclaredField("parkBlocker")); + Class wk = WorkQueue.class; + QBASE = U.objectFieldOffset(wk.getDeclaredField("base")); + QLOCK = U.objectFieldOffset(wk.getDeclaredField("qlock")); + Class ak = ForkJoinTask[].class; + ABASE = U.arrayBaseOffset(ak); + int scale = U.arrayIndexScale(ak); + if((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + } + catch (Exception e) { + throw new Error(e); + } + + submitters = new ThreadLocal(); + defaultForkJoinWorkerThreadFactory = new DefaultForkJoinWorkerThreadFactory(); + modifyThreadPermission = new RuntimePermission("modifyThread"); + + common = java.security.AccessController + .doPrivileged(new java.security.PrivilegedAction() { + public ForkJoinPool run() { + return makeCommonPool(); + } + }); + int par = common.parallelism; // report 1 even if threads disabled + commonParallelism = par > 0 ? par : 1; + } + + /** + * Creates and returns the common pool, respecting user settings + * specified via system properties. + */ + private static ForkJoinPool makeCommonPool() { + int parallelism = -1; + ForkJoinWorkerThreadFactory factory = defaultForkJoinWorkerThreadFactory; + UncaughtExceptionHandler handler = null; + try { // ignore exceptions in accessing/parsing properties + String pp = System + .getProperty("java.util.concurrent.ForkJoinPool.common.parallelism"); + String fp = System + .getProperty("java.util.concurrent.ForkJoinPool.common.threadFactory"); + String hp = System + .getProperty("java.util.concurrent.ForkJoinPool.common.exceptionHandler"); + if(pp != null) + parallelism = Integer.parseInt(pp); + if(fp != null) + factory = ((ForkJoinWorkerThreadFactory) ClassLoader + .getSystemClassLoader().loadClass(fp).newInstance()); + if(hp != null) + handler = ((UncaughtExceptionHandler) ClassLoader + .getSystemClassLoader().loadClass(hp).newInstance()); + } + catch (Exception ignore) {} + + if(parallelism < 0 && // default 1 less than #cores + (parallelism = Runtime.getRuntime().availableProcessors() - 1) < 0) + parallelism = 0; + if(parallelism > MAX_CAP) + parallelism = MAX_CAP; + return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE, + "ForkJoinPool.commonPool-worker-"); + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java new file mode 100644 index 0000000000..fc1162eb8a --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java @@ -0,0 +1,1633 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.lang.reflect.Constructor; + +/** + * Abstract base class for tasks that run within a {@link ForkJoinPool}. + * A {@code ForkJoinTask} is a thread-like entity that is much + * lighter weight than a normal thread. Huge numbers of tasks and + * subtasks may be hosted by a small number of actual threads in a + * ForkJoinPool, at the price of some usage limitations. + * + *

+ * A "main" {@code ForkJoinTask} begins execution when it is explicitly + * submitted to a {@link ForkJoinPool}, or, if not already engaged in a ForkJoin + * computation, commenced in the {@link ForkJoinPool#commonPool()} via + * {@link #fork}, {@link #invoke}, or related methods. Once started, it will + * usually in turn start other subtasks. As indicated by the name of this class, + * many programs using {@code ForkJoinTask} employ only methods {@link #fork} + * and {@link #join}, or derivatives such as {@link #invokeAll(ForkJoinTask...) + * invokeAll}. However, this class also provides a number of other methods that + * can come into play in advanced usages, as well as extension mechanics that + * allow support of new forms of fork/join processing. + * + *

+ * A {@code ForkJoinTask} is a lightweight form of {@link Future}. The + * efficiency of {@code ForkJoinTask}s stems from a set of restrictions (that + * are only partially statically enforceable) reflecting their main use as + * computational tasks calculating pure functions or operating on purely + * isolated objects. The primary coordination mechanisms are {@link #fork}, that + * arranges asynchronous execution, and {@link #join}, that doesn't proceed + * until the task's result has been computed. Computations should ideally avoid + * {@code synchronized} methods or blocks, and should minimize other blocking + * synchronization apart from joining other tasks or using synchronizers such as + * Phasers that are advertised to cooperate with fork/join scheduling. + * Subdividable tasks should also not perform blocking I/O, and should ideally + * access variables that are completely independent of those accessed by other + * running tasks. These guidelines are loosely enforced by not permitting + * checked exceptions such as {@code IOExceptions} to be thrown. However, + * computations may still encounter unchecked exceptions, that are rethrown to + * callers attempting to join them. These exceptions may additionally include + * {@link RejectedExecutionException} stemming from internal resource + * exhaustion, such as failure to allocate internal task queues. Rethrown + * exceptions behave in the same way as regular exceptions, but, when possible, + * contain stack traces (as displayed for example using + * {@code ex.printStackTrace()}) of both the thread that initiated the + * computation as well as the thread actually encountering the exception; + * minimally only the latter. + * + *

+ * It is possible to define and use ForkJoinTasks that may block, but doing do + * requires three further considerations: (1) Completion of few if any + * other tasks should be dependent on a task that blocks on external + * synchronization or I/O. Event-style async tasks that are never joined (for + * example, those subclassing {@link CountedCompleter}) often fall into this + * category. (2) To minimize resource impact, tasks should be small; ideally + * performing only the (possibly) blocking action. (3) Unless the + * {@link ForkJoinPool.ManagedBlocker} API is used, or the number of possibly + * blocked tasks is known to be less than the pool's + * {@link ForkJoinPool#getParallelism} level, the pool cannot guarantee that + * enough threads will be available to ensure progress or good performance. + * + *

+ * The primary method for awaiting completion and extracting results of a task + * is {@link #join}, but there are several variants: The {@link Future#get} + * methods support interruptible and/or timed waits for completion and report + * results using {@code Future} conventions. Method {@link #invoke} is + * semantically equivalent to {@code fork(); join()} but always attempts to + * begin execution in the current thread. The "quiet" forms of these + * methods do not extract results or report exceptions. These may be useful when + * a set of tasks are being executed, and you need to delay processing of + * results or exceptions until all complete. Method {@code invokeAll} (available + * in multiple versions) performs the most common form of parallel invocation: + * forking a set of tasks and joining them all. + * + *

+ * In the most typical usages, a fork-join pair act like a call (fork) and + * return (join) from a parallel recursive function. As is the case with other + * forms of recursive calls, returns (joins) should be performed + * innermost-first. For example, {@code a.fork(); + * b.fork(); b.join(); a.join();} is likely to be substantially more efficient + * than joining {@code a} before {@code b}. + * + *

+ * The execution status of tasks may be queried at several levels of detail: + * {@link #isDone} is true if a task completed in any way (including the case + * where a task was cancelled without executing); {@link #isCompletedNormally} + * is true if a task completed without cancellation or encountering an + * exception; {@link #isCancelled} is true if the task was cancelled (in which + * case {@link #getException} returns a + * {@link java.util.concurrent.CancellationException}); and + * {@link #isCompletedAbnormally} is true if a task was either cancelled or + * encountered an exception, in which case {@link #getException} will return + * either the encountered exception or + * {@link java.util.concurrent.CancellationException}. + * + *

+ * The ForkJoinTask class is not usually directly subclassed. Instead, you + * subclass one of the abstract classes that support a particular style of + * fork/join processing, typically {@link RecursiveAction} for most computations + * that do not return results, {@link RecursiveTask} for those that do, and + * {@link CountedCompleter} for those in which completed actions trigger other + * actions. Normally, a concrete ForkJoinTask subclass declares fields + * comprising its parameters, established in a constructor, and then defines a + * {@code compute} method that somehow uses the control methods supplied by this + * base class. + * + *

+ * Method {@link #join} and its variants are appropriate for use only when + * completion dependencies are acyclic; that is, the parallel computation can be + * described as a directed acyclic graph (DAG). Otherwise, executions may + * encounter a form of deadlock as tasks cyclically wait for each other. + * However, this framework supports other methods and techniques (for example + * the use of {@link java.util.concurrent.Phaser Phaser}, {@link #helpQuiesce}, + * and {@link #complete}) that may be of use in constructing custom subclasses + * for problems that are not statically structured as DAGs. To support such + * usages, a ForkJoinTask may be atomically tagged with a {@code short} + * value using {@link #setForkJoinTaskTag} or + * {@link #compareAndSetForkJoinTaskTag} and checked using + * {@link #getForkJoinTaskTag}. The ForkJoinTask implementation does not use + * these {@code protected} methods or tags for any purpose, but they may be of + * use in the construction of specialized subclasses. For example, parallel + * graph traversals can use the supplied methods to avoid revisiting nodes/tasks + * that have already been processed. (Method names for tagging are bulky in part + * to encourage definition of methods that reflect their usage patterns.) + * + *

+ * Most base support methods are {@code final}, to prevent overriding of + * implementations that are intrinsically tied to the underlying lightweight + * task scheduling framework. Developers creating new basic styles of fork/join + * processing should minimally implement {@code protected} methods {@link #exec}, {@link #setRawResult}, and {@link #getRawResult}, while also introducing an + * abstract computational method that can be implemented in its subclasses, + * possibly relying on other {@code protected} methods provided by this class. + * + *

+ * ForkJoinTasks should perform relatively small amounts of computation. Large + * tasks should be split into smaller subtasks, usually via recursive + * decomposition. As a very rough rule of thumb, a task should perform more than + * 100 and less than 10000 basic computational steps, and should avoid + * indefinite looping. If tasks are too big, then parallelism cannot improve + * throughput. If too small, then memory and internal task maintenance overhead + * may overwhelm processing. + * + *

+ * This class provides {@code adapt} methods for {@link Runnable} and + * {@link Callable}, that may be of use when mixing execution of + * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are of this + * form, consider using a pool constructed in asyncMode. + * + *

+ * ForkJoinTasks are {@code Serializable}, which enables them to be used in + * extensions such as remote execution frameworks. It is sensible to serialize + * tasks only before or after, but not during, execution. Serialization is not + * relied on during execution itself. + * + * @since 1.7 + * @author Doug Lea + */ +public abstract class ForkJoinTask implements Future, Serializable { + + /* + * See the internal documentation of class ForkJoinPool for a + * general implementation overview. ForkJoinTasks are mainly + * responsible for maintaining their "status" field amidst relays + * to methods in ForkJoinWorkerThread and ForkJoinPool. + * + * The methods of this class are more-or-less layered into + * (1) basic status maintenance + * (2) execution and awaiting completion + * (3) user-level methods that additionally report results. + * This is sometimes hard to see because this file orders exported + * methods in a way that flows well in javadocs. + */ + + /* + * The status field holds run control status bits packed into a + * single int to minimize footprint and to ensure atomicity (via + * CAS). Status is initially zero, and takes on nonnegative + * values until completed, upon which status (anded with + * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks + * undergoing blocking waits by other threads have the SIGNAL bit + * set. Completion of a stolen task with SIGNAL set awakens any + * waiters via notifyAll. Even though suboptimal for some + * purposes, we use basic builtin wait/notify to take advantage of + * "monitor inflation" in JVMs that we would otherwise need to + * emulate to avoid adding further per-task bookkeeping overhead. + * We want these monitors to be "fat", i.e., not use biasing or + * thin-lock techniques, so use some odd coding idioms that tend + * to avoid them, mainly by arranging that every synchronized + * block performs a wait, notifyAll or both. + * + * These control bits occupy only (some of) the upper half (16 + * bits) of status field. The lower bits are used for user-defined + * tags. + */ + + /** The run status of this task */ + volatile int status; // accessed directly by pool and workers + static final int DONE_MASK = 0xf0000000; // mask out non-completion bits + static final int NORMAL = 0xf0000000; // must be negative + static final int CANCELLED = 0xc0000000; // must be < NORMAL + static final int EXCEPTIONAL = 0x80000000; // must be < CANCELLED + static final int SIGNAL = 0x00010000; // must be >= 1 << 16 + static final int SMASK = 0x0000ffff; // short bits for tags + + /** + * Marks completion and wakes up threads waiting to join this + * task. + * + * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL + * @return completion status on exit + */ + private int setCompletion(int completion) { + for (int s;;) { + if((s = status) < 0) + return s; + if(U.compareAndSwapInt(this, STATUS, s, s | completion)) { + if((s >>> 16) != 0) + synchronized (this) { + notifyAll(); + } + return completion; + } + } + } + + /** + * Primary execution method for stolen tasks. Unless done, calls + * exec and records status if completed, but doesn't wait for + * completion otherwise. + * + * @return status on exit from this method + */ + final int doExec() { + int s; + boolean completed; + if((s = status) >= 0) { + try { + completed = exec(); + } + catch (Throwable rex) { + return setExceptionalCompletion(rex); + } + if(completed) + s = setCompletion(NORMAL); + } + return s; + } + + /** + * Tries to set SIGNAL status unless already completed. Used by + * ForkJoinPool. Other variants are directly incorporated into + * externalAwaitDone etc. + * + * @return true if successful + */ + final boolean trySetSignal() { + int s = status; + return s >= 0 && U.compareAndSwapInt(this, STATUS, s, s | SIGNAL); + } + + /** + * Blocks a non-worker-thread until completion. + * + * @return status upon completion + */ + private int externalAwaitDone() { + int s; + ForkJoinPool cp = ForkJoinPool.common; + if((s = status) >= 0) { + if(cp != null) { + if(this instanceof CountedCompleter) + s = cp.externalHelpComplete((CountedCompleter) this); + else if(cp.tryExternalUnpush(this)) + s = doExec(); + } + if(s >= 0 && (s = status) >= 0) { + boolean interrupted = false; + do { + if(U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + synchronized (this) { + if(status >= 0) { + try { + wait(); + } + catch (InterruptedException ie) { + interrupted = true; + } + } + else + notifyAll(); + } + } + } + while ((s = status) >= 0); + if(interrupted) + Thread.currentThread().interrupt(); + } + } + return s; + } + + /** + * Blocks a non-worker-thread until completion or interruption. + */ + private int externalInterruptibleAwaitDone() throws InterruptedException { + int s; + ForkJoinPool cp = ForkJoinPool.common; + if(Thread.interrupted()) + throw new InterruptedException(); + if((s = status) >= 0 && cp != null) { + if(this instanceof CountedCompleter) + cp.externalHelpComplete((CountedCompleter) this); + else if(cp.tryExternalUnpush(this)) + doExec(); + } + while ((s = status) >= 0) { + if(U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + synchronized (this) { + if(status >= 0) + wait(); + else + notifyAll(); + } + } + } + return s; + } + + /** + * Implementation for join, get, quietlyJoin. Directly handles + * only cases of already-completed, external wait, and + * unfork+exec. Others are relayed to ForkJoinPool.awaitJoin. + * + * @return status upon completion + */ + private int doJoin() { + int s; + Thread t; + ForkJoinWorkerThread wt; + ForkJoinPool.WorkQueue w; + return (s = status) < 0 ? s + : ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? (w = (wt = (ForkJoinWorkerThread) t).workQueue) + .tryUnpush(this) && (s = doExec()) < 0 ? s : wt.pool + .awaitJoin(w, this) : externalAwaitDone(); + } + + /** + * Implementation for invoke, quietlyInvoke. + * + * @return status upon completion + */ + private int doInvoke() { + int s; + Thread t; + ForkJoinWorkerThread wt; + return (s = doExec()) < 0 ? s + : ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? (wt = (ForkJoinWorkerThread) t).pool + .awaitJoin(wt.workQueue, this) : externalAwaitDone(); + } + + // Exception table support + + /** + * Table of exceptions thrown by tasks, to enable reporting by + * callers. Because exceptions are rare, we don't directly keep + * them with task objects, but instead use a weak ref table. Note + * that cancellation exceptions don't appear in the table, but are + * instead recorded as status values. + * + * Note: These statics are initialized below in static block. + */ + private static final ExceptionNode[] exceptionTable; + private static final ReentrantLock exceptionTableLock; + private static final ReferenceQueue exceptionTableRefQueue; + + /** + * Fixed capacity for exceptionTable. + */ + private static final int EXCEPTION_MAP_CAPACITY = 32; + + /** + * Key-value nodes for exception table. The chained hash table + * uses identity comparisons, full locking, and weak references + * for keys. The table has a fixed capacity because it only + * maintains task exceptions long enough for joiners to access + * them, so should never become very large for sustained + * periods. However, since we do not know when the last joiner + * completes, we must use weak references and expunge them. We do + * so on each operation (hence full locking). Also, some thread in + * any ForkJoinPool will call helpExpungeStaleExceptions when its + * pool becomes isQuiescent. + */ + static final class ExceptionNode extends WeakReference> { + final Throwable ex; + ExceptionNode next; + final long thrower; // use id not ref to avoid weak cycles + final int hashCode; // store task hashCode before weak ref disappears + + ExceptionNode(ForkJoinTask task, Throwable ex, ExceptionNode next) { + super(task, exceptionTableRefQueue); + this.ex = ex; + this.next = next; + this.thrower = Thread.currentThread().getId(); + this.hashCode = System.identityHashCode(task); + } + } + + /** + * Records exception and sets status. + * + * @return status on exit + */ + final int recordExceptionalCompletion(Throwable ex) { + int s; + if((s = status) >= 0) { + int h = System.identityHashCode(this); + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + expungeStaleExceptions(); + ExceptionNode[] t = exceptionTable; + int i = h & (t.length - 1); + for (ExceptionNode e = t[i];; e = e.next) { + if(e == null) { + t[i] = new ExceptionNode(this, ex, t[i]); + break; + } + if(e.get() == this) // already present + break; + } + } + finally { + lock.unlock(); + } + s = setCompletion(EXCEPTIONAL); + } + return s; + } + + /** + * Records exception and possibly propagates. + * + * @return status on exit + */ + private int setExceptionalCompletion(Throwable ex) { + int s = recordExceptionalCompletion(ex); + if((s & DONE_MASK) == EXCEPTIONAL) + internalPropagateException(ex); + return s; + } + + /** + * Hook for exception propagation support for tasks with completers. + */ + void internalPropagateException(Throwable ex) {} + + /** + * Cancels, ignoring any exceptions thrown by cancel. Used during + * worker and pool shutdown. Cancel is spec'ed not to throw any + * exceptions, but if it does anyway, we have no recourse during + * shutdown, so guard against this case. + */ + static final void cancelIgnoringExceptions(ForkJoinTask t) { + if(t != null && t.status >= 0) { + try { + t.cancel(false); + } + catch (Throwable ignore) {} + } + } + + /** + * Removes exception node and clears status. + */ + private void clearExceptionalCompletion() { + int h = System.identityHashCode(this); + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + ExceptionNode[] t = exceptionTable; + int i = h & (t.length - 1); + ExceptionNode e = t[i]; + ExceptionNode pred = null; + while (e != null) { + ExceptionNode next = e.next; + if(e.get() == this) { + if(pred == null) + t[i] = next; + else + pred.next = next; + break; + } + pred = e; + e = next; + } + expungeStaleExceptions(); + status = 0; + } + finally { + lock.unlock(); + } + } + + /** + * Returns a rethrowable exception for the given task, if + * available. To provide accurate stack traces, if the exception + * was not thrown by the current thread, we try to create a new + * exception of the same type as the one thrown, but with the + * recorded exception as its cause. If there is no such + * constructor, we instead try to use a no-arg constructor, + * followed by initCause, to the same effect. If none of these + * apply, or any fail due to other exceptions, we return the + * recorded exception, which is still correct, although it may + * contain a misleading stack trace. + * + * @return the exception, or null if none + */ + private Throwable getThrowableException() { + if((status & DONE_MASK) != EXCEPTIONAL) + return null; + int h = System.identityHashCode(this); + ExceptionNode e; + final ReentrantLock lock = exceptionTableLock; + lock.lock(); + try { + expungeStaleExceptions(); + ExceptionNode[] t = exceptionTable; + e = t[h & (t.length - 1)]; + while (e != null && e.get() != this) + e = e.next; + } + finally { + lock.unlock(); + } + Throwable ex; + if(e == null || (ex = e.ex) == null) + return null; + if(false && e.thrower != Thread.currentThread().getId()) { + Class ec = ex.getClass(); + try { + Constructor noArgCtor = null; + Constructor[] cs = ec.getConstructors();// public ctors only + for (int i = 0; i < cs.length; ++i) { + Constructor c = cs[i]; + Class[] ps = c.getParameterTypes(); + if(ps.length == 0) + noArgCtor = c; + else if(ps.length == 1 && ps[0] == Throwable.class) + return (Throwable) (c.newInstance(ex)); + } + if(noArgCtor != null) { + Throwable wx = (Throwable) (noArgCtor.newInstance()); + wx.initCause(ex); + return wx; + } + } + catch (Exception ignore) {} + } + return ex; + } + + /** + * Poll stale refs and remove them. Call only while holding lock. + */ + /** + * Poll stale refs and remove them. Call only while holding lock. + */ + private static void expungeStaleExceptions() { + for (Object x; (x = exceptionTableRefQueue.poll()) != null;) { + if(x instanceof ExceptionNode) { + int hashCode = ((ExceptionNode) x).hashCode; + ExceptionNode[] t = exceptionTable; + int i = hashCode & (t.length - 1); + ExceptionNode e = t[i]; + ExceptionNode pred = null; + while (e != null) { + ExceptionNode next = e.next; + if(e == x) { + if(pred == null) + t[i] = next; + else + pred.next = next; + break; + } + pred = e; + e = next; + } + } + } + } + + /** + * If lock is available, poll stale refs and remove them. + * Called from ForkJoinPool when pools become quiescent. + */ + static final void helpExpungeStaleExceptions() { + final ReentrantLock lock = exceptionTableLock; + if(lock.tryLock()) { + try { + expungeStaleExceptions(); + } + finally { + lock.unlock(); + } + } + } + + /** + * A version of "sneaky throw" to relay exceptions + */ + static void rethrow(Throwable ex) { + if(ex != null) + ForkJoinTask. uncheckedThrow(ex); + } + + /** + * The sneaky part of sneaky throw, relying on generics + * limitations to evade compiler complaints about rethrowing + * unchecked exceptions + */ + @SuppressWarnings("unchecked") + static void uncheckedThrow(Throwable t) throws T { + throw (T) t; // rely on vacuous cast + } + + /** + * Throws exception, if any, associated with the given status. + */ + private void reportException(int s) { + if(s == CANCELLED) + throw new CancellationException(); + if(s == EXCEPTIONAL) + rethrow(getThrowableException()); + } + + // public methods + + /** + * Arranges to asynchronously execute this task in the pool the + * current task is running in, if applicable, or using the + * {@link ForkJoinPool#commonPool()} if not {@link #inForkJoinPool}. While + * it is not necessarily enforced, it is a usage error to fork a + * task more than once unless it has completed and been + * reinitialized. Subsequent modifications to the state of this + * task or any data it operates on are not necessarily + * consistently observable by any thread other than the one + * executing it unless preceded by a call to {@link #join} or + * related methods, or a call to {@link #isDone} returning {@code true}. + * + * @return {@code this}, to simplify usage + */ + public final ForkJoinTask fork() { + Thread t; + if((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) + ((ForkJoinWorkerThread) t).workQueue.push(this); + else + ForkJoinPool.common.externalPush(this); + return this; + } + + /** + * Returns the result of the computation when it {@link #isDone is + * done}. This method differs from {@link #get()} in that + * abnormal completion results in {@code RuntimeException} or {@code Error}, + * not {@code ExecutionException}, and that + * interrupts of the calling thread do not cause the + * method to abruptly return by throwing {@code InterruptedException}. + * + * @return the computed result + */ + public final V join() { + int s; + if((s = doJoin() & DONE_MASK) != NORMAL) + reportException(s); + return getRawResult(); + } + + /** + * Commences performing this task, awaits its completion if + * necessary, and returns its result, or throws an (unchecked) + * {@code RuntimeException} or {@code Error} if the underlying + * computation did so. + * + * @return the computed result + */ + public final V invoke() { + int s; + if((s = doInvoke() & DONE_MASK) != NORMAL) + reportException(s); + return getRawResult(); + } + + /** + * Forks the given tasks, returning when {@code isDone} holds for + * each task or an (unchecked) exception is encountered, in which + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, the + * other may be cancelled. However, the execution status of + * individual tasks is not guaranteed upon exceptional return. The + * status of each task may be obtained using {@link #getException()} and + * related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. + * + * @param t1 the first task + * @param t2 the second task + * @throws NullPointerException if any task is null + */ + public static void invokeAll(ForkJoinTask t1, ForkJoinTask t2) { + int s1, s2; + t2.fork(); + if((s1 = t1.doInvoke() & DONE_MASK) != NORMAL) + t1.reportException(s1); + if((s2 = t2.doJoin() & DONE_MASK) != NORMAL) + t2.reportException(s2); + } + + /** + * Forks the given tasks, returning when {@code isDone} holds for + * each task or an (unchecked) exception is encountered, in which + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, others + * may be cancelled. However, the execution status of individual + * tasks is not guaranteed upon exceptional return. The status of + * each task may be obtained using {@link #getException()} and + * related methods to check if they have been cancelled, completed + * normally or exceptionally, or left unprocessed. + * + * @param tasks the tasks + * @throws NullPointerException if any task is null + */ + public static void invokeAll(ForkJoinTask... tasks) { + Throwable ex = null; + int last = tasks.length - 1; + for (int i = last; i >= 0; --i) { + ForkJoinTask t = tasks[i]; + if(t == null) { + if(ex == null) + ex = new NullPointerException(); + } + else if(i != 0) + t.fork(); + else if(t.doInvoke() < NORMAL && ex == null) + ex = t.getException(); + } + for (int i = 1; i <= last; ++i) { + ForkJoinTask t = tasks[i]; + if(t != null) { + if(ex != null) + t.cancel(false); + else if(t.doJoin() < NORMAL) + ex = t.getException(); + } + } + if(ex != null) + rethrow(ex); + } + + /** + * Forks all tasks in the specified collection, returning when + * {@code isDone} holds for each task or an (unchecked) exception + * is encountered, in which case the exception is rethrown. If + * more than one task encounters an exception, then this method + * throws any one of these exceptions. If any task encounters an + * exception, others may be cancelled. However, the execution + * status of individual tasks is not guaranteed upon exceptional + * return. The status of each task may be obtained using + * {@link #getException()} and related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. + * + * @param tasks the collection of tasks + * @param the type of the values returned from the tasks + * @return the tasks argument, to simplify usage + * @throws NullPointerException if tasks or any element are null + */ + public static > Collection invokeAll( + Collection tasks) { + if(!(tasks instanceof RandomAccess) || !(tasks instanceof List)) { + invokeAll(tasks.toArray(new ForkJoinTask[tasks.size()])); + return tasks; + } + @SuppressWarnings("unchecked") List> ts = (List>) tasks; + Throwable ex = null; + int last = ts.size() - 1; + for (int i = last; i >= 0; --i) { + ForkJoinTask t = ts.get(i); + if(t == null) { + if(ex == null) + ex = new NullPointerException(); + } + else if(i != 0) + t.fork(); + else if(t.doInvoke() < NORMAL && ex == null) + ex = t.getException(); + } + for (int i = 1; i <= last; ++i) { + ForkJoinTask t = ts.get(i); + if(t != null) { + if(ex != null) + t.cancel(false); + else if(t.doJoin() < NORMAL) + ex = t.getException(); + } + } + if(ex != null) + rethrow(ex); + return tasks; + } + + /** + * Attempts to cancel execution of this task. This attempt will + * fail if the task has already completed or could not be + * cancelled for some other reason. If successful, and this task + * has not started when {@code cancel} is called, execution of + * this task is suppressed. After this method returns + * successfully, unless there is an intervening call to + * {@link #reinitialize}, subsequent calls to {@link #isCancelled}, + * {@link #isDone}, and {@code cancel} will return {@code true} and calls to + * {@link #join} and related methods will result in + * {@code CancellationException}. + * + *

+ * This method may be overridden in subclasses, but if so, must still ensure + * that these properties hold. In particular, the {@code cancel} method + * itself must not throw exceptions. + * + *

+ * This method is designed to be invoked by other tasks. To + * terminate the current task, you can just return or throw an unchecked + * exception from its computation method, or invoke + * {@link #completeExceptionally(Throwable)}. + * + * @param mayInterruptIfRunning this value has no effect in the + * default implementation because interrupts are not used to + * control cancellation. + * + * @return {@code true} if this task is now cancelled + */ + public boolean cancel(boolean mayInterruptIfRunning) { + return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED; + } + + public final boolean isDone() { + return status < 0; + } + + public final boolean isCancelled() { + return (status & DONE_MASK) == CANCELLED; + } + + /** + * Returns {@code true} if this task threw an exception or was cancelled. + * + * @return {@code true} if this task threw an exception or was cancelled + */ + public final boolean isCompletedAbnormally() { + return status < NORMAL; + } + + /** + * Returns {@code true} if this task completed without throwing an + * exception and was not cancelled. + * + * @return {@code true} if this task completed without throwing an + * exception and was not cancelled + */ + public final boolean isCompletedNormally() { + return (status & DONE_MASK) == NORMAL; + } + + /** + * Returns the exception thrown by the base computation, or a + * {@code CancellationException} if cancelled, or {@code null} if + * none or if the method has not yet completed. + * + * @return the exception, or {@code null} if none + */ + public final Throwable getException() { + int s = status & DONE_MASK; + return ((s >= NORMAL) ? null + : (s == CANCELLED) ? new CancellationException() + : getThrowableException()); + } + + /** + * Completes this task abnormally, and if not already aborted or + * cancelled, causes it to throw the given exception upon {@code join} and + * related operations. This method may be used + * to induce exceptions in asynchronous tasks, or to force + * completion of tasks that would not otherwise complete. Its use + * in other situations is discouraged. This method is + * overridable, but overridden versions must invoke {@code super} + * implementation to maintain guarantees. + * + * @param ex the exception to throw. If this exception is not a + * {@code RuntimeException} or {@code Error}, the actual + * exception + * thrown will be a {@code RuntimeException} with cause + * {@code ex}. + */ + public void completeExceptionally(Throwable ex) { + setExceptionalCompletion((ex instanceof RuntimeException) + || (ex instanceof Error) ? ex : new RuntimeException(ex)); + } + + /** + * Completes this task, and if not already aborted or cancelled, + * returning the given value as the result of subsequent + * invocations of {@code join} and related operations. This method + * may be used to provide results for asynchronous tasks, or to + * provide alternative handling for tasks that would not otherwise + * complete normally. Its use in other situations is + * discouraged. This method is overridable, but overridden + * versions must invoke {@code super} implementation to maintain + * guarantees. + * + * @param value the result value for this task + */ + public void complete(V value) { + try { + setRawResult(value); + } + catch (Throwable rex) { + setExceptionalCompletion(rex); + return; + } + setCompletion(NORMAL); + } + + /** + * Completes this task normally without setting a value. The most + * recent value established by {@link #setRawResult} (or {@code null} by + * default) will be returned as the result of subsequent + * invocations of {@code join} and related operations. + * + * @since 1.8 + */ + public final void quietlyComplete() { + setCompletion(NORMAL); + } + + /** + * Waits if necessary for the computation to complete, and then + * retrieves its result. + * + * @return the computed result + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an + * exception + * @throws InterruptedException if the current thread is not a + * member of a ForkJoinPool and was interrupted while waiting + */ + public final V get() throws InterruptedException, ExecutionException { + int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ? doJoin() + : externalInterruptibleAwaitDone(); + Throwable ex; + if((s &= DONE_MASK) == CANCELLED) + throw new CancellationException(); + if(s == EXCEPTIONAL && (ex = getThrowableException()) != null) + throw new ExecutionException(ex); + return getRawResult(); + } + + /** + * Waits if necessary for at most the given time for the computation + * to complete, and then retrieves its result, if available. + * + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return the computed result + * @throws CancellationException if the computation was cancelled + * @throws ExecutionException if the computation threw an + * exception + * @throws InterruptedException if the current thread is not a + * member of a ForkJoinPool and was interrupted while waiting + * @throws TimeoutException if the wait timed out + */ + public final V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if(Thread.interrupted()) + throw new InterruptedException(); + // Messy in part because we measure in nanosecs, but wait in millisecs + int s; + long ms; + long ns = unit.toNanos(timeout); + ForkJoinPool cp; + if((s = status) >= 0 && ns > 0L) { + long deadline = System.nanoTime() + ns; + ForkJoinPool p = null; + ForkJoinPool.WorkQueue w = null; + Thread t = Thread.currentThread(); + if(t instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread wt = (ForkJoinWorkerThread) t; + p = wt.pool; + w = wt.workQueue; + p.helpJoinOnce(w, this); // no retries on failure + } + else if((cp = ForkJoinPool.common) != null) { + if(this instanceof CountedCompleter) + cp.externalHelpComplete((CountedCompleter) this); + else if(cp.tryExternalUnpush(this)) + doExec(); + } + boolean canBlock = false; + boolean interrupted = false; + try { + while ((s = status) >= 0) { + if(w != null && w.qlock < 0) + cancelIgnoringExceptions(this); + else if(!canBlock) { + if(p == null || p.tryCompensate(p.ctl)) + canBlock = true; + } + else { + if((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L + && U.compareAndSwapInt(this, STATUS, s, s + | SIGNAL)) { + synchronized (this) { + if(status >= 0) { + try { + wait(ms); + } + catch (InterruptedException ie) { + if(p == null) + interrupted = true; + } + } + else + notifyAll(); + } + } + if((s = status) < 0 || interrupted + || (ns = deadline - System.nanoTime()) <= 0L) + break; + } + } + } + finally { + if(p != null && canBlock) + p.incrementActiveCount(); + } + if(interrupted) + throw new InterruptedException(); + } + if((s &= DONE_MASK) != NORMAL) { + Throwable ex; + if(s == CANCELLED) + throw new CancellationException(); + if(s != EXCEPTIONAL) + throw new TimeoutException(); + if((ex = getThrowableException()) != null) + throw new ExecutionException(ex); + } + return getRawResult(); + } + + /** + * Joins this task, without returning its result or throwing its + * exception. This method may be useful when processing + * collections of tasks when some have been cancelled or otherwise + * known to have aborted. + */ + public final void quietlyJoin() { + doJoin(); + } + + /** + * Commences performing this task and awaits its completion if + * necessary, without returning its result or throwing its + * exception. + */ + public final void quietlyInvoke() { + doInvoke(); + } + + /** + * Possibly executes tasks until the pool hosting the current task + * {@link ForkJoinPool#isQuiescent is quiescent}. This method may + * be of use in designs in which many tasks are forked, but none + * are explicitly joined, instead executing them until all are + * processed. + */ + public static void helpQuiesce() { + Thread t; + if((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread wt = (ForkJoinWorkerThread) t; + wt.pool.helpQuiescePool(wt.workQueue); + } + else + ForkJoinPool.quiesceCommonPool(); + } + + /** + * Resets the internal bookkeeping state of this task, allowing a + * subsequent {@code fork}. This method allows repeated reuse of + * this task, but only if reuse occurs when this task has either + * never been forked, or has been forked, then completed and all + * outstanding joins of this task have also completed. Effects + * under any other usage conditions are not guaranteed. + * This method may be useful when executing + * pre-constructed trees of subtasks in loops. + * + *

+ * Upon completion of this method, {@code isDone()} reports {@code false}, + * and {@code getException()} reports {@code null}. However, the value + * returned by {@code getRawResult} is unaffected. To clear this value, you + * can invoke {@code setRawResult(null)}. + */ + public void reinitialize() { + if((status & DONE_MASK) == EXCEPTIONAL) + clearExceptionalCompletion(); + else + status = 0; + } + + /** + * Returns the pool hosting the current task execution, or null + * if this task is executing outside of any ForkJoinPool. + * + * @see #inForkJoinPool + * @return the pool, or {@code null} if none + */ + public static ForkJoinPool getPool() { + Thread t = Thread.currentThread(); + return (t instanceof ForkJoinWorkerThread) ? ((ForkJoinWorkerThread) t).pool + : null; + } + + /** + * Returns {@code true} if the current thread is a + * {@link ForkJoinWorkerThread} executing as a ForkJoinPool computation. + * + * @return {@code true} if the current thread is a + * {@link ForkJoinWorkerThread} executing as a ForkJoinPool + * computation, + * or {@code false} otherwise + */ + public static boolean inForkJoinPool() { + return Thread.currentThread() instanceof ForkJoinWorkerThread; + } + + /** + * Tries to unschedule this task for execution. This method will + * typically (but is not guaranteed to) succeed if this task is + * the most recently forked task by the current thread, and has + * not commenced executing in another thread. This method may be + * useful when arranging alternative local processing of tasks + * that could have been, but were not, stolen. + * + * @return {@code true} if unforked + */ + public boolean tryUnfork() { + Thread t; + return (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? ((ForkJoinWorkerThread) t).workQueue + .tryUnpush(this) : ForkJoinPool.common.tryExternalUnpush(this)); + } + + /** + * Returns an estimate of the number of tasks that have been + * forked by the current worker thread but not yet executed. This + * value may be useful for heuristic decisions about whether to + * fork other tasks. + * + * @return the number of tasks + */ + public static int getQueuedTaskCount() { + Thread t; + ForkJoinPool.WorkQueue q; + if((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) + q = ((ForkJoinWorkerThread) t).workQueue; + else + q = ForkJoinPool.commonSubmitterQueue(); + return (q == null) ? 0 : q.queueSize(); + } + + /** + * Returns an estimate of how many more locally queued tasks are + * held by the current worker thread than there are other worker + * threads that might steal them, or zero if this thread is not + * operating in a ForkJoinPool. This value may be useful for + * heuristic decisions about whether to fork other tasks. In many + * usages of ForkJoinTasks, at steady state, each worker should + * aim to maintain a small constant surplus (for example, 3) of + * tasks, and to process computations locally if this threshold is + * exceeded. + * + * @return the surplus number of tasks, which may be negative + */ + public static int getSurplusQueuedTaskCount() { + return ForkJoinPool.getSurplusQueuedTaskCount(); + } + + // Extension methods + + /** + * Returns the result that would be returned by {@link #join}, even + * if this task completed abnormally, or {@code null} if this task + * is not known to have been completed. This method is designed + * to aid debugging, as well as to support extensions. Its use in + * any other context is discouraged. + * + * @return the result, or {@code null} if not completed + */ + public abstract V getRawResult(); + + /** + * Forces the given value to be returned as a result. This method + * is designed to support extensions, and should not in general be + * called otherwise. + * + * @param value the value + */ + protected abstract void setRawResult(V value); + + /** + * Immediately performs the base action of this task and returns + * true if, upon return from this method, this task is guaranteed + * to have completed normally. This method may return false + * otherwise, to indicate that this task is not necessarily + * complete (or is not known to be complete), for example in + * asynchronous actions that require explicit invocations of + * completion methods. This method may also throw an (unchecked) + * exception to indicate abnormal exit. This method is designed to + * support extensions, and should not in general be called + * otherwise. + * + * @return {@code true} if this task is known to have completed normally + */ + protected abstract boolean exec(); + + /** + * Returns, but does not unschedule or execute, a task queued by + * the current thread but not yet executed, if one is immediately + * available. There is no guarantee that this task will actually + * be polled or executed next. Conversely, this method may return + * null even if a task exists but cannot be accessed without + * contention with other threads. This method is designed + * primarily to support extensions, and is unlikely to be useful + * otherwise. + * + * @return the next task, or {@code null} if none are available + */ + protected static ForkJoinTask peekNextLocalTask() { + Thread t; + ForkJoinPool.WorkQueue q; + if((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) + q = ((ForkJoinWorkerThread) t).workQueue; + else + q = ForkJoinPool.commonSubmitterQueue(); + return (q == null) ? null : q.peek(); + } + + /** + * Unschedules and returns, without executing, the next task + * queued by the current thread but not yet executed, if the + * current thread is operating in a ForkJoinPool. This method is + * designed primarily to support extensions, and is unlikely to be + * useful otherwise. + * + * @return the next task, or {@code null} if none are available + */ + protected static ForkJoinTask pollNextLocalTask() { + Thread t; + return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? ((ForkJoinWorkerThread) t).workQueue + .nextLocalTask() : null; + } + + /** + * If the current thread is operating in a ForkJoinPool, + * unschedules and returns, without executing, the next task + * queued by the current thread but not yet executed, if one is + * available, or if not available, a task that was forked by some + * other thread, if available. Availability may be transient, so a + * {@code null} result does not necessarily imply quiescence of + * the pool this task is operating in. This method is designed + * primarily to support extensions, and is unlikely to be useful + * otherwise. + * + * @return a task, or {@code null} if none are available + */ + protected static ForkJoinTask pollTask() { + Thread t; + ForkJoinWorkerThread wt; + return ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ? (wt = (ForkJoinWorkerThread) t).pool + .nextTaskFor(wt.workQueue) : null; + } + + // tag operations + + /** + * Returns the tag for this task. + * + * @return the tag for this task + * @since 1.8 + */ + public final short getForkJoinTaskTag() { + return (short) status; + } + + /** + * Atomically sets the tag value for this task. + * + * @param tag the tag value + * @return the previous value of the tag + * @since 1.8 + */ + public final short setForkJoinTaskTag(short tag) { + for (int s;;) { + if(U.compareAndSwapInt(this, STATUS, s = status, (s & ~SMASK) + | (tag & SMASK))) + return (short) s; + } + } + + /** + * Atomically conditionally sets the tag value for this task. + * Among other applications, tags can be used as visit markers + * in tasks operating on graphs, as in methods that check: + * {@code if (task.compareAndSetForkJoinTaskTag((short)0, (short)1))} before + * processing, otherwise exiting because the node has + * already been visited. + * + * @param e the expected tag value + * @param tag the new tag value + * @return {@code true} if successful; i.e., the current value was + * equal to e and is now tag. + * @since 1.8 + */ + public final boolean compareAndSetForkJoinTaskTag(short e, short tag) { + for (int s;;) { + if((short) (s = status) != e) + return false; + if(U.compareAndSwapInt(this, STATUS, s, (s & ~SMASK) + | (tag & SMASK))) + return true; + } + } + + /** + * Adaptor for Runnables. This implements RunnableFuture + * to be compliant with AbstractExecutorService constraints + * when used in ForkJoinPool. + */ + static final class AdaptedRunnable extends ForkJoinTask implements + RunnableFuture { + final Runnable runnable; + T result; + + AdaptedRunnable(Runnable runnable, T result) { + if(runnable == null) + throw new NullPointerException(); + this.runnable = runnable; + this.result = result; // OK to set this even before completion + } + + public final T getRawResult() { + return result; + } + + public final void setRawResult(T v) { + result = v; + } + + public final boolean exec() { + runnable.run(); + return true; + } + + public final void run() { + invoke(); + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + /** + * Adaptor for Runnables without results + */ + static final class AdaptedRunnableAction extends ForkJoinTask implements + RunnableFuture { + final Runnable runnable; + + AdaptedRunnableAction(Runnable runnable) { + if(runnable == null) + throw new NullPointerException(); + this.runnable = runnable; + } + + public final Void getRawResult() { + return null; + } + + public final void setRawResult(Void v) {} + + public final boolean exec() { + runnable.run(); + return true; + } + + public final void run() { + invoke(); + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + /** + * Adaptor for Runnables in which failure forces worker exception + */ + static final class RunnableExecuteAction extends ForkJoinTask { + final Runnable runnable; + + RunnableExecuteAction(Runnable runnable) { + if(runnable == null) + throw new NullPointerException(); + this.runnable = runnable; + } + + public final Void getRawResult() { + return null; + } + + public final void setRawResult(Void v) {} + + public final boolean exec() { + runnable.run(); + return true; + } + + void internalPropagateException(Throwable ex) { + rethrow(ex); // rethrow outside exec() catches. + } + + private static final long serialVersionUID = 5232453952276885070L; + } + + /** + * Adaptor for Callables + */ + static final class AdaptedCallable extends ForkJoinTask implements + RunnableFuture { + final Callable callable; + T result; + + AdaptedCallable(Callable callable) { + if(callable == null) + throw new NullPointerException(); + this.callable = callable; + } + + public final T getRawResult() { + return result; + } + + public final void setRawResult(T v) { + result = v; + } + + public final boolean exec() { + try { + result = callable.call(); + return true; + } + catch (Error err) { + throw err; + } + catch (RuntimeException rex) { + throw rex; + } + catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public final void run() { + invoke(); + } + + private static final long serialVersionUID = 2838392045355241008L; + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code run} method + * of the given {@code Runnable} as its action, and returns + * a null result upon {@link #join}. + * + * @param runnable the runnable action + * @return the task + */ + public static ForkJoinTask adapt(Runnable runnable) { + return new AdaptedRunnableAction(runnable); + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code run} method + * of the given {@code Runnable} as its action, and returns + * the given result upon {@link #join}. + * + * @param runnable the runnable action + * @param result the result upon completion + * @param the type of the result + * @return the task + */ + public static ForkJoinTask adapt(Runnable runnable, T result) { + return new AdaptedRunnable(runnable, result); + } + + /** + * Returns a new {@code ForkJoinTask} that performs the {@code call} method + * of the given {@code Callable} as its action, and returns + * its result upon {@link #join}, translating any checked exceptions + * encountered into {@code RuntimeException}. + * + * @param callable the callable action + * @param the type of the callable's result + * @return the task + */ + public static ForkJoinTask adapt(Callable callable) { + return new AdaptedCallable(callable); + } + + // Serialization support + + private static final long serialVersionUID = -7721805057305804111L; + + /** + * Saves this task to a stream (that is, serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @serialData the current run status and the exception thrown + * during execution, or {@code null} if none + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + s.writeObject(getException()); + } + + /** + * Reconstitutes this task from a stream (that is, deserializes it). + * + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + Object ex = s.readObject(); + if(ex != null) + setExceptionalCompletion((Throwable) ex); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long STATUS; + + static { + exceptionTableLock = new ReentrantLock(); + exceptionTableRefQueue = new ReferenceQueue(); + exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY]; + try { + U = getUnsafe(); + Class k = ForkJoinTask.class; + STATUS = U.objectFieldOffset(k.getDeclaredField("status")); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinWorkerThread.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinWorkerThread.java new file mode 100644 index 0000000000..8fe72feac2 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinWorkerThread.java @@ -0,0 +1,127 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +/** + * A thread managed by a {@link ForkJoinPool}, which executes + * {@link ForkJoinTask}s. + * This class is subclassable solely for the sake of adding + * functionality -- there are no overridable methods dealing with + * scheduling or execution. However, you can override initialization + * and termination methods surrounding the main task processing loop. + * If you do create such a subclass, you will also need to supply a + * custom {@link ForkJoinPool.ForkJoinWorkerThreadFactory} to + * {@linkplain ForkJoinPool#ForkJoinPool use it} in a {@code ForkJoinPool}. + * + * @since 1.7 + * @author Doug Lea + */ +public class ForkJoinWorkerThread extends Thread { + /* + * ForkJoinWorkerThreads are managed by ForkJoinPools and perform + * ForkJoinTasks. For explanation, see the internal documentation + * of class ForkJoinPool. + * + * This class just maintains links to its pool and WorkQueue. The + * pool field is set immediately upon construction, but the + * workQueue field is not set until a call to registerWorker + * completes. This leads to a visibility race, that is tolerated + * by requiring that the workQueue field is only accessed by the + * owning thread. + */ + + final ForkJoinPool pool; // the pool this thread works in + final ForkJoinPool.WorkQueue workQueue; // work-stealing mechanics + + /** + * Creates a ForkJoinWorkerThread operating in the given pool. + * + * @param pool the pool this thread works in + * @throws NullPointerException if pool is null + */ + protected ForkJoinWorkerThread(ForkJoinPool pool) { + // Use a placeholder until a useful name can be set in registerWorker + super("aForkJoinWorkerThread"); + this.pool = pool; + this.workQueue = pool.registerWorker(this); + } + + /** + * Returns the pool hosting this thread. + * + * @return the pool + */ + public ForkJoinPool getPool() { + return pool; + } + + /** + * Returns the unique index number of this thread in its pool. + * The returned value ranges from zero to the maximum number of + * threads (minus one) that may exist in the pool, and does not + * change during the lifetime of the thread. This method may be + * useful for applications that track status or collect results + * per-worker-thread rather than per-task. + * + * @return the index number + */ + public int getPoolIndex() { + return workQueue.poolIndex >>> 1; // ignore odd/even tag bit + } + + /** + * Initializes internal state after construction but before + * processing any tasks. If you override this method, you must + * invoke {@code super.onStart()} at the beginning of the method. + * Initialization requires care: Most fields must have legal + * default values, to ensure that attempted accesses from other + * threads work correctly even before this thread starts + * processing tasks. + */ + protected void onStart() {} + + /** + * Performs cleanup associated with termination of this worker + * thread. If you override this method, you must invoke + * {@code super.onTermination} at the end of the overridden method. + * + * @param exception the exception causing this thread to abort due + * to an unrecoverable error, or {@code null} if completed + * normally + */ + protected void onTermination(Throwable exception) {} + + /** + * This method is required to be public, but should never be + * called explicitly. It performs the main run loop to execute + * {@link ForkJoinTask}s. + */ + public void run() { + Throwable exception = null; + try { + onStart(); + pool.runWorker(workQueue); + } + catch (Throwable ex) { + exception = ex; + } + finally { + try { + onTermination(exception); + } + catch (Throwable ex) { + if(exception == null) + exception = ex; + } + finally { + pool.deregisterWorker(this, exception); + } + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdder.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdder.java new file mode 100644 index 0000000000..a190f7e013 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdder.java @@ -0,0 +1,210 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicLong; + +/** + * One or more variables that together maintain an initially zero {@code long} + * sum. When updates (method {@link #add}) are contended + * across threads, the set of variables may grow dynamically to reduce + * contention. Method {@link #sum} (or, equivalently, {@link #longValue}) + * returns the current total combined across the + * variables maintaining the sum. + * + *

+ * This class is usually preferable to {@link AtomicLong} when multiple threads + * update a common sum that is used for purposes such as collecting statistics, + * not for fine-grained synchronization control. Under low update contention, + * the two classes have similar characteristics. But under high contention, + * expected throughput of this class is significantly higher, at the expense of + * higher space consumption. + * + *

+ * This class extends {@link Number}, but does not define methods such + * as {@code equals}, {@code hashCode} and {@code compareTo} because instances + * are expected to be mutated, and so are not useful as collection keys. + * + *

+ * org.cinchapi.vendor.jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of plus for use in retryUpdate + */ + final long fn(long v, long x) { + return v + x; + } + + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() {} + + /** + * Adds the given value. + * + * @param x the value to add + */ + public void add(long x) { + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + if((hc = threadHashCode.get()) == null || as == null + || (n = as.length) < 1 || (a = as[(n - 1) & hc[0]]) == null + || !(uncontended = a.cas(v = a.value, v + x))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Equivalent to {@code add(1)}. + */ + public void increment() { + add(1L); + } + + /** + * Equivalent to {@code add(-1)}. + */ + public void decrement() { + add(-1L); + } + + /** + * Returns the current sum. The returned value is NOT an + * atomic snapshot; invocation in the absence of concurrent + * updates returns an accurate result, but concurrent updates that + * occur while the sum is being calculated might not be + * incorporated. + * + * @return the sum + */ + public long sum() { + long sum = base; + Cell[] as = cells; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) + sum += a.value; + } + } + return sum; + } + + /** + * Resets variables maintaining the sum to zero. This method may + * be a useful alternative to creating a new adder, but is only + * effective if there are no concurrent updates. Because this + * method is intrinsically racy, it should only be used when it is + * known that no threads are concurrently updating. + */ + public void reset() { + internalReset(0L); + } + + /** + * Equivalent in effect to {@link #sum} followed by {@link #reset}. This + * method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is not + * guaranteed to be the final value occurring before + * the reset. + * + * @return the sum + */ + public long sumThenReset() { + long sum = base; + Cell[] as = cells; + base = 0L; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + /** + * Returns the String representation of the {@link #sum}. + * + * @return the String representation of the {@link #sum} + */ + public String toString() { + return Long.toString(sum()); + } + + /** + * Equivalent to {@link #sum}. + * + * @return the sum + */ + public long longValue() { + return sum(); + } + + /** + * Returns the {@link #sum} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int) sum(); + } + + /** + * Returns the {@link #sum} as a {@code float} after a widening primitive + * conversion. + */ + public float floatValue() { + return (float) sum(); + } + + /** + * Returns the {@link #sum} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double) sum(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeLong(sum()); + } + + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java new file mode 100644 index 0000000000..0120671f8f --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java @@ -0,0 +1,203 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +package org.cinchapi.vendor.jsr166e; + +import org.cinchapi.vendor.jsr166e.LongAdder; +import org.cinchapi.vendor.org.cinchapi.vendor.jsr166e.ConcurrentHashMapV8; + +import java.util.Map; +import java.util.Set; +import java.io.Serializable; + +/** + * A keyed table of adders, that may be useful in computing frequency + * counts and histograms, or may be used as a form of multiset. A + * {@link LongAdder} is associated with each key. Keys are added to + * the table implicitly upon any attempt to update, or may be added + * explicitly using method {@link #install}. + * + *

+ * org.cinchapi.vendor.jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongAdderTable implements Serializable { + /** Relies on default serialization */ + private static final long serialVersionUID = 7249369246863182397L; + + /** The underlying map */ + private final ConcurrentHashMapV8 map; + + static final class CreateAdder implements + ConcurrentHashMapV8.Fun { + public LongAdder apply(Object unused) { + return new LongAdder(); + } + } + + private static final CreateAdder createAdder = new CreateAdder(); + + /** + * Creates a new empty table. + */ + public LongAdderTable() { + map = new ConcurrentHashMapV8(); + } + + /** + * If the given key does not already exist in the table, inserts + * the key with initial sum of zero; in either case returning the + * adder associated with this key. + * + * @param key the key + * @return the adder associated with the key + */ + public LongAdder install(K key) { + return map.computeIfAbsent(key, createAdder); + } + + /** + * Adds the given value to the sum associated with the given + * key. If the key does not already exist in the table, it is + * inserted. + * + * @param key the key + * @param x the value to add + */ + public void add(K key, long x) { + map.computeIfAbsent(key, createAdder).add(x); + } + + /** + * Increments the sum associated with the given key. If the key + * does not already exist in the table, it is inserted. + * + * @param key the key + */ + public void increment(K key) { + add(key, 1L); + } + + /** + * Decrements the sum associated with the given key. If the key + * does not already exist in the table, it is inserted. + * + * @param key the key + */ + public void decrement(K key) { + add(key, -1L); + } + + /** + * Returns the sum associated with the given key, or zero if the + * key does not currently exist in the table. + * + * @param key the key + * @return the sum associated with the key, or zero if the key is + * not in the table + */ + public long sum(K key) { + LongAdder a = map.get(key); + return a == null ? 0L : a.sum(); + } + + /** + * Resets the sum associated with the given key to zero if the key + * exists in the table. This method does NOT add or + * remove the key from the table (see {@link #remove}). + * + * @param key the key + */ + public void reset(K key) { + LongAdder a = map.get(key); + if(a != null) + a.reset(); + } + + /** + * Resets the sum associated with the given key to zero if the key + * exists in the table. This method does NOT add or + * remove the key from the table (see {@link #remove}). + * + * @param key the key + * @return the previous sum, or zero if the key is not + * in the table + */ + public long sumThenReset(K key) { + LongAdder a = map.get(key); + return a == null ? 0L : a.sumThenReset(); + } + + /** + * Returns the sum totalled across all keys. + * + * @return the sum totalled across all keys + */ + public long sumAll() { + long sum = 0L; + for (LongAdder a : map.values()) + sum += a.sum(); + return sum; + } + + /** + * Resets the sum associated with each key to zero. + */ + public void resetAll() { + for (LongAdder a : map.values()) + a.reset(); + } + + /** + * Totals, then resets, the sums associated with all keys. + * + * @return the sum totalled across all keys + */ + public long sumThenResetAll() { + long sum = 0L; + for (LongAdder a : map.values()) + sum += a.sumThenReset(); + return sum; + } + + /** + * Removes the given key from the table. + * + * @param key the key + */ + public void remove(K key) { + map.remove(key); + } + + /** + * Removes all keys from the table. + */ + public void removeAll() { + map.clear(); + } + + /** + * Returns the current set of keys. + * + * @return the current set of keys + */ + public Set keySet() { + return map.keySet(); + } + + /** + * Returns the current set of key-value mappings. + * + * @return the current set of key-value mappings + */ + public Set> entrySet() { + return map.entrySet(); + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongMaxUpdater.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongMaxUpdater.java new file mode 100644 index 0000000000..137ce68919 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongMaxUpdater.java @@ -0,0 +1,193 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * One or more variables that together maintain a running {@code long} maximum + * with initial value {@code Long.MIN_VALUE}. When updates + * (method {@link #update}) are contended across threads, the set of + * variables may grow dynamically to reduce contention. Method {@link #max} (or, + * equivalently, {@link #longValue}) returns the current + * maximum across the variables maintaining updates. + * + *

+ * This class extends {@link Number}, but does not define methods such + * as {@code equals}, {@code hashCode} and {@code compareTo} because instances + * are expected to be mutated, and so are not useful as collection keys. + * + *

+ * org.cinchapi.vendor.jsr166e note: This class is targeted to be placed in + * java.util.concurrent.atomic. + * + * @since 1.8 + * @author Doug Lea + */ +public class LongMaxUpdater extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + /** + * Version of max for use in retryUpdate + */ + final long fn(long v, long x) { + return v > x ? v : x; + } + + /** + * Creates a new instance with initial maximum of {@code Long.MIN_VALUE}. + */ + public LongMaxUpdater() { + base = Long.MIN_VALUE; + } + + /** + * Updates the maximum to be at least the given value. + * + * @param x the value to update + */ + public void update(long x) { + Cell[] as; + long b, v; + int[] hc; + Cell a; + int n; + if((as = cells) != null || (b = base) < x && !casBase(b, x)) { + boolean uncontended = true; + if((hc = threadHashCode.get()) == null || as == null + || (n = as.length) < 1 || (a = as[(n - 1) & hc[0]]) == null + || ((v = a.value) < x && !(uncontended = a.cas(v, x)))) + retryUpdate(x, hc, uncontended); + } + } + + /** + * Returns the current maximum. The returned value is NOT an atomic + * snapshot; invocation in the absence of + * concurrent updates returns an accurate result, but concurrent + * updates that occur while the value is being calculated might + * not be incorporated. + * + * @return the maximum + */ + public long max() { + Cell[] as = cells; + long max = base; + if(as != null) { + int n = as.length; + long v; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null && (v = a.value) > max) + max = v; + } + } + return max; + } + + /** + * Resets variables maintaining updates to {@code Long.MIN_VALUE}. + * This method may be a useful alternative to creating a new + * updater, but is only effective if there are no concurrent + * updates. Because this method is intrinsically racy, it should + * only be used when it is known that no threads are concurrently + * updating. + */ + public void reset() { + internalReset(Long.MIN_VALUE); + } + + /** + * Equivalent in effect to {@link #max} followed by {@link #reset}. This + * method may apply for example during quiescent + * points between multithreaded computations. If there are + * updates concurrent with this method, the returned value is not + * guaranteed to be the final value occurring before + * the reset. + * + * @return the maximum + */ + public long maxThenReset() { + Cell[] as = cells; + long max = base; + base = Long.MIN_VALUE; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) { + long v = a.value; + a.value = Long.MIN_VALUE; + if(v > max) + max = v; + } + } + } + return max; + } + + /** + * Returns the String representation of the {@link #max}. + * + * @return the String representation of the {@link #max} + */ + public String toString() { + return Long.toString(max()); + } + + /** + * Equivalent to {@link #max}. + * + * @return the maximum + */ + public long longValue() { + return max(); + } + + /** + * Returns the {@link #max} as an {@code int} after a narrowing + * primitive conversion. + */ + public int intValue() { + return (int) max(); + } + + /** + * Returns the {@link #max} as a {@code float} after a widening primitive + * conversion. + */ + public float floatValue() { + return (float) max(); + } + + /** + * Returns the {@link #max} as a {@code double} after a widening + * primitive conversion. + */ + public double doubleValue() { + return (double) max(); + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + s.writeLong(max()); + } + + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException { + s.defaultReadObject(); + busy = 0; + cells = null; + base = s.readLong(); + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveAction.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveAction.java new file mode 100644 index 0000000000..a3b596a2be --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveAction.java @@ -0,0 +1,194 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +/** + * A recursive resultless {@link ForkJoinTask}. This class + * establishes conventions to parameterize resultless actions as {@code Void} + * {@code ForkJoinTask}s. Because {@code null} is the + * only valid value of type {@code Void}, methods such as {@code join} always + * return {@code null} upon completion. + * + *

+ * Sample Usages. Here is a simple but complete ForkJoin sort that sorts + * a given {@code long[]} array: + * + *

+ * {
+ *     @code
+ *     static class SortTask extends RecursiveAction {
+ *         final long[] array;
+ *         final int lo, hi;
+ * 
+ *         SortTask(long[] array, int lo, int hi) {
+ *             this.array = array;
+ *             this.lo = lo;
+ *             this.hi = hi;
+ *         }
+ * 
+ *         SortTask(long[] array) {
+ *             this(array, 0, array.length);
+ *         }
+ * 
+ *         protected void compute() {
+ *             if(hi - lo < THRESHOLD)
+ *                 sortSequentially(lo, hi);
+ *             else {
+ *                 int mid = (lo + hi) >>> 1;
+ *                 invokeAll(new SortTask(array, lo, mid), new SortTask(array,
+ *                         mid, hi));
+ *                 merge(lo, mid, hi);
+ *             }
+ *         }
+ * 
+ *         // implementation details follow:
+ *         static final int THRESHOLD = 1000;
+ * 
+ *         void sortSequentially(int lo, int hi) {
+ *             Arrays.sort(array, lo, hi);
+ *         }
+ * 
+ *         void merge(int lo, int mid, int hi) {
+ *             long[] buf = Arrays.copyOfRange(array, lo, mid);
+ *             for (int i = 0, j = lo, k = mid; i < buf.length; j++)
+ *                 array[j] = (k == hi || buf[i] < array[k]) ? buf[i++]
+ *                         : array[k++];
+ *         }
+ *     }
+ * }
+ * 
+ * + * You could then sort {@code anArray} by creating {@code new + * SortTask(anArray)} and invoking it in a ForkJoinPool. As a more concrete + * simple example, the following task increments each element of an array: + * + *
+ * {
+ *     @code
+ *     class IncrementTask extends RecursiveAction {
+ *         final long[] array;
+ *         final int lo, hi;
+ * 
+ *         IncrementTask(long[] array, int lo, int hi) {
+ *             this.array = array;
+ *             this.lo = lo;
+ *             this.hi = hi;
+ *         }
+ * 
+ *         protected void compute() {
+ *             if(hi - lo < THRESHOLD) {
+ *                 for (int i = lo; i < hi; ++i)
+ *                     array[i]++;
+ *             }
+ *             else {
+ *                 int mid = (lo + hi) >>> 1;
+ *                 invokeAll(new IncrementTask(array, lo, mid), new IncrementTask(
+ *                         array, mid, hi));
+ *             }
+ *         }
+ *     }
+ * }
+ * 
+ * + *

+ * The following example illustrates some refinements and idioms that may lead + * to better performance: RecursiveActions need not be fully recursive, so long + * as they maintain the basic divide-and-conquer approach. Here is a class that + * sums the squares of each element of a double array, by subdividing out only + * the right-hand-sides of repeated divisions by two, and keeping track of them + * with a chain of {@code next} references. It uses a dynamic threshold based on + * method {@code getSurplusQueuedTaskCount}, but counterbalances potential + * excess partitioning by directly performing leaf actions on unstolen tasks + * rather than further subdividing. + * + *

+ * {@code
+ * double sumOfSquares(ForkJoinPool pool, double[] array) {
+ *   int n = array.length;
+ *   Applyer a = new Applyer(array, 0, n, null);
+ *   pool.invoke(a);
+ *   return a.result;
+ * }
+ * 
+ * class Applyer extends RecursiveAction {
+ *   final double[] array;
+ *   final int lo, hi;
+ *   double result;
+ *   Applyer next; // keeps track of right-hand-side tasks
+ *   Applyer(double[] array, int lo, int hi, Applyer next) {
+ *     this.array = array; this.lo = lo; this.hi = hi;
+ *     this.next = next;
+ *   }
+ * 
+ *   double atLeaf(int l, int h) {
+ *     double sum = 0;
+ *     for (int i = l; i < h; ++i) // perform leftmost base step
+ *       sum += array[i] * array[i];
+ *     return sum;
+ *   }
+ * 
+ *   protected void compute() {
+ *     int l = lo;
+ *     int h = hi;
+ *     Applyer right = null;
+ *     while (h - l > 1 && getSurplusQueuedTaskCount() <= 3) {
+ *       int mid = (l + h) >>> 1;
+ *       right = new Applyer(array, mid, h, right);
+ *       right.fork();
+ *       h = mid;
+ *     }
+ *     double sum = atLeaf(l, h);
+ *     while (right != null) {
+ *       if (right.tryUnfork()) // directly calculate if not stolen
+ *         sum += right.atLeaf(right.lo, right.hi);
+ *       else {
+ *         right.join();
+ *         sum += right.result;
+ *       }
+ *       right = right.next;
+ *     }
+ *     result = sum;
+ *   }
+ * }}
+ * 
+ * + * @since 1.7 + * @author Doug Lea + */ +public abstract class RecursiveAction extends ForkJoinTask { + private static final long serialVersionUID = 5232453952276485070L; + + /** + * The main computation performed by this task. + */ + protected abstract void compute(); + + /** + * Always returns {@code null}. + * + * @return {@code null} always + */ + public final Void getRawResult() { + return null; + } + + /** + * Requires null completion value. + */ + protected final void setRawResult(Void mustBeNull) {} + + /** + * Implements execution conventions for RecursiveActions. + */ + protected final boolean exec() { + compute(); + return true; + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveTask.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveTask.java new file mode 100644 index 0000000000..c1516a6e67 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/RecursiveTask.java @@ -0,0 +1,80 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +/** + * A recursive result-bearing {@link ForkJoinTask}. + * + *

+ * For a classic example, here is a task computing Fibonacci numbers: + * + *

+ * {
+ *     @code
+ *     class Fibonacci extends RecursiveTask<Integer> {
+ *         final int n;
+ * 
+ *         Fibonacci(int n) {
+ *             this.n = n;
+ *         }
+ * 
+ *         protected Integer compute() {
+ *             if(n <= 1)
+ *                 return n;
+ *             Fibonacci f1 = new Fibonacci(n - 1);
+ *             f1.fork();
+ *             Fibonacci f2 = new Fibonacci(n - 2);
+ *             return f2.compute() + f1.join();
+ *         }
+ *     }
+ * }
+ * 
+ * + * However, besides being a dumb way to compute Fibonacci functions (there is a + * simple fast linear algorithm that you'd use in practice), this is likely to + * perform poorly because the smallest subtasks are too small to be worthwhile + * splitting up. Instead, as is the case for nearly all fork/join applications, + * you'd pick some minimum granularity size (for example 10 here) for which you + * always sequentially solve rather than subdividing. + * + * @since 1.7 + * @author Doug Lea + */ +public abstract class RecursiveTask extends ForkJoinTask { + private static final long serialVersionUID = 5232453952276485270L; + + /** + * The result of the computation. + */ + V result; + + /** + * The main computation performed by this task. + * + * @return the result of the computation + */ + protected abstract V compute(); + + public final V getRawResult() { + return result; + } + + protected final void setRawResult(V value) { + result = value; + } + + /** + * Implements execution conventions for RecursiveTask. + */ + protected final boolean exec() { + result = compute(); + return true; + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/StampedLock.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/StampedLock.java new file mode 100644 index 0000000000..089436ad26 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/StampedLock.java @@ -0,0 +1,1462 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * A capability-based lock with three modes for controlling read/write + * access. The state of a StampedLock consists of a version and mode. + * Lock acquisition methods return a stamp that represents and + * controls access with respect to a lock state; "try" versions of + * these methods may instead return the special value zero to + * represent failure to acquire access. Lock release and conversion + * methods require stamps as arguments, and fail if they do not match + * the state of the lock. The three modes are: + * + *
    + * + *
  • Writing. Method {@link #writeLock} possibly blocks waiting for + * exclusive access, returning a stamp that can be used in method + * {@link #unlockWrite} to release the lock. Untimed and timed versions of + * {@code tryWriteLock} are also provided. When the lock is held in write mode, + * no read locks may be obtained, and all optimistic read validations will fail. + *
  • + * + *
  • Reading. Method {@link #readLock} possibly blocks waiting for + * non-exclusive access, returning a stamp that can be used in method + * {@link #unlockRead} to release the lock. Untimed and timed versions of + * {@code tryReadLock} are also provided.
  • + * + *
  • Optimistic Reading. Method {@link #tryOptimisticRead} returns a + * non-zero stamp only if the lock is not currently held in write mode. Method + * {@link #validate} returns true if the lock has not been acquired in write + * mode since obtaining a given stamp. This mode can be thought of as an + * extremely weak version of a read-lock, that can be broken by a writer at any + * time. The use of optimistic mode for short read-only code segments often + * reduces contention and improves throughput. However, its use is inherently + * fragile. Optimistic read sections should only read fields and hold them in + * local variables for later use after validation. Fields read while in + * optimistic mode may be wildly inconsistent, so usage applies only when you + * are familiar enough with data representations to check consistency and/or + * repeatedly invoke method {@code validate()}. For example, such steps are + * typically required when first reading an object or array reference, and then + * accessing one of its fields, elements or methods.
  • + * + *
+ * + *

+ * This class also supports methods that conditionally provide conversions + * across the three modes. For example, method {@link #tryConvertToWriteLock} + * attempts to "upgrade" a mode, returning a valid write stamp if (1) already in + * writing mode (2) in reading mode and there are no other readers or (3) in + * optimistic mode and the lock is available. The forms of these methods are + * designed to help reduce some of the code bloat that otherwise occurs in + * retry-based designs. + * + *

+ * StampedLocks are designed for use as internal utilities in the development of + * thread-safe components. Their use relies on knowledge of the internal + * properties of the data, objects, and methods they are protecting. They are + * not reentrant, so locked bodies should not call other unknown methods that + * may try to re-acquire locks (although you may pass a stamp to other methods + * that can use or convert it). The use of read lock modes relies on the + * associated code sections being side-effect-free. Unvalidated optimistic read + * sections cannot call methods that are not known to tolerate potential + * inconsistencies. Stamps use finite representations, and are not + * cryptographically secure (i.e., a valid stamp may be guessable). Stamp values + * may recycle after (no sooner than) one year of continuous operation. A stamp + * held without use or validation for longer than this period may fail to + * validate correctly. StampedLocks are serializable, but always deserialize + * into initial unlocked state, so they are not useful for remote locking. + * + *

+ * The scheduling policy of StampedLock does not consistently prefer readers + * over writers or vice versa. All "try" methods are best-effort and do not + * necessarily conform to any scheduling or fairness policy. A zero return from + * any "try" method for acquiring or converting locks does not carry any + * information about the state of the lock; a subsequent invocation may succeed. + * + *

+ * Because it supports coordinated usage across multiple lock modes, this class + * does not directly implement the {@link Lock} or {@link ReadWriteLock} + * interfaces. However, a StampedLock may be viewed {@link #asReadLock()}, + * {@link #asWriteLock()}, or {@link #asReadWriteLock()} in applications + * requiring only the associated set of functionality. + * + *

+ * Sample Usage. The following illustrates some usage idioms in a class + * that maintains simple two-dimensional points. The sample code illustrates + * some try/catch conventions even though they are not strictly needed here + * because no exceptions can occur in their bodies.
+ * + *

+ * {
+ *     @code
+ *     class Point {
+ *         private double x, y;
+ *         private final StampedLock sl = new StampedLock();
+ * 
+ *         void move(double deltaX, double deltaY) { // an exclusively locked
+ *                                                   // method
+ *             long stamp = sl.writeLock();
+ *             try {
+ *                 x += deltaX;
+ *                 y += deltaY;
+ *             }
+ *             finally {
+ *                 sl.unlockWrite(stamp);
+ *             }
+ *         }
+ * 
+ *         double distanceFromOrigin() { // A read-only method
+ *             long stamp = sl.tryOptimisticRead();
+ *             double currentX = x, currentY = y;
+ *             if(!sl.validate(stamp)) {
+ *                 stamp = sl.readLock();
+ *                 try {
+ *                     currentX = x;
+ *                     currentY = y;
+ *                 }
+ *                 finally {
+ *                     sl.unlockRead(stamp);
+ *                 }
+ *             }
+ *             return Math.sqrt(currentX * currentX + currentY * currentY);
+ *         }
+ * 
+ *         void moveIfAtOrigin(double newX, double newY) { // upgrade
+ *             // Could instead start with optimistic, not read mode
+ *             long stamp = sl.readLock();
+ *             try {
+ *                 while (x == 0.0 && y == 0.0) {
+ *                     long ws = sl.tryConvertToWriteLock(stamp);
+ *                     if(ws != 0L) {
+ *                         stamp = ws;
+ *                         x = newX;
+ *                         y = newY;
+ *                         break;
+ *                     }
+ *                     else {
+ *                         sl.unlockRead(stamp);
+ *                         stamp = sl.writeLock();
+ *                     }
+ *                 }
+ *             }
+ *             finally {
+ *                 sl.unlock(stamp);
+ *             }
+ *         }
+ *     }
+ * }
+ * 
+ * + * @since 1.8 + * @author Doug Lea + */ +public class StampedLock implements java.io.Serializable { + /* + * Algorithmic notes: + * + * The design employs elements of Sequence locks + * (as used in linux kernels; see Lameter's + * http://www.lameter.com/gelato2005.pdf + * and elsewhere; see + * Boehm's http://www.hpl.hp.com/techreports/2012/HPL-2012-68.html) + * and Ordered RW locks (see Shirako et al + * http://dl.acm.org/citation.cfm?id=2312015) + * + * Conceptually, the primary state of the lock includes a sequence + * number that is odd when write-locked and even otherwise. + * However, this is offset by a reader count that is non-zero when + * read-locked. The read count is ignored when validating + * "optimistic" seqlock-reader-style stamps. Because we must use + * a small finite number of bits (currently 7) for readers, a + * supplementary reader overflow word is used when the number of + * readers exceeds the count field. We do this by treating the max + * reader count value (RBITS) as a spinlock protecting overflow + * updates. + * + * Waiters use a modified form of CLH lock used in + * AbstractQueuedSynchronizer (see its internal documentation for + * a fuller account), where each node is tagged (field mode) as + * either a reader or writer. Sets of waiting readers are grouped + * (linked) under a common node (field cowait) so act as a single + * node with respect to most CLH mechanics. By virtue of the + * queue structure, wait nodes need not actually carry sequence + * numbers; we know each is greater than its predecessor. This + * simplifies the scheduling policy to a mainly-FIFO scheme that + * incorporates elements of Phase-Fair locks (see Brandenburg & + * Anderson, especially http://www.cs.unc.edu/~bbb/diss/). In + * particular, we use the phase-fair anti-barging rule: If an + * incoming reader arrives while read lock is held but there is a + * queued writer, this incoming reader is queued. (This rule is + * responsible for some of the complexity of method acquireRead, + * but without it, the lock becomes highly unfair.) Method release + * does not (and sometimes cannot) itself wake up cowaiters. This + * is done by the primary thread, but helped by any other threads + * with nothing better to do in methods acquireRead and + * acquireWrite. + * + * These rules apply to threads actually queued. All tryLock forms + * opportunistically try to acquire locks regardless of preference + * rules, and so may "barge" their way in. Randomized spinning is + * used in the acquire methods to reduce (increasingly expensive) + * context switching while also avoiding sustained memory + * thrashing among many threads. We limit spins to the head of + * queue. A thread spin-waits up to SPINS times (where each + * iteration decreases spin count with 50% probability) before + * blocking. If, upon wakening it fails to obtain lock, and is + * still (or becomes) the first waiting thread (which indicates + * that some other thread barged and obtained lock), it escalates + * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of + * continually losing to barging threads. + * + * Nearly all of these mechanics are carried out in methods + * acquireWrite and acquireRead, that, as typical of such code, + * sprawl out because actions and retries rely on consistent sets + * of locally cached reads. + * + * As noted in Boehm's paper (above), sequence validation (mainly + * method validate()) requires stricter ordering rules than apply + * to normal volatile reads (of "state"). In the absence of (but + * continual hope for) explicit JVM support of intrinsics with + * double-sided reordering prohibition, or corresponding fence + * intrinsics, we for now uncomfortably rely on the fact that the + * Unsafe.getXVolatile intrinsic must have this property + * (syntactic volatile reads do not) for internal purposes anyway, + * even though it is not documented. + * + * The memory layout keeps lock state and queue pointers together + * (normally on the same cache line). This usually works well for + * read-mostly loads. In most other cases, the natural tendency of + * adaptive-spin CLH locks to reduce memory contention lessens + * motivation to further spread out contended locations, but might + * be subject to future improvements. + */ + + private static final long serialVersionUID = -6001602636862214147L; + + /** Number of processors, for spin control */ + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** Maximum number of retries before enqueuing on acquisition */ + private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0; + + /** Maximum number of retries before blocking at head on acquisition */ + private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0; + + /** Maximum number of retries before re-blocking */ + private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0; + + /** The period for yielding when waiting for overflow spinlock */ + private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1 + + /** The number of bits to use for reader count before overflowing */ + private static final int LG_READERS = 7; + + // Values for lock state and stamp operations + private static final long RUNIT = 1L; + private static final long WBIT = 1L << LG_READERS; + private static final long RBITS = WBIT - 1L; + private static final long RFULL = RBITS - 1L; + private static final long ABITS = RBITS | WBIT; + private static final long SBITS = ~RBITS; // note overlap with ABITS + + // Initial value for lock state; avoid failure value zero + private static final long ORIGIN = WBIT << 1; + + // Special value from cancelled acquire methods so caller can throw IE + private static final long INTERRUPTED = 1L; + + // Values for node status; order matters + private static final int WAITING = -1; + private static final int CANCELLED = 1; + + // Modes for nodes (int not boolean to allow arithmetic) + private static final int RMODE = 0; + private static final int WMODE = 1; + + /** Wait nodes */ + static final class WNode { + volatile WNode prev; + volatile WNode next; + volatile WNode cowait; // list of linked readers + volatile Thread thread; // non-null while possibly parked + volatile int status; // 0, WAITING, or CANCELLED + final int mode; // RMODE or WMODE + + WNode(int m, WNode p) { + mode = m; + prev = p; + } + } + + /** Head of CLH queue */ + private transient volatile WNode whead; + /** Tail (last) of CLH queue */ + private transient volatile WNode wtail; + + // views + transient ReadLockView readLockView; + transient WriteLockView writeLockView; + transient ReadWriteLockView readWriteLockView; + + /** Lock sequence/state */ + private transient volatile long state; + /** extra reader count when state read count saturated */ + private transient int readerOverflow; + + /** + * Creates a new lock, initially in unlocked state. + */ + public StampedLock() { + state = ORIGIN; + } + + /** + * Exclusively acquires the lock, blocking if necessary + * until available. + * + * @return a stamp that can be used to unlock or convert mode + */ + public long writeLock() { + long s, next; // bypass acquireWrite in fully unlocked case only + return ((((s = state) & ABITS) == 0L && U.compareAndSwapLong(this, + STATE, s, next = s + WBIT)) ? next : acquireWrite(false, 0L)); + } + + /** + * Exclusively acquires the lock if it is immediately available. + * + * @return a stamp that can be used to unlock or convert mode, + * or zero if the lock is not available + */ + public long tryWriteLock() { + long s, next; + return ((((s = state) & ABITS) == 0L && U.compareAndSwapLong(this, + STATE, s, next = s + WBIT)) ? next : 0L); + } + + /** + * Exclusively acquires the lock if it is available within the + * given time and the current thread has not been interrupted. + * Behavior under timeout and interruption matches that specified + * for method {@link Lock#tryLock(long,TimeUnit)}. + * + * @param time the maximum time to wait for the lock + * @param unit the time unit of the {@code time} argument + * @return a stamp that can be used to unlock or convert mode, + * or zero if the lock is not available + * @throws InterruptedException if the current thread is interrupted + * before acquiring the lock + */ + public long tryWriteLock(long time, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(time); + if(!Thread.interrupted()) { + long next, deadline; + if((next = tryWriteLock()) != 0L) + return next; + if(nanos <= 0L) + return 0L; + if((deadline = System.nanoTime() + nanos) == 0L) + deadline = 1L; + if((next = acquireWrite(true, deadline)) != INTERRUPTED) + return next; + } + throw new InterruptedException(); + } + + /** + * Exclusively acquires the lock, blocking if necessary + * until available or the current thread is interrupted. + * Behavior under interruption matches that specified + * for method {@link Lock#lockInterruptibly()}. + * + * @return a stamp that can be used to unlock or convert mode + * @throws InterruptedException if the current thread is interrupted + * before acquiring the lock + */ + public long writeLockInterruptibly() throws InterruptedException { + long next; + if(!Thread.interrupted() + && (next = acquireWrite(true, 0L)) != INTERRUPTED) + return next; + throw new InterruptedException(); + } + + /** + * Non-exclusively acquires the lock, blocking if necessary + * until available. + * + * @return a stamp that can be used to unlock or convert mode + */ + public long readLock() { + long s = state, next; // bypass acquireRead on common uncontended case + return ((whead == wtail && (s & ABITS) < RFULL && U.compareAndSwapLong( + this, STATE, s, next = s + RUNIT)) ? next : acquireRead(false, + 0L)); + } + + /** + * Non-exclusively acquires the lock if it is immediately available. + * + * @return a stamp that can be used to unlock or convert mode, + * or zero if the lock is not available + */ + public long tryReadLock() { + for (;;) { + long s, m, next; + if((m = (s = state) & ABITS) == WBIT) + return 0L; + else if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + return next; + } + else if((next = tryIncReaderOverflow(s)) != 0L) + return next; + } + } + + /** + * Non-exclusively acquires the lock if it is available within the + * given time and the current thread has not been interrupted. + * Behavior under timeout and interruption matches that specified + * for method {@link Lock#tryLock(long,TimeUnit)}. + * + * @param time the maximum time to wait for the lock + * @param unit the time unit of the {@code time} argument + * @return a stamp that can be used to unlock or convert mode, + * or zero if the lock is not available + * @throws InterruptedException if the current thread is interrupted + * before acquiring the lock + */ + public long tryReadLock(long time, TimeUnit unit) + throws InterruptedException { + long s, m, next, deadline; + long nanos = unit.toNanos(time); + if(!Thread.interrupted()) { + if((m = (s = state) & ABITS) != WBIT) { + if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + return next; + } + else if((next = tryIncReaderOverflow(s)) != 0L) + return next; + } + if(nanos <= 0L) + return 0L; + if((deadline = System.nanoTime() + nanos) == 0L) + deadline = 1L; + if((next = acquireRead(true, deadline)) != INTERRUPTED) + return next; + } + throw new InterruptedException(); + } + + /** + * Non-exclusively acquires the lock, blocking if necessary + * until available or the current thread is interrupted. + * Behavior under interruption matches that specified + * for method {@link Lock#lockInterruptibly()}. + * + * @return a stamp that can be used to unlock or convert mode + * @throws InterruptedException if the current thread is interrupted + * before acquiring the lock + */ + public long readLockInterruptibly() throws InterruptedException { + long next; + if(!Thread.interrupted() + && (next = acquireRead(true, 0L)) != INTERRUPTED) + return next; + throw new InterruptedException(); + } + + /** + * Returns a stamp that can later be validated, or zero + * if exclusively locked. + * + * @return a stamp, or zero if exclusively locked + */ + public long tryOptimisticRead() { + long s; + return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L; + } + + /** + * Returns true if the lock has not been exclusively acquired + * since issuance of the given stamp. Always returns false if the + * stamp is zero. Always returns true if the stamp represents a + * currently held lock. Invoking this method with a value not + * obtained from {@link #tryOptimisticRead} or a locking method + * for this lock has no defined effect or result. + * + * @param stamp a stamp + * @return {@code true} if the lock has not been exclusively acquired + * since issuance of the given stamp; else false + */ + public boolean validate(long stamp) { + // See above about current use of getLongVolatile here + return (stamp & SBITS) == (U.getLongVolatile(this, STATE) & SBITS); + } + + /** + * If the lock state matches the given stamp, releases the + * exclusive lock. + * + * @param stamp a stamp returned by a write-lock operation + * @throws IllegalMonitorStateException if the stamp does + * not match the current state of this lock + */ + public void unlockWrite(long stamp) { + WNode h; + if(state != stamp || (stamp & WBIT) == 0L) + throw new IllegalMonitorStateException(); + state = (stamp += WBIT) == 0L ? ORIGIN : stamp; + if((h = whead) != null && h.status != 0) + release(h); + } + + /** + * If the lock state matches the given stamp, releases the + * non-exclusive lock. + * + * @param stamp a stamp returned by a read-lock operation + * @throws IllegalMonitorStateException if the stamp does + * not match the current state of this lock + */ + public void unlockRead(long stamp) { + long s, m; + WNode h; + for (;;) { + if(((s = state) & SBITS) != (stamp & SBITS) + || (stamp & ABITS) == 0L || (m = s & ABITS) == 0L + || m == WBIT) + throw new IllegalMonitorStateException(); + if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if(m == RUNIT && (h = whead) != null && h.status != 0) + release(h); + break; + } + } + else if(tryDecReaderOverflow(s) != 0L) + break; + } + } + + /** + * If the lock state matches the given stamp, releases the + * corresponding mode of the lock. + * + * @param stamp a stamp returned by a lock operation + * @throws IllegalMonitorStateException if the stamp does + * not match the current state of this lock + */ + public void unlock(long stamp) { + long a = stamp & ABITS, m, s; + WNode h; + while (((s = state) & SBITS) == (stamp & SBITS)) { + if((m = s & ABITS) == 0L) + break; + else if(m == WBIT) { + if(a != m) + break; + state = (s += WBIT) == 0L ? ORIGIN : s; + if((h = whead) != null && h.status != 0) + release(h); + return; + } + else if(a == 0L || a >= WBIT) + break; + else if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if(m == RUNIT && (h = whead) != null && h.status != 0) + release(h); + return; + } + } + else if(tryDecReaderOverflow(s) != 0L) + return; + } + throw new IllegalMonitorStateException(); + } + + /** + * If the lock state matches the given stamp, performs one of + * the following actions. If the stamp represents holding a write + * lock, returns it. Or, if a read lock, if the write lock is + * available, releases the read lock and returns a write stamp. + * Or, if an optimistic read, returns a write stamp only if + * immediately available. This method returns zero in all other + * cases. + * + * @param stamp a stamp + * @return a valid write stamp, or zero on failure + */ + public long tryConvertToWriteLock(long stamp) { + long a = stamp & ABITS, m, s, next; + while (((s = state) & SBITS) == (stamp & SBITS)) { + if((m = s & ABITS) == 0L) { + if(a != 0L) + break; + if(U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) + return next; + } + else if(m == WBIT) { + if(a != m) + break; + return stamp; + } + else if(m == RUNIT && a != 0L) { + if(U.compareAndSwapLong(this, STATE, s, next = s - RUNIT + WBIT)) + return next; + } + else + break; + } + return 0L; + } + + /** + * If the lock state matches the given stamp, performs one of + * the following actions. If the stamp represents holding a write + * lock, releases it and obtains a read lock. Or, if a read lock, + * returns it. Or, if an optimistic read, acquires a read lock and + * returns a read stamp only if immediately available. This method + * returns zero in all other cases. + * + * @param stamp a stamp + * @return a valid read stamp, or zero on failure + */ + public long tryConvertToReadLock(long stamp) { + long a = stamp & ABITS, m, s, next; + WNode h; + while (((s = state) & SBITS) == (stamp & SBITS)) { + if((m = s & ABITS) == 0L) { + if(a != 0L) + break; + else if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + return next; + } + else if((next = tryIncReaderOverflow(s)) != 0L) + return next; + } + else if(m == WBIT) { + if(a != m) + break; + state = next = s + (WBIT + RUNIT); + if((h = whead) != null && h.status != 0) + release(h); + return next; + } + else if(a != 0L && a < WBIT) + return stamp; + else + break; + } + return 0L; + } + + /** + * If the lock state matches the given stamp then, if the stamp + * represents holding a lock, releases it and returns an + * observation stamp. Or, if an optimistic read, returns it if + * validated. This method returns zero in all other cases, and so + * may be useful as a form of "tryUnlock". + * + * @param stamp a stamp + * @return a valid optimistic read stamp, or zero on failure + */ + public long tryConvertToOptimisticRead(long stamp) { + long a = stamp & ABITS, m, s, next; + WNode h; + for (;;) { + s = U.getLongVolatile(this, STATE); // see above + if(((s = state) & SBITS) != (stamp & SBITS)) + break; + if((m = s & ABITS) == 0L) { + if(a != 0L) + break; + return s; + } + else if(m == WBIT) { + if(a != m) + break; + state = next = (s += WBIT) == 0L ? ORIGIN : s; + if((h = whead) != null && h.status != 0) + release(h); + return next; + } + else if(a == 0L || a >= WBIT) + break; + else if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) { + if(m == RUNIT && (h = whead) != null && h.status != 0) + release(h); + return next & SBITS; + } + } + else if((next = tryDecReaderOverflow(s)) != 0L) + return next & SBITS; + } + return 0L; + } + + /** + * Releases the write lock if it is held, without requiring a + * stamp value. This method may be useful for recovery after + * errors. + * + * @return {@code true} if the lock was held, else false + */ + public boolean tryUnlockWrite() { + long s; + WNode h; + if(((s = state) & WBIT) != 0L) { + state = (s += WBIT) == 0L ? ORIGIN : s; + if((h = whead) != null && h.status != 0) + release(h); + return true; + } + return false; + } + + /** + * Releases one hold of the read lock if it is held, without + * requiring a stamp value. This method may be useful for recovery + * after errors. + * + * @return {@code true} if the read lock was held, else false + */ + public boolean tryUnlockRead() { + long s, m; + WNode h; + while ((m = (s = state) & ABITS) != 0L && m < WBIT) { + if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if(m == RUNIT && (h = whead) != null && h.status != 0) + release(h); + return true; + } + } + else if(tryDecReaderOverflow(s) != 0L) + return true; + } + return false; + } + + // status monitoring methods + + /** + * Returns combined state-held and overflow read count for given + * state s. + */ + private int getReadLockCount(long s) { + long readers; + if((readers = s & RBITS) >= RFULL) + readers = RFULL + readerOverflow; + return (int) readers; + } + + /** + * Returns {@code true} if the lock is currently held exclusively. + * + * @return {@code true} if the lock is currently held exclusively + */ + public boolean isWriteLocked() { + return (state & WBIT) != 0L; + } + + /** + * Returns {@code true} if the lock is currently held non-exclusively. + * + * @return {@code true} if the lock is currently held non-exclusively + */ + public boolean isReadLocked() { + return (state & RBITS) != 0L; + } + + /** + * Queries the number of read locks held for this lock. This + * method is designed for use in monitoring system state, not for + * synchronization control. + * + * @return the number of read locks held + */ + public int getReadLockCount() { + return getReadLockCount(state); + } + + /** + * Returns a string identifying this lock, as well as its lock + * state. The state, in brackets, includes the String {@code "Unlocked"} or + * the String {@code "Write-locked"} or the String {@code "Read-locks:"} + * followed by the current number of + * read-locks held. + * + * @return a string identifying this lock, as well as its lock state + */ + public String toString() { + long s = state; + return super.toString() + + ((s & ABITS) == 0L ? "[Unlocked]" + : (s & WBIT) != 0L ? "[Write-locked]" : "[Read-locks:" + + getReadLockCount(s) + "]"); + } + + // views + + /** + * Returns a plain {@link Lock} view of this StampedLock in which + * the {@link Lock#lock} method is mapped to {@link #readLock}, + * and similarly for other methods. The returned Lock does not + * support a {@link Condition}; method {@link Lock#newCondition()} throws + * {@code UnsupportedOperationException}. + * + * @return the lock + */ + public Lock asReadLock() { + ReadLockView v; + return ((v = readLockView) != null ? v + : (readLockView = new ReadLockView())); + } + + /** + * Returns a plain {@link Lock} view of this StampedLock in which + * the {@link Lock#lock} method is mapped to {@link #writeLock}, + * and similarly for other methods. The returned Lock does not + * support a {@link Condition}; method {@link Lock#newCondition()} throws + * {@code UnsupportedOperationException}. + * + * @return the lock + */ + public Lock asWriteLock() { + WriteLockView v; + return ((v = writeLockView) != null ? v + : (writeLockView = new WriteLockView())); + } + + /** + * Returns a {@link ReadWriteLock} view of this StampedLock in + * which the {@link ReadWriteLock#readLock()} method is mapped to + * {@link #asReadLock()}, and {@link ReadWriteLock#writeLock()} to + * {@link #asWriteLock()}. + * + * @return the lock + */ + public ReadWriteLock asReadWriteLock() { + ReadWriteLockView v; + return ((v = readWriteLockView) != null ? v + : (readWriteLockView = new ReadWriteLockView())); + } + + // view classes + + final class ReadLockView implements Lock { + public void lock() { + readLock(); + } + + public void lockInterruptibly() throws InterruptedException { + readLockInterruptibly(); + } + + public boolean tryLock() { + return tryReadLock() != 0L; + } + + public boolean tryLock(long time, TimeUnit unit) + throws InterruptedException { + return tryReadLock(time, unit) != 0L; + } + + public void unlock() { + unstampedUnlockRead(); + } + + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + + final class WriteLockView implements Lock { + public void lock() { + writeLock(); + } + + public void lockInterruptibly() throws InterruptedException { + writeLockInterruptibly(); + } + + public boolean tryLock() { + return tryWriteLock() != 0L; + } + + public boolean tryLock(long time, TimeUnit unit) + throws InterruptedException { + return tryWriteLock(time, unit) != 0L; + } + + public void unlock() { + unstampedUnlockWrite(); + } + + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + + final class ReadWriteLockView implements ReadWriteLock { + public Lock readLock() { + return asReadLock(); + } + + public Lock writeLock() { + return asWriteLock(); + } + } + + // Unlock methods without stamp argument checks for view classes. + // Needed because view-class lock methods throw away stamps. + + final void unstampedUnlockWrite() { + WNode h; + long s; + if(((s = state) & WBIT) == 0L) + throw new IllegalMonitorStateException(); + state = (s += WBIT) == 0L ? ORIGIN : s; + if((h = whead) != null && h.status != 0) + release(h); + } + + final void unstampedUnlockRead() { + for (;;) { + long s, m; + WNode h; + if((m = (s = state) & ABITS) == 0L || m >= WBIT) + throw new IllegalMonitorStateException(); + else if(m < RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if(m == RUNIT && (h = whead) != null && h.status != 0) + release(h); + break; + } + } + else if(tryDecReaderOverflow(s) != 0L) + break; + } + } + + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + state = ORIGIN; // reset to unlocked state + } + + // internals + + /** + * Tries to increment readerOverflow by first setting state + * access bits value to RBITS, indicating hold of spinlock, + * then updating, then releasing. + * + * @param s a reader overflow stamp: (s & ABITS) >= RFULL + * @return new stamp on success, else zero + */ + private long tryIncReaderOverflow(long s) { + // assert (s & ABITS) >= RFULL; + if((s & ABITS) == RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s | RBITS)) { + ++readerOverflow; + state = s; + return s; + } + } + else if((ThreadLocalRandom.current().nextInt() & OVERFLOW_YIELD_RATE) == 0) + Thread.yield(); + return 0L; + } + + /** + * Tries to decrement readerOverflow. + * + * @param s a reader overflow stamp: (s & ABITS) >= RFULL + * @return new stamp on success, else zero + */ + private long tryDecReaderOverflow(long s) { + // assert (s & ABITS) >= RFULL; + if((s & ABITS) == RFULL) { + if(U.compareAndSwapLong(this, STATE, s, s | RBITS)) { + int r; + long next; + if((r = readerOverflow) > 0) { + readerOverflow = r - 1; + next = s; + } + else + next = s - RUNIT; + state = next; + return next; + } + } + else if((ThreadLocalRandom.current().nextInt() & OVERFLOW_YIELD_RATE) == 0) + Thread.yield(); + return 0L; + } + + /** + * Wakes up the successor of h (normally whead). This is normally + * just h.next, but may require traversal from wtail if next + * pointers are lagging. This may fail to wake up an acquiring + * thread when one or more have been cancelled, but the cancel + * methods themselves provide extra safeguards to ensure liveness. + */ + private void release(WNode h) { + if(h != null) { + WNode q; + Thread w; + U.compareAndSwapInt(h, WSTATUS, WAITING, 0); + if((q = h.next) == null || q.status == CANCELLED) { + for (WNode t = wtail; t != null && t != h; t = t.prev) + if(t.status <= 0) + q = t; + } + if(q != null && (w = q.thread) != null) + U.unpark(w); + } + } + + /** + * See above for explanation. + * + * @param interruptible true if should check interrupts and if so + * return INTERRUPTED + * @param deadline if nonzero, the System.nanoTime value to timeout + * at (and return zero) + * @return next state, or INTERRUPTED + */ + private long acquireWrite(boolean interruptible, long deadline) { + WNode node = null, p; + for (int spins = -1;;) { // spin while enqueuing + long m, s, ns; + if((m = (s = state) & ABITS) == 0L) { + if(U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) + return ns; + } + else if(spins < 0) + spins = (m == WBIT && wtail == whead) ? SPINS : 0; + else if(spins > 0) { + if(ThreadLocalRandom.current().nextInt() >= 0) + --spins; + } + else if((p = wtail) == null) { // initialize queue + WNode hd = new WNode(WMODE, null); + if(U.compareAndSwapObject(this, WHEAD, null, hd)) + wtail = hd; + } + else if(node == null) + node = new WNode(WMODE, p); + else if(node.prev != p) + node.prev = p; + else if(U.compareAndSwapObject(this, WTAIL, p, node)) { + p.next = node; + break; + } + } + + for (int spins = -1;;) { + WNode h, np, pp; + int ps; + if((h = whead) == p) { + if(spins < 0) + spins = HEAD_SPINS; + else if(spins < MAX_HEAD_SPINS) + spins <<= 1; + for (int k = spins;;) { // spin at head + long s, ns; + if(((s = state) & ABITS) == 0L) { + if(U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) { + whead = node; + node.prev = null; + return ns; + } + } + else if(ThreadLocalRandom.current().nextInt() >= 0 + && --k <= 0) + break; + } + } + else if(h != null) { // help release stale waiters + WNode c; + Thread w; + while ((c = h.cowait) != null) { + if(U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) + && (w = c.thread) != null) + U.unpark(w); + } + } + if(whead == h) { + if((np = node.prev) != p) { + if(np != null) + (p = np).next = node; // stale + } + else if((ps = p.status) == 0) + U.compareAndSwapInt(p, WSTATUS, 0, WAITING); + else if(ps == CANCELLED) { + if((pp = p.prev) != null) { + node.prev = pp; + pp.next = node; + } + } + else { + long time; // 0 argument to park means no timeout + if(deadline == 0L) + time = 0L; + else if((time = deadline - System.nanoTime()) <= 0L) + return cancelWaiter(node, node, false); + Thread wt = Thread.currentThread(); + U.putObject(wt, PARKBLOCKER, this); + node.thread = wt; + if(p.status < 0 && (p != h || (state & ABITS) != 0L) + && whead == h && node.prev == p) + U.park(false, time); // emulate LockSupport.park + node.thread = null; + U.putObject(wt, PARKBLOCKER, null); + if(interruptible && Thread.interrupted()) + return cancelWaiter(node, node, true); + } + } + } + } + + /** + * See above for explanation. + * + * @param interruptible true if should check interrupts and if so + * return INTERRUPTED + * @param deadline if nonzero, the System.nanoTime value to timeout + * at (and return zero) + * @return next state, or INTERRUPTED + */ + private long acquireRead(boolean interruptible, long deadline) { + WNode node = null, p; + for (int spins = -1;;) { + WNode h; + if((h = whead) == (p = wtail)) { + for (long m, s, ns;;) { + if((m = (s = state) & ABITS) < RFULL ? U + .compareAndSwapLong(this, STATE, s, ns = s + RUNIT) + : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) + return ns; + else if(m >= WBIT) { + if(spins > 0) { + if(ThreadLocalRandom.current().nextInt() >= 0) + --spins; + } + else { + if(spins == 0) { + WNode nh = whead, np = wtail; + if((nh == h && np == p) || (h = nh) != (p = np)) + break; + } + spins = SPINS; + } + } + } + } + if(p == null) { // initialize queue + WNode hd = new WNode(WMODE, null); + if(U.compareAndSwapObject(this, WHEAD, null, hd)) + wtail = hd; + } + else if(node == null) + node = new WNode(RMODE, p); + else if(h == p || p.mode != RMODE) { + if(node.prev != p) + node.prev = p; + else if(U.compareAndSwapObject(this, WTAIL, p, node)) { + p.next = node; + break; + } + } + else if(!U.compareAndSwapObject(p, WCOWAIT, node.cowait = p.cowait, + node)) + node.cowait = null; + else { + for (;;) { + WNode pp, c; + Thread w; + if((h = whead) != null && (c = h.cowait) != null + && U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) + && (w = c.thread) != null) // help release + U.unpark(w); + if(h == (pp = p.prev) || h == p || pp == null) { + long m, s, ns; + do { + if((m = (s = state) & ABITS) < RFULL ? U + .compareAndSwapLong(this, STATE, s, ns = s + + RUNIT) + : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) + return ns; + } + while (m < WBIT); + } + if(whead == h && p.prev == pp) { + long time; + if(pp == null || h == p || p.status > 0) { + node = null; // throw away + break; + } + if(deadline == 0L) + time = 0L; + else if((time = deadline - System.nanoTime()) <= 0L) + return cancelWaiter(node, p, false); + Thread wt = Thread.currentThread(); + U.putObject(wt, PARKBLOCKER, this); + node.thread = wt; + if((h != pp || (state & ABITS) == WBIT) && whead == h + && p.prev == pp) + U.park(false, time); + node.thread = null; + U.putObject(wt, PARKBLOCKER, null); + if(interruptible && Thread.interrupted()) + return cancelWaiter(node, p, true); + } + } + } + } + + for (int spins = -1;;) { + WNode h, np, pp; + int ps; + if((h = whead) == p) { + if(spins < 0) + spins = HEAD_SPINS; + else if(spins < MAX_HEAD_SPINS) + spins <<= 1; + for (int k = spins;;) { // spin at head + long m, s, ns; + if((m = (s = state) & ABITS) < RFULL ? U + .compareAndSwapLong(this, STATE, s, ns = s + RUNIT) + : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { + WNode c; + Thread w; + whead = node; + node.prev = null; + while ((c = node.cowait) != null) { + if(U.compareAndSwapObject(node, WCOWAIT, c, + c.cowait) && (w = c.thread) != null) + U.unpark(w); + } + return ns; + } + else if(m >= WBIT + && ThreadLocalRandom.current().nextInt() >= 0 + && --k <= 0) + break; + } + } + else if(h != null) { + WNode c; + Thread w; + while ((c = h.cowait) != null) { + if(U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) + && (w = c.thread) != null) + U.unpark(w); + } + } + if(whead == h) { + if((np = node.prev) != p) { + if(np != null) + (p = np).next = node; // stale + } + else if((ps = p.status) == 0) + U.compareAndSwapInt(p, WSTATUS, 0, WAITING); + else if(ps == CANCELLED) { + if((pp = p.prev) != null) { + node.prev = pp; + pp.next = node; + } + } + else { + long time; + if(deadline == 0L) + time = 0L; + else if((time = deadline - System.nanoTime()) <= 0L) + return cancelWaiter(node, node, false); + Thread wt = Thread.currentThread(); + U.putObject(wt, PARKBLOCKER, this); + node.thread = wt; + if(p.status < 0 && (p != h || (state & ABITS) == WBIT) + && whead == h && node.prev == p) + U.park(false, time); + node.thread = null; + U.putObject(wt, PARKBLOCKER, null); + if(interruptible && Thread.interrupted()) + return cancelWaiter(node, node, true); + } + } + } + } + + /** + * If node non-null, forces cancel status and unsplices it from + * queue if possible and wakes up any cowaiters (of the node, or + * group, as applicable), and in any case helps release current + * first waiter if lock is free. (Calling with null arguments + * serves as a conditional form of release, which is not currently + * needed but may be needed under possible future cancellation + * policies). This is a variant of cancellation methods in + * AbstractQueuedSynchronizer (see its detailed explanation in AQS + * internal documentation). + * + * @param node if nonnull, the waiter + * @param group either node or the group node is cowaiting with + * @param interrupted if already interrupted + * @return INTERRUPTED if interrupted or Thread.interrupted, else zero + */ + private long cancelWaiter(WNode node, WNode group, boolean interrupted) { + if(node != null && group != null) { + Thread w; + node.status = CANCELLED; + // unsplice cancelled nodes from group + for (WNode p = group, q; (q = p.cowait) != null;) { + if(q.status == CANCELLED) { + U.compareAndSwapObject(p, WCOWAIT, q, q.cowait); + p = group; // restart + } + else + p = q; + } + if(group == node) { + for (WNode r = group.cowait; r != null; r = r.cowait) { + if((w = r.thread) != null) + U.unpark(w); // wake up uncancelled co-waiters + } + for (WNode pred = node.prev; pred != null;) { // unsplice + WNode succ, pp; // find valid successor + while ((succ = node.next) == null + || succ.status == CANCELLED) { + WNode q = null; // find successor the slow way + for (WNode t = wtail; t != null && t != node; t = t.prev) + if(t.status != CANCELLED) + q = t; // don't link if succ cancelled + if(succ == q || // ensure accurate successor + U.compareAndSwapObject(node, WNEXT, succ, + succ = q)) { + if(succ == null && node == wtail) + U.compareAndSwapObject(this, WTAIL, node, pred); + break; + } + } + if(pred.next == node) // unsplice pred link + U.compareAndSwapObject(pred, WNEXT, node, succ); + if(succ != null && (w = succ.thread) != null) { + succ.thread = null; + U.unpark(w); // wake up succ to observe new pred + } + if(pred.status != CANCELLED || (pp = pred.prev) == null) + break; + node.prev = pp; // repeat if new pred wrong/cancelled + U.compareAndSwapObject(pp, WNEXT, pred, succ); + pred = pp; + } + } + } + WNode h; // Possibly release first waiter + while ((h = whead) != null) { + long s; + WNode q; // similar to release() but check eligibility + if((q = h.next) == null || q.status == CANCELLED) { + for (WNode t = wtail; t != null && t != h; t = t.prev) + if(t.status <= 0) + q = t; + } + if(h == whead) { + if(q != null && h.status == 0 && ((s = state) & ABITS) != WBIT + && // waiter is eligible + (s == 0L || q.mode == RMODE)) + release(h); + break; + } + } + return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; + } + + // Unsafe mechanics + private static final sun.misc.Unsafe U; + private static final long STATE; + private static final long WHEAD; + private static final long WTAIL; + private static final long WNEXT; + private static final long WSTATUS; + private static final long WCOWAIT; + private static final long PARKBLOCKER; + + static { + try { + U = getUnsafe(); + Class k = StampedLock.class; + Class wk = WNode.class; + STATE = U.objectFieldOffset(k.getDeclaredField("state")); + WHEAD = U.objectFieldOffset(k.getDeclaredField("whead")); + WTAIL = U.objectFieldOffset(k.getDeclaredField("wtail")); + WSTATUS = U.objectFieldOffset(wk.getDeclaredField("status")); + WNEXT = U.objectFieldOffset(wk.getDeclaredField("next")); + WCOWAIT = U.objectFieldOffset(wk.getDeclaredField("cowait")); + Class tk = Thread.class; + PARKBLOCKER = U.objectFieldOffset(tk + .getDeclaredField("parkBlocker")); + + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/Striped64.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/Striped64.java new file mode 100644 index 0000000000..e92f54aecb --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/Striped64.java @@ -0,0 +1,348 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.util.Random; + +/** + * A package-local class holding common representation and mechanics + * for classes supporting dynamic striping on 64bit values. The class + * extends Number so that concrete subclasses must publicly do so. + */ +@SuppressWarnings("serial") +abstract class Striped64 extends Number { + /* + * This class maintains a lazily-initialized table of atomically + * updated variables, plus an extra "base" field. The table size + * is a power of two. Indexing uses masked per-thread hash codes. + * Nearly all declarations in this class are package-private, + * accessed directly by subclasses. + * + * Table entries are of class Cell; a variant of AtomicLong padded + * to reduce cache contention on most processors. Padding is + * overkill for most Atomics because they are usually irregularly + * scattered in memory and thus don't interfere much with each + * other. But Atomic objects residing in arrays will tend to be + * placed adjacent to each other, and so will most often share + * cache lines (with a huge negative performance impact) without + * this precaution. + * + * In part because Cells are relatively large, we avoid creating + * them until they are needed. When there is no contention, all + * updates are made to the base field. Upon first contention (a + * failed CAS on base update), the table is initialized to size 2. + * The table size is doubled upon further contention until + * reaching the nearest power of two greater than or equal to the + * number of CPUS. Table slots remain empty (null) until they are + * needed. + * + * A single spinlock ("busy") is used for initializing and + * resizing the table, as well as populating slots with new Cells. + * There is no need for a blocking lock; when the lock is not + * available, threads try other slots (or the base). During these + * retries, there is increased contention and reduced locality, + * which is still better than alternatives. + * + * Per-thread hash codes are initialized to random values. + * Contention and/or table collisions are indicated by failed + * CASes when performing an update operation (see method + * retryUpdate). Upon a collision, if the table size is less than + * the capacity, it is doubled in size unless some other thread + * holds the lock. If a hashed slot is empty, and lock is + * available, a new Cell is created. Otherwise, if the slot + * exists, a CAS is tried. Retries proceed by "double hashing", + * using a secondary hash (Marsaglia XorShift) to try to find a + * free slot. + * + * The table size is capped because, when there are more threads + * than CPUs, supposing that each thread were bound to a CPU, + * there would exist a perfect hash function mapping threads to + * slots that eliminates collisions. When we reach capacity, we + * search for this mapping by randomly varying the hash codes of + * colliding threads. Because search is random, and collisions + * only become known via CAS failures, convergence can be slow, + * and because threads are typically not bound to CPUS forever, + * may not occur at all. However, despite these limitations, + * observed contention rates are typically low in these cases. + * + * It is possible for a Cell to become unused when threads that + * once hashed to it terminate, as well as in the case where + * doubling the table causes no thread to hash to it under + * expanded mask. We do not try to detect or remove such cells, + * under the assumption that for long-running instances, observed + * contention levels will recur, so the cells will eventually be + * needed again; and for short-lived ones, it does not matter. + */ + + /** + * Padded variant of AtomicLong supporting only raw accesses plus CAS. + * The value field is placed between pads, hoping that the JVM doesn't + * reorder them. + * + * JVM intrinsics note: It would be possible to use a release-only + * form of CAS here, if it were provided. + */ + static final class Cell { + volatile long p0, p1, p2, p3, p4, p5, p6; + volatile long value; + volatile long q0, q1, q2, q3, q4, q5, q6; + + Cell(long x) { + value = x; + } + + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + static { + try { + UNSAFE = getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset(ak + .getDeclaredField("value")); + } + catch (Exception e) { + throw new Error(e); + } + } + + } + + /** + * ThreadLocal holding a single-slot int array holding hash code. + * Unlike the JDK8 version of this class, we use a suboptimal + * int[] representation to avoid introducing a new type that can + * impede class-unloading when ThreadLocals are not removed. + */ + static final ThreadLocal threadHashCode = new ThreadLocal(); + + /** + * Generator of new random hash codes + */ + static final Random rng = new Random(); + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + /** + * Base value, used mainly when there is no contention, but also as + * a fallback during table initialization races. Updated via CAS. + */ + transient volatile long base; + + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + */ + transient volatile int busy; + + /** + * Package-private default constructor + */ + Striped64() {} + + /** + * CASes the base field. + */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val); + } + + /** + * CASes the busy field from 0 to 1 to acquire lock. + */ + final boolean casBusy() { + return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1); + } + + /** + * Computes the function of current and new value. Subclasses + * should open-code this update function for most uses, but the + * virtualized form is needed within retryUpdate. + * + * @param currentValue the current value (of either base or a cell) + * @param newValue the argument from a user update call + * @return result of the update function + */ + abstract long fn(long currentValue, long newValue); + + /** + * Handles cases of updates involving initialization, resizing, + * creating new Cells, and/or contention. See above for + * explanation. This method suffers the usual non-modularity + * problems of optimistic retry code, relying on rechecked sets of + * reads. + * + * @param x the value + * @param hc the hash code holder + * @param wasUncontended false if CAS failed before call + */ + final void retryUpdate(long x, int[] hc, boolean wasUncontended) { + int h; + if(hc == null) { + threadHashCode.set(hc = new int[1]); // Initialize randomly + int r = rng.nextInt(); // Avoid zero to allow xorShift rehash + h = hc[0] = (r == 0) ? 1 : r; + } + else + h = hc[0]; + boolean collide = false; // True if last slot nonempty + for (;;) { + Cell[] as; + Cell a; + int n; + long v; + if((as = cells) != null && (n = as.length) > 0) { + if((a = as[(n - 1) & h]) == null) { + if(busy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if(busy == 0 && casBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; + int m, j; + if((rs = cells) != null && (m = rs.length) > 0 + && rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } + finally { + busy = 0; + } + if(created) + break; + continue; // Slot is now non-empty + } + } + collide = false; + } + else if(!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + else if(a.cas(v = a.value, fn(v, x))) + break; + else if(n >= NCPU || cells != as) + collide = false; // At max size or stale + else if(!collide) + collide = true; + else if(busy == 0 && casBusy()) { + try { + if(cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } + finally { + busy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h ^= h << 13; // Rehash + h ^= h >>> 17; + h ^= h << 5; + hc[0] = h; // Record index for next time + } + else if(busy == 0 && cells == as && casBusy()) { + boolean init = false; + try { // Initialize table + if(cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } + finally { + busy = 0; + } + if(init) + break; + } + else if(casBase(v = base, fn(v, x))) + break; // Fall back on using base + } + } + + /** + * Sets base and all cells to the given value. + */ + final void internalReset(long initialValue) { + Cell[] as = cells; + base = initialValue; + if(as != null) { + int n = as.length; + for (int i = 0; i < n; ++i) { + Cell a = as[i]; + if(a != null) + a.value = initialValue; + } + } + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long baseOffset; + private static final long busyOffset; + static { + try { + UNSAFE = getUnsafe(); + Class sk = Striped64.class; + baseOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("base")); + busyOffset = UNSAFE.objectFieldOffset(sk.getDeclaredField("busy")); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java new file mode 100644 index 0000000000..03c78891e2 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java @@ -0,0 +1,199 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e; + +import java.util.Random; + +/** + * A random number generator isolated to the current thread. Like the + * global {@link java.util.Random} generator used by the {@link java.lang.Math} + * class, a {@code ThreadLocalRandom} is initialized + * with an internally generated seed that may not otherwise be + * modified. When applicable, use of {@code ThreadLocalRandom} rather + * than shared {@code Random} objects in concurrent programs will + * typically encounter much less overhead and contention. Use of + * {@code ThreadLocalRandom} is particularly appropriate when multiple + * tasks (for example, each a {@link ForkJoinTask}) use random numbers + * in parallel in thread pools. + * + *

+ * Usages of this class should typically be of the form: + * {@code ThreadLocalRandom.current().nextX(...)} (where {@code X} is + * {@code Int}, {@code Long}, etc). When all usages are of this form, it is + * never possible to accidently share a {@code ThreadLocalRandom} across + * multiple threads. + * + *

+ * This class also provides additional commonly used bounded random generation + * methods. + * + * @since 1.7 + * @author Doug Lea + */ +public class ThreadLocalRandom extends Random { + // same constants as Random, but must be redeclared because private + private static final long multiplier = 0x5DEECE66DL; + private static final long addend = 0xBL; + private static final long mask = (1L << 48) - 1; + + /** + * The random seed. We can't use super.seed. + */ + private long rnd; + + /** + * Initialization flag to permit calls to setSeed to succeed only + * while executing the Random constructor. We can't allow others + * since it would cause setting seed in one part of a program to + * unintentionally impact other usages by the thread. + */ + boolean initialized; + + // Padding to help avoid memory contention among seed updates in + // different TLRs in the common case that they are located near + // each other. + private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; + + /** + * The actual ThreadLocal + */ + private static final ThreadLocal localRandom = new ThreadLocal() { + protected ThreadLocalRandom initialValue() { + return new ThreadLocalRandom(); + } + }; + + /** + * Constructor called only by localRandom.initialValue. + */ + ThreadLocalRandom() { + super(); + initialized = true; + } + + /** + * Returns the current thread's {@code ThreadLocalRandom}. + * + * @return the current thread's {@code ThreadLocalRandom} + */ + public static ThreadLocalRandom current() { + return localRandom.get(); + } + + /** + * Throws {@code UnsupportedOperationException}. Setting seeds in + * this generator is not supported. + * + * @throws UnsupportedOperationException always + */ + public void setSeed(long seed) { + if(initialized) + throw new UnsupportedOperationException(); + rnd = (seed ^ multiplier) & mask; + } + + protected int next(int bits) { + rnd = (rnd * multiplier + addend) & mask; + return (int) (rnd >>> (48 - bits)); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public int nextInt(int least, int bound) { + if(least >= bound) + throw new IllegalArgumentException(); + return nextInt(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public long nextLong(long n) { + if(n <= 0) + throw new IllegalArgumentException("n must be positive"); + // Divide n by two until small enough for nextInt. On each + // iteration (at most 31 of them but usually much less), + // randomly choose both whether to include high bit in result + // (offset) and whether to continue with the lower vs upper + // half (which makes a difference only if odd). + long offset = 0; + while (n >= Integer.MAX_VALUE) { + int bits = next(2); + long half = n >>> 1; + long nextn = ((bits & 2) == 0) ? half : n - half; + if((bits & 1) == 0) + offset += n - nextn; + n = nextn; + } + return offset + nextInt((int) n); + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public long nextLong(long least, long bound) { + if(least >= bound) + throw new IllegalArgumentException(); + return nextLong(bound - least) + least; + } + + /** + * Returns a pseudorandom, uniformly distributed {@code double} value + * between 0 (inclusive) and the specified value (exclusive). + * + * @param n the bound on the random number to be returned. Must be + * positive. + * @return the next value + * @throws IllegalArgumentException if n is not positive + */ + public double nextDouble(double n) { + if(n <= 0) + throw new IllegalArgumentException("n must be positive"); + return nextDouble() * n; + } + + /** + * Returns a pseudorandom, uniformly distributed value between the + * given least value (inclusive) and bound (exclusive). + * + * @param least the least value returned + * @param bound the upper bound (exclusive) + * @return the next value + * @throws IllegalArgumentException if least greater than or equal + * to bound + */ + public double nextDouble(double least, double bound) { + if(least >= bound) + throw new IllegalArgumentException(); + return nextDouble() * (bound - least) + least; + } + + private static final long serialVersionUID = -5851777807851030925L; +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDouble.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDouble.java new file mode 100644 index 0000000000..2c252927c7 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDouble.java @@ -0,0 +1,294 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e.extra; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; + +/** + * A {@code double} value that may be updated atomically. See the + * {@link java.util.concurrent.atomic} package specification for + * description of the properties of atomic variables. An {@code AtomicDouble} is + * used in applications such as atomic accumulation, + * and cannot be used as a replacement for a {@link Double}. However, + * this class does extend {@code Number} to allow uniform access by + * tools and utilities that deal with numerically-based classes. + * + *

+ * This class compares primitive {@code double} values in methods such as + * {@link #compareAndSet} by comparing their bitwise representation using + * {@link Double#doubleToRawLongBits}, which differs from both the primitive + * double {@code ==} operator and from {@link Double#equals}, as if implemented + * by: + * + *

+ * {@code
+ * static boolean bitEquals(double x, double y) {
+ *   long xBits = Double.doubleToRawLongBits(x);
+ *   long yBits = Double.doubleToRawLongBits(y);
+ *   return xBits == yBits;
+ * }}
+ * 
+ * + * @see org.cinchapi.vendor.jsr166e.DoubleAdder + * @see org.cinchapi.vendor.jsr166e.DoubleMaxUpdater + * + * @author Doug Lea + * @author Martin Buchholz + */ +public class AtomicDouble extends Number implements java.io.Serializable { + private static final long serialVersionUID = -8405198993435143622L; + + private transient volatile long value; + + /** + * Creates a new {@code AtomicDouble} with the given initial value. + * + * @param initialValue the initial value + */ + public AtomicDouble(double initialValue) { + value = doubleToRawLongBits(initialValue); + } + + /** + * Creates a new {@code AtomicDouble} with initial value {@code 0.0}. + */ + public AtomicDouble() { + // assert doubleToRawLongBits(0.0) == 0L; + } + + /** + * Gets the current value. + * + * @return the current value + */ + public final double get() { + return longBitsToDouble(value); + } + + /** + * Sets to the given value. + * + * @param newValue the new value + */ + public final void set(double newValue) { + long next = doubleToRawLongBits(newValue); + value = next; + } + + /** + * Eventually sets to the given value. + * + * @param newValue the new value + */ + public final void lazySet(double newValue) { + long next = doubleToRawLongBits(newValue); + unsafe.putOrderedLong(this, valueOffset, next); + } + + /** + * Atomically sets to the given value and returns the old value. + * + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(double newValue) { + long next = doubleToRawLongBits(newValue); + while (true) { + long current = value; + if(unsafe.compareAndSwapLong(this, valueOffset, current, next)) + return longBitsToDouble(current); + } + } + + /** + * Atomically sets the value to the given updated value + * if the current value is bitwise equal + * to the expected value. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful. False return indicates that + * the actual value was not bitwise equal to the expected value. + */ + public final boolean compareAndSet(double expect, double update) { + return unsafe.compareAndSwapLong(this, valueOffset, + doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + /** + * Atomically sets the value to the given updated value + * if the current value is bitwise equal + * to the expected value. + * + *

+ * May fail spuriously and does not provide ordering guarantees, so is + * only rarely an appropriate alternative to {@code compareAndSet}. + * + * @param expect the expected value + * @param update the new value + * @return {@code true} if successful + */ + public final boolean weakCompareAndSet(double expect, double update) { + return compareAndSet(expect, update); + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(double delta) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if(unsafe.compareAndSwapLong(this, valueOffset, current, next)) + return currentVal; + } + } + + /** + * Atomically adds the given value to the current value. + * + * @param delta the value to add + * @return the updated value + */ + public final double addAndGet(double delta) { + while (true) { + long current = value; + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if(unsafe.compareAndSwapLong(this, valueOffset, current, next)) + return nextVal; + } + } + + /** + * Returns the String representation of the current value. + * + * @return the String representation of the current value + */ + public String toString() { + return Double.toString(get()); + } + + /** + * Returns the value of this {@code AtomicDouble} as an {@code int} after a + * narrowing primitive conversion. + */ + public int intValue() { + return (int) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code long} after a + * narrowing primitive conversion. + */ + public long longValue() { + return (long) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code float} after a + * narrowing primitive conversion. + */ + public float floatValue() { + return (float) get(); + } + + /** + * Returns the value of this {@code AtomicDouble} as a {@code double}. + */ + public double doubleValue() { + return get(); + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @serialData The current value is emitted (a {@code double}). + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + + s.writeDouble(get()); + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + set(s.readDouble()); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe unsafe = getUnsafe(); + private static final long valueOffset; + + static { + try { + valueOffset = unsafe.objectFieldOffset(AtomicDouble.class + .getDeclaredField("value")); + } + catch (Exception ex) { + throw new Error(ex); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDoubleArray.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDoubleArray.java new file mode 100644 index 0000000000..fa96b9ab19 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/AtomicDoubleArray.java @@ -0,0 +1,339 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e.extra; + +import static java.lang.Double.doubleToRawLongBits; +import static java.lang.Double.longBitsToDouble; + +/** + * A {@code double} array in which elements may be updated atomically. + * See the {@link java.util.concurrent.atomic} package specification + * for description of the properties of atomic variables. + * + *

+ * This class compares primitive {@code double} values in methods such as + * {@link #compareAndSet} by comparing their bitwise representation using + * {@link Double#doubleToRawLongBits}, which differs from both the primitive + * double {@code ==} operator and from {@link Double#equals}, as if implemented + * by: + * + *

+ * {@code
+ * static boolean bitEquals(double x, double y) {
+ *   long xBits = Double.doubleToRawLongBits(x);
+ *   long yBits = Double.doubleToRawLongBits(y);
+ *   return xBits == yBits;
+ * }}
+ * 
+ * + * @author Doug Lea + * @author Martin Buchholz + */ +public class AtomicDoubleArray implements java.io.Serializable { + private static final long serialVersionUID = -2308431214976778248L; + + private final transient long[] array; + + private long checkedByteOffset(int i) { + if(i < 0 || i >= array.length) + throw new IndexOutOfBoundsException("index " + i); + + return byteOffset(i); + } + + private static long byteOffset(int i) { + return ((long) i << shift) + base; + } + + /** + * Creates a new {@code AtomicDoubleArray} of the given length, + * with all elements initially zero. + * + * @param length the length of the array + */ + public AtomicDoubleArray(int length) { + array = new long[length]; + } + + /** + * Creates a new {@code AtomicDoubleArray} with the same length + * as, and all elements copied from, the given array. + * + * @param array the array to copy elements from + * @throws NullPointerException if array is null + */ + public AtomicDoubleArray(double[] array) { + // Visibility guaranteed by final field guarantees + final int len = array.length; + final long[] a = new long[len]; + for (int i = 0; i < len; i++) + a[i] = doubleToRawLongBits(array[i]); + this.array = a; + } + + /** + * Returns the length of the array. + * + * @return the length of the array + */ + public final int length() { + return array.length; + } + + /** + * Gets the current value at position {@code i}. + * + * @param i the index + * @return the current value + */ + public final double get(int i) { + return longBitsToDouble(getRaw(checkedByteOffset(i))); + } + + private long getRaw(long offset) { + return unsafe.getLongVolatile(array, offset); + } + + /** + * Sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void set(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + unsafe.putLongVolatile(array, checkedByteOffset(i), next); + } + + /** + * Eventually sets the element at position {@code i} to the given value. + * + * @param i the index + * @param newValue the new value + */ + public final void lazySet(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + unsafe.putOrderedLong(array, checkedByteOffset(i), next); + } + + /** + * Atomically sets the element at position {@code i} to the given value + * and returns the old value. + * + * @param i the index + * @param newValue the new value + * @return the previous value + */ + public final double getAndSet(int i, double newValue) { + long next = doubleToRawLongBits(newValue); + long offset = checkedByteOffset(i); + while (true) { + long current = getRaw(offset); + if(compareAndSetRaw(offset, current, next)) + return longBitsToDouble(current); + } + } + + /** + * Atomically sets the element at position {@code i} to the given + * updated value + * if the current value is bitwise equal + * to the expected value. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. + */ + public final boolean compareAndSet(int i, double expect, double update) { + return compareAndSetRaw(checkedByteOffset(i), + doubleToRawLongBits(expect), doubleToRawLongBits(update)); + } + + private boolean compareAndSetRaw(long offset, long expect, long update) { + return unsafe.compareAndSwapLong(array, offset, expect, update); + } + + /** + * Atomically sets the element at position {@code i} to the given + * updated value + * if the current value is bitwise equal + * to the expected value. + * + *

+ * May fail spuriously and does not provide ordering guarantees, so is + * only rarely an appropriate alternative to {@code compareAndSet}. + * + * @param i the index + * @param expect the expected value + * @param update the new value + * @return true if successful + */ + public final boolean weakCompareAndSet(int i, double expect, double update) { + return compareAndSet(i, expect, update); + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the previous value + */ + public final double getAndAdd(int i, double delta) { + long offset = checkedByteOffset(i); + while (true) { + long current = getRaw(offset); + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if(compareAndSetRaw(offset, current, next)) + return currentVal; + } + } + + /** + * Atomically adds the given value to the element at index {@code i}. + * + * @param i the index + * @param delta the value to add + * @return the updated value + */ + public double addAndGet(int i, double delta) { + long offset = checkedByteOffset(i); + while (true) { + long current = getRaw(offset); + double currentVal = longBitsToDouble(current); + double nextVal = currentVal + delta; + long next = doubleToRawLongBits(nextVal); + if(compareAndSetRaw(offset, current, next)) + return nextVal; + } + } + + /** + * Returns the String representation of the current values of array. + * + * @return the String representation of the current values of array + */ + public String toString() { + int iMax = array.length - 1; + if(iMax == -1) + return "[]"; + + // Double.toString(Math.PI).length() == 17 + StringBuilder b = new StringBuilder((17 + 2) * (iMax + 1)); + b.append('['); + for (int i = 0;; i++) { + b.append(longBitsToDouble(getRaw(byteOffset(i)))); + if(i == iMax) + return b.append(']').toString(); + b.append(',').append(' '); + } + } + + /** + * Saves the state to a stream (that is, serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @serialData The length of the array is emitted (int), followed by all + * of its elements (each a {@code double}) in the proper order. + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + s.defaultWriteObject(); + + // Write out array length + int length = length(); + s.writeInt(length); + + // Write out all elements in the proper order. + for (int i = 0; i < length; i++) + s.writeDouble(get(i)); + } + + /** + * Reconstitutes the instance from a stream (that is, deserializes it). + * + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object + * could not be found + * @throws java.io.IOException if an I/O error occurs + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + // Read in array length and allocate array + int length = s.readInt(); + unsafe.putObjectVolatile(this, arrayOffset, new long[length]); + + // Read in all elements in the proper order. + for (int i = 0; i < length; i++) + set(i, s.readDouble()); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe unsafe = getUnsafe(); + private static final long arrayOffset; + private static final int base = unsafe.arrayBaseOffset(long[].class); + private static final int shift; + + static { + try { + Class k = AtomicDoubleArray.class; + arrayOffset = unsafe.objectFieldOffset(k.getDeclaredField("array")); + int scale = unsafe.arrayIndexScale(long[].class); + if((scale & (scale - 1)) != 0) + throw new Error("data type scale not a power of two"); + shift = 31 - Integer.numberOfLeadingZeros(scale); + } + catch (Exception e) { + throw new Error(e); + } + } + + /** + * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package. + * Replace with a simple call to Unsafe.getUnsafe when integrating + * into a jdk. + * + * @return a sun.misc.Unsafe + */ + private static sun.misc.Unsafe getUnsafe() { + try { + return sun.misc.Unsafe.getUnsafe(); + } + catch (SecurityException tryReflectionInstead) {} + try { + return java.security.AccessController + .doPrivileged(new java.security.PrivilegedExceptionAction() { + public sun.misc.Unsafe run() throws Exception { + Class k = sun.misc.Unsafe.class; + for (java.lang.reflect.Field f : k + .getDeclaredFields()) { + f.setAccessible(true); + Object x = f.get(null); + if(k.isInstance(x)) + return k.cast(x); + } + throw new NoSuchFieldError("the Unsafe"); + } + }); + } + catch (java.security.PrivilegedActionException e) { + throw new RuntimeException("Could not initialize intrinsics", + e.getCause()); + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java new file mode 100644 index 0000000000..7fb3ef0884 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java @@ -0,0 +1,1633 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e.extra; +import org.cinchapi.vendor.jsr166e.StampedLock; +import java.util.*; + +/** + * A class with the same methods and array-based characteristics as + * {@link java.util.Vector} but with reduced contention and improved + * throughput when invocations of read-only methods by multiple + * threads are most common. + * + *

The iterators returned by this class's {@link #iterator() + * iterator} and {@link #listIterator(int) listIterator} methods are + * best-effort in the presence of concurrent modifications, and do + * NOT throw {@link ConcurrentModificationException}. An + * iterator's {@code next()} method returns consecutive elements as + * they appear in the underlying array upon each access. Alternatively, + * method {@link #snapshotIterator} may be used for deterministic + * traversals, at the expense of making a copy, and unavailability of + * method {@code Iterator.remove}. + * + *

Otherwise, this class supports all methods, under the same + * documented specifications, as {@code Vector}. Consult {@link + * java.util.Vector} for detailed specifications. Additionally, this + * class provides methods {@link #addIfAbsent} and {@link + * #addAllAbsent}. + * + * @author Doug Lea + */ +public class ReadMostlyVector + implements List, RandomAccess, Cloneable, java.io.Serializable { + private static final long serialVersionUID = 8673264195747942595L; + + /* + * This class exists mainly as a vehicle to exercise various + * constructions using SequenceLocks. Read-only methods + * take one of a few forms: + * + * Short methods,including get(index), continually retry obtaining + * a snapshot of array, count, and element, using sequence number + * to validate. + * + * Methods that are potentially O(n) (or worse) try once in + * read-only mode, and then lock. When in read-only mode, they + * validate only at the end of an array scan unless the element is + * actually used (for example, as an argument of method equals). + * + * We rely on some invariants that are always true, even for field + * reads in read-only mode that have not yet been validated: + * - array != null + * - count >= 0 + */ + + /** + * The maximum size of array to allocate. + * See CopyOnWriteArrayList for explanation. + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + // fields are non-private to simplify nested class access + Object[] array; + final StampedLock lock; + int count; + final int capacityIncrement; + + /** + * Creates an empty vector with the given initial capacity and + * capacity increment. + * + * @param initialCapacity the initial capacity of the underlying array + * @param capacityIncrement if non-zero, the number to + * add when resizing to accommodate additional elements. + * If zero, the array size is doubled when resized. + * + * @throws IllegalArgumentException if initial capacity is negative + */ + public ReadMostlyVector(int initialCapacity, int capacityIncrement) { + super(); + if (initialCapacity < 0) + throw new IllegalArgumentException("Illegal Capacity: "+ + initialCapacity); + this.array = new Object[initialCapacity]; + this.capacityIncrement = capacityIncrement; + this.lock = new StampedLock(); + } + + /** + * Creates an empty vector with the given initial capacity. + * + * @param initialCapacity the initial capacity of the underlying array + * @throws IllegalArgumentException if initial capacity is negative + */ + public ReadMostlyVector(int initialCapacity) { + this(initialCapacity, 0); + } + + /** + * Creates an empty vector. + */ + public ReadMostlyVector() { + this.capacityIncrement = 0; + this.lock = new StampedLock(); + } + + /** + * Creates a vector containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection of initially held elements + * @throws NullPointerException if the specified collection is null + */ + public ReadMostlyVector(Collection c) { + Object[] elements = c.toArray(); + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elements.getClass() != Object[].class) + elements = Arrays.copyOf(elements, elements.length, Object[].class); + this.array = elements; + this.count = elements.length; + this.capacityIncrement = 0; + this.lock = new StampedLock(); + } + + // internal constructor for clone + ReadMostlyVector(Object[] array, int count, int capacityIncrement) { + this.array = array; + this.count = count; + this.capacityIncrement = capacityIncrement; + this.lock = new StampedLock(); + } + + static final int INITIAL_CAP = 16; + + // For explanation, see CopyOnWriteArrayList + final Object[] grow(int minCapacity) { + Object[] items; + int newCapacity; + if ((items = array) == null) + newCapacity = INITIAL_CAP; + else { + int oldCapacity = array.length; + newCapacity = oldCapacity + ((capacityIncrement > 0) ? + capacityIncrement : oldCapacity); + } + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity - MAX_ARRAY_SIZE > 0) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + else if (minCapacity > MAX_ARRAY_SIZE) + newCapacity = Integer.MAX_VALUE; + else + newCapacity = MAX_ARRAY_SIZE; + } + return array = ((items == null) ? + new Object[newCapacity] : + Arrays.copyOf(items, newCapacity)); + } + + /* + * Internal versions of most base functionality, wrapped + * in different ways from public methods from this class + * as well as sublist and iterator classes. + */ + + static int findFirstIndex(Object[] items, Object x, int index, int fence) { + int len; + if (items != null && (len = items.length) > 0) { + int start = (index < 0) ? 0 : index; + int bound = (fence < len) ? fence : len; + for (int i = start; i < bound; ++i) { + Object e = items[i]; + if ((x == null) ? e == null : x.equals(e)) + return i; + } + } + return -1; + } + + static int findLastIndex(Object[] items, Object x, int index, int origin) { + int len; + if (items != null && (len = items.length) > 0) { + int last = (index < len) ? index : len - 1; + int start = (origin < 0) ? 0 : origin; + for (int i = last; i >= start; --i) { + Object e = items[i]; + if ((x == null) ? e == null : x.equals(e)) + return i; + } + } + return -1; + } + + final void rawAdd(E e) { + int n = count; + Object[] items = array; + if (items == null || n >= items.length) + items = grow(n + 1); + items[n] = e; + count = n + 1; + } + + final void rawAddAt(int index, E e) { + int n = count; + Object[] items = array; + if (index > n) + throw new ArrayIndexOutOfBoundsException(index); + if (items == null || n >= items.length) + items = grow(n + 1); + if (index < n) + System.arraycopy(items, index, items, index + 1, n - index); + items[index] = e; + count = n + 1; + } + + final boolean rawAddAllAt(int index, Object[] elements) { + int n = count; + Object[] items = array; + if (index < 0 || index > n) + throw new ArrayIndexOutOfBoundsException(index); + int len = elements.length; + if (len == 0) + return false; + int newCount = n + len; + if (items == null || newCount >= items.length) + items = grow(newCount); + int mv = n - index; + if (mv > 0) + System.arraycopy(items, index, items, index + len, mv); + System.arraycopy(elements, 0, items, index, len); + count = newCount; + return true; + } + + final boolean rawRemoveAt(int index) { + int n = count - 1; + Object[] items = array; + if (items == null || index < 0 || index > n) + return false; + int mv = n - index; + if (mv > 0) + System.arraycopy(items, index + 1, items, index, mv); + items[n] = null; + count = n; + return true; + } + + /** + * Internal version of removeAll for lists and sublists. In this + * and other similar methods below, the bound argument is, if + * non-negative, the purported upper bound of a list/sublist, or + * is left negative if the bound should be determined via count + * field under lock. + */ + final boolean lockedRemoveAll(Collection c, int origin, int bound) { + boolean removed = false; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + int n = count; + int fence = bound < 0 || bound > n ? n : bound; + if (origin >= 0 && origin < fence) { + for (Object x : c) { + while (rawRemoveAt(findFirstIndex(array, x, origin, fence))) + removed = true; + } + } + } finally { + lock.unlockWrite(stamp); + } + return removed; + } + + final boolean lockedRetainAll(Collection c, int origin, int bound) { + final StampedLock lock = this.lock; + boolean removed = false; + if (c != this) { + long stamp = lock.writeLock(); + try { + Object[] items; + int i, n; + if ((items = array) != null && (n = count) > 0 && + n < items.length && (i = origin) >= 0) { + int fence = bound < 0 || bound > n ? n : bound; + while (i < fence) { + if (c.contains(items[i])) + ++i; + else { + --fence; + int mv = --n - i; + if (mv > 0) + System.arraycopy(items, i + 1, items, i, mv); + } + } + if (count != n) { + count = n; + removed = true; + } + } + } finally { + lock.unlockWrite(stamp); + } + } + return removed; + } + + final void internalClear(int origin, int bound) { + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + int removed = fence - origin; + int newCount = n - removed; + int mv = n - (origin + removed); + if (mv > 0) + System.arraycopy(items, origin + removed, items, origin, mv); + for (int i = n; i < newCount; ++i) + items[i] = null; + count = newCount; + } + } + + final boolean internalContainsAll(Collection c, int origin, int bound) { + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + for (Object e : c) { + if (findFirstIndex(items, e, origin, fence) < 0) + return false; + } + } + else if (!c.isEmpty()) + return false; + return true; + } + + final boolean internalEquals(List list, int origin, int bound) { + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + Iterator it = list.iterator(); + for (int i = origin; i < fence; ++i) { + if (!it.hasNext()) + return false; + Object y = it.next(); + Object x = items[i]; + if (x != y && (x == null || !x.equals(y))) + return false; + } + if (it.hasNext()) + return false; + } + else if (!list.isEmpty()) + return false; + return true; + } + + final int internalHashCode(int origin, int bound) { + int hash = 1; + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + for (int i = origin; i < fence; ++i) { + Object e = items[i]; + hash = 31*hash + (e == null ? 0 : e.hashCode()); + } + } + return hash; + } + + final String internalToString(int origin, int bound) { + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + int i = (origin < 0) ? 0 : origin; + if (i != fence) { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (;;) { + Object e = items[i]; + sb.append((e == this) ? "(this Collection)" : e.toString()); + if (++i < fence) + sb.append(',').append(' '); + else + return sb.append(']').toString(); + } + } + } + return "[]"; + } + + final Object[] internalToArray(int origin, int bound) { + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + int i = (origin < 0) ? 0 : origin; + if (i != fence) + return Arrays.copyOfRange(items, i, fence, Object[].class); + } + return new Object[0]; + } + + @SuppressWarnings("unchecked") + final T[] internalToArray(T[] a, int origin, int bound) { + int alen = a.length; + Object[] items; + int n, len; + if ((items = array) != null && (len = items.length) > 0) { + if (origin < 0) + origin = 0; + if ((n = count) > len) + n = len; + int fence = bound < 0 || bound > n ? n : bound; + int i = (origin < 0) ? 0 : origin; + int rlen = fence - origin; + if (rlen > 0) { + if (alen >= rlen) { + System.arraycopy(items, 0, a, origin, rlen); + if (alen > rlen) + a[rlen] = null; + return a; + } + return (T[]) Arrays.copyOfRange(items, i, fence, a.getClass()); + } + } + if (alen > 0) + a[0] = null; + return a; + } + + // public List methods + + public boolean add(E e) { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + rawAdd(e); + } finally { + lock.unlockWrite(stamp); + } + return true; + } + + public void add(int index, E element) { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + rawAddAt(index, element); + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean addAll(Collection c) { + Object[] elements = c.toArray(); + int len = elements.length; + if (len == 0) + return false; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items = array; + int n = count; + int newCount = n + len; + if (items == null || newCount >= items.length) + items = grow(newCount); + System.arraycopy(elements, 0, items, n, len); + count = newCount; + } finally { + lock.unlockWrite(stamp); + } + return true; + } + + public boolean addAll(int index, Collection c) { + Object[] elements = c.toArray(); + boolean ret; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + ret = rawAddAllAt(index, elements); + } finally { + lock.unlockWrite(stamp); + } + return ret; + } + + public void clear() { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + int n = count; + Object[] items = array; + if (items != null) { + for (int i = 0; i < n; i++) + items[i] = null; + } + count = 0; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean contains(Object o) { + return indexOf(o, 0) >= 0; + } + + public boolean containsAll(Collection c) { + boolean ret; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + ret = internalContainsAll(c, 0, -1); + } finally { + lock.unlockRead(stamp); + } + return ret; + } + + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof List)) + return false; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + return internalEquals((List)o, 0, -1); + } finally { + lock.unlockRead(stamp); + } + } + + public E get(int index) { + final StampedLock lock = this.lock; + long stamp = lock.tryOptimisticRead(); + Object[] items; + if (index >= 0 && (items = array) != null && + index < count && index < items.length) { + @SuppressWarnings("unchecked") E e = (E)items[index]; + if (lock.validate(stamp)) + return e; + } + return lockedGet(index); + } + + @SuppressWarnings("unchecked") private E lockedGet(int index) { + boolean oobe = false; + E e = null; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + Object[] items; + if ((items = array) != null && index < items.length && + index < count && index >= 0) + e = (E)items[index]; + else + oobe = true; + } finally { + lock.unlockRead(stamp); + } + if (oobe) + throw new ArrayIndexOutOfBoundsException(index); + return e; + } + + public int hashCode() { + int h; + final StampedLock lock = this.lock; + long s = lock.readLock(); + try { + h = internalHashCode(0, -1); + } finally { + lock.unlockRead(s); + } + return h; + } + + public int indexOf(Object o) { + int idx; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + idx = findFirstIndex(array, o, 0, count); + } finally { + lock.unlockRead(stamp); + } + return idx; + } + + public boolean isEmpty() { + final StampedLock lock = this.lock; + long stamp = lock.tryOptimisticRead(); + return count == 0; // no need for validation + } + + public Iterator iterator() { + return new Itr(this, 0); + } + + public int lastIndexOf(Object o) { + int idx; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + idx = findLastIndex(array, o, count - 1, 0); + } finally { + lock.unlockRead(stamp); + } + return idx; + } + + public ListIterator listIterator() { + return new Itr(this, 0); + } + + public ListIterator listIterator(int index) { + return new Itr(this, index); + } + + @SuppressWarnings("unchecked") public E remove(int index) { + E oldValue = null; + boolean oobe = false; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + if (index < 0 || index >= count) + oobe = true; + else { + oldValue = (E) array[index]; + rawRemoveAt(index); + } + } finally { + lock.unlockWrite(stamp); + } + if (oobe) + throw new ArrayIndexOutOfBoundsException(index); + return oldValue; + } + + public boolean remove(Object o) { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + return rawRemoveAt(findFirstIndex(array, o, 0, count)); + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean removeAll(Collection c) { + return lockedRemoveAll(c, 0, -1); + } + + public boolean retainAll(Collection c) { + return lockedRetainAll(c, 0, -1); + } + + @SuppressWarnings("unchecked") public E set(int index, E element) { + E oldValue = null; + boolean oobe = false; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items = array; + if (items == null || index < 0 || index >= count) + oobe = true; + else { + oldValue = (E) items[index]; + items[index] = element; + } + } finally { + lock.unlockWrite(stamp); + } + if (oobe) + throw new ArrayIndexOutOfBoundsException(index); + return oldValue; + } + + public int size() { + final StampedLock lock = this.lock; + long stamp = lock.tryOptimisticRead(); + return count; // no need for validation + } + + private int lockedSize() { + int n; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + n = count; + } finally { + lock.unlockRead(stamp); + } + return n; + } + + public List subList(int fromIndex, int toIndex) { + int ssize = toIndex - fromIndex; + if (ssize >= 0 && fromIndex >= 0) { + ReadMostlyVectorSublist ret = null; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + if (toIndex <= count) + ret = new ReadMostlyVectorSublist(this, fromIndex, ssize); + } finally { + lock.unlockRead(stamp); + } + if (ret != null) + return ret; + } + + throw new ArrayIndexOutOfBoundsException(fromIndex < 0 ? fromIndex : toIndex); + } + + public Object[] toArray() { + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + return internalToArray(0, -1); + } finally { + lock.unlockRead(stamp); + } + } + + public T[] toArray(T[] a) { + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + return internalToArray(a, 0, -1); + } finally { + lock.unlockRead(stamp); + } + } + + public String toString() { + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + return internalToString(0, -1); + } finally { + lock.unlockRead(stamp); + } + } + + // ReadMostlyVector-only methods + + /** + * Appends the element, if not present. + * + * @param e element to be added to this list, if absent + * @return {@code true} if the element was added + */ + public boolean addIfAbsent(E e) { + boolean ret; + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + if (findFirstIndex(array, e, 0, count) < 0) { + rawAdd(e); + ret = true; + } + else + ret = false; + } finally { + lock.unlockWrite(stamp); + } + return ret; + } + + /** + * Appends all of the elements in the specified collection that + * are not already contained in this list, to the end of + * this list, in the order that they are returned by the + * specified collection's iterator. + * + * @param c collection containing elements to be added to this list + * @return the number of elements added + * @throws NullPointerException if the specified collection is null + * @see #addIfAbsent(Object) + */ + public int addAllAbsent(Collection c) { + int added = 0; + Object[] cs = c.toArray(); + int clen = cs.length; + if (clen != 0) { + long stamp = lock.writeLock(); + try { + for (int i = 0; i < clen; ++i) { + @SuppressWarnings("unchecked") + E e = (E) cs[i]; + if (findFirstIndex(array, e, 0, count) < 0) { + rawAdd(e); + ++added; + } + } + } finally { + lock.unlockWrite(stamp); + } + } + return added; + } + + /** + * Returns an iterator operating over a snapshot copy of the + * elements of this collection created upon construction of the + * iterator. The iterator does NOT support the + * {@code remove} method. + * + * @return an iterator over the elements in this list in proper sequence + */ + public Iterator snapshotIterator() { + return new SnapshotIterator(this); + } + + static final class SnapshotIterator implements Iterator { + private final Object[] items; + private int cursor; + SnapshotIterator(ReadMostlyVector v) { items = v.toArray(); } + public boolean hasNext() { return cursor < items.length; } + @SuppressWarnings("unchecked") public E next() { + if (cursor < items.length) + return (E) items[cursor++]; + throw new NoSuchElementException(); + } + public void remove() { throw new UnsupportedOperationException() ; } + } + + /** Interface describing a void action of one argument */ + public interface Action { void apply(A a); } + + public void forEachReadOnly(Action action) { + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + Object[] items; + int len, n; + if ((items = array) != null && (len = items.length) > 0 && + (n = count) <= len) { + for (int i = 0; i < n; ++i) { + @SuppressWarnings("unchecked") E e = (E)items[i]; + action.apply(e); + } + } + } finally { + lock.unlockRead(stamp); + } + } + + // Vector-only methods + + /** See {@link Vector#firstElement} */ + public E firstElement() { + final StampedLock lock = this.lock; + long stamp = lock.tryOptimisticRead(); + Object[] items; + if ((items = array) != null && count > 0 && items.length > 0) { + @SuppressWarnings("unchecked") E e = (E)items[0]; + if (lock.validate(stamp)) + return e; + } + return lockedFirstElement(); + } + + @SuppressWarnings("unchecked") private E lockedFirstElement() { + Object e = null; + boolean oobe = false; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + Object[] items = array; + if (items != null && count > 0 && items.length > 0) + e = items[0]; + else + oobe = true; + } finally { + lock.unlockRead(stamp); + } + if (oobe) + throw new NoSuchElementException(); + return (E) e; + } + + /** See {@link Vector#lastElement} */ + public E lastElement() { + final StampedLock lock = this.lock; + long stamp = lock.tryOptimisticRead(); + Object[] items; + int i; + if ((items = array) != null && (i = count - 1) >= 0 && + i < items.length) { + @SuppressWarnings("unchecked") E e = (E)items[i]; + if (lock.validate(stamp)) + return e; + } + return lockedLastElement(); + } + + @SuppressWarnings("unchecked") private E lockedLastElement() { + Object e = null; + boolean oobe = false; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + Object[] items = array; + int i = count - 1; + if (items != null && i >= 0 && i < items.length) + e = items[i]; + else + oobe = true; + } finally { + lock.unlockRead(stamp); + } + if (oobe) + throw new NoSuchElementException(); + return (E) e; + } + + /** See {@link Vector#indexOf(Object, int)} */ + public int indexOf(Object o, int index) { + if (index < 0) + throw new ArrayIndexOutOfBoundsException(index); + int idx; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + idx = findFirstIndex(array, o, index, count); + } finally { + lock.unlockRead(stamp); + } + return idx; + } + + /** See {@link Vector#lastIndexOf(Object, int)} */ + public int lastIndexOf(Object o, int index) { + boolean oobe = false; + int idx = -1; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + if (index < count) + idx = findLastIndex(array, o, index, 0); + else + oobe = true; + } finally { + lock.unlockRead(stamp); + } + if (oobe) + throw new ArrayIndexOutOfBoundsException(index); + return idx; + } + + /** See {@link Vector#setSize} */ + public void setSize(int newSize) { + if (newSize < 0) + throw new ArrayIndexOutOfBoundsException(newSize); + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items; + int n = count; + if (newSize > n) + grow(newSize); + else if ((items = array) != null) { + for (int i = newSize ; i < n ; i++) + items[i] = null; + } + count = newSize; + } finally { + lock.unlockWrite(stamp); + } + } + + /** See {@link Vector#copyInto} */ + public void copyInto(Object[] anArray) { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items; + if ((items = array) != null) + System.arraycopy(items, 0, anArray, 0, count); + } finally { + lock.unlockWrite(stamp); + } + } + + /** See {@link Vector#trimToSize} */ + public void trimToSize() { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items = array; + int n = count; + if (items != null && n < items.length) + array = Arrays.copyOf(items, n); + } finally { + lock.unlockWrite(stamp); + } + } + + /** See {@link Vector#ensureCapacity} */ + public void ensureCapacity(int minCapacity) { + if (minCapacity > 0) { + final StampedLock lock = this.lock; + long stamp = lock.writeLock(); + try { + Object[] items = array; + int cap = (items == null) ? 0 : items.length; + if (minCapacity - cap > 0) + grow(minCapacity); + } finally { + lock.unlockWrite(stamp); + } + } + } + + /** See {@link Vector#elements} */ + public Enumeration elements() { + return new Itr(this, 0); + } + + /** See {@link Vector#capacity} */ + public int capacity() { + return array.length; + } + + /** See {@link Vector#elementAt} */ + public E elementAt(int index) { + return get(index); + } + + /** See {@link Vector#setElementAt} */ + public void setElementAt(E obj, int index) { + set(index, obj); + } + + /** See {@link Vector#removeElementAt} */ + public void removeElementAt(int index) { + remove(index); + } + + /** See {@link Vector#insertElementAt} */ + public void insertElementAt(E obj, int index) { + add(index, obj); + } + + /** See {@link Vector#addElement} */ + public void addElement(E obj) { + add(obj); + } + + /** See {@link Vector#removeElement} */ + public boolean removeElement(Object obj) { + return remove(obj); + } + + /** See {@link Vector#removeAllElements} */ + public void removeAllElements() { + clear(); + } + + // other methods + + public ReadMostlyVector clone() { + Object[] a = null; + int n; + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + Object[] items = array; + if (items == null) + n = 0; + else { + int len = items.length; + if ((n = count) > len) + n = len; + a = Arrays.copyOf(items, n); + } + } finally { + lock.unlockRead(stamp); + } + return new ReadMostlyVector(a, n, capacityIncrement); + } + + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + final StampedLock lock = this.lock; + long stamp = lock.readLock(); + try { + s.defaultWriteObject(); + } finally { + lock.unlockRead(stamp); + } + } + + static final class Itr implements ListIterator, Enumeration { + final StampedLock lock; + final ReadMostlyVector list; + Object[] items; + long seq; + int cursor; + int fence; + int lastRet; + + Itr(ReadMostlyVector list, int index) { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + this.list = list; + this.lock = lock; + this.items = list.array; + this.fence = list.count; + this.cursor = index; + this.lastRet = -1; + } finally { + this.seq = lock.tryConvertToOptimisticRead(stamp); + } + if (index < 0 || index > fence) + throw new ArrayIndexOutOfBoundsException(index); + } + + public boolean hasPrevious() { + return cursor > 0; + } + + public int nextIndex() { + return cursor; + } + + public int previousIndex() { + return cursor - 1; + } + + public boolean hasNext() { + return cursor < fence; + } + + public E next() { + int i = cursor; + Object[] es = items; + if (es == null || i < 0 || i >= fence || i >= es.length) + throw new NoSuchElementException(); + @SuppressWarnings("unchecked") E e = (E)es[i]; + lastRet = i; + cursor = i + 1; + if (!lock.validate(seq)) + throw new ConcurrentModificationException(); + return e; + } + + public E previous() { + int i = cursor - 1; + Object[] es = items; + if (es == null || i < 0 || i >= fence || i >= es.length) + throw new NoSuchElementException(); + @SuppressWarnings("unchecked") E e = (E)es[i]; + lastRet = i; + cursor = i; + if (!lock.validate(seq)) + throw new ConcurrentModificationException(); + return e; + } + + public void remove() { + int i = lastRet; + if (i < 0) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + list.rawRemoveAt(i); + fence = list.count; + cursor = i; + lastRet = -1; + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + + public void set(E e) { + int i = lastRet; + Object[] es = items; + if (es == null || i < 0 | i >= fence) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + es[i] = e; + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + + public void add(E e) { + int i = cursor; + if (i < 0) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + list.rawAddAt(i, e); + items = list.array; + fence = list.count; + cursor = i + 1; + lastRet = -1; + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + + public boolean hasMoreElements() { return hasNext(); } + public E nextElement() { return next(); } + } + + static final class ReadMostlyVectorSublist + implements List, RandomAccess, java.io.Serializable { + private static final long serialVersionUID = 3041673470172026059L; + + final ReadMostlyVector list; + final int offset; + volatile int size; + + ReadMostlyVectorSublist(ReadMostlyVector list, + int offset, int size) { + this.list = list; + this.offset = offset; + this.size = size; + } + + private void rangeCheck(int index) { + if (index < 0 || index >= size) + throw new ArrayIndexOutOfBoundsException(index); + } + + public boolean add(E element) { + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + int c = size; + list.rawAddAt(c + offset, element); + size = c + 1; + } finally { + lock.unlockWrite(stamp); + } + return true; + } + + public void add(int index, E element) { + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + if (index < 0 || index > size) + throw new ArrayIndexOutOfBoundsException(index); + list.rawAddAt(index + offset, element); + ++size; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean addAll(Collection c) { + Object[] elements = c.toArray(); + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + int s = size; + int pc = list.count; + list.rawAddAllAt(offset + s, elements); + int added = list.count - pc; + size = s + added; + return added != 0; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean addAll(int index, Collection c) { + Object[] elements = c.toArray(); + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + int s = size; + if (index < 0 || index > s) + throw new ArrayIndexOutOfBoundsException(index); + int pc = list.count; + list.rawAddAllAt(index + offset, elements); + int added = list.count - pc; + size = s + added; + return added != 0; + } finally { + lock.unlockWrite(stamp); + } + } + + public void clear() { + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + list.internalClear(offset, offset + size); + size = 0; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean contains(Object o) { + return indexOf(o) >= 0; + } + + public boolean containsAll(Collection c) { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalContainsAll(c, offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof List)) + return false; + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalEquals((List)(o), offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + public E get(int index) { + if (index < 0 || index >= size) + throw new ArrayIndexOutOfBoundsException(index); + return list.get(index + offset); + } + + public int hashCode() { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalHashCode(offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + public int indexOf(Object o) { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + int idx = findFirstIndex(list.array, o, offset, offset + size); + return idx < 0 ? -1 : idx - offset; + } finally { + lock.unlockRead(stamp); + } + } + + public boolean isEmpty() { + return size() == 0; + } + + public Iterator iterator() { + return new SubItr(this, offset); + } + + public int lastIndexOf(Object o) { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + int idx = findLastIndex(list.array, o, offset + size - 1, offset); + return idx < 0 ? -1 : idx - offset; + } finally { + lock.unlockRead(stamp); + } + } + + public ListIterator listIterator() { + return new SubItr(this, offset); + } + + public ListIterator listIterator(int index) { + return new SubItr(this, index + offset); + } + + public E remove(int index) { + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + Object[] items = list.array; + int i = index + offset; + if (items == null || index < 0 || index >= size || i >= items.length) + throw new ArrayIndexOutOfBoundsException(index); + @SuppressWarnings("unchecked") E result = (E)items[i]; + list.rawRemoveAt(i); + size--; + return result; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean remove(Object o) { + final StampedLock lock = list.lock; + long stamp = lock.writeLock(); + try { + if (list.rawRemoveAt(findFirstIndex(list.array, o, offset, + offset + size))) { + --size; + return true; + } + else + return false; + } finally { + lock.unlockWrite(stamp); + } + } + + public boolean removeAll(Collection c) { + return list.lockedRemoveAll(c, offset, offset + size); + } + + public boolean retainAll(Collection c) { + return list.lockedRetainAll(c, offset, offset + size); + } + + public E set(int index, E element) { + if (index < 0 || index >= size) + throw new ArrayIndexOutOfBoundsException(index); + return list.set(index+offset, element); + } + + public int size() { + return size; + } + + public List subList(int fromIndex, int toIndex) { + int c = size; + int ssize = toIndex - fromIndex; + if (fromIndex < 0) + throw new ArrayIndexOutOfBoundsException(fromIndex); + if (toIndex > c || ssize < 0) + throw new ArrayIndexOutOfBoundsException(toIndex); + return new ReadMostlyVectorSublist(list, offset+fromIndex, ssize); + } + + public Object[] toArray() { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalToArray(offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + public T[] toArray(T[] a) { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalToArray(a, offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + public String toString() { + final StampedLock lock = list.lock; + long stamp = lock.readLock(); + try { + return list.internalToString(offset, offset + size); + } finally { + lock.unlockRead(stamp); + } + } + + } + + static final class SubItr implements ListIterator { + final ReadMostlyVectorSublist sublist; + final ReadMostlyVector list; + final StampedLock lock; + Object[] items; + long seq; + int cursor; + int origin; + int fence; + int lastRet; + + SubItr(ReadMostlyVectorSublist sublist, int index) { + final StampedLock lock = sublist.list.lock; + long stamp = lock.readLock(); + try { + this.sublist = sublist; + this.list = sublist.list; + this.lock = lock; + this.cursor = index; + this.origin = sublist.offset; + this.fence = origin + sublist.size; + this.lastRet = -1; + } finally { + this.seq = lock.tryConvertToOptimisticRead(stamp); + } + if (index < 0 || cursor > fence) + throw new ArrayIndexOutOfBoundsException(index); + } + + public int nextIndex() { + return cursor - origin; + } + + public int previousIndex() { + return cursor - origin - 1; + } + + public boolean hasNext() { + return cursor < fence; + } + + public boolean hasPrevious() { + return cursor > origin; + } + + public E next() { + int i = cursor; + Object[] es = items; + if (es == null || i < origin || i >= fence || i >= es.length) + throw new NoSuchElementException(); + @SuppressWarnings("unchecked") E e = (E)es[i]; + lastRet = i; + cursor = i + 1; + if (!lock.validate(seq)) + throw new ConcurrentModificationException(); + return e; + } + + public E previous() { + int i = cursor - 1; + Object[] es = items; + if (es == null || i < 0 || i >= fence || i >= es.length) + throw new NoSuchElementException(); + @SuppressWarnings("unchecked") E e = (E)es[i]; + lastRet = i; + cursor = i; + if (!lock.validate(seq)) + throw new ConcurrentModificationException(); + return e; + } + + public void remove() { + int i = lastRet; + if (i < 0) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + list.rawRemoveAt(i); + fence = origin + sublist.size; + cursor = i; + lastRet = -1; + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + + public void set(E e) { + int i = lastRet; + if (i < origin || i >= fence) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + list.set(i, e); + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + + public void add(E e) { + int i = cursor; + if (i < origin || i >= fence) + throw new IllegalStateException(); + if ((seq = lock.tryConvertToWriteLock(seq)) == 0) + throw new ConcurrentModificationException(); + try { + list.rawAddAt(i, e); + items = list.array; + fence = origin + sublist.size; + cursor = i + 1; + lastRet = -1; + } finally { + seq = lock.tryConvertToOptimisticRead(seq); + } + } + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/SequenceLock.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/SequenceLock.java new file mode 100644 index 0000000000..0149813d49 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/SequenceLock.java @@ -0,0 +1,688 @@ +/* + * Note: this was copied from Doug Lea's CVS repository + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +package org.cinchapi.vendor.jsr166e.extra; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.AbstractQueuedLongSynchronizer; +import java.util.Collection; +import java.io.ObjectInputStream; +import java.io.IOException; + +/** + * A reentrant mutual exclusion {@link Lock} in which each lock + * acquisition or release advances a sequence number. When the + * sequence number (accessible using {@link #getSequence()}) is odd, + * the lock is held. When it is even (i.e., ({@code lock.getSequence() + * & 1L) == 0L}), the lock is released. Method {@link #awaitAvailability} can be + * used to await availability of the lock, + * returning its current sequence number. Sequence numbers (as well as + * reentrant hold counts) are of type {@code long} to ensure that they + * will not wrap around until hundreds of years of use under current + * processor rates. A SequenceLock can be created with a specified + * number of spins. Attempts to acquire the lock in method {@link #lock} will + * retry at least the given number of times before + * blocking. If not specified, a default, possibly platform-specific, + * value is used. + * + *

+ * Except for the lack of support for specified fairness policies, or + * {@link Condition} objects, a SequenceLock can be used in the same way as + * {@link ReentrantLock}. It provides similar status and monitoring methods, + * such as {@link #isHeldByCurrentThread}. SequenceLocks may be preferable in + * contexts in which multiple threads invoke short read-only methods much more + * frequently than fully locked methods. + * + *

+ * Methods {@code awaitAvailability} and {@code getSequence} can be used + * together to define (partially) optimistic read-only methods that are usually + * more efficient than ReadWriteLocks when they apply. These methods should in + * general be structured as loops that await lock availability, then read + * {@code volatile} fields into local variables (and may further read other + * values derived from these, for example the {@code length} of a + * {@code volatile} array), and retry if the sequence number changed while doing + * so. Alternatively, because {@code awaitAvailability} accommodates reentrancy, + * a method can retry a bounded number of times before switching to locking + * mode. While conceptually straightforward, expressing these ideas can be + * verbose. For example: + * + *

+ * {
+ *     @code
+ *     class Point {
+ *         private volatile double x, y;
+ *         private final SequenceLock sl = new SequenceLock();
+ * 
+ *         // an exclusively locked method
+ *         void move(double deltaX, double deltaY) {
+ *             sl.lock();
+ *             try {
+ *                 x += deltaX;
+ *                 y += deltaY;
+ *             }
+ *             finally {
+ *                 sl.unlock();
+ *             }
+ *         }
+ * 
+ *         // A read-only method
+ *         double distanceFromOriginV1() {
+ *             double currentX, currentY;
+ *             long seq;
+ *             do {
+ *                 seq = sl.awaitAvailability();
+ *                 currentX = x;
+ *                 currentY = y;
+ *             }
+ *             while (sl.getSequence() != seq); // retry if sequence changed
+ *             return Math.sqrt(currentX * currentX + currentY * currentY);
+ *         }
+ * 
+ *         // Uses bounded retries before locking
+ *         double distanceFromOriginV2() {
+ *             double currentX, currentY;
+ *             long seq;
+ *             int retries = RETRIES_BEFORE_LOCKING; // for example 8
+ *             try {
+ *                 do {
+ *                     if(--retries < 0)
+ *                         sl.lock();
+ *                     seq = sl.awaitAvailability();
+ *                     currentX = x;
+ *                     currentY = y;
+ *                 }
+ *                 while (sl.getSequence() != seq);
+ *             }
+ *             finally {
+ *                 if(retries < 0)
+ *                     sl.unlock();
+ *             }
+ *             return Math.sqrt(currentX * currentX + currentY * currentY);
+ *         }
+ *     }
+ * }
+ * 
+ * + * @since 1.8 + * @author Doug Lea + */ +public class SequenceLock implements Lock, java.io.Serializable { + private static final long serialVersionUID = 7373984872572414699L; + + static final class Sync extends AbstractQueuedLongSynchronizer { + private static final long serialVersionUID = 2540673546047039555L; + + /** + * The number of times to spin in lock() and awaitAvailability(). + */ + final int spins; + + /** + * The number of reentrant holds on this lock. Uses a long for + * compatibility with other AbstractQueuedLongSynchronizer + * operations. Accessed only by lock holder. + */ + long holds; + + Sync(int spins) { + this.spins = spins; + } + + // overrides of AQLS methods + + public final boolean isHeldExclusively() { + return (getState() & 1L) != 0L + && getExclusiveOwnerThread() == Thread.currentThread(); + } + + public final boolean tryAcquire(long acquires) { + Thread current = Thread.currentThread(); + long c = getState(); + if((c & 1L) == 0L) { + if(compareAndSetState(c, c + 1L)) { + holds = acquires; + setExclusiveOwnerThread(current); + return true; + } + } + else if(current == getExclusiveOwnerThread()) { + holds += acquires; + return true; + } + return false; + } + + public final boolean tryRelease(long releases) { + if(Thread.currentThread() != getExclusiveOwnerThread()) + throw new IllegalMonitorStateException(); + if((holds -= releases) == 0L) { + setExclusiveOwnerThread(null); + setState(getState() + 1L); + return true; + } + return false; + } + + public final long tryAcquireShared(long unused) { + return (((getState() & 1L) == 0L) ? 1L + : (getExclusiveOwnerThread() == Thread.currentThread()) ? 0L + : -1L); + } + + public final boolean tryReleaseShared(long unused) { + return (getState() & 1L) == 0L; + } + + public final Condition newCondition() { + throw new UnsupportedOperationException(); + } + + // Other methods in support of SequenceLock + + final long getSequence() { + return getState(); + } + + final void lock() { + int k = spins; + while (!tryAcquire(1L)) { + if(k == 0) { + acquire(1L); + break; + } + --k; + } + } + + final long awaitAvailability() { + long s; + while (((s = getState()) & 1L) != 0L + && getExclusiveOwnerThread() != Thread.currentThread()) { + acquireShared(1L); + releaseShared(1L); + } + return s; + } + + final long tryAwaitAvailability(long nanos) + throws InterruptedException, TimeoutException { + Thread current = Thread.currentThread(); + for (;;) { + long s = getState(); + if((s & 1L) == 0L || getExclusiveOwnerThread() == current) { + releaseShared(1L); + return s; + } + if(!tryAcquireSharedNanos(1L, nanos)) + throw new TimeoutException(); + // since tryAcquireSharedNanos doesn't return seq + // retry with minimal wait time. + nanos = 1L; + } + } + + final boolean isLocked() { + return (getState() & 1L) != 0L; + } + + final Thread getOwner() { + return (getState() & 1L) == 0L ? null : getExclusiveOwnerThread(); + } + + final long getHoldCount() { + return isHeldExclusively() ? holds : 0; + } + + private void readObject(ObjectInputStream s) throws IOException, + ClassNotFoundException { + s.defaultReadObject(); + holds = 0L; + setState(0L); // reset to unlocked state + } + } + + private final Sync sync; + + /** + * The default spin value for constructor. Future versions of this + * class might choose platform-specific values. Currently, except + * on uniprocessors, it is set to a small value that overcomes near + * misses between releases and acquires. + */ + static final int DEFAULT_SPINS = Runtime.getRuntime().availableProcessors() > 1 ? 64 + : 0; + + /** + * Creates an instance of {@code SequenceLock} with the default + * number of retry attempts to acquire the lock before blocking. + */ + public SequenceLock() { + sync = new Sync(DEFAULT_SPINS); + } + + /** + * Creates an instance of {@code SequenceLock} that will retry + * attempts to acquire the lock at least the given number of times + * before blocking. + * + * @param spins the number of times before blocking + */ + public SequenceLock(int spins) { + sync = new Sync(spins); + } + + /** + * Returns the current sequence number of this lock. The sequence + * number is advanced upon each acquire or release action. When + * this value is odd, the lock is held; when even, it is released. + * + * @return the current sequence number + */ + public long getSequence() { + return sync.getSequence(); + } + + /** + * Returns the current sequence number when the lock is, or + * becomes, available. A lock is available if it is either + * released, or is held by the current thread. If the lock is not + * available, the current thread becomes disabled for thread + * scheduling purposes and lies dormant until the lock has been + * released by some other thread. + * + * @return the current sequence number + */ + public long awaitAvailability() { + return sync.awaitAvailability(); + } + + /** + * Returns the current sequence number if the lock is, or + * becomes, available within the specified waiting time. + * + *

+ * If the lock is not available, the current thread becomes disabled for + * thread scheduling purposes and lies dormant until one of three things + * happens: + * + *

    + * + *
  • The lock becomes available, in which case the current sequence number + * is returned. + * + *
  • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, in which case this method throws + * {@link InterruptedException}. + * + *
  • The specified waiting time elapses, in which case this method throws + * {@link TimeoutException}. + * + *
+ * + * @param timeout the time to wait for availability + * @param unit the time unit of the timeout argument + * @return the current sequence number if the lock is available + * upon return from this method + * @throws InterruptedException if the current thread is interrupted + * @throws TimeoutException if the lock was not available within + * the specified waiting time + * @throws NullPointerException if the time unit is null + */ + public long tryAwaitAvailability(long timeout, TimeUnit unit) + throws InterruptedException, TimeoutException { + return sync.tryAwaitAvailability(unit.toNanos(timeout)); + } + + /** + * Acquires the lock. + * + *

+ * If the current thread already holds this lock then the hold count is + * incremented by one and the method returns immediately without + * incrementing the sequence number. + * + *

+ * If this lock not held by another thread, this method increments the + * sequence number (which thus becomes an odd number), sets the lock hold + * count to one, and returns immediately. + * + *

+ * If the lock is held by another thread then the current thread may retry + * acquiring this lock, depending on the {@code spin} count established in + * constructor. If the lock is still not acquired, the current thread + * becomes disabled for thread scheduling purposes and lies dormant until + * enabled by some other thread releasing the lock. + */ + public void lock() { + sync.lock(); + } + + /** + * Acquires the lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. + * + *

+ * If the current thread already holds this lock then the hold count is + * incremented by one and the method returns immediately without + * incrementing the sequence number. + * + *

+ * If this lock not held by another thread, this method increments the + * sequence number (which thus becomes an odd number), sets the lock hold + * count to one, and returns immediately. + * + *

+ * If the lock is held by another thread then the current thread may retry + * acquiring this lock, depending on the {@code spin} count established in + * constructor. If the lock is still not acquired, the current thread + * becomes disabled for thread scheduling purposes and lies dormant until + * one of two things happens: + * + *

    + * + *
  • The lock is acquired by the current thread; or + * + *
  • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread. + * + *
+ * + *

+ * If the lock is acquired by the current thread then the lock hold count is + * set to one and the sequence number is incremented. + * + *

+ * If the current thread: + * + *

    + * + *
  • has its interrupted status set on entry to this method; or + * + *
  • is {@linkplain Thread#interrupt interrupted} while acquiring the + * lock, + * + *
+ * + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. + * + *

+ * In this implementation, as this method is an explicit interruption point, + * preference is given to responding to the interrupt over normal or + * reentrant acquisition of the lock. + * + * @throws InterruptedException if the current thread is interrupted + */ + public void lockInterruptibly() throws InterruptedException { + sync.acquireInterruptibly(1L); + } + + /** + * Acquires the lock only if it is not held by another thread at the time + * of invocation. + * + *

+ * If the current thread already holds this lock then the hold count is + * incremented by one and the method returns {@code true} without + * incrementing the sequence number. + * + *

+ * If this lock not held by another thread, this method increments the + * sequence number (which thus becomes an odd number), sets the lock hold + * count to one, and returns {@code true}. + * + *

+ * If the lock is held by another thread then this method returns + * {@code false}. + * + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} otherwise + */ + public boolean tryLock() { + return sync.tryAcquire(1L); + } + + /** + * Acquires the lock if it is not held by another thread within the given + * waiting time and the current thread has not been + * {@linkplain Thread#interrupt interrupted}. + * + *

+ * If the current thread already holds this lock then the hold count is + * incremented by one and the method returns immediately without + * incrementing the sequence number. + * + *

+ * If this lock not held by another thread, this method increments the + * sequence number (which thus becomes an odd number), sets the lock hold + * count to one, and returns immediately. + * + *

+ * If the lock is held by another thread then the current thread may retry + * acquiring this lock, depending on the {@code spin} count established in + * constructor. If the lock is still not acquired, the current thread + * becomes disabled for thread scheduling purposes and lies dormant until + * one of three things happens: + * + *

    + * + *
  • The lock is acquired by the current thread; or + * + *
  • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread; or + * + *
  • The specified waiting time elapses + * + *
+ * + *

+ * If the lock is acquired then the value {@code true} is returned and the + * lock hold count is set to one. + * + *

+ * If the current thread: + * + *

    + * + *
  • has its interrupted status set on entry to this method; or + * + *
  • is {@linkplain Thread#interrupt interrupted} while acquiring the + * lock, + * + *
+ * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. + * + *

+ * If the specified waiting time elapses then the value {@code false} is + * returned. If the time is less than or equal to zero, the method will not + * wait at all. + * + *

+ * In this implementation, as this method is an explicit interruption point, + * preference is given to responding to the interrupt over normal or + * reentrant acquisition of the lock, and over reporting the elapse of the + * waiting time. + * + * @param timeout the time to wait for the lock + * @param unit the time unit of the timeout argument + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} if the waiting time elapsed before + * the lock could be acquired + * @throws InterruptedException if the current thread is interrupted + * @throws NullPointerException if the time unit is null + */ + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + return sync.tryAcquireNanos(1L, unit.toNanos(timeout)); + } + + /** + * Attempts to release this lock. + * + *

+ * If the current thread is the holder of this lock then the hold count is + * decremented. If the hold count is now zero then the sequence number is + * incremented (thus becoming an even number) and the lock is released. If + * the current thread is not the holder of this lock then + * {@link IllegalMonitorStateException} is thrown. + * + * @throws IllegalMonitorStateException if the current thread does not + * hold this lock + */ + public void unlock() { + sync.release(1); + } + + /** + * Throws UnsupportedOperationException. SequenceLocks + * do not support Condition objects. + * + * @throws UnsupportedOperationException always + */ + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + /** + * Queries the number of holds on this lock by the current thread. + * + *

+ * A thread has a hold on a lock for each lock action that is not matched by + * an unlock action. + * + *

+ * The hold count information is typically only used for testing and + * debugging purposes. + * + * @return the number of holds on this lock by the current thread, + * or zero if this lock is not held by the current thread + */ + public long getHoldCount() { + return sync.getHoldCount(); + } + + /** + * Queries if this lock is held by the current thread. + * + * @return {@code true} if current thread holds this lock and {@code false} + * otherwise + */ + public boolean isHeldByCurrentThread() { + return sync.isHeldExclusively(); + } + + /** + * Queries if this lock is held by any thread. This method is + * designed for use in monitoring of the system state, + * not for synchronization control. + * + * @return {@code true} if any thread holds this lock and {@code false} + * otherwise + */ + public boolean isLocked() { + return sync.isLocked(); + } + + /** + * Returns the thread that currently owns this lock, or {@code null} if not + * owned. When this method is called by a + * thread that is not the owner, the return value reflects a + * best-effort approximation of current lock status. For example, + * the owner may be momentarily {@code null} even if there are + * threads trying to acquire the lock but have not yet done so. + * This method is designed to facilitate construction of + * subclasses that provide more extensive lock monitoring + * facilities. + * + * @return the owner, or {@code null} if not owned + */ + protected Thread getOwner() { + return sync.getOwner(); + } + + /** + * Queries whether any threads are waiting to acquire this lock. Note that + * because cancellations may occur at any time, a {@code true} return does + * not guarantee that any other thread will ever + * acquire this lock. This method is designed primarily for use in + * monitoring of the system state. + * + * @return {@code true} if there may be other threads waiting to + * acquire the lock + */ + public final boolean hasQueuedThreads() { + return sync.hasQueuedThreads(); + } + + /** + * Queries whether the given thread is waiting to acquire this + * lock. Note that because cancellations may occur at any time, a + * {@code true} return does not guarantee that this thread + * will ever acquire this lock. This method is designed primarily for use + * in monitoring of the system state. + * + * @param thread the thread + * @return {@code true} if the given thread is queued waiting for this lock + * @throws NullPointerException if the thread is null + */ + public final boolean hasQueuedThread(Thread thread) { + return sync.isQueued(thread); + } + + /** + * Returns an estimate of the number of threads waiting to + * acquire this lock. The value is only an estimate because the number of + * threads may change dynamically while this method traverses + * internal data structures. This method is designed for use in + * monitoring of the system state, not for synchronization + * control. + * + * @return the estimated number of threads waiting for this lock + */ + public final int getQueueLength() { + return sync.getQueueLength(); + } + + /** + * Returns a collection containing threads that may be waiting to + * acquire this lock. Because the actual set of threads may change + * dynamically while constructing this result, the returned + * collection is only a best-effort estimate. The elements of the + * returned collection are in no particular order. This method is + * designed to facilitate construction of subclasses that provide + * more extensive monitoring facilities. + * + * @return the collection of threads + */ + protected Collection getQueuedThreads() { + return sync.getQueuedThreads(); + } + + /** + * Returns a string identifying this lock, as well as its lock state. + * The state, in brackets, includes either the String {@code "Unlocked"} or + * the String {@code "Locked by"} followed by the + * {@linkplain Thread#getName name} of the owning thread. + * + * @return a string identifying this lock, as well as its lock state + */ + public String toString() { + Thread o = sync.getOwner(); + return super.toString() + + ((o == null) ? "[Unlocked]" : "[Locked by thread " + + o.getName() + "]"); + } + +} diff --git a/licenses/jsr166e b/licenses/jsr166e new file mode 100644 index 0000000000..1625c17936 --- /dev/null +++ b/licenses/jsr166e @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file From e34f4b625134dc3ff08feec37ac159d24bbda032 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 5 Oct 2014 19:20:34 -0400 Subject: [PATCH 004/100] fix import --- .../main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java index 0120671f8f..4e3a1af8ee 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/LongAdderTable.java @@ -7,7 +7,7 @@ package org.cinchapi.vendor.jsr166e; import org.cinchapi.vendor.jsr166e.LongAdder; -import org.cinchapi.vendor.org.cinchapi.vendor.jsr166e.ConcurrentHashMapV8; +import org.cinchapi.vendor.jsr166e.ConcurrentHashMapV8; import java.util.Map; import java.util.Set; From b8d2bc48d1332657fa21b497b49389217bd456b8 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Mon, 6 Oct 2014 06:40:28 -0400 Subject: [PATCH 005/100] remove warnings --- .../main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java | 2 ++ .../main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java | 3 ++- .../java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java | 1 + .../org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java | 5 +++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java index f0dc0d0795..d58b77a939 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinPool.java @@ -1877,6 +1877,7 @@ private final void helpRelease(long c, WorkQueue[] ws, WorkQueue w, * @return 0 if no progress can be made, negative if task * known complete, else positive */ + @SuppressWarnings("rawtypes") private int tryHelpStealer(WorkQueue joiner, ForkJoinTask task) { int stat = 0, steps = 0; // bound to avoid cycles if(task != null && joiner != null && joiner.base - joiner.top >= 0) { // hoist @@ -2372,6 +2373,7 @@ private boolean tryTerminate(boolean now, boolean enable) { * Returns common pool queue for a thread that has submitted at * least one task. */ + @SuppressWarnings("unused") static WorkQueue commonSubmitterQueue() { Submitter z; ForkJoinPool p; diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java index fc1162eb8a..3df2f6ede4 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ForkJoinTask.java @@ -538,6 +538,7 @@ private void clearExceptionalCompletion() { * * @return the exception, or null if none */ + @SuppressWarnings("unused") private Throwable getThrowableException() { if((status & DONE_MASK) != EXCEPTIONAL) return null; @@ -805,7 +806,7 @@ public static > Collection invokeAll( invokeAll(tasks.toArray(new ForkJoinTask[tasks.size()])); return tasks; } - @SuppressWarnings("unchecked") List> ts = (List>) tasks; + List> ts = (List>) tasks; Throwable ex = null; int last = ts.size() - 1; for (int i = last; i >= 0; --i) { diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java index 03c78891e2..ffd2ae06f8 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/ThreadLocalRandom.java @@ -58,6 +58,7 @@ public class ThreadLocalRandom extends Random { // Padding to help avoid memory contention among seed updates in // different TLRs in the common case that they are located near // each other. + @SuppressWarnings("unused") private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; /** diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java index 7fb3ef0884..d0cc135ba8 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/jsr166e/extra/ReadMostlyVector.java @@ -8,6 +8,7 @@ */ package org.cinchapi.vendor.jsr166e.extra; import org.cinchapi.vendor.jsr166e.StampedLock; + import java.util.*; /** @@ -622,6 +623,7 @@ public int indexOf(Object o) { return idx; } + @SuppressWarnings("unused") public boolean isEmpty() { final StampedLock lock = this.lock; long stamp = lock.tryOptimisticRead(); @@ -711,12 +713,14 @@ public boolean retainAll(Collection c) { return oldValue; } + @SuppressWarnings("unused") public int size() { final StampedLock lock = this.lock; long stamp = lock.tryOptimisticRead(); return count; // no need for validation } + @SuppressWarnings("unused") private int lockedSize() { int n; final StampedLock lock = this.lock; @@ -1262,6 +1266,7 @@ static final class ReadMostlyVectorSublist this.size = size; } + @SuppressWarnings("unused") private void rangeCheck(int index) { if (index < 0 || index >= size) throw new ArrayIndexOutOfBoundsException(index); From b8f215fb6baa90fbaa6800f7a9d8f5b6a23fc58b Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 9 Oct 2014 11:31:41 -0400 Subject: [PATCH 006/100] use murmur3 hashing instead of md5 for tokens --- .../java/org/cinchapi/concourse/server/concurrent/Token.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 2f742ac237..9a553cca36 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -60,12 +60,12 @@ public static Token fromByteBuffer(ByteBuffer bytes) { * @return the Token */ public static Token wrap(Object... objects) { - return new Token(ByteBuffer.wrap(Hashing.md5() + return new Token(ByteBuffer.wrap(Hashing.murmur3_128() .hashUnencodedChars(Arrays.toString(objects)).asBytes())); } /** - * The sequence of bytes is a MD5 hash. + * The sequence of bytes is a 128-bit (16 byte) hash. */ private final ByteBuffer bytes; From 788ca481951ee8cc02e321f30d3da15be095cb67 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 11:45:27 -0400 Subject: [PATCH 007/100] use stamped lock for bloom filters --- .../server/storage/cache/BloomFilter.java | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index 9a7c6ef8aa..ace20a0aeb 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -33,11 +33,14 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantReadWriteLock; +import javax.annotation.concurrent.ThreadSafe; + import org.cinchapi.concourse.server.io.Byteable; import org.cinchapi.concourse.server.io.Composite; import org.cinchapi.concourse.server.io.FileSystem; import org.cinchapi.concourse.server.io.Serializables; import org.cinchapi.concourse.server.io.Syncable; +import org.cinchapi.vendor.jsr166e.StampedLock; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; @@ -50,6 +53,7 @@ * * @author jnelson */ +@ThreadSafe public class BloomFilter implements Syncable { /** @@ -137,7 +141,7 @@ protected ObjectStreamClass readClassDescriptor() * Lock used to ensure the object is ThreadSafe. This lock provides access * to a masterLock.readLock()() and masterLock.writeLock()(). */ - private final ReentrantReadWriteLock masterLock = new ReentrantReadWriteLock(); + private final StampedLock lock = new StampedLock(); /** * The wrapped bloom filter. This is where the data is actually stored. @@ -176,17 +180,21 @@ private BloomFilter(String file, @Override public void sync() { - masterLock.readLock().lock(); + Preconditions.checkState(file != null, "Cannot sync a " + + "BloomFilter that does not have an associated file"); FileChannel channel = FileSystem.getFileChannel(file); - try { - Preconditions.checkState(file != null, "Cannot sync a " - + "BloomFilter that does not have an associated file"); - Serializables.write(source, channel); // CON-164 - } - finally { - FileSystem.closeFileChannel(channel); - masterLock.readLock().unlock(); + long stamp = lock.tryOptimisticRead(); + Serializables.write(source, channel); // CON-164 + if(!lock.validate(stamp)) { + stamp = lock.readLock(); + try { + Serializables.write(source, channel); // CON-164 + } + finally { + lock.unlockRead(stamp); + } } + FileSystem.closeFileChannel(channel); } /** @@ -197,13 +205,18 @@ public void sync() { * @return {@code true} if {@code byteables} might exist */ public boolean mightContain(Byteable... byteables) { - masterLock.readLock().lock(); - try { - return source.mightContain(Composite.create(byteables)); - } - finally { - masterLock.readLock().unlock(); + long stamp = lock.tryOptimisticRead(); + boolean mightContain = source.mightContain(Composite.create(byteables)); + if(!lock.validate(stamp)) { + stamp = lock.readLock(); + try { + mightContain = source.mightContain(Composite.create(byteables)); + } + finally { + lock.unlockRead(stamp); + } } + return mightContain; } /** @@ -224,12 +237,12 @@ public boolean mightContain(Byteable... byteables) { * called. */ public boolean put(Byteable... byteables) { - masterLock.writeLock().lock(); + long stamp = lock.writeLock(); try { return source.put(Composite.create(byteables)); } finally { - masterLock.writeLock().unlock(); + lock.unlockWrite(stamp); } } } From 3b7306ee515471f5d3384715870bbd47c09a9260 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 11:46:24 -0400 Subject: [PATCH 008/100] use hashcode based hasing for Tokens --- .../concourse/server/concurrent/Token.java | 6 +- .../org/cinchapi/concourse/util/TArrays.java | 72 ++++++++++++++++++ .../cinchapi/concourse/util/TArraysTest.java | 76 +++++++++++++++++++ 3 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java create mode 100644 concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 9a553cca36..58f2465796 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -24,12 +24,11 @@ package org.cinchapi.concourse.server.concurrent; import java.nio.ByteBuffer; -import java.util.Arrays; import org.cinchapi.concourse.server.io.Byteable; import org.cinchapi.concourse.util.ByteBuffers; +import org.cinchapi.concourse.util.TArrays; -import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; /** @@ -60,8 +59,7 @@ public static Token fromByteBuffer(ByteBuffer bytes) { * @return the Token */ public static Token wrap(Object... objects) { - return new Token(ByteBuffer.wrap(Hashing.murmur3_128() - .hashUnencodedChars(Arrays.toString(objects)).asBytes())); + return new Token(TArrays.hash(objects)); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java new file mode 100644 index 0000000000..efce7461a3 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java @@ -0,0 +1,72 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +import com.google.common.base.Throwables; + +/** + * Utilities for dealing with generic arrays. + * + * @author jnelson + */ +public final class TArrays { + + /** + * Convert an array of objects (possibly of mixed types) to a ByteBuffer. + * This method, in some sense, is akin to returning a hash of the objects, + * except the size of the returned buffer will vary depending upon the + * number of objects that are in the array. + *

+ * This method assumes that the hashCode of the objects in the array are + * defined in a manner that will yield the same value between JVM sessions. + * There are no guarantees made about the content of returned ByteBuffer + * except that objects that are considered equal will always returned byte + * buffers that are equal. + *

+ * + * @param objects + * @return the hash for the objects + */ + public static ByteBuffer hash(Object... objects) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (Object object : objects) { + // We make a best effort to "uniquely" identify each object in + // the array by that object's hashcode and the name of the + // object's class. + baos.write(object.hashCode()); + baos.write(object.getClass().getName().getBytes()); + } + return ByteBuffer.wrap(baos.toByteArray()); + } + catch (IOException e) { + throw Throwables.propagate(e); + } + } + +} diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java new file mode 100644 index 0000000000..871df2d467 --- /dev/null +++ b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java @@ -0,0 +1,76 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.nio.ByteBuffer; +import java.util.List; + +import org.cinchapi.concourse.ConcourseBaseTest; +import org.cinchapi.concourse.server.model.PrimaryKey; +import org.cinchapi.concourse.server.model.Text; +import org.cinchapi.concourse.server.model.Value; +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.collect.Lists; + +/** + * Unit tests for the {@link TArrays} class. + * + * @author jnelson + */ +public class TArraysTest extends ConcourseBaseTest { + + @Test + public void testConsistentHashing() { + // Test that the byte buffer that is returned is consistent from JVM + // session to JVM (e.g. it depends on persistent data). + List bytes = Lists.newArrayList(-58, 111, 114, 103, 46, 99, + 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, 110, 99, 111, + 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, 114, 46, 109, + 111, 100, 101, 108, 46, 84, 101, 120, 116, 19, 111, 114, 103, + 46, 99, 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, 110, 99, + 111, 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, 114, 46, + 109, 111, 100, 101, 108, 46, 86, 97, 108, 117, 101, 1, 111, + 114, 103, 46, 99, 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, + 110, 99, 111, 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, + 114, 46, 109, 111, 100, 101, 108, 46, 80, 114, 105, 109, 97, + 114, 121, 75, 101, 121); + Object[] data = { Text.wrap("foo"), + Value.wrap(Convert.javaToThrift("bar")), PrimaryKey.wrap(1) }; + ByteBuffer buf = TArrays.hash(data); + for (int i = 0; i < buf.capacity(); i++) { + Assert.assertEquals(buf.get(), bytes.get(i).byteValue()); + } + } + + @Test + public void testEqualObjectsHaveEqualHash(){ + Object[] data = {TestData.getText(), TestData.getValue(), TestData.getPrimaryKey()}; + ByteBuffer a = TArrays.hash(data); + ByteBuffer b = TArrays.hash(data); + Assert.assertEquals(a, b); + } + +} From d2c693a71b43e1c7dd6ab08871589ada3e722b6f Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 11:48:40 -0400 Subject: [PATCH 009/100] no warnings and no unnecessary variable creation --- .../concourse/server/storage/cache/BloomFilter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index ace20a0aeb..2b0e7bb413 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -31,7 +31,6 @@ import java.io.ObjectStreamClass; import java.nio.channels.FileChannel; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.concurrent.ThreadSafe; @@ -206,11 +205,12 @@ public void sync() { */ public boolean mightContain(Byteable... byteables) { long stamp = lock.tryOptimisticRead(); - boolean mightContain = source.mightContain(Composite.create(byteables)); + Composite composite = Composite.create(byteables); + boolean mightContain = source.mightContain(composite); if(!lock.validate(stamp)) { stamp = lock.readLock(); try { - mightContain = source.mightContain(Composite.create(byteables)); + mightContain = source.mightContain(composite); } finally { lock.unlockRead(stamp); From 33bdfdbf958a82fbb3169e96ecd54c77eadf552e Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 12:01:28 -0400 Subject: [PATCH 010/100] reset channel if the optimisitc read does not work --- .../concourse/server/storage/cache/BloomFilter.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index 2b0e7bb413..2e526d659a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -178,6 +178,10 @@ private BloomFilter(String file, } @Override + // NOTE: It seems counter intuitive, but we take a read lock in this + // method instead of a write lock so that readers can concurrently use + // the bloom filter in memory while the content is being written to + // disk. public void sync() { Preconditions.checkState(file != null, "Cannot sync a " + "BloomFilter that does not have an associated file"); @@ -187,8 +191,12 @@ public void sync() { if(!lock.validate(stamp)) { stamp = lock.readLock(); try { + channel.position(0); Serializables.write(source, channel); // CON-164 } + catch (IOException e) { + throw Throwables.propagate(e); + } finally { lock.unlockRead(stamp); } From ec6b139dac13abb911a5eddb729dfff90d7f9b59 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 14:14:39 -0400 Subject: [PATCH 011/100] use list instead of set for ranges that are created --- .../concourse/server/concurrent/RangeTokens.java | 9 ++++----- .../cinchapi/concourse/server/storage/Engine.java | 6 +++--- .../concourse/server/storage/Transaction.java | 13 +++++++------ .../server/concurrent/RangeTokensTest.java | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeTokens.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeTokens.java index c8df778692..ba0467b270 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeTokens.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeTokens.java @@ -23,13 +23,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -import java.util.Set; - +import java.util.List; import org.cinchapi.common.util.Range; import org.cinchapi.concourse.server.model.Value; import org.cinchapi.concourse.thrift.Operator; -import com.google.common.collect.Sets; +import com.google.common.collect.Lists; /** * A collection of utility functions for dealing with {@link RangeToken} @@ -45,8 +44,8 @@ public final class RangeTokens { * @param rangeToken * @return the Range */ - public static Set> convertToRange(RangeToken rangeToken) { - Set> ranges = Sets.newHashSet(); + public static Iterable> convertToRange(RangeToken rangeToken) { + List> ranges = Lists.newArrayListWithCapacity(2); if(rangeToken.getOperator() == Operator.EQUALS || rangeToken.getOperator() == Operator.REGEX || rangeToken.getOperator() == null) { // null operator means diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 650ba11668..463ce2b194 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -355,7 +355,7 @@ public boolean add(String key, TObject value, long record) { public void addVersionChangeListener(Token token, VersionChangeListener listener) { if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { rangeVersionChangeListeners.put(range, listener); @@ -519,7 +519,7 @@ public long getVersion(String key, long record) { @Restricted public void notifyVersionChange(Token token) { if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { for (VersionChangeListener listener : rangeVersionChangeListeners @@ -554,7 +554,7 @@ public boolean remove(String key, TObject value, long record) { public void removeVersionChangeListener(Token token, VersionChangeListener listener) { if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { rangeVersionChangeListeners.remove(range, listener); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Transaction.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Transaction.java index dc44d95d20..e15ef5ff07 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Transaction.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Transaction.java @@ -31,7 +31,6 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.util.Iterator; -import java.util.Set; import org.cinchapi.common.util.NonBlockingHashMultimap; import org.cinchapi.common.util.NonBlockingRangeMap; @@ -166,10 +165,12 @@ long record = write.getRecord().longValue(); public void addVersionChangeListener(Token token, VersionChangeListener listener) { ((Compoundable) destination).addVersionChangeListener(token, this); - // This rest of this implementation is unnecessary since Transactions are assumed to - // be isolated (e.g. single-threaded), but is kept here for unit test consistency. + // This rest of this implementation is unnecessary since Transactions + // are assumed to + // be isolated (e.g. single-threaded), but is kept here for unit test + // consistency. if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { rangeVersionChangeListeners.put(range, listener); @@ -202,7 +203,7 @@ public long getVersion(String key, long record) { @Restricted public void notifyVersionChange(Token token) { if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { for (VersionChangeListener listener : rangeVersionChangeListeners @@ -226,7 +227,7 @@ public void removeVersionChangeListener(Token token, // This implementation is unnecessary since Transactions are assumed to // be isolated (e.g. single-threaded), but is kept here for consistency. if(token instanceof RangeToken) { - Set> ranges = RangeTokens + Iterable> ranges = RangeTokens .convertToRange((RangeToken) token); for (Range range : ranges) { rangeVersionChangeListeners.remove(range, listener); diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/RangeTokensTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/RangeTokensTest.java index cc1767a92b..9ae50f81dc 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/RangeTokensTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/RangeTokensTest.java @@ -23,8 +23,6 @@ */ package org.cinchapi.concourse.server.concurrent; -import java.util.Set; - import org.cinchapi.common.util.Range; import org.cinchapi.concourse.ConcourseBaseTest; import org.cinchapi.concourse.server.model.Text; @@ -35,7 +33,7 @@ import org.junit.Test; import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; +import com.google.common.collect.Lists; /** * Unit tests for {@link RangeTokens}. @@ -74,10 +72,12 @@ public void testConvertNotEqualRangeToken() { Operator operator = Operator.NOT_EQUALS; Value value = TestData.getValue(); RangeToken token = RangeToken.forReading(key, operator, value); - Set> ranges = RangeTokens.convertToRange(token); - Assert.assertEquals(Sets.newHashSet( - Range.exclusive(Value.NEGATIVE_INFINITY, value), - Range.exclusive(value, Value.POSITIVE_INFINITY)), ranges); + Iterable> ranges = RangeTokens.convertToRange(token); + Assert.assertEquals( + Lists.newArrayList( + Range.exclusive(Value.NEGATIVE_INFINITY, value), + Range.exclusive(value, Value.POSITIVE_INFINITY)), + ranges); } @Test From cbd80bac7dae8296762bc0b35e38320fb4faeed6 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 18:41:18 -0400 Subject: [PATCH 012/100] fuck jline --- .../main/java/org/cinchapi/common/util/NonBlockingRangeMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingRangeMap.java b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingRangeMap.java index 2e1358e07a..01b70a3e1a 100644 --- a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingRangeMap.java +++ b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingRangeMap.java @@ -25,7 +25,7 @@ import java.util.Set; -import com.beust.jcommander.internal.Sets; +import com.google.common.collect.Sets; /** * A {@link NonBlockingRangeMap} is one that focuses on high throughput in the From 94b4424d1390ebbf03d400b69cf483784bec7133 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 18:41:42 -0400 Subject: [PATCH 013/100] compare numbers of same type naturally --- .../org/cinchapi/concourse/util/Numbers.java | 34 ++++++++++++++++--- .../cinchapi/concourse/util/NumbersTest.java | 7 ++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java b/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java index 5264a37c2a..58f5328519 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java @@ -55,10 +55,36 @@ public static double percent(Number numerator, Number denominator) { * greater than {@code b}. */ public static int compare(Number a, Number b) { - // TODO review - BigDecimal first = new BigDecimal(a.toString()); - BigDecimal second = new BigDecimal(b.toString()); - return first.compareTo(second); + if((a.getClass() == Integer.class || a.getClass() == int.class) + && (b.getClass() == Integer.class || b.getClass() == int.class)) { + return Integer.compare(a.intValue(), b.intValue()); + } + else if((a.getClass() == Long.class || a.getClass() == long.class) + && (b.getClass() == Long.class || b.getClass() == long.class)) { + return Long.compare(a.longValue(), b.longValue()); + } + else if((a.getClass() == Float.class || a.getClass() == float.class) + && (b.getClass() == Float.class || b.getClass() == float.class)) { + return Float.compare(a.floatValue(), b.floatValue()); + } + else if((a.getClass() == Double.class || a.getClass() == double.class) + && (b.getClass() == Double.class || b.getClass() == double.class)) { + return Double.compare(a.doubleValue(), b.doubleValue()); + } + else if((a.getClass() == Short.class || a.getClass() == short.class) + && (b.getClass() == Short.class || b.getClass() == short.class)) { + return Short.compare(a.shortValue(), b.shortValue()); + } + else if((a.getClass() == Byte.class || a.getClass() == byte.class) + && (b.getClass() == Byte.class || b.getClass() == byte.class)) { + return Long.compare(a.byteValue(), b.byteValue()); + } + else { + // TODO review + BigDecimal first = new BigDecimal(a.toString()); + BigDecimal second = new BigDecimal(b.toString()); + return first.compareTo(second); + } } /** diff --git a/concourse/src/test/java/org/cinchapi/concourse/util/NumbersTest.java b/concourse/src/test/java/org/cinchapi/concourse/util/NumbersTest.java index b283ff9bf7..1d441e6b50 100644 --- a/concourse/src/test/java/org/cinchapi/concourse/util/NumbersTest.java +++ b/concourse/src/test/java/org/cinchapi/concourse/util/NumbersTest.java @@ -42,5 +42,12 @@ public void testMax() { public void testMin() { Assert.assertEquals(-11, Numbers.min(1, 8, 2.4, 3.789, 10, -11)); } + + @Test + public void testCompareNumbers(){ + Number a = Random.getNegativeNumber(); + Number b = Random.getPositiveNumber(); + Assert.assertTrue(Numbers.compare(a, b) < 0); + } } From 4446f837da407edb8b126f7ecb701b417ea79677 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 14 Oct 2014 21:25:40 -0400 Subject: [PATCH 014/100] don't use bytearrayoutputstream for token generation --- .../org/cinchapi/concourse/util/TArrays.java | 28 +++++++------------ .../cinchapi/concourse/util/TArraysTest.java | 12 +------- 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java index efce7461a3..530f786702 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java @@ -23,12 +23,8 @@ */ package org.cinchapi.concourse.util; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.nio.ByteBuffer; -import com.google.common.base.Throwables; - /** * Utilities for dealing with generic arrays. * @@ -50,23 +46,19 @@ public final class TArrays { *

* * @param objects - * @return the hash for the objects + * @return the hash for the objects */ public static ByteBuffer hash(Object... objects) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (Object object : objects) { - // We make a best effort to "uniquely" identify each object in - // the array by that object's hashcode and the name of the - // object's class. - baos.write(object.hashCode()); - baos.write(object.getClass().getName().getBytes()); - } - return ByteBuffer.wrap(baos.toByteArray()); - } - catch (IOException e) { - throw Throwables.propagate(e); + ByteBuffer bytes = ByteBuffer.allocate(8 * objects.length); + for (Object object : objects) { + // We make a best effort to "uniquely" identify each object in + // the array by that object's hashcode and the name of the + // object's class. + bytes.putInt(object.hashCode()); + bytes.putInt(object.getClass().getName().hashCode()); } + bytes.flip(); + return bytes; } } diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java index 871df2d467..7502951066 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java @@ -46,17 +46,7 @@ public class TArraysTest extends ConcourseBaseTest { public void testConsistentHashing() { // Test that the byte buffer that is returned is consistent from JVM // session to JVM (e.g. it depends on persistent data). - List bytes = Lists.newArrayList(-58, 111, 114, 103, 46, 99, - 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, 110, 99, 111, - 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, 114, 46, 109, - 111, 100, 101, 108, 46, 84, 101, 120, 116, 19, 111, 114, 103, - 46, 99, 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, 110, 99, - 111, 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, 114, 46, - 109, 111, 100, 101, 108, 46, 86, 97, 108, 117, 101, 1, 111, - 114, 103, 46, 99, 105, 110, 99, 104, 97, 112, 105, 46, 99, 111, - 110, 99, 111, 117, 114, 115, 101, 46, 115, 101, 114, 118, 101, - 114, 46, 109, 111, 100, 101, 108, 46, 80, 114, 105, 109, 97, - 114, 121, 75, 101, 121); + List bytes = Lists.newArrayList(0,1,-116,-58,-22,32,-65,21,0,1,124,19,90,17,84,-119,0,0,0,1,-55,19,-84,-27); Object[] data = { Text.wrap("foo"), Value.wrap(Convert.javaToThrift("bar")), PrimaryKey.wrap(1) }; ByteBuffer buf = TArrays.hash(data); From 93e3638f5c92daf663ea629ac15301e4dfff7cc1 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 15 Oct 2014 09:48:33 -0400 Subject: [PATCH 015/100] precompile pattern for env sanitization --- .../cinchapi/concourse/util/Environments.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/Environments.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/Environments.java index 73644201b4..ffdbcb7346 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/Environments.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/Environments.java @@ -25,6 +25,9 @@ import static org.cinchapi.concourse.server.GlobalState.DEFAULT_ENVIRONMENT; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.cinchapi.concourse.server.GlobalState; import com.google.common.base.Strings; @@ -36,6 +39,13 @@ */ public final class Environments { + /** + * The pattern that is used for sanitizing the environment. We compile it + * statically so that we can avoid the overhead of doing it for every + * sanitization request. + */ + private static final Pattern SANITIZER = Pattern.compile("[^A-Za-z0-9_]"); + /** * Ensure that we return the correct environment with * alphanumeric-char name for the specified {@code env}. @@ -47,11 +57,10 @@ public final class Environments { */ public static String sanitize(String env) { env = Strings.isNullOrEmpty(env) ? DEFAULT_ENVIRONMENT : env; - env = env.replaceAll("[^A-Za-z0-9_]", ""); // ConcourseServer checks to - // make sure sanitizing the - // default environment won't - // turn it into the empty - // string + Matcher matcher = SANITIZER.matcher(env); + env = matcher.replaceAll(""); // ConcourseServer checks to make sure + // sanitizing the default environment + // won't turn it into an empty string return env; } From 08172b51d1c14ce805f5b250058e6b2aafb101c9 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 16 Oct 2014 10:58:12 -0400 Subject: [PATCH 016/100] add transferBytes methods to reduce the amount of bytebuffer creation and copying Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/security/LegacyAccessManager.java concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java --- .../concourse/server/concurrent/Token.java | 7 +- .../concourse/server/io/Byteable.java | 29 ++- .../concourse/server/io/Composite.java | 5 + .../concourse/server/model/Position.java | 38 ++-- .../concourse/server/model/PrimaryKey.java | 40 ++-- .../cinchapi/concourse/server/model/Text.java | 15 +- .../concourse/server/model/Value.java | 42 +++-- .../server/storage/AtomicOperation.java | 9 +- .../concourse/server/storage/db/Block.java | 177 ++++++++++-------- .../server/storage/db/BlockIndex.java | 59 +++--- .../concourse/server/storage/db/Revision.java | 66 ++++--- .../concourse/server/storage/temp/Buffer.java | 2 +- .../concourse/server/storage/temp/Write.java | 36 ++-- .../cinchapi/concourse/util/ByteBuffers.java | 37 ++-- 14 files changed, 339 insertions(+), 223 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 58f2465796..8ce7c7ef1e 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -88,7 +88,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { return ByteBuffers.asReadOnlyBuffer(bytes); } - + @Override public int hashCode() { return getBytes().hashCode(); @@ -105,4 +105,9 @@ public String toString() { .encode(ByteBuffers.toByteArray(getBytes())).toLowerCase(); } + @Override + public void transferBytes(ByteBuffer buffer) { + ByteBuffers.copy(bytes, buffer); + } + } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java index faf4c1ac24..1aa8d88ba9 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java @@ -59,18 +59,41 @@ */ public interface Byteable { + /** + * Returns a byte sequence that represents this object. + * + * @return the byte sequence. + */ + public ByteBuffer getBytes(); + /** * Returns the total number of bytes used to represent this object. + *

+ * It is recommended that the value returned from this method NOT depend on + * a call to {@link #getBytes()}. Therefore, the implementing class should + * keep track of the size separately, if necessary. + *

* * @return the number of bytes. */ public int size(); /** - * Returns a byte sequence that represents this object. + * Copy the byte sequence that represents this object to the {@code buffer}. + *

+ * If the binary representation for this object depends on that of another + * Byteable, then the implementation of this method should gather those + * bytes using the {@link #transferBytes(ByteBuffer)} method for the + * other Byteable. + *

+ *

+ * DO NOT make any modifications to {@code buffer} other + * than filling it with bytes for this class (i.e. do not rewind the buffer + * or change its position). + *

* - * @return the byte sequence. + * @param buffer */ - public ByteBuffer getBytes(); + public void transferBytes(ByteBuffer buffer); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index 9b5c976750..21b76e5b0b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -128,4 +128,9 @@ public String toString() { .toString(); } + @Override + public void transferBytes(ByteBuffer buffer) { + ByteBuffers.copy(bytes, buffer); + } + } \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java index 5455af3d68..ac48ebbdab 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java @@ -79,9 +79,10 @@ public static Position wrap(PrimaryKey primaryKey, int index) { public static final int SIZE = PrimaryKey.SIZE + 4; // index /** - * The PrimaryKey of the record that this Position represents. + * A cached copy of the binary representation that is returned from + * {@link #getBytes()}. */ - private final PrimaryKey primaryKey; + private transient ByteBuffer bytes; /** * The index that this Position represents. @@ -89,10 +90,9 @@ public static Position wrap(PrimaryKey primaryKey, int index) { private final int index; /** - * A cached copy of the binary representation that is returned from - * {@link #getBytes()}. + * The PrimaryKey of the record that this Position represents. */ - private transient ByteBuffer bytes; + private final PrimaryKey primaryKey; /** * Construct a new instance. @@ -149,17 +149,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - bytes.put(primaryKey.getBytes()); - // NOTE: Storing the index as an int instead of some size aware - // variable length is probably overkill since most indexes will be - // smaller than Byte.MAX_SIZE or Short.MAX_SIZE, but having variable - // size indexes means that the size of the entire Position (as an - // int) must be stored before the Position for proper - // deserialization. By storing the index as an int, the size of each - // Position is constant so we won't need to store the overall size - // prior to the Position to deserialize it, which is actually more - // space efficient. - bytes.putInt(index); + transferBytes(bytes); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -197,4 +188,19 @@ public String toString() { return "Position " + index + " in Record " + primaryKey; } + @Override + public void transferBytes(ByteBuffer buffer) { + primaryKey.transferBytes(buffer); + // NOTE: Storing the index as an int instead of some size aware + // variable length is probably overkill since most indexes will be + // smaller than Byte.MAX_SIZE or Short.MAX_SIZE, but having variable + // size indexes means that the size of the entire Position (as an + // int) must be stored before the Position for proper + // deserialization. By storing the index as an int, the size of each + // Position is constant so we won't need to store the overall size + // prior to the Position to deserialize it, which is actually more + // space efficient. + buffer.putInt(index); + } + } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java index 37d00c0b94..b8da76e4dc 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java @@ -77,17 +77,17 @@ public static PrimaryKey wrap(long data) { */ public static final int SIZE = 8; - /** - * The underlying data that represents this PrimaryKey. - */ - private final long data; - /** * A cached copy of the binary representation that is returned from * {@link #getBytes()}. */ private transient ByteBuffer bytes = null; + /** + * The underlying data that represents this PrimaryKey. + */ + private final long data; + /** * Construct a new instance. * @@ -117,8 +117,12 @@ public int compareTo(PrimaryKey other) { } @Override - public int size() { - return SIZE; + public boolean equals(Object obj) { + if(obj instanceof PrimaryKey) { + final PrimaryKey other = (PrimaryKey) obj; + return Longs.compare(data, other.data) == 0; + } + return false; } /** @@ -134,7 +138,8 @@ public int size() { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(SIZE); - bytes.putLong(data); + transferBytes(bytes); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -144,15 +149,6 @@ public int hashCode() { return Longs.hashCode(data); } - @Override - public boolean equals(Object obj) { - if(obj instanceof PrimaryKey) { - final PrimaryKey other = (PrimaryKey) obj; - return Longs.compare(data, other.data) == 0; - } - return false; - } - /** * Return the long representation of this PrimaryKey. * @@ -162,11 +158,21 @@ public long longValue() { return data; } + @Override + public int size() { + return SIZE; + } + @Override public String toString() { return UnsignedLongs.toString(data); } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.putLong(data); + } + /** * A {@link Comparator} that is used to sort PrimaryKey objects. * diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index c221271034..da7494ffdf 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -70,17 +70,17 @@ public static Text wrap(String string) { */ public static final Text EMPTY = Text.wrap(""); - /** - * The wrapped string. - */ - private final String text; - /** * Master byte sequence that represents this object. Read-only duplicates * are made when returning from {@link #getBytes()}. */ private transient ByteBuffer bytes = null; + /** + * The wrapped string. + */ + private final String text; + /** * Construct an instance that wraps the {@code text} string. * @@ -138,4 +138,9 @@ public String toString() { return text; } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.put(ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8))); + } + } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index a16764e5ed..f2c36f1b59 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -76,7 +76,7 @@ public static Value fromByteBuffer(ByteBuffer bytes) { TObject data = extractTObjectAndCache(bytes, type); return new Value(data, bytes); } - + /** * Return the optimized Value of {@code value}. * @@ -84,7 +84,7 @@ public static Value fromByteBuffer(ByteBuffer bytes) { * @return the optimized Value */ public static Value optimize(Value value) { - if (value.getType() == Type.TAG) { + if(value.getType() == Type.TAG) { return Value.wrap(Convert .javaToThrift(value.getObject().toString())); } @@ -124,14 +124,6 @@ private static TObject extractTObjectAndCache(ByteBuffer bytes, Type type) { return new TObject(ByteBuffer.wrap(array), type); } - /** - * A constant representing the largest possible Value. This shouldn't be - * used in normal operations, but should only be used to indicate an - * infinite range. - */ - public static Value POSITIVE_INFINITY = Value.wrap(Convert - .javaToThrift(Long.MAX_VALUE)); - /** * A constant representing the smallest possible Value. This should be used * in normal operations, but should only be used to indicate an infinite @@ -141,16 +133,17 @@ private static TObject extractTObjectAndCache(ByteBuffer bytes, Type type) { .javaToThrift(Long.MIN_VALUE)); /** - * The minimum number of bytes needed to encode every Value. + * A constant representing the largest possible Value. This shouldn't be + * used in normal operations, but should only be used to indicate an + * infinite range. */ - private static final int CONSTANT_SIZE = 1; // type(1) + public static Value POSITIVE_INFINITY = Value.wrap(Convert + .javaToThrift(Long.MAX_VALUE)); /** - * The underlying data represented by this Value. This representation is - * used when serializing/deserializing the data for RPC or disk and network - * I/O. + * The minimum number of bytes needed to encode every Value. */ - private final TObject data; + private static final int CONSTANT_SIZE = 1; // type(1) /** * A cached copy of the binary representation that is returned from @@ -159,6 +152,13 @@ private static TObject extractTObjectAndCache(ByteBuffer bytes, Type type) { @Nullable private transient ByteBuffer bytes = null; + /** + * The underlying data represented by this Value. This representation is + * used when serializing/deserializing the data for RPC or disk and network + * I/O. + */ + private final TObject data; + /** * The java representation of the underlying {@link #data}. This * representation is used when interacting with other components in the JVM. @@ -213,8 +213,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - bytes.put((byte) data.getType().ordinal()); - bytes.put(data.bufferForData()); + transferBytes(bytes); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -265,6 +265,12 @@ public String toString() { return getObject().toString() + " (" + getType() + ")"; } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.put((byte) data.getType().ordinal()); + buffer.put(data.bufferForData()); + } + /** * A {@link Comparator} that is used to sort Values using weak typing. * diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index 949061c0d0..a4402d3ae6 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -620,12 +620,17 @@ public ByteBuffer getBytes() { // happens if the AtomicOperation is not aborted before an attempt // to commit, so its best to not create a copy if we don't have to ByteBuffer bytes = ByteBuffer.allocate(size()); - bytes.put((byte) type.ordinal()); - bytes.put(token.getBytes()); + transferBytes(bytes); bytes.rewind(); return bytes; } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.put((byte) type.ordinal()); + token.transferBytes(buffer); + } + /** * Return the lock that is described by this LockDescription. This * method DOES NOT return a TLock, but will return a ReadLock or diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index 2535bfa0f4..da7ed44e5c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -139,6 +139,12 @@ public static String getId(String filename) { return FileSystem.getSimpleName(filename); } + /** + * The extension for the block file. + */ + @PackagePrivate + static final String BLOCK_NAME_EXTENSION = ".blk"; + /** * The expected number of Block insertions. This number is used to size the * Block's internal data structures. This value should be large enough to @@ -148,12 +154,6 @@ public static String getId(String filename) { */ private static final int EXPECTED_INSERTIONS = GlobalState.BUFFER_PAGE_SIZE; - /** - * The extension for the block file. - */ - @PackagePrivate - static final String BLOCK_NAME_EXTENSION = ".blk"; - /** * The extension for the {@link BloomFilter} file. */ @@ -164,19 +164,18 @@ public static String getId(String filename) { */ private static final String INDEX_NAME_EXTENSION = ".indx"; + /** + * The flag that indicates whether the Block is mutable or not. A Block is + * mutable until a call to {@link #sync()} stores it to disk. + */ + protected transient boolean mutable; + /** * The master lock for {@link #write} and {@link #read}. DO NOT use this * lock directly. */ private final ReentrantReadWriteLock master = new ReentrantReadWriteLock(); - /** - * An exclusive lock that permits only one writer and no reader. Use this - * lock to ensure that no seek occurs while data is being inserted into the - * Block. - */ - protected final WriteLock write = master.writeLock(); - /** * A shared lock that permits many readers and no writer. Use this lock to * ensure that no data insert occurs while a seek is happening within the @@ -185,22 +184,22 @@ public static String getId(String filename) { protected final ReadLock read = master.readLock(); /** - * The flag that indicates whether the Block is mutable or not. A Block is - * mutable until a call to {@link #sync()} stores it to disk. + * An exclusive lock that permits only one writer and no reader. Use this + * lock to ensure that no seek occurs while data is being inserted into the + * Block. */ - protected transient boolean mutable; + protected final WriteLock write = master.writeLock(); /** - * The running size of the Block. This number only refers to the size of the - * Revisions that are stored in the block file. The size for the filter and - * index are tracked separately. + * The location of the block file. */ - private transient int size; + private final String file; /** - * The location of the block file. + * A fixed size filter that is used to test whether elements are contained + * in the Block without actually looking through the Block. */ - private final String file; + private final BloomFilter filter; /** * The unique id for the block. Each component of the block is named after @@ -209,6 +208,14 @@ public static String getId(String filename) { */ private final String id; + /** + * The index to determine which bytes in the block pertain to a locator or + * locator/key pair. + */ + private final BlockIndex index; // Since the index is only used for + // immutable blocks, it is only populated + // during the call to #getBytes() + /** * A collection that contains all the Revisions that have been inserted into * the Block. This collection is sorted on the fly as elements are inserted. @@ -220,18 +227,11 @@ public static String getId(String filename) { private TreeMultiset> revisions; /** - * A fixed size filter that is used to test whether elements are contained - * in the Block without actually looking through the Block. - */ - private final BloomFilter filter; - - /** - * The index to determine which bytes in the block pertain to a locator or - * locator/key pair. + * The running size of the Block. This number only refers to the size of the + * Revisions that are stored in the block file. The size for the filter and + * index are tracked separately. */ - private final BlockIndex index; // Since the index is only used for - // immutable blocks, it is only populated - // during the call to #getBytes() + private transient int size; /** * Construct a new instance. @@ -283,56 +283,7 @@ public ByteBuffer getBytes() { read.lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size); - L locator = null; - K key = null; - int position = 0; - for (Revision revision : revisions) { - bytes.putInt(revision.size()); - bytes.put(revision.getBytes()); - position = bytes.position() - revision.size() - 4; - /* - * States that trigger this condition to be true: - * 1. This is the first locator we've seen - * 2. This locator is different than the last one we've seen - */ - if(locator == null || !locator.equals(revision.getLocator())) { - index.putStart(position, revision.getLocator()); - if(locator != null) { - // There was a locator before us (we are not the first!) - // and we need to record the end index. - index.putEnd(position - 1, locator); - } - } - /* - * NOTE: IF key == null, then it must be the case that locator - * == null since they are set at the same time. Therefore we do - * not need to explicitly check for that condition below - * - * States that trigger this condition to be true: - * 1. This is the first key we've seen - * 2. This key is different than the last one we've seen - * (regardless of whether the locator is different or the same!) - * 3. This key is the same as the last one we've seen, but the - * locator is different. - */ - if(key == null || !key.equals(revision.getKey()) - || !locator.equals(revision.getLocator())) { - index.putStart(position, revision.getLocator(), - revision.getKey()); - if(key != null) { - // There was a locator, key before us (we are not the - // first!) and we need to record the end index. - index.putEnd(position - 1, locator, key); - } - } - locator = revision.getLocator(); - key = revision.getKey(); - } - if(revisions.size() > 0) { - position = bytes.position() - 1; - index.putEnd(position, locator); - index.putEnd(position, locator, key); - } + transferBytes(bytes); bytes.rewind(); return bytes; } @@ -489,6 +440,66 @@ public String toString() { return getClass().getSimpleName() + " " + id; } + @Override + public void transferBytes(ByteBuffer buffer) { + read.lock(); + try { + L locator = null; + K key = null; + int position = 0; + for (Revision revision : revisions) { + buffer.putInt(revision.size()); + buffer.put(revision.getBytes()); + position = buffer.position() - revision.size() - 4; + /* + * States that trigger this condition to be true: + * 1. This is the first locator we've seen + * 2. This locator is different than the last one we've seen + */ + if(locator == null || !locator.equals(revision.getLocator())) { + index.putStart(position, revision.getLocator()); + if(locator != null) { + // There was a locator before us (we are not the first!) + // and we need to record the end index. + index.putEnd(position - 1, locator); + } + } + /* + * NOTE: IF key == null, then it must be the case that locator + * == null since they are set at the same time. Therefore we do + * not need to explicitly check for that condition below + * + * States that trigger this condition to be true: + * 1. This is the first key we've seen + * 2. This key is different than the last one we've seen + * (regardless of whether the locator is different or the same!) + * 3. This key is the same as the last one we've seen, but the + * locator is different. + */ + if(key == null || !key.equals(revision.getKey()) + || !locator.equals(revision.getLocator())) { + index.putStart(position, revision.getLocator(), + revision.getKey()); + if(key != null) { + // There was a locator, key before us (we are not the + // first!) and we need to record the end index. + index.putEnd(position - 1, locator, key); + } + } + locator = revision.getLocator(); + key = revision.getKey(); + } + if(revisions.size() > 0) { + position = buffer.position() - 1; + index.putEnd(position, locator); + index.putEnd(position, locator, key); + } + } + finally { + read.unlock(); + } + } + /** * Return a dump of the revisions in the block as a String. This method * primarily exists for debugging using the {@link DumpToolCli} tool. diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java index d360217e5e..b24181de07 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java @@ -81,10 +81,9 @@ public static BlockIndex open(String file) { public static final int NO_ENTRY = -1; /** - * Lock used to ensure the object is ThreadSafe. This lock provides access - * to a masterLock.readLock()() and masterLock.writeLock()(). + * The entries contained in the index. */ - private final ReentrantReadWriteLock masterLock = new ReentrantReadWriteLock(); + private Map entries; /** * The file where the BlockIndex is stored during an diskSync. @@ -92,26 +91,27 @@ public static BlockIndex open(String file) { private final String file; /** - * The running size of the index in bytes. + * Lock used to ensure the object is ThreadSafe. This lock provides access + * to a masterLock.readLock()() and masterLock.writeLock()(). */ - private transient int size = 0; + private final ReentrantReadWriteLock masterLock = new ReentrantReadWriteLock(); /** - * The entries contained in the index. + * A flag that indicates if this index is mutable. An index is no longer + * mutable after it has been synced. */ - private Map entries; + private boolean mutable; /** - * A {@link SoftReference} to the entries contained in the index that is - * used to reduce memory overhead. + * The running size of the index in bytes. */ - private SoftReference> softEntries; + private transient int size = 0; /** - * A flag that indicates if this index is mutable. An index is no longer - * mutable after it has been synced. + * A {@link SoftReference} to the entries contained in the index that is + * used to reduce memory overhead. */ - private boolean mutable; + private SoftReference> softEntries; /** * Lazily construct an existing instance from the data in {@code file}. @@ -143,10 +143,7 @@ public ByteBuffer getBytes() { masterLock.readLock().lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size()); - for (Entry entry : entries.values()) { - bytes.putInt(entry.size()); - bytes.put(entry.getBytes()); - } + transferBytes(bytes); bytes.rewind(); return bytes; } @@ -284,6 +281,21 @@ public void sync() { } } + @Override + public void transferBytes(ByteBuffer buffer) { + Preconditions.checkState(mutable); + masterLock.readLock().lock(); + try { + for (Entry entry : entries.values()) { + buffer.putInt(entry.size()); + entry.transferBytes(buffer); + } + } + finally { + masterLock.readLock().unlock(); + } + } + /** * Return {@code true} if this index is considered loaded meaning * all of its entries are available in memory. @@ -346,9 +358,9 @@ private final class Entry implements Byteable { private static final int CONSTANT_SIZE = 8; // start(4), end(4) + private int end = NO_ENTRY; private final Composite key; private int start = NO_ENTRY; - private int end = NO_ENTRY; /** * Construct an instance that represents an existing Entry from a @@ -378,9 +390,7 @@ public Entry(Composite key) { @Override public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); - bytes.putInt(start); - bytes.putInt(end); - bytes.put(key.getBytes()); + transferBytes(bytes); bytes.rewind(); return bytes; } @@ -435,6 +445,13 @@ public int size() { return CONSTANT_SIZE + key.size(); } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.putInt(start); + buffer.putInt(end); + key.transferBytes(buffer); + } + } } \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java index 959b3ed75f..7c3ec57300 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java @@ -113,11 +113,16 @@ public static SecondaryRevision createSecondaryRevision(Text key, static final int VARIABLE_SIZE = -1; /** - * An field indicating the action performed to generate this Revision. This - * information is recorded so that we can efficiently purge history while - * maintaining consistent state. + * A cached copy of the binary representation that is returned from + * {@link #getBytes()}. */ - private final Action type; + private transient ByteBuffer bytes = null; + + /** + * The secondary component used to locate the field in the index, to which + * this Revision belongs. + */ + private final K key; /** * The primary component used to locate the index, to which this Revision @@ -126,10 +131,17 @@ public static SecondaryRevision createSecondaryRevision(Text key, private final L locator; /** - * The secondary component used to locate the field in the index, to which - * this Revision belongs. + * The number of bytes used to store the Revision. This value depends on + * the number of variable sized components. */ - private final K key; + private transient final int size; + + /** + * An field indicating the action performed to generate this Revision. This + * information is recorded so that we can efficiently purge history while + * maintaining consistent state. + */ + private final Action type; /** * The tertiary component that typically represents the payload for what @@ -143,18 +155,6 @@ public static SecondaryRevision createSecondaryRevision(Text key, */ private final long version; - /** - * A cached copy of the binary representation that is returned from - * {@link #getBytes()}. - */ - private transient ByteBuffer bytes = null; - - /** - * The number of bytes used to store the Revision. This value depends on - * the number of variable sized components. - */ - private transient final int size; - /** * Construct an instance that represents an existing Revision from a * ByteBuffer. This constructor is public so as to comply with the @@ -245,17 +245,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - bytes.put((byte) type.ordinal()); - bytes.putLong(version); - if(xLocatorSize() == VARIABLE_SIZE) { - bytes.putInt(locator.size()); - } - bytes.put(locator.getBytes()); - if(xKeySize() == VARIABLE_SIZE) { - bytes.putInt(key.size()); - } - bytes.put(key.getBytes()); - bytes.put(value.getBytes()); + transferBytes(bytes); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -322,6 +313,21 @@ public String toString() { + version; } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.put((byte) type.ordinal()); + buffer.putLong(version); + if(xLocatorSize() == VARIABLE_SIZE) { + bytes.putInt(locator.size()); + } + locator.transferBytes(buffer); + if(xKeySize() == VARIABLE_SIZE) { + buffer.putInt(key.size()); + } + key.transferBytes(buffer); + value.transferBytes(buffer); + } + /** * Return the class of the {@link #key} type. * diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 3ac92c7f7b..a3e847af5c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -891,7 +891,7 @@ public void append(Write write) throws CapacityException { if(content.remaining() >= write.size() + 4) { index(write); content.putInt(write.size()); - content.put(write.getBytes()); + write.transferBytes(content); content.force(); } else { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java index 403d3b2a4d..6cc4031168 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java @@ -120,23 +120,23 @@ public static Write remove(String key, TObject value, long record) { // version(8), // keySize(4) + /** + * A cached copy of the binary representation that is returned from + * {@link #getBytes()}. + */ + @Nullable + private transient ByteBuffer bytes = null; + private final Text key; + private final PrimaryKey record; /** * Indicates the action that generated the Write. The type information is * recorded so that the Database knows how to apply the Write when accepting * it from a transport. */ private final Action type; - private final Text key; private final Value value; - private final PrimaryKey record; - private final long version; - /** - * A cached copy of the binary representation that is returned from - * {@link #getBytes()}. - */ - @Nullable - private transient ByteBuffer bytes = null; + private final long version; /** * Construct a new instance. @@ -207,12 +207,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - bytes.putInt(key.size()); - bytes.put((byte) type.ordinal()); - bytes.putLong(version); - bytes.put(record.getBytes()); - bytes.put(key.getBytes()); - bytes.put(value.getBytes()); + transferBytes(bytes); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -298,4 +294,14 @@ public String toString() { + version; } + @Override + public void transferBytes(ByteBuffer buffer) { + buffer.putInt(key.size()); + buffer.put((byte) type.ordinal()); + buffer.putLong(version); + record.transferBytes(buffer); + key.transferBytes(buffer); + value.transferBytes(buffer); + } + } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java index 30a6050001..0e6c6f1496 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java @@ -85,6 +85,21 @@ public static ByteBuffer clone(ByteBuffer buffer) { return clone; } + /** + * Transfer the bytes from {@code source} to {@code destination} and resets + * {@code source} so that its position remains unchanged. The position of + * the {@code destination} is incremented by the number of bytes that are + * transferred. + * + * @param source + * @param destination + */ + public static void copy(ByteBuffer source, ByteBuffer destination) { + int position = source.position(); + destination.put(source); + source.position(position); + } + /** * Encode the remaining bytes in as {@link ByteBuffer} as a hex string and * maintain the current position. @@ -102,17 +117,6 @@ public static String encodeAsHexString(ByteBuffer buffer) { return sb.toString(); } - /** - * Relative get method. Reads the byte at the current position in - * {@code buffer} as a boolean, and then increments the position. - * - * @param buffer - * @return the boolean value at the current position - */ - public static boolean getBoolean(ByteBuffer buffer) { - return buffer.get() > 0 ? true : false; - } - /** * Return a ByteBuffer that has a copy of {@code length} bytes from * {@code buffer} starting from the current position. This method will @@ -131,6 +135,17 @@ public static ByteBuffer get(ByteBuffer buffer, int length) { return ByteBuffer.wrap(backingArray); } + /** + * Relative get method. Reads the byte at the current position in + * {@code buffer} as a boolean, and then increments the position. + * + * @param buffer + * @return the boolean value at the current position + */ + public static boolean getBoolean(ByteBuffer buffer) { + return buffer.get() > 0 ? true : false; + } + /** * Relative get method. Reads the enum at the current position in * {@code buffer} and then increments the position by four. From a80f3a9f5fd09206f1698884919a13c71acdb66a Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 17 Oct 2014 12:03:52 -0400 Subject: [PATCH 017/100] change transferBytes to copyToByteBuffer to make method intention clearler Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/security/LegacyAccessManager.java --- .../concourse/server/concurrent/Token.java | 6 +-- .../concourse/server/io/Byteable.java | 42 ++++++++++------- .../concourse/server/io/Composite.java | 4 +- .../concourse/server/model/Position.java | 28 +++++------ .../concourse/server/model/PrimaryKey.java | 6 +-- .../cinchapi/concourse/server/model/Text.java | 6 +-- .../concourse/server/model/Value.java | 6 +-- .../server/storage/AtomicOperation.java | 8 ++-- .../concourse/server/storage/db/Block.java | 8 ++-- .../server/storage/db/BlockIndex.java | 12 ++--- .../concourse/server/storage/db/Revision.java | 14 +++--- .../concourse/server/storage/temp/Buffer.java | 5 +- .../concourse/server/storage/temp/Write.java | 12 ++--- .../cinchapi/concourse/util/TArraysTest.java | 2 +- .../cinchapi/concourse/util/ByteBuffers.java | 47 ++++++++++++++++++- 15 files changed, 130 insertions(+), 76 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 8ce7c7ef1e..38946e80b2 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -88,7 +88,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { return ByteBuffers.asReadOnlyBuffer(bytes); } - + @Override public int hashCode() { return getBytes().hashCode(); @@ -106,8 +106,8 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { - ByteBuffers.copy(bytes, buffer); + public void copyToByteBuffer(ByteBuffer buffer) { + ByteBuffers.copyAndRewindSource(bytes, buffer); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java index 1aa8d88ba9..77c75646be 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java @@ -59,6 +59,30 @@ */ public interface Byteable { + /** + * Copy the byte sequence that represents this object to the {@code buffer}. + * This method should be idempotent, so if the object caches its byte + * representation, be sure to reset the position after copying data to the + * buffer. + *

+ * This method is primary intended for pass-through gathering where data + * from multiple Byteables can be copied to a single bytebuffer without + * doing any unnecessary intermediate copying. So, if the binary + * representation for this object depends on that of another Byteable, then + * the implementation of this method should gather those bytes using the + * {@link #copyToByteBuffer(ByteBuffer)} method for the other Byteable. + *

+ *

+ * DO NOT make any modifications to {@code buffer} other + * than filling it with bytes for this class (i.e. do not rewind the buffer + * or change its position). It is assumed that the caller will rewind or + * flip the buffer after this method completes. + *

+ * + * @param buffer + */ + public void copyToByteBuffer(ByteBuffer buffer); + /** * Returns a byte sequence that represents this object. * @@ -78,22 +102,4 @@ public interface Byteable { */ public int size(); - /** - * Copy the byte sequence that represents this object to the {@code buffer}. - *

- * If the binary representation for this object depends on that of another - * Byteable, then the implementation of this method should gather those - * bytes using the {@link #transferBytes(ByteBuffer)} method for the - * other Byteable. - *

- *

- * DO NOT make any modifications to {@code buffer} other - * than filling it with bytes for this class (i.e. do not rewind the buffer - * or change its position). - *

- * - * @param buffer - */ - public void transferBytes(ByteBuffer buffer); - } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index 21b76e5b0b..3ea56b7fcf 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -129,8 +129,8 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { - ByteBuffers.copy(bytes, buffer); + public void copyToByteBuffer(ByteBuffer buffer) { + ByteBuffers.copyAndRewindSource(bytes, buffer); } } \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java index ac48ebbdab..4359b7c363 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java @@ -149,8 +149,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -189,18 +189,18 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { - primaryKey.transferBytes(buffer); - // NOTE: Storing the index as an int instead of some size aware - // variable length is probably overkill since most indexes will be - // smaller than Byte.MAX_SIZE or Short.MAX_SIZE, but having variable - // size indexes means that the size of the entire Position (as an - // int) must be stored before the Position for proper - // deserialization. By storing the index as an int, the size of each - // Position is constant so we won't need to store the overall size - // prior to the Position to deserialize it, which is actually more - // space efficient. - buffer.putInt(index); + public void copyToByteBuffer(ByteBuffer buffer) { + // NOTE: Storing the index as an int instead of some size aware + // variable length is probably overkill since most indexes will be + // smaller than Byte.MAX_SIZE or Short.MAX_SIZE, but having variable + // size indexes means that the size of the entire Position (as an + // int) must be stored before the Position for proper + // deserialization. By storing the index as an int, the size of each + // Position is constant so we won't need to store the overall size + // prior to the Position to deserialize it, which is actually more + // space efficient. + primaryKey.copyToByteBuffer(buffer); + buffer.putInt(index); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java index b8da76e4dc..a619d280e4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java @@ -138,8 +138,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(SIZE); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -169,7 +169,7 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.putLong(data); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index da7494ffdf..c70a49e36a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -118,7 +118,7 @@ public boolean equals(Object obj) { @Override public ByteBuffer getBytes() { if(bytes == null) { - bytes = ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8)); + bytes = ByteBuffers.fromString(text); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -139,8 +139,8 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { - buffer.put(ByteBuffer.wrap(text.getBytes(StandardCharsets.UTF_8))); + public void copyToByteBuffer(ByteBuffer buffer) { + ByteBuffers.putString(text, buffer); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index f2c36f1b59..8e220991a9 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -213,8 +213,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -266,7 +266,7 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.put((byte) data.getType().ordinal()); buffer.put(data.bufferForData()); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index a4402d3ae6..346c8ff29b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -620,15 +620,15 @@ public ByteBuffer getBytes() { // happens if the AtomicOperation is not aborted before an attempt // to commit, so its best to not create a copy if we don't have to ByteBuffer bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); return bytes; } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.put((byte) type.ordinal()); - token.transferBytes(buffer); + token.copyToByteBuffer(buffer); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index da7ed44e5c..1e8517c0d4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -283,8 +283,8 @@ public ByteBuffer getBytes() { read.lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); return bytes; } finally { @@ -441,7 +441,7 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { read.lock(); try { L locator = null; @@ -449,7 +449,7 @@ public void transferBytes(ByteBuffer buffer) { int position = 0; for (Revision revision : revisions) { buffer.putInt(revision.size()); - buffer.put(revision.getBytes()); + revision.copyToByteBuffer(buffer); position = buffer.position() - revision.size() - 4; /* * States that trigger this condition to be true: diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java index b24181de07..34698c0144 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java @@ -143,7 +143,7 @@ public ByteBuffer getBytes() { masterLock.readLock().lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); + copyToByteBuffer(bytes); bytes.rewind(); return bytes; } @@ -282,13 +282,13 @@ public void sync() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { Preconditions.checkState(mutable); masterLock.readLock().lock(); try { for (Entry entry : entries.values()) { buffer.putInt(entry.size()); - entry.transferBytes(buffer); + entry.copyToByteBuffer(buffer); } } finally { @@ -390,7 +390,7 @@ public Entry(Composite key) { @Override public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); + copyToByteBuffer(bytes); bytes.rewind(); return bytes; } @@ -446,10 +446,10 @@ public int size() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.putInt(start); buffer.putInt(end); - key.transferBytes(buffer); + key.copyToByteBuffer(buffer); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java index 7c3ec57300..1aba6817cb 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java @@ -245,8 +245,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -314,18 +314,18 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.put((byte) type.ordinal()); buffer.putLong(version); if(xLocatorSize() == VARIABLE_SIZE) { - bytes.putInt(locator.size()); + buffer.putInt(locator.size()); } - locator.transferBytes(buffer); + locator.copyToByteBuffer(buffer); if(xKeySize() == VARIABLE_SIZE) { buffer.putInt(key.size()); } - key.transferBytes(buffer); - value.transferBytes(buffer); + key.copyToByteBuffer(buffer); + value.copyToByteBuffer(buffer); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index a3e847af5c..734c193f09 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -890,8 +890,11 @@ public void append(Write write) throws CapacityException { try { if(content.remaining() >= write.size() + 4) { index(write); + ByteBuffer bytes = ByteBuffer.allocate(write.size()); + write.copyToByteBuffer(bytes); + bytes.flip(); content.putInt(write.size()); - write.transferBytes(content); + content.put(bytes); content.force(); } else { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java index 6cc4031168..b8a13ed3b0 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java @@ -207,8 +207,8 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - transferBytes(bytes); - bytes.rewind(); + copyToByteBuffer(bytes); + bytes.flip(); } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -295,13 +295,13 @@ public String toString() { } @Override - public void transferBytes(ByteBuffer buffer) { + public void copyToByteBuffer(ByteBuffer buffer) { buffer.putInt(key.size()); buffer.put((byte) type.ordinal()); buffer.putLong(version); - record.transferBytes(buffer); - key.transferBytes(buffer); - value.transferBytes(buffer); + record.copyToByteBuffer(buffer); + key.copyToByteBuffer(buffer); + value.copyToByteBuffer(buffer); } } diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java index 7502951066..724a485f4c 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/util/TArraysTest.java @@ -62,5 +62,5 @@ public void testEqualObjectsHaveEqualHash(){ ByteBuffer b = TArrays.hash(data); Assert.assertEquals(a, b); } - + } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java index 0e6c6f1496..049fa8676a 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java @@ -94,7 +94,8 @@ public static ByteBuffer clone(ByteBuffer buffer) { * @param source * @param destination */ - public static void copy(ByteBuffer source, ByteBuffer destination) { + public static void copyAndRewindSource(ByteBuffer source, + ByteBuffer destination) { int position = source.position(); destination.put(source); source.position(position); @@ -117,6 +118,23 @@ public static String encodeAsHexString(ByteBuffer buffer) { return sb.toString(); } + /** + * Return a byte buffer that has the UTF-8 encoding for {@code string}. This + * method uses some optimization techniques and is the preferable way to + * convert strings to byte buffers than doing so manually. + * + * @param string + * @return the byte buffer with the {@code string} data. + */ + public static ByteBuffer fromString(String string) { + try { + return ByteBuffer.wrap(string.getBytes(CHARSET)); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + /** * Return a ByteBuffer that has a copy of {@code length} bytes from * {@code buffer} starting from the current position. This method will @@ -190,6 +208,25 @@ public static String getString(ByteBuffer buffer, Charset charset) { } } + /** + * Put the UTF-8 encoding for the {@code source} string into the + * {@code destination} byte buffer. This method uses some optimization + * techniques and is the preferable way to add strings to byte buffers than + * doing so manually. + * + * @param source + * @param destination + */ + public static void putString(String source, ByteBuffer destination) { + try { + byte[] bytes = source.getBytes(CHARSET); + destination.put(bytes); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + /** * Return a new ByteBuffer whose content is a shared subsequence of the * content in {@code buffer} starting at the current position to @@ -273,4 +310,12 @@ public static byte[] toByteArray(ByteBuffer buffer) { } } + /** + * The name of the Charset to use for encoding/decoding. We use the name + * instead of the charset object because Java caches encoders when + * referencing them by name, but creates a new encorder object when + * referencing them by Charset object. + */ + private static final String CHARSET = StandardCharsets.UTF_8.name(); + } From 6d664ff640441d2d8a7f4b75470b222d69358144 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 17 Oct 2014 12:35:03 -0400 Subject: [PATCH 018/100] fix issue with composites not being flipped properly --- .../main/java/org/cinchapi/concourse/server/io/Composite.java | 1 + .../org/cinchapi/concourse/server/storage/db/BlockIndex.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index 3ea56b7fcf..aa557310ee 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -96,6 +96,7 @@ private Composite(Byteable... byteables) { for (Byteable byteable : byteables) { bytes.put(byteable.getBytes()); } + bytes.flip(); } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java index 34698c0144..10f5593872 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java @@ -144,7 +144,7 @@ public ByteBuffer getBytes() { try { ByteBuffer bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.rewind(); + bytes.flip(); return bytes; } finally { @@ -391,7 +391,7 @@ public Entry(Composite key) { public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.rewind(); + bytes.flip(); return bytes; } From 3d415d49381fffd9637565e0cdfb07a0a04717e7 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 17 Oct 2014 15:58:47 -0400 Subject: [PATCH 019/100] cache the decoders were use for extracting strings for bytebuffers --- .../cinchapi/concourse/util/ByteBuffers.java | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java index 049fa8676a..cd11bcf786 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java @@ -29,6 +29,7 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; +import java.util.concurrent.ConcurrentLinkedQueue; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; @@ -198,14 +199,27 @@ public static String getString(ByteBuffer buffer) { * @return the string value at the current position */ public static String getString(ByteBuffer buffer, Charset charset) { + CharsetDecoder decoder = null; try { - CharsetDecoder decoder = charset.newDecoder(); + if(charset == StandardCharsets.UTF_8) { + while (decoder == null) { + decoder = DECODERS.poll(); + } + } + else { + decoder = charset.newDecoder(); + } decoder.onMalformedInput(CodingErrorAction.IGNORE); return decoder.decode(buffer).toString(); } catch (CharacterCodingException e) { throw Throwables.propagate(e); } + finally { + if(decoder != null && charset == StandardCharsets.UTF_8) { + DECODERS.offer(decoder); + } + } } /** @@ -318,4 +332,26 @@ public static byte[] toByteArray(ByteBuffer buffer) { */ private static final String CHARSET = StandardCharsets.UTF_8.name(); + /** + * The number of UTF-8 decoders to create for concurrent access. + */ + private static final int NUM_DECODERS = 10; + + /** + * A collection of UTF-8 decoders that can be concurrently used. We use this + * to avoid creating a new decoder every time we need to decode a string + * while still allowing multi-threaded access. + */ + private static final ConcurrentLinkedQueue DECODERS = new ConcurrentLinkedQueue(); + static { + try { + for (int i = 0; i < NUM_DECODERS; i++) { + DECODERS.add(StandardCharsets.UTF_8.newDecoder()); + } + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + } From 4555d3d4bb5137be427142e0dba0a2a8e73cb44d Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 18:37:13 -0400 Subject: [PATCH 020/100] Use synchronized multisets instead of concurrent ones in LockService locks because the expected contention isn't high enough to warrant the overhead of a concurrent data structure --- .../server/concurrent/GuavaInternals.java | 89 +++++++++++++++++++ .../server/concurrent/LockService.java | 14 +-- .../server/concurrent/RangeLockService.java | 14 +-- 3 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/GuavaInternals.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/GuavaInternals.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/GuavaInternals.java new file mode 100644 index 0000000000..1bf4c2eca2 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/GuavaInternals.java @@ -0,0 +1,89 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.concurrent; + +import java.lang.reflect.Method; + +import com.google.common.base.Throwables; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; + +/** + * A collection of methods that perform hacks in order to access some helpful + * internal methods in third party libraries like Guava. + * + * @author jnelson + */ +public final class GuavaInternals { + + private GuavaInternals() {/* noop */} + + /** + * com.google.common.collect.Synchronized + *

+ * Keep a cached copy so we only incur the reflection cost once. + *

+ */ + private static final Class synchronizedClass; + + /** + * Synchronized#multiset(Multiset, Object) + *

+ * Keep a cached copy so we only incur the reflection cost once. + *

+ */ + private static final Method createSynchronizedMultisetMethod; + static { + try { + synchronizedClass = Class + .forName("com.google.common.collect.Synchronized"); + createSynchronizedMultisetMethod = synchronizedClass + .getDeclaredMethod("multiset", Multiset.class, Object.class); + createSynchronizedMultisetMethod.setAccessible(true); + } + catch (ReflectiveOperationException e) { + throw Throwables.propagate(e); + } + } + + /** + * Return a hash multiset that uses synchronization to control concurrent + * access. This is typically appropriate in cases where contention is + * possible but not high enough to warrant the overhead of the + * {@link ConcurrentHashMultiset}. + * + * @return the multiset + */ + @SuppressWarnings("unchecked") + public static Multiset newSynchronizedHashMultiset() { + try { + return (Multiset) createSynchronizedMultisetMethod.invoke(null, + HashMultiset.create(), new Object()); + } + catch (ReflectiveOperationException e) { + throw Throwables.propagate(e); + } + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java index 671d73e64f..f508844c1b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import com.google.common.base.Objects; -import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.Multiset; /** * A global service that provides ReadLock and WriteLock instances for a given @@ -178,8 +178,8 @@ private final class TokenReadWriteLock extends ReentrantReadWriteLock { * associated with any threads then it can be safely removed from the * cache. */ - private final ConcurrentHashMultiset readers = ConcurrentHashMultiset - .create(); + private final Multiset readers = GuavaInternals + .newSynchronizedHashMultiset(); /** * We keep track of all the threads that have requested (but not @@ -187,8 +187,8 @@ private final class TokenReadWriteLock extends ReentrantReadWriteLock { * associated with any threads then it can be safely removed from the * cache. */ - private final ConcurrentHashMultiset writers = ConcurrentHashMultiset - .create(); + private final Multiset writers = GuavaInternals + .newSynchronizedHashMultiset(); /** * The token that represents the notion this lock controls @@ -229,7 +229,7 @@ public ReadLock readLock() { @Override public void unlock() { super.unlock(); - readers.removeExactly(Thread.currentThread(), 1); + readers.remove(Thread.currentThread(), 1); locks.remove(token, new TokenReadWriteLock(token)); } @@ -249,7 +249,7 @@ public WriteLock writeLock() { @Override public void unlock() { super.unlock(); - writers.removeExactly(Thread.currentThread(), 1); + writers.remove(Thread.currentThread(), 1); locks.remove(token, new TokenReadWriteLock(token)); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index 87d29b7e87..e40f723997 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -39,7 +39,7 @@ import com.google.common.base.Objects; import com.google.common.base.Preconditions; -import com.google.common.collect.ConcurrentHashMultiset; +import com.google.common.collect.Multiset; /** * A global service that provides ReadLock and WriteLock instances for a given @@ -335,8 +335,8 @@ private final class RangeReadWriteLock extends ReentrantReadWriteLock { * associated with any threads then it can be safely removed from the * cache. */ - private final ConcurrentHashMultiset readers = ConcurrentHashMultiset - .create(); + private final Multiset readers = GuavaInternals + .newSynchronizedHashMultiset(); /** * We keep track of all the threads that have requested (but not @@ -344,8 +344,8 @@ private final class RangeReadWriteLock extends ReentrantReadWriteLock { * associated with any threads then it can be safely removed from the * cache. */ - private final ConcurrentHashMultiset writers = ConcurrentHashMultiset - .create(); + private final Multiset writers = GuavaInternals + .newSynchronizedHashMultiset(); /** * The token that represents the notion this lock controls @@ -404,7 +404,7 @@ public boolean tryLock() { @Override public void unlock() { super.unlock(); - readers.removeExactly(Thread.currentThread(), 1); + readers.remove(Thread.currentThread(), 1); locks.remove(token, new RangeReadWriteLock(token)); } @@ -443,7 +443,7 @@ public boolean tryLock() { @Override public void unlock() { super.unlock(); - writers.removeExactly(Thread.currentThread(), 1); + writers.remove(Thread.currentThread(), 1); locks.remove(token, new RangeReadWriteLock(token)); } From 5982584ac64f14de5fff4ee468dd5f0b9079d40a Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 20:46:55 -0400 Subject: [PATCH 021/100] dont make intermediary buffer copies while constructing composite --- .../main/java/org/cinchapi/concourse/server/io/Composite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index aa557310ee..7f6ad4d5b4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -94,7 +94,7 @@ private Composite(Byteable... byteables) { } bytes = ByteBuffer.allocate(size); for (Byteable byteable : byteables) { - bytes.put(byteable.getBytes()); + byteable.copyToByteBuffer(bytes); } bytes.flip(); } From c73679980998004a1bea48b3defd1bd2c49bf79d Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 20:48:17 -0400 Subject: [PATCH 022/100] only notify buffertransporthread to wakeup if its necessary to do so --- .../org/cinchapi/concourse/server/storage/temp/Buffer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 734c193f09..e4ad54b840 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -467,10 +467,11 @@ public long getVersion(String key, long record) { public boolean insert(Write write) { writeLock.lock(); try { + boolean notify = pages.size() == 2 && currentPage.size == 0; currentPage.append(write); - if(pages.size() > 1) { + if(notify) { synchronized (transportable) { - transportable.notifyAll(); + transportable.notify(); } } } From 2dbe36bd2303d3f1428bece6314d8e63bbb305c7 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 20:48:55 -0400 Subject: [PATCH 023/100] change equality definition for (range) lock service locks to be faster and prevent concurrentmodificationexception --- .../concourse/server/concurrent/LockService.java | 9 ++++++--- .../concourse/server/concurrent/RangeLockService.java | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java index f508844c1b..99ece5846a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java @@ -206,11 +206,14 @@ public TokenReadWriteLock(Token token) { @Override public boolean equals(Object object) { + // This is a BAD implementation for equality, but for our purposes + // we only care to see that the tokens are the same and there are no + // readers or writers. if(object instanceof TokenReadWriteLock) { TokenReadWriteLock other = (TokenReadWriteLock) object; return token.equals(other.token) - && readers.equals(other.readers) - && writers.equals(other.writers); + && readers.size() == other.readers.size() + && writers.size() == other.writers.size(); } else { return false; @@ -219,7 +222,7 @@ public boolean equals(Object object) { @Override public int hashCode() { - return Objects.hashCode(token, readers, writers); + return Objects.hashCode(token, readers.size(), writers.size()); } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index e40f723997..3a20c82572 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -363,11 +363,14 @@ public RangeReadWriteLock(RangeToken token) { @Override public boolean equals(Object object) { + // This is a BAD implementation for equality, but for our purposes + // we only care to see that the tokens are the same and there are no + // readers or writers. if(object instanceof RangeReadWriteLock) { RangeReadWriteLock other = (RangeReadWriteLock) object; return token.equals(other.token) - && readers.equals(other.readers) - && writers.equals(other.writers); + && readers.size() == other.readers.size() + && writers.size() == other.writers.size(); } else { return false; From 2c2a355a245eb51d45c2627005c76e7a02e64b1c Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 20:57:14 -0400 Subject: [PATCH 024/100] access tobject bytebuffer directly to get size and avoid copying --- .../main/java/org/cinchapi/concourse/server/model/Value.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index 8e220991a9..610c22b049 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -257,7 +257,7 @@ public int hashCode() { @Override public int size() { - return CONSTANT_SIZE + data.bufferForData().capacity(); + return CONSTANT_SIZE + data.data.capacity(); } @Override From be1f09b83eb0020f4c0760bde224e21df110fb9c Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 21:31:41 -0400 Subject: [PATCH 025/100] don't make intermediary copies of value bytes when constructing the range token --- .../org/cinchapi/concourse/server/concurrent/RangeToken.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java index 618ba80aa0..36f8ab6e97 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java @@ -128,11 +128,12 @@ private static ByteBuffer serialize(Text key, Operator operator, ByteBuffer bytes = ByteBuffer.allocate(size); bytes.put(operator != null ? (byte) operator.ordinal() : NULL_OPERATOR); bytes.putInt(key.size()); - bytes.put(key.getBytes()); + key.copyToByteBuffer(bytes); for (Value value : values) { bytes.putInt(value.size()); - bytes.put(value.getBytes()); + value.copyToByteBuffer(bytes); } + bytes.flip(); return bytes; } From b3e6ebdc1ac0f6ced803fc73da9c5a8a26e1d642 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 21:40:19 -0400 Subject: [PATCH 026/100] no unnecessary buffer copies for text objects --- .../java/org/cinchapi/concourse/server/model/Text.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index c70a49e36a..b66984a71b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -130,7 +130,7 @@ public int hashCode() { @Override public int size() { - return getBytes().capacity(); + return bytes == null ? getBytes().capacity() : bytes.capacity(); } @Override @@ -140,7 +140,13 @@ public String toString() { @Override public void copyToByteBuffer(ByteBuffer buffer) { - ByteBuffers.putString(text, buffer); + if(bytes == null) { + ByteBuffers.putString(text, buffer); + } + else { + buffer.put(bytes); + bytes.flip(); + } } } From 4898c89e4c5d44cf318c6ae0dab850bb8ea3eb52 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 18 Oct 2014 22:14:18 -0400 Subject: [PATCH 027/100] rewind instead of flip --- .../src/main/java/org/cinchapi/concourse/server/model/Text.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index b66984a71b..f8ae354c47 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -145,7 +145,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { } else { buffer.put(bytes); - bytes.flip(); + bytes.rewind(); } } From 99f9592241ead9e47689a120fd78567c68285233 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 07:54:28 -0400 Subject: [PATCH 028/100] in the Numbers#compare method, cache the class of both params so we don't keep making unnecessary method calls --- .../org/cinchapi/concourse/util/Numbers.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java b/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java index 58f5328519..c07da6b3cb 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/Numbers.java @@ -55,28 +55,30 @@ public static double percent(Number numerator, Number denominator) { * greater than {@code b}. */ public static int compare(Number a, Number b) { - if((a.getClass() == Integer.class || a.getClass() == int.class) - && (b.getClass() == Integer.class || b.getClass() == int.class)) { + Class aClass = a.getClass(); + Class bClass = b.getClass(); + if((aClass == int.class || aClass == Integer.class) + && (bClass == int.class || bClass == Integer.class)) { return Integer.compare(a.intValue(), b.intValue()); } - else if((a.getClass() == Long.class || a.getClass() == long.class) - && (b.getClass() == Long.class || b.getClass() == long.class)) { + else if((aClass == long.class || aClass == Long.class) + && (bClass == long.class || bClass == Long.class)) { return Long.compare(a.longValue(), b.longValue()); } - else if((a.getClass() == Float.class || a.getClass() == float.class) - && (b.getClass() == Float.class || b.getClass() == float.class)) { + else if((aClass == float.class || aClass == Float.class) + && (bClass == float.class || bClass == Float.class)) { return Float.compare(a.floatValue(), b.floatValue()); } - else if((a.getClass() == Double.class || a.getClass() == double.class) - && (b.getClass() == Double.class || b.getClass() == double.class)) { + else if((aClass == double.class || aClass == Double.class) + && (bClass == double.class || bClass == Double.class)) { return Double.compare(a.doubleValue(), b.doubleValue()); } - else if((a.getClass() == Short.class || a.getClass() == short.class) - && (b.getClass() == Short.class || b.getClass() == short.class)) { + else if((aClass == short.class || aClass == Short.class) + && (bClass == short.class || bClass == Short.class)) { return Short.compare(a.shortValue(), b.shortValue()); } - else if((a.getClass() == Byte.class || a.getClass() == byte.class) - && (b.getClass() == Byte.class || b.getClass() == byte.class)) { + else if((aClass == byte.class || aClass == Byte.class) + && (bClass == byte.class || bClass == Byte.class)) { return Long.compare(a.byteValue(), b.byteValue()); } else { From 4bf82a266aa0ec02f6997854eabbbcbfdb66afa5 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 08:17:18 -0400 Subject: [PATCH 029/100] rewind again to be safe? --- .../src/main/java/org/cinchapi/concourse/server/model/Text.java | 1 + 1 file changed, 1 insertion(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index f8ae354c47..6c1ea933e0 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -144,6 +144,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { ByteBuffers.putString(text, buffer); } else { + bytes.rewind(); buffer.put(bytes); bytes.rewind(); } From 67c73fb17d7b3683dbf0439f4edd0d6b9c31c521 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 08:21:49 -0400 Subject: [PATCH 030/100] don't lock inline so we don't compile tokens twice Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java --- .../concourse/server/storage/Engine.java | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 463ce2b194..3a00239835 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.concurrent.ThreadSafe; @@ -339,14 +340,16 @@ long record = write.getRecord().longValue(); @Override public boolean add(String key, TObject value, long record) { - lockService.getWriteLock(key, record).lock(); - rangeLockService.getWriteLock(key, value).lock(); + Lock write = lockService.getWriteLock(key, record); + Lock range = rangeLockService.getWriteLock(key, value); + write.lock(); + range.lock(); try { return addUnsafe(key, value, record); } finally { - lockService.getWriteLock(key, record).unlock(); - rangeLockService.getWriteLock(key, value).unlock(); + write.unlock(); + range.unlock(); } } @@ -369,12 +372,13 @@ public void addVersionChangeListener(Token token, @Override public Map audit(long record) { transportLock.readLock().lock(); - lockService.getReadLock(record).lock(); + Lock read = lockService.getReadLock(record); + read.lock(); try { return super.audit(record); } finally { - lockService.getReadLock(record).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -382,12 +386,13 @@ public Map audit(long record) { @Override public Map audit(String key, long record) { transportLock.readLock().lock(); - lockService.getReadLock(key, record).lock(); + Lock read = lockService.getReadLock(key, record); + read.lock(); try { return super.audit(key, record); } finally { - lockService.getReadLock(key, record).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -395,12 +400,13 @@ public Map audit(String key, long record) { @Override public Map> browse(long record) { transportLock.readLock().lock(); - lockService.getReadLock(record).lock(); + Lock read = lockService.getReadLock(record); + read.lock(); try { return super.browse(record); } finally { - lockService.getReadLock(record).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -419,12 +425,13 @@ public Map> browse(long record, long timestamp) { @Override public Map> browse(String key) { transportLock.readLock().lock(); - lockService.getReadLock(key).lock(); + Lock read = lockService.getReadLock(key); + read.lock(); try { return super.browse(key); } finally { - lockService.getReadLock(key).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -457,12 +464,13 @@ public String dump(String id) { @Override public Set fetch(String key, long record) { transportLock.readLock().lock(); - lockService.getReadLock(key, record).lock(); + Lock read = lockService.getReadLock(key, record); + read.lock(); try { return super.fetch(key, record); } finally { - lockService.getReadLock(key, record).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -538,14 +546,16 @@ public void notifyVersionChange(Token token) { @Override public boolean remove(String key, TObject value, long record) { - lockService.getWriteLock(key, record).lock(); - rangeLockService.getWriteLock(key, value).lock(); + Lock write = lockService.getWriteLock(key, record); + Lock range = rangeLockService.getWriteLock(key, value); + write.lock(); + range.lock(); try { return removeUnsafe(key, value, record); } finally { - lockService.getWriteLock(key, record).unlock(); - rangeLockService.getWriteLock(key, value).unlock(); + write.unlock(); + range.unlock(); } } @@ -580,7 +590,6 @@ public Set search(String key, String query) { } } - @Override public void start() { if(!running) { Logger.info("Starting the '{}' Engine...", environment); @@ -639,12 +648,13 @@ public void stop() { @Override public boolean verify(String key, TObject value, long record) { transportLock.readLock().lock(); - lockService.getReadLock(key, record).lock(); + Lock read = lockService.getReadLock(key, record); + read.lock(); try { return super.verify(key, value, record); } finally { - lockService.getReadLock(key, record).unlock(); + read.unlock(); transportLock.readLock().unlock(); } } @@ -676,12 +686,13 @@ protected Map> doExplore(long timestamp, String key, protected Map> doExplore(String key, Operator operator, TObject... values) { transportLock.readLock().lock(); - rangeLockService.getReadLock(key, operator, values).lock(); + Lock range = rangeLockService.getReadLock(key, operator, values); + range.lock(); try { return super.doExplore(key, operator, values); } finally { - rangeLockService.getReadLock(key, operator, values).unlock(); + range.unlock(); transportLock.readLock().unlock(); } } From 32bc45fbcdf63aa3ec30188cb497ce6f8cbc7461 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 11:53:23 -0400 Subject: [PATCH 031/100] use different thrift protocols and transports --- .../concourse/server/ConcourseServer.java | 6 +++ .../org/cinchapi/concourse/Concourse.java | 39 ++++++++++++------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 4467c5848e..6ef247d11f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -45,9 +45,11 @@ import javax.management.ObjectName; import org.apache.thrift.TException; +import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; +import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.cinchapi.concourse.annotate.Atomic; @@ -291,6 +293,10 @@ public ConcourseServer(int port, String bufferStore, String dbStore) this); Args args = new TThreadPoolServer.Args(socket); args.processor(processor); + args.inputProtocolFactory(new TCompactProtocol.Factory()); + args.outputProtocolFactory(new TCompactProtocol.Factory()); + args.inputTransportFactory(new TFastFramedTransport.Factory()); + args.outputTransportFactory(new TFastFramedTransport.Factory()); args.maxWorkerThreads(NUM_WORKER_THREADS); args.executorService(Executors .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat( diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index 33eba89dbb..ca40540239 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -31,11 +31,13 @@ import java.util.concurrent.Callable; import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang.StringUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; @@ -67,18 +69,19 @@ /** *

* Concourse is a schemaless and distributed version control database with - * optimistic availability, serializable transactions and full-text search. - * Concourse provides a more intuitive approach to data management that is easy - * to deploy, access and scale with minimal tuning while also maintaining the - * referential integrity and ACID characteristics of traditional database - * systems. + * automatic indexing, acid transactions and full-text search. Concourse + * provides a more intuitive approach to data management that is easy to deploy, + * access and scale with minimal tuning while also maintaining the referential + * integrity and ACID characteristics of traditional database systems. *

*

Data Model

*

- * The Concourse data model is lightweight and flexible which enables it to - * support any kind of data at very large scales. Concourse trades unnecessary - * structural notions of schemas, tables and indexes for a more natural modeling - * of data based solely on the following concepts: + * The Concourse data model is lightweight and flexible. Unlike other databases, + * Concourse is completely schemaless and does not hold data in tables or + * collections. Instead, Concourse is simply a distributed graph of records. + * Each record has multiple keys. And each key has one or more distinct values. + * Like any graph, you can link records to one another. And the structure of one + * record does not affect the structure of another. *

*

*

    @@ -90,7 +93,7 @@ * different {@code keys}, and the {@code keys} in one {@code record} do not * affect those in another {@code record}. *
  • Value — A dynamically typed quantity that is - * mapped from a {@code key} in a {@code record}. + * associated with a {@code key} in a {@code record}. *
*

*

Data Types

@@ -116,9 +119,17 @@ * {@link #commit()} and {@link #abort()} methods. * *

+ *

Thread Safety

+ *

+ * You should not use the same client connection in multiple + * threads. If you need to interact with Concourse using multiple threads, you + * should create a separate connection for each thread or use a + * {@link ConnectionPool}. + *

* * @author jnelson */ +@NotThreadSafe public abstract class Concourse implements AutoCloseable { /** @@ -1214,10 +1225,11 @@ public Client(String host, int port, String username, String password, this.username = ClientSecurity.encrypt(username); this.password = ClientSecurity.encrypt(password); this.environment = environment; - final TTransport transport = new TSocket(host, port); + final TTransport transport = new TFastFramedTransport(new TSocket( + host, port)); try { transport.open(); - TProtocol protocol = new TBinaryProtocol(transport); + TProtocol protocol = new TCompactProtocol(transport); client = new ConcourseService.Client(protocol); authenticate(); Runtime.getRuntime().addShutdownHook(new Thread("shutdown") { @@ -1274,7 +1286,6 @@ public boolean add(final String key, final T value, .isBlank((String) value)))) { // CON-21 return execute(new Callable() { - @Override public Boolean call() throws Exception { return client.add(key, Convert.javaToThrift(value), record, creds, transaction, environment); From 21665d2e8ba66444936b5e2ed49cf525a1a3d457 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 12:03:27 -0400 Subject: [PATCH 032/100] Revert "use different thrift protocols and transports" This reverts commit 0d9a9063008c1141840757e80113ff253ea3d9ae. --- .../concourse/server/ConcourseServer.java | 6 --- .../org/cinchapi/concourse/Concourse.java | 39 +++++++------------ 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 6ef247d11f..4467c5848e 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -45,11 +45,9 @@ import javax.management.ObjectName; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; -import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.cinchapi.concourse.annotate.Atomic; @@ -293,10 +291,6 @@ public ConcourseServer(int port, String bufferStore, String dbStore) this); Args args = new TThreadPoolServer.Args(socket); args.processor(processor); - args.inputProtocolFactory(new TCompactProtocol.Factory()); - args.outputProtocolFactory(new TCompactProtocol.Factory()); - args.inputTransportFactory(new TFastFramedTransport.Factory()); - args.outputTransportFactory(new TFastFramedTransport.Factory()); args.maxWorkerThreads(NUM_WORKER_THREADS); args.executorService(Executors .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat( diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index ca40540239..33eba89dbb 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -31,13 +31,11 @@ import java.util.concurrent.Callable; import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang.StringUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; @@ -69,19 +67,18 @@ /** *

* Concourse is a schemaless and distributed version control database with - * automatic indexing, acid transactions and full-text search. Concourse - * provides a more intuitive approach to data management that is easy to deploy, - * access and scale with minimal tuning while also maintaining the referential - * integrity and ACID characteristics of traditional database systems. + * optimistic availability, serializable transactions and full-text search. + * Concourse provides a more intuitive approach to data management that is easy + * to deploy, access and scale with minimal tuning while also maintaining the + * referential integrity and ACID characteristics of traditional database + * systems. *

*

Data Model

*

- * The Concourse data model is lightweight and flexible. Unlike other databases, - * Concourse is completely schemaless and does not hold data in tables or - * collections. Instead, Concourse is simply a distributed graph of records. - * Each record has multiple keys. And each key has one or more distinct values. - * Like any graph, you can link records to one another. And the structure of one - * record does not affect the structure of another. + * The Concourse data model is lightweight and flexible which enables it to + * support any kind of data at very large scales. Concourse trades unnecessary + * structural notions of schemas, tables and indexes for a more natural modeling + * of data based solely on the following concepts: *

*

*

    @@ -93,7 +90,7 @@ * different {@code keys}, and the {@code keys} in one {@code record} do not * affect those in another {@code record}. *
  • Value — A dynamically typed quantity that is - * associated with a {@code key} in a {@code record}. + * mapped from a {@code key} in a {@code record}. *
*

*

Data Types

@@ -119,17 +116,9 @@ * {@link #commit()} and {@link #abort()} methods. * *

- *

Thread Safety

- *

- * You should not use the same client connection in multiple - * threads. If you need to interact with Concourse using multiple threads, you - * should create a separate connection for each thread or use a - * {@link ConnectionPool}. - *

* * @author jnelson */ -@NotThreadSafe public abstract class Concourse implements AutoCloseable { /** @@ -1225,11 +1214,10 @@ public Client(String host, int port, String username, String password, this.username = ClientSecurity.encrypt(username); this.password = ClientSecurity.encrypt(password); this.environment = environment; - final TTransport transport = new TFastFramedTransport(new TSocket( - host, port)); + final TTransport transport = new TSocket(host, port); try { transport.open(); - TProtocol protocol = new TCompactProtocol(transport); + TProtocol protocol = new TBinaryProtocol(transport); client = new ConcourseService.Client(protocol); authenticate(); Runtime.getRuntime().addShutdownHook(new Thread("shutdown") { @@ -1286,6 +1274,7 @@ public boolean add(final String key, final T value, .isBlank((String) value)))) { // CON-21 return execute(new Callable() { + @Override public Boolean call() throws Exception { return client.add(key, Convert.javaToThrift(value), record, creds, transaction, environment); From 91a72d168f64394d9fb16d6921d469222f928fb4 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 18:07:42 -0400 Subject: [PATCH 033/100] synchronize lazy setting of byte buffer for Text objects --- .../concourse/server/concurrent/RangeToken.java | 2 +- .../org/cinchapi/concourse/server/io/Composite.java | 2 +- .../cinchapi/concourse/server/model/Position.java | 2 +- .../cinchapi/concourse/server/model/PrimaryKey.java | 2 +- .../org/cinchapi/concourse/server/model/Text.java | 13 +++++++++---- .../org/cinchapi/concourse/server/model/Value.java | 2 +- .../concourse/server/storage/AtomicOperation.java | 2 +- .../cinchapi/concourse/server/storage/db/Block.java | 2 +- .../concourse/server/storage/db/BlockIndex.java | 4 ++-- .../concourse/server/storage/db/Revision.java | 2 +- .../concourse/server/storage/temp/Buffer.java | 2 +- .../concourse/server/storage/temp/Write.java | 2 +- .../java/org/cinchapi/concourse/util/TArrays.java | 2 +- 13 files changed, 22 insertions(+), 17 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java index 36f8ab6e97..963378724c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java @@ -133,7 +133,7 @@ private static ByteBuffer serialize(Text key, Operator operator, bytes.putInt(value.size()); value.copyToByteBuffer(bytes); } - bytes.flip(); + bytes.rewind(); return bytes; } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index 7f6ad4d5b4..e76594a03d 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -96,7 +96,7 @@ private Composite(Byteable... byteables) { for (Byteable byteable : byteables) { byteable.copyToByteBuffer(bytes); } - bytes.flip(); + bytes.rewind(); } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java index 4359b7c363..e1e6177db7 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java @@ -150,7 +150,7 @@ public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java index a619d280e4..f673667534 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java @@ -139,7 +139,7 @@ public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(SIZE); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index 6c1ea933e0..9b13fb5596 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -81,6 +81,11 @@ public static Text wrap(String string) { */ private final String text; + /** + * A mutex used to synchronized the lazy setting of the byte buffer. + */ + private final Object mutex = new Object(); + /** * Construct an instance that wraps the {@code text} string. * @@ -118,7 +123,9 @@ public boolean equals(Object obj) { @Override public ByteBuffer getBytes() { if(bytes == null) { - bytes = ByteBuffers.fromString(text); + synchronized (mutex) { + bytes = ByteBuffers.fromString(text); + } } return ByteBuffers.asReadOnlyBuffer(bytes); } @@ -144,9 +151,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { ByteBuffers.putString(text, buffer); } else { - bytes.rewind(); - buffer.put(bytes); - bytes.rewind(); + buffer.put(getBytes()); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index 610c22b049..e0b9682fa5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -214,7 +214,7 @@ public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index 346c8ff29b..303e428d71 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -621,7 +621,7 @@ public ByteBuffer getBytes() { // to commit, so its best to not create a copy if we don't have to ByteBuffer bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); return bytes; } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index 1e8517c0d4..c117402b2f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -284,7 +284,7 @@ public ByteBuffer getBytes() { try { ByteBuffer bytes = ByteBuffer.allocate(size); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); return bytes; } finally { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java index 10f5593872..34698c0144 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java @@ -144,7 +144,7 @@ public ByteBuffer getBytes() { try { ByteBuffer bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); return bytes; } finally { @@ -391,7 +391,7 @@ public Entry(Composite key) { public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); return bytes; } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java index 1aba6817cb..b099fece7f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java @@ -246,7 +246,7 @@ public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index e4ad54b840..40e52058e8 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -893,7 +893,7 @@ public void append(Write write) throws CapacityException { index(write); ByteBuffer bytes = ByteBuffer.allocate(write.size()); write.copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); content.putInt(write.size()); content.put(bytes); content.force(); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java index b8a13ed3b0..12a679a591 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java @@ -208,7 +208,7 @@ public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); copyToByteBuffer(bytes); - bytes.flip(); + bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java index 530f786702..3ec0434b6c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TArrays.java @@ -57,7 +57,7 @@ public static ByteBuffer hash(Object... objects) { bytes.putInt(object.hashCode()); bytes.putInt(object.getClass().getName().hashCode()); } - bytes.flip(); + bytes.rewind(); return bytes; } From 79ef8056632a7f6de0f7e08e8619a8c59b9cf00b Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 19 Oct 2014 18:08:01 -0400 Subject: [PATCH 034/100] javadoc update --- .../src/main/java/org/cinchapi/concourse/util/ByteBuffers.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java index cd11bcf786..6e2ba13b6f 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java @@ -224,7 +224,8 @@ public static String getString(ByteBuffer buffer, Charset charset) { /** * Put the UTF-8 encoding for the {@code source} string into the - * {@code destination} byte buffer. This method uses some optimization + * {@code destination} byte buffer and increment the position by the length + * of the strings byte sequence. This method uses some optimization * techniques and is the preferable way to add strings to byte buffers than * doing so manually. * From c29f8dd84b78fa3f1018186edb79fe31aa884bc2 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Mon, 20 Oct 2014 07:38:40 -0400 Subject: [PATCH 035/100] javadoc update --- .../org/cinchapi/concourse/Concourse.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index 33eba89dbb..bddde1ea67 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -67,18 +67,19 @@ /** *

* Concourse is a schemaless and distributed version control database with - * optimistic availability, serializable transactions and full-text search. - * Concourse provides a more intuitive approach to data management that is easy - * to deploy, access and scale with minimal tuning while also maintaining the - * referential integrity and ACID characteristics of traditional database - * systems. + * automatic indexing, acid transactions and full-text search. Concourse + * provides a more intuitive approach to data management that is easy to deploy, + * access and scale with minimal tuning while also maintaining the referential + * integrity and ACID characteristics of traditional database systems. *

*

Data Model

*

- * The Concourse data model is lightweight and flexible which enables it to - * support any kind of data at very large scales. Concourse trades unnecessary - * structural notions of schemas, tables and indexes for a more natural modeling - * of data based solely on the following concepts: + * The Concourse data model is lightweight and flexible. Unlike other databases, + * Concourse is completely schemaless and does not hold data in tables or + * collections. Instead, Concourse is simply a distributed graph of records. + * Each record has multiple keys. And each key has one or more distinct values. + * Like any graph, you can link records to one another. And the structure of one + * record does not affect the structure of another. *

*

*

    @@ -90,7 +91,7 @@ * different {@code keys}, and the {@code keys} in one {@code record} do not * affect those in another {@code record}. *
  • Value — A dynamically typed quantity that is - * mapped from a {@code key} in a {@code record}. + * associated with a {@code key} in a {@code record}. *
*

*

Data Types

@@ -116,6 +117,13 @@ * {@link #commit()} and {@link #abort()} methods. * *

+ *

Thread Safety

+ *

+ * You should not use the same client connection in multiple + * threads. If you need to interact with Concourse using multiple threads, you + * should create a separate connection for each thread or use a + * {@link ConnectionPool}. + *

* * @author jnelson */ From f8ace6da42aca33bcf10b9ed928f1ce804b7d2bf Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 22 Oct 2014 16:55:05 -0400 Subject: [PATCH 036/100] Revert "Revert "use different thrift protocols and transports"" This reverts commit b840430e9058d3a84080669279f969b9e7bb8496. --- .../cinchapi/concourse/server/ConcourseServer.java | 6 ++++++ .../main/java/org/cinchapi/concourse/Concourse.java | 11 +++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 4467c5848e..6ef247d11f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -45,9 +45,11 @@ import javax.management.ObjectName; import org.apache.thrift.TException; +import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; +import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.cinchapi.concourse.annotate.Atomic; @@ -291,6 +293,10 @@ public ConcourseServer(int port, String bufferStore, String dbStore) this); Args args = new TThreadPoolServer.Args(socket); args.processor(processor); + args.inputProtocolFactory(new TCompactProtocol.Factory()); + args.outputProtocolFactory(new TCompactProtocol.Factory()); + args.inputTransportFactory(new TFastFramedTransport.Factory()); + args.outputTransportFactory(new TFastFramedTransport.Factory()); args.maxWorkerThreads(NUM_WORKER_THREADS); args.executorService(Executors .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat( diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index bddde1ea67..ca40540239 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -31,11 +31,13 @@ import java.util.concurrent.Callable; import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang.StringUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; @@ -127,6 +129,7 @@ * * @author jnelson */ +@NotThreadSafe public abstract class Concourse implements AutoCloseable { /** @@ -1222,10 +1225,11 @@ public Client(String host, int port, String username, String password, this.username = ClientSecurity.encrypt(username); this.password = ClientSecurity.encrypt(password); this.environment = environment; - final TTransport transport = new TSocket(host, port); + final TTransport transport = new TFastFramedTransport(new TSocket( + host, port)); try { transport.open(); - TProtocol protocol = new TBinaryProtocol(transport); + TProtocol protocol = new TCompactProtocol(transport); client = new ConcourseService.Client(protocol); authenticate(); Runtime.getRuntime().addShutdownHook(new Thread("shutdown") { @@ -1282,7 +1286,6 @@ public boolean add(final String key, final T value, .isBlank((String) value)))) { // CON-21 return execute(new Callable() { - @Override public Boolean call() throws Exception { return client.add(key, Convert.javaToThrift(value), record, creds, transaction, environment); From 8230b31e70bfcc078fd88968b0598af9751b03af Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 22 Oct 2014 16:56:09 -0400 Subject: [PATCH 037/100] Revert "Revert "Revert "use different thrift protocols and transports""" This reverts commit 38d3e27af375e48d7c78099f4c8322233d0ddf56. --- .../cinchapi/concourse/server/ConcourseServer.java | 6 ------ .../main/java/org/cinchapi/concourse/Concourse.java | 11 ++++------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 6ef247d11f..4467c5848e 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -45,11 +45,9 @@ import javax.management.ObjectName; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.server.TThreadPoolServer.Args; -import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.cinchapi.concourse.annotate.Atomic; @@ -293,10 +291,6 @@ public ConcourseServer(int port, String bufferStore, String dbStore) this); Args args = new TThreadPoolServer.Args(socket); args.processor(processor); - args.inputProtocolFactory(new TCompactProtocol.Factory()); - args.outputProtocolFactory(new TCompactProtocol.Factory()); - args.inputTransportFactory(new TFastFramedTransport.Factory()); - args.outputTransportFactory(new TFastFramedTransport.Factory()); args.maxWorkerThreads(NUM_WORKER_THREADS); args.executorService(Executors .newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat( diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index ca40540239..bddde1ea67 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -31,13 +31,11 @@ import java.util.concurrent.Callable; import javax.annotation.Nullable; -import javax.annotation.concurrent.NotThreadSafe; import org.apache.commons.lang.StringUtils; import org.apache.thrift.TException; -import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; -import org.apache.thrift.transport.TFastFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; @@ -129,7 +127,6 @@ * * @author jnelson */ -@NotThreadSafe public abstract class Concourse implements AutoCloseable { /** @@ -1225,11 +1222,10 @@ public Client(String host, int port, String username, String password, this.username = ClientSecurity.encrypt(username); this.password = ClientSecurity.encrypt(password); this.environment = environment; - final TTransport transport = new TFastFramedTransport(new TSocket( - host, port)); + final TTransport transport = new TSocket(host, port); try { transport.open(); - TProtocol protocol = new TCompactProtocol(transport); + TProtocol protocol = new TBinaryProtocol(transport); client = new ConcourseService.Client(protocol); authenticate(); Runtime.getRuntime().addShutdownHook(new Thread("shutdown") { @@ -1286,6 +1282,7 @@ public boolean add(final String key, final T value, .isBlank((String) value)))) { // CON-21 return execute(new Callable() { + @Override public Boolean call() throws Exception { return client.add(key, Convert.javaToThrift(value), record, creds, transaction, environment); From c19fd4ab8e587536b9cbea5b0d416b7517d5c719 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 23 Oct 2014 16:27:12 -0400 Subject: [PATCH 038/100] implement privileged locking so that buffer transports are de-prioritized when contending with reads --- .../concurrent/PriorityReadWriteLock.java | 315 ++++++++++++++++++ .../concourse/server/storage/Engine.java | 4 +- .../concourse/server/storage/temp/Buffer.java | 6 +- 3 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java new file mode 100644 index 0000000000..af3a43fd9a --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java @@ -0,0 +1,315 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.concurrent; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * An extension of a {@link ReentrantReadWriteLock} that has a preference for + * either readers or writers. + *

+ * A {@link PriorityReadWriteLock} is useful in cases when there is a desire to + * prefer readers over writers or vice versa under contention. Prioritized + * actors will try to grab their locks immediately whereas unprivileged ones + * will deferentially linger for a while before attempting to grab theirs. This + * means that a prioritized actor will always grab her lock before an + * unprioritized one who tries to grab his at the same time. + *

+ *

+ * These locks have logic to ensure that unprioritized actors only defer to + * prioritized ones in the event that there is contention for the lock. If there + * is none, then unprioritized actors will generally grab their locks + * immediately. + *

+ * + * @author jnelson + */ +@SuppressWarnings("serial") +public class PriorityReadWriteLock extends ReentrantReadWriteLock { + + /** + * Return a {@link PriorityReadWriteLock} that has a preference for + * readers over writers under contention. + * + * @return the lock + */ + public static PriorityReadWriteLock prioritizeReads() { + return new PriorityReadWriteLock(true); + } + + /** + * Return a {@link PriorityReadWriteLock} that has a preference for + * writers over readers under contention. + * + * @return the lock + */ + public static PriorityReadWriteLock prioritizeWrites() { + return new PriorityReadWriteLock(false); + } + + /** + * A flag that indicates that unprivileged actor must spin before trying to + * grab the lock. By default, this is set to {@code false}, but a privileged + * actor will set this to {@code true} when they try to grab the lock. + * Conversely, unprivileged actors always set this to {@code false}, which + * means that they generally won't ever spin while there aren't any lock + * attempts from privileged actors. + */ + private volatile boolean forceSpin = false; + + /** + * The lock that is returned from the {@link #readLock()} method. + */ + private final ReadLock readLock; + + /** + * The lock that is returned from the {@link #writeLock()} method. + */ + private final WriteLock writeLock; + + /** + * Construct a new instance. If {@code privilegedReads} is {@code true}, + * then the resulting lock with be biased towards read locking and biased + * against write locking. The opposite is true if the parameter is + * {@code false}. + * + * @param privilegedReads + */ + private PriorityReadWriteLock(boolean privilegedReads) { + readLock = privilegedReads ? new PriorityReadLock(this) + : new UnpriorityReadLock(this); + writeLock = privilegedReads ? new UnpriorityWriteLock(this) + : new PriorityWriteLock(this); + } + + @Override + public ReadLock readLock() { + return readLock; + } + + @Override + public WriteLock writeLock() { + return writeLock; + } + + /** + * Spin if it is necessary to do so. + */ + private void trySpin() { + if(forceSpin) { + Threads.sleep(1); + } + } + + /** + * An {@link ReadLock} that does not defer to writers. + * + * @author jnelson + */ + private final class PriorityReadLock extends ReadLock { + + /** + * Construct a new instance. + * + * @param lock + */ + protected PriorityReadLock(PriorityReadWriteLock lock) { + super(lock); + } + + @Override + public void lock() { + forceSpin = true; + super.lock(); + } + + @Override + public void lockInterruptibly() throws InterruptedException { + forceSpin = true; + super.lockInterruptibly(); + } + + @Override + public boolean tryLock() { + forceSpin = true; + return super.tryLock(); + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + forceSpin = true; + return super.tryLock(timeout, unit); + } + + } + + /** + * An {@link WriteLock} that does not defer to readers. + * + * @author jnelson + */ + private final class PriorityWriteLock extends WriteLock { + + /** + * Construct a new instance. + * + * @param lock + */ + protected PriorityWriteLock(PriorityReadWriteLock lock) { + super(lock); + } + + @Override + public void lock() { + forceSpin = true; + super.lock(); + } + + @Override + public void lockInterruptibly() throws InterruptedException { + forceSpin = true; + super.lockInterruptibly(); + } + + @Override + public boolean tryLock() { + forceSpin = true; + return super.tryLock(); + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + forceSpin = true; + return super.tryLock(timeout, unit); + } + } + + /** + * An {@link ReadLock} that defers to writers under contention. + * + * @author jnelson + */ + private final class UnpriorityReadLock extends ReadLock { + + /** + * Construct a new instance. + * + * @param lock + */ + protected UnpriorityReadLock(PriorityReadWriteLock lock) { + super(lock); + } + + @Override + public void lock() { + trySpin(); + super.lock(); + forceSpin = false; + } + + @Override + public void lockInterruptibly() throws InterruptedException { + trySpin(); + super.lockInterruptibly(); + forceSpin = false; + } + + @Override + public boolean tryLock() { + trySpin(); + forceSpin = false; + return super.tryLock(); + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + trySpin(); + if(super.tryLock(timeout, unit)) { + forceSpin = false; + return true; + } + else { + return false; + } + } + + } + + /** + * An {@link WriteLock} that defers to readers under contention. + * + * @author jnelson + */ + private final class UnpriorityWriteLock extends WriteLock { + + /** + * Constructa new instance. + * + * @param lock + */ + protected UnpriorityWriteLock(PriorityReadWriteLock lock) { + super(lock); + } + + @Override + public void lock() { + trySpin(); + super.lock(); + forceSpin = false; + } + + @Override + public void lockInterruptibly() throws InterruptedException { + trySpin(); + super.lockInterruptibly(); + forceSpin = false; + } + + @Override + public boolean tryLock() { + trySpin(); + forceSpin = false; + return super.tryLock(); + } + + @Override + public boolean tryLock(long timeout, TimeUnit unit) + throws InterruptedException { + trySpin(); + if(super.tryLock(timeout, unit)) { + forceSpin = false; + return true; + } + else { + return false; + } + } + + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 3a00239835..be54e9bfa8 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -48,6 +48,7 @@ import org.cinchapi.concourse.annotate.Restricted; import org.cinchapi.concourse.server.GlobalState; import org.cinchapi.concourse.server.concurrent.LockService; +import org.cinchapi.concourse.server.concurrent.PriorityReadWriteLock; import org.cinchapi.concourse.server.concurrent.RangeLockService; import org.cinchapi.concourse.server.concurrent.RangeToken; import org.cinchapi.concourse.server.concurrent.RangeTokens; @@ -233,7 +234,8 @@ public final class Engine extends BufferedStore implements * transported from the Buffer to the Database after the Database context is * captured and sent to the Buffer to finish the buffered reading. */ - private final ReentrantReadWriteLock transportLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock transportLock = PriorityReadWriteLock + .prioritizeReads(); /** * A collection of listeners that should be notified of a version change for diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 40e52058e8..368fa66722 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -44,6 +44,7 @@ import org.cinchapi.concourse.Tag; import org.cinchapi.concourse.annotate.Restricted; import org.cinchapi.concourse.server.GlobalState; +import org.cinchapi.concourse.server.concurrent.PriorityReadWriteLock; import org.cinchapi.concourse.server.concurrent.Locks; import org.cinchapi.concourse.server.io.ByteableCollections; import org.cinchapi.concourse.server.io.FileSystem; @@ -198,7 +199,8 @@ public void remove() { * old writes concurrently while prohibiting reading the buffer and * transporting writes at the same time. */ - private final ReentrantReadWriteLock transportLock = new ReentrantReadWriteLock(); + private final ReentrantReadWriteLock transportLock = PriorityReadWriteLock + .prioritizeReads(); /** * A monitor that is used to make a thread block while waiting for the @@ -1250,7 +1252,7 @@ private void index(Write write) throws CapacityException { // the bloom filter hashing filter.put(write.getRecord(), write.getKey(), write.getValue()); writes[size] = write; - size++; + ++size; } else { throw new CapacityException(); From 2a62f8a57230eecb0047645a7d17cf9a0cbe8d61 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 23 Oct 2014 18:29:18 -0400 Subject: [PATCH 039/100] catch interrupt and set thread status --- .../concourse/server/concurrent/PriorityReadWriteLock.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java index af3a43fd9a..75349afb94 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/PriorityReadWriteLock.java @@ -119,7 +119,12 @@ public WriteLock writeLock() { */ private void trySpin() { if(forceSpin) { - Threads.sleep(1); + try { + Thread.sleep(1); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } } From 7fe81fc121c7675b1d2d6ae160d1858913843fde Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 23 Oct 2014 18:29:35 -0400 Subject: [PATCH 040/100] Add logic to gradually escalate the rate of buffer transports at the expense of decreasing the frequency of cycles. This helps to ensure that reads aren't interrupted by transports, but that transports can do more work in the event that there are no reads --- .../concourse/server/storage/Engine.java | 6 +- .../concourse/server/storage/temp/Buffer.java | 75 ++++++++++++++++--- .../concourse/server/storage/EngineTest.java | 2 +- .../server/storage/temp/BufferTest.java | 2 + 4 files changed, 69 insertions(+), 16 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index be54e9bfa8..7794773225 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -149,9 +149,9 @@ public final class Engine extends BufferedStore implements * The number of milliseconds that the {@link BufferTransportThread} sleeps * after each transport in order to avoid CPU thrashing. */ - protected static int BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = 5; // visible - // for - // testing + protected static int BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = 100; // visible + // for + // testing /** * A flag to indicate that the {@link BufferTransportThrread} has appeared diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 368fa66722..36f3a9ee32 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -228,6 +228,25 @@ public void remove() { */ private AtomicLong timeOfLastTransport = new AtomicLong(Time.now()); + /** + * The number of items to transport to the Database per attempt. There is a + * tension between transporting and reading data (e.g. reads cannot happen + * while a transport occurs and vice versa). Transports are most efficient + * when they can batch up the amount of work per cycle, but that would be + * reads are blocked longer. So this variable indicates how many items + * should be transported in a single cycle. Each time a transport happens, + * this value will increase, but it will be decreased whenever a read + * occurs. This allows us to be more aggressive with transports when there + * are no reads happening, and also allows us to scale back transports when + * reads do occur. + */ + private int transportRate = 1; + + /** + * The multiplier that is used when increasing the rate of transport. + */ + protected int transportRateMultiplier = 2; // visible for testing + /** * Construct a Buffer that is backed by the default location, which is * {@link GlobalState#BUFFER_DIRECTORY}. @@ -254,6 +273,7 @@ public Buffer(String directory) { public Map audit(long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.audit(record); } finally { @@ -265,6 +285,7 @@ public Map audit(long record) { public Map audit(String key, long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.audit(key, record); } finally { @@ -276,6 +297,7 @@ public Map audit(String key, long record) { public Map> browse(long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(record); } finally { @@ -287,6 +309,7 @@ public Map> browse(long record) { public Map> browse(long record, long timestamp) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(record, timestamp); } finally { @@ -299,6 +322,7 @@ public Map> browse(long record, long timestamp, Map> context) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(record, timestamp, context); } finally { @@ -310,6 +334,7 @@ public Map> browse(long record, long timestamp, public Map> browse(String key) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(key); } finally { @@ -321,6 +346,7 @@ public Map> browse(String key) { public Map> browse(String key, long timestamp) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(key, timestamp); } finally { @@ -333,6 +359,7 @@ public Map> browse(String key, long timestamp, Map> context) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.browse(key, timestamp, context); } finally { @@ -345,6 +372,7 @@ public Set describe(long record, long timestamp, Map> ktv) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.describe(record, timestamp, ktv); } finally { @@ -370,6 +398,7 @@ public String dump() { public Set fetch(String key, long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.fetch(key, record); } finally { @@ -381,6 +410,7 @@ public Set fetch(String key, long record) { public Set fetch(String key, long record, long timestamp) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.fetch(key, record, timestamp); } finally { @@ -393,6 +423,7 @@ public Set fetch(String key, long record, long timestamp, Set values) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.fetch(key, record, timestamp, values); } finally { @@ -405,6 +436,7 @@ public Map> explore(Map> context, long timestamp, String key, Operator operator, TObject... values) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.explore(context, timestamp, key, operator, values); } finally { @@ -436,6 +468,7 @@ public long getTimeOfLastTransport() { public long getVersion(long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.getVersion(record); } finally { @@ -447,6 +480,7 @@ public long getVersion(long record) { public long getVersion(String key) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.getVersion(key); } finally { @@ -458,6 +492,7 @@ public long getVersion(String key) { public long getVersion(String key, long record) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.getVersion(key, record); } finally { @@ -591,6 +626,7 @@ private void flip() { public Set search(String key, String query) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.search(key, query); } finally { @@ -634,27 +670,31 @@ public void stop() { } /** - * {@inheritDoc} This method will transport the first write in the buffer. + * {@inheritDoc} This method will transport at least one write from the + * buffer, in chronological order. */ @Override public void transport(PermanentStore destination) { - // It makes sense to only transport one write at a time because - // transporting blocks reading and all writes must read at least once, - // so we want to minimize the overhead per write. if(pages.size() > 1 && !transportLock.writeLock().isHeldByCurrentThread() && transportLock.writeLock().tryLock()) { try { Page page = pages.get(0); - if(page.hasNext()) { - destination.accept(page.next()); - timeOfLastTransport.set(Time.now()); - page.remove(); - } - else { - ((Database) destination).triggerSync(); - removePage(); + int i = 0; + while (i < transportRate) { + if(page.hasNext()) { + destination.accept(page.next()); + timeOfLastTransport.set(Time.now()); + page.remove(); + ++i; + } + else { + ((Database) destination).triggerSync(); + removePage(); + break; + } } + transportRate *= transportRateMultiplier; } finally { transportLock.writeLock().unlock(); @@ -666,6 +706,7 @@ public void transport(PermanentStore destination) { public boolean verify(String key, TObject value, long record, long timestamp) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.verify(key, value, record, timestamp); } finally { @@ -677,6 +718,7 @@ public boolean verify(String key, TObject value, long record, long timestamp) { public boolean verify(Write write, long timestamp, boolean exists) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); for (Page page : pages) { if(page.mightContain(write) && page.locallyContains(write, timestamp)) { @@ -720,6 +762,7 @@ protected Map> doExplore(long timestamp, String key, Operator operator, TObject... values) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.doExplore(timestamp, key, operator, values); } finally { @@ -732,6 +775,7 @@ protected Map> doExplore(String key, Operator operator, TObject... values) { transportLock.readLock().lock(); try { + scaleBackTransportRate(); return super.doExplore(key, operator, values); } finally { @@ -768,6 +812,13 @@ private void removePage() { } } + /** + * Scale back the number of items that are transported in a single cycle. + */ + private void scaleBackTransportRate() { + transportRate = 1; + } + /** * A {@link Page} represents a granular section of the {@link Buffer}. Pages * are an append-only iterator over a sequence of {@link Write} objects. diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java index 5dae76dfa7..0e5ef43208 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java @@ -159,7 +159,7 @@ public void testBufferTransportBlockingIfWritesAreNotWithinThreshold() { engine.start(); engine.add(TestData.getString(), TestData.getTObject(), TestData.getLong()); - Threads.sleep(Engine.BUFFER_TRANSPORT_THREAD_ALLOWABLE_INACTIVITY_THRESHOLD_IN_MILLISECONDS + 1); + Threads.sleep(Engine.BUFFER_TRANSPORT_THREAD_ALLOWABLE_INACTIVITY_THRESHOLD_IN_MILLISECONDS + 10); engine.add(TestData.getString(), TestData.getTObject(), TestData.getLong()); Assert.assertTrue(engine.bufferTransportThreadHasEverPaused.get()); diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/temp/BufferTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/temp/BufferTest.java index 2e07769838..6a06c8c636 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/temp/BufferTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/temp/BufferTest.java @@ -127,6 +127,7 @@ public void run() { @Test public void testIteratorAfterTransport() { + ((Buffer) store).transportRateMultiplier = 1; List writes = getWrites(); int j = 0; for (Write write : writes) { @@ -161,6 +162,7 @@ public void testIteratorAfterTransport() { @Test public void testReverseIteratorAfterTransport() { + ((Buffer) store).transportRateMultiplier = 1; List writes = getWrites(); for (Write write : writes) { add(write.getKey().toString(), write.getValue().getTObject(), write From 46b6202715e0af3c1f299c9713499965bfb16a63 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 23 Oct 2014 19:26:07 -0400 Subject: [PATCH 041/100] add max transport rate to prevent integer overflow --- .../cinchapi/concourse/server/storage/temp/Buffer.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 36f3a9ee32..26903d847f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -247,6 +247,11 @@ public void remove() { */ protected int transportRateMultiplier = 2; // visible for testing + /** + * Don't let the transport rate exceede this value. + */ + private static int MAX_TRANSPORT_RATE = 8192; + /** * Construct a Buffer that is backed by the default location, which is * {@link GlobalState#BUFFER_DIRECTORY}. @@ -694,7 +699,8 @@ public void transport(PermanentStore destination) { break; } } - transportRate *= transportRateMultiplier; + transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE + : (transportRate * transportRateMultiplier); } finally { transportLock.writeLock().unlock(); From 1873017cddfae430a70172c9be540bef0cda227f Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 06:27:36 -0400 Subject: [PATCH 042/100] javadoc update --- .../org/cinchapi/concourse/server/storage/temp/Buffer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 26903d847f..1c3045fa51 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -248,7 +248,7 @@ public void remove() { protected int transportRateMultiplier = 2; // visible for testing /** - * Don't let the transport rate exceede this value. + * Don't let the transport rate exceed this value. */ private static int MAX_TRANSPORT_RATE = 8192; @@ -689,7 +689,6 @@ public void transport(PermanentStore destination) { while (i < transportRate) { if(page.hasNext()) { destination.accept(page.next()); - timeOfLastTransport.set(Time.now()); page.remove(); ++i; } @@ -699,6 +698,7 @@ public void transport(PermanentStore destination) { break; } } + timeOfLastTransport.set(Time.now()); transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE : (transportRate * transportRateMultiplier); } From c2a9399a51da20e50115633587e9ab6831658f6a Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 06:28:33 -0400 Subject: [PATCH 043/100] construct token and rangetoken inner lock instances only once --- .../server/concurrent/LockService.java | 50 ++++--- .../server/concurrent/RangeLockService.java | 123 ++++++++++-------- 2 files changed, 96 insertions(+), 77 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java index 99ece5846a..9ba60809cf 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java @@ -195,6 +195,34 @@ private final class TokenReadWriteLock extends ReentrantReadWriteLock { */ private final Token token; + /** + * The lock that is returned from the {@link #readLock()} method. + */ + private final ReadLock readLock = new ReadLock(this) { + + @Override + public void unlock() { + super.unlock(); + readers.remove(Thread.currentThread(), 1); + locks.remove(token, new TokenReadWriteLock(token)); + } + + }; + + /** + * The lock that is returned from the {@link #writeLock()} method. + */ + private final WriteLock writeLock = new WriteLock(this) { + + @Override + public void unlock() { + super.unlock(); + writers.remove(Thread.currentThread(), 1); + locks.remove(token, new TokenReadWriteLock(token)); + } + + }; + /** * Construct a new instance. * @@ -227,16 +255,7 @@ public int hashCode() { @Override public ReadLock readLock() { - return new ReadLock(this) { - - @Override - public void unlock() { - super.unlock(); - readers.remove(Thread.currentThread(), 1); - locks.remove(token, new TokenReadWriteLock(token)); - } - - }; + return readLock; } @Override @@ -247,16 +266,7 @@ public String toString() { @Override public WriteLock writeLock() { - return new WriteLock(this) { - - @Override - public void unlock() { - super.unlock(); - writers.remove(Thread.currentThread(), 1); - locks.remove(token, new TokenReadWriteLock(token)); - } - - }; + return writeLock; } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index 3a20c82572..b55091159f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -352,6 +352,70 @@ private final class RangeReadWriteLock extends ReentrantReadWriteLock { */ private final RangeToken token; + /** + * The lock that is retuned from the {@link #readLock()} method. + */ + private final ReadLock readLock = new ReadLock(this) { + + @Override + public void lock() { + while (isRangeBlocked(LockType.READ, token)) { + continue; + } + super.lock(); + } + + @Override + public boolean tryLock() { + if(!isRangeBlocked(LockType.READ, token) && super.tryLock()) { + return true; + } + else { + return false; + } + } + + @Override + public void unlock() { + super.unlock(); + readers.remove(Thread.currentThread(), 1); + locks.remove(token, new RangeReadWriteLock(token)); + } + + }; + + /** + * The lock that is returned from the {@link #writeLock()} method. + */ + private final WriteLock writeLock = new WriteLock(this) { + + @Override + public void lock() { + while (isRangeBlocked(LockType.WRITE, token)) { + continue; + } + super.lock(); + } + + @Override + public boolean tryLock() { + if(!isRangeBlocked(LockType.WRITE, token) && super.tryLock()) { + return true; + } + else { + return false; + } + } + + @Override + public void unlock() { + super.unlock(); + writers.remove(Thread.currentThread(), 1); + locks.remove(token, new RangeReadWriteLock(token)); + } + + }; + /** * Construct a new instance. * @@ -384,34 +448,7 @@ public int hashCode() { @Override public ReadLock readLock() { - return new ReadLock(this) { - - @Override - public void lock() { - while (isRangeBlocked(LockType.READ, token)) { - continue; - } - super.lock(); - } - - @Override - public boolean tryLock() { - if(!isRangeBlocked(LockType.READ, token) && super.tryLock()) { - return true; - } - else { - return false; - } - } - - @Override - public void unlock() { - super.unlock(); - readers.remove(Thread.currentThread(), 1); - locks.remove(token, new RangeReadWriteLock(token)); - } - - }; + return readLock; } @Override @@ -422,35 +459,7 @@ public String toString() { @Override public WriteLock writeLock() { - return new WriteLock(this) { - - @Override - public void lock() { - while (isRangeBlocked(LockType.WRITE, token)) { - continue; - } - super.lock(); - } - - @Override - public boolean tryLock() { - if(!isRangeBlocked(LockType.WRITE, token) - && super.tryLock()) { - return true; - } - else { - return false; - } - } - - @Override - public void unlock() { - super.unlock(); - writers.remove(Thread.currentThread(), 1); - locks.remove(token, new RangeReadWriteLock(token)); - } - - }; + return writeLock; } } From d586b78a38d3fe8016f8232e2375fa25a480ce4d Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 06:45:32 -0400 Subject: [PATCH 044/100] directly compare numerical values that have the same type instead of going through the Numbers#compare method --- .../concourse/server/model/Value.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index e0b9682fa5..8372eafafe 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -297,6 +297,27 @@ else if(v1 == NEGATIVE_INFINITY) { else if(v2 == NEGATIVE_INFINITY) { return 1; } + else if(v1.getType() == Type.INTEGER + && v2.getType() == Type.INTEGER) { + int o1 = (int) v1.getObject(); + int o2 = (int) v2.getObject(); + return Integer.compare(o1, o2); + } + else if(v1.getType() == Type.LONG && v2.getType() == Type.LONG) { + long o1 = (long) v1.getObject(); + long o2 = (long) v2.getObject(); + return Long.compare(o1, o2); + } + else if(v1.getType() == Type.FLOAT && v2.getType() == Type.FLOAT) { + float o1 = (float) v1.getObject(); + float o2 = (float) v2.getObject(); + return Float.compare(o1, o2); + } + else if(v1.getType() == Type.DOUBLE && v2.getType() == Type.DOUBLE) { + double o1 = (double) v1.getObject(); + double o2 = (double) v2.getObject(); + return Double.compare(o1, o2); + } else { Object o1 = v1.getObject(); Object o2 = v2.getObject(); From af9dd75b551fd42fc06aee739e25ffe2b1e541fa Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 07:14:31 -0400 Subject: [PATCH 045/100] Revert "directly compare numerical values that have the same type instead of going through the Numbers#compare method" This reverts commit d9eb6e13235db34f5b5fa73ac01e1b811ffc83d7. --- .../concourse/server/model/Value.java | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index 8372eafafe..e0b9682fa5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -297,27 +297,6 @@ else if(v1 == NEGATIVE_INFINITY) { else if(v2 == NEGATIVE_INFINITY) { return 1; } - else if(v1.getType() == Type.INTEGER - && v2.getType() == Type.INTEGER) { - int o1 = (int) v1.getObject(); - int o2 = (int) v2.getObject(); - return Integer.compare(o1, o2); - } - else if(v1.getType() == Type.LONG && v2.getType() == Type.LONG) { - long o1 = (long) v1.getObject(); - long o2 = (long) v2.getObject(); - return Long.compare(o1, o2); - } - else if(v1.getType() == Type.FLOAT && v2.getType() == Type.FLOAT) { - float o1 = (float) v1.getObject(); - float o2 = (float) v2.getObject(); - return Float.compare(o1, o2); - } - else if(v1.getType() == Type.DOUBLE && v2.getType() == Type.DOUBLE) { - double o1 = (double) v1.getObject(); - double o2 = (double) v2.getObject(); - return Double.compare(o1, o2); - } else { Object o1 = v1.getObject(); Object o2 = v2.getObject(); From f93ee1402286fba1c1dcd267ca3a4037a756fb02 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 07:17:27 -0400 Subject: [PATCH 046/100] Revert "construct token and rangetoken inner lock instances only once" This reverts commit 54e502d6f9c478af071e7e28fb57255be00402e4. --- .../server/concurrent/LockService.java | 50 +++---- .../server/concurrent/RangeLockService.java | 123 ++++++++---------- 2 files changed, 77 insertions(+), 96 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java index 9ba60809cf..99ece5846a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java @@ -195,34 +195,6 @@ private final class TokenReadWriteLock extends ReentrantReadWriteLock { */ private final Token token; - /** - * The lock that is returned from the {@link #readLock()} method. - */ - private final ReadLock readLock = new ReadLock(this) { - - @Override - public void unlock() { - super.unlock(); - readers.remove(Thread.currentThread(), 1); - locks.remove(token, new TokenReadWriteLock(token)); - } - - }; - - /** - * The lock that is returned from the {@link #writeLock()} method. - */ - private final WriteLock writeLock = new WriteLock(this) { - - @Override - public void unlock() { - super.unlock(); - writers.remove(Thread.currentThread(), 1); - locks.remove(token, new TokenReadWriteLock(token)); - } - - }; - /** * Construct a new instance. * @@ -255,7 +227,16 @@ public int hashCode() { @Override public ReadLock readLock() { - return readLock; + return new ReadLock(this) { + + @Override + public void unlock() { + super.unlock(); + readers.remove(Thread.currentThread(), 1); + locks.remove(token, new TokenReadWriteLock(token)); + } + + }; } @Override @@ -266,7 +247,16 @@ public String toString() { @Override public WriteLock writeLock() { - return writeLock; + return new WriteLock(this) { + + @Override + public void unlock() { + super.unlock(); + writers.remove(Thread.currentThread(), 1); + locks.remove(token, new TokenReadWriteLock(token)); + } + + }; } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index b55091159f..3a20c82572 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -352,70 +352,6 @@ private final class RangeReadWriteLock extends ReentrantReadWriteLock { */ private final RangeToken token; - /** - * The lock that is retuned from the {@link #readLock()} method. - */ - private final ReadLock readLock = new ReadLock(this) { - - @Override - public void lock() { - while (isRangeBlocked(LockType.READ, token)) { - continue; - } - super.lock(); - } - - @Override - public boolean tryLock() { - if(!isRangeBlocked(LockType.READ, token) && super.tryLock()) { - return true; - } - else { - return false; - } - } - - @Override - public void unlock() { - super.unlock(); - readers.remove(Thread.currentThread(), 1); - locks.remove(token, new RangeReadWriteLock(token)); - } - - }; - - /** - * The lock that is returned from the {@link #writeLock()} method. - */ - private final WriteLock writeLock = new WriteLock(this) { - - @Override - public void lock() { - while (isRangeBlocked(LockType.WRITE, token)) { - continue; - } - super.lock(); - } - - @Override - public boolean tryLock() { - if(!isRangeBlocked(LockType.WRITE, token) && super.tryLock()) { - return true; - } - else { - return false; - } - } - - @Override - public void unlock() { - super.unlock(); - writers.remove(Thread.currentThread(), 1); - locks.remove(token, new RangeReadWriteLock(token)); - } - - }; - /** * Construct a new instance. * @@ -448,7 +384,34 @@ public int hashCode() { @Override public ReadLock readLock() { - return readLock; + return new ReadLock(this) { + + @Override + public void lock() { + while (isRangeBlocked(LockType.READ, token)) { + continue; + } + super.lock(); + } + + @Override + public boolean tryLock() { + if(!isRangeBlocked(LockType.READ, token) && super.tryLock()) { + return true; + } + else { + return false; + } + } + + @Override + public void unlock() { + super.unlock(); + readers.remove(Thread.currentThread(), 1); + locks.remove(token, new RangeReadWriteLock(token)); + } + + }; } @Override @@ -459,7 +422,35 @@ public String toString() { @Override public WriteLock writeLock() { - return writeLock; + return new WriteLock(this) { + + @Override + public void lock() { + while (isRangeBlocked(LockType.WRITE, token)) { + continue; + } + super.lock(); + } + + @Override + public boolean tryLock() { + if(!isRangeBlocked(LockType.WRITE, token) + && super.tryLock()) { + return true; + } + else { + return false; + } + } + + @Override + public void unlock() { + super.unlock(); + writers.remove(Thread.currentThread(), 1); + locks.remove(token, new RangeReadWriteLock(token)); + } + + }; } } From 6c62eadf8a600a4dc6ef6ede7983cb6457bf8fb5 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 13:19:36 -0400 Subject: [PATCH 047/100] speed up buffer transport if there are no reads Conflicts: concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java --- .../concourse/server/storage/Engine.java | 12 ++++---- .../concourse/server/storage/temp/Buffer.java | 29 +++++++++++++++++++ .../concourse/server/storage/temp/Limbo.java | 10 +++++++ .../concourse/server/storage/EngineTest.java | 8 ++--- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 7794773225..b6789e5c51 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -146,12 +146,10 @@ public final class Engine extends BufferedStore implements // testing /** - * The number of milliseconds that the {@link BufferTransportThread} sleeps - * after each transport in order to avoid CPU thrashing. + * If this value is > 0, then we will sleep for this amount instead of what + * the buffer suggests. This is mainly used for testing. */ - protected static int BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = 100; // visible - // for - // testing + protected int bufferTransportThreadSleepInMs = 0; // visible for testing /** * A flag to indicate that the {@link BufferTransportThrread} has appeared @@ -810,7 +808,9 @@ public void run() { try { // NOTE: This thread needs to sleep for a small amount of // time to avoid thrashing - Thread.sleep(BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS); + int sleep = bufferTransportThreadSleepInMs > 0 ? bufferTransportThreadSleepInMs + : buffer.getDesiredTransportSleepTimeInMs(); + Thread.sleep(sleep); bufferTransportThreadLastWakeUp.set(Time.now()); } catch (InterruptedException e) { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 1c3045fa51..71f3156b35 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -242,6 +243,22 @@ public void remove() { */ private int transportRate = 1; + /** + * The maximum number of milliseconds to sleep between transport cycles. + */ + private static final int MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 100; + + /** + * The minimum number of milliseconds to sleep between transport cycles. + */ + private static final int MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 5; + + /** + * The number of milliseconds to sleep between transport cycles. + */ + private final AtomicInteger transportThreadSleepTimeInMs = new AtomicInteger( + MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + /** * The multiplier that is used when increasing the rate of transport. */ @@ -459,6 +476,11 @@ public String getBackingStore() { return directory; } + @Override + public int getDesiredTransportSleepTimeInMs() { + return transportThreadSleepTimeInMs.get(); + } + /** * Return the timestamp of the most recent data transport from the Buffer. * @@ -701,6 +723,12 @@ public void transport(PermanentStore destination) { timeOfLastTransport.set(Time.now()); transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE : (transportRate * transportRateMultiplier); + transportThreadSleepTimeInMs + .addAndGet(-MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + if(transportThreadSleepTimeInMs.get() < MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS) { + transportThreadSleepTimeInMs + .set(MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + } } finally { transportLock.writeLock().unlock(); @@ -823,6 +851,7 @@ private void removePage() { */ private void scaleBackTransportRate() { transportRate = 1; + transportThreadSleepTimeInMs.set(MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java index 92fd935580..f361cf0b5f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java @@ -619,6 +619,16 @@ public void waitUntilTransportable() { // the appropriate conditions. } + /** + * Return the number of milliseconds that this store desires any back to + * back transport requests to pause in between. + * + * @return the pause time + */ + public int getDesiredTransportSleepTimeInMs() { + return 0; + } + @Override protected Map> doExplore(long timestamp, String key, Operator operator, TObject... values) { diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java index 0e5ef43208..e28d3c53d9 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java @@ -171,17 +171,16 @@ public void testBufferTransportBlockingIfWritesAreNotWithinThreshold() { public void testBufferTransportThreadWillRestartIfHung() { int frequency = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS; int threshold = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS; - int sleep = Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS; final AtomicBoolean done = new AtomicBoolean(false); try { Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = 100; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = 500; int lag = 5000; - Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS - + lag; String loc = TestData.DATA_DIR + File.separator + Time.now(); final Engine engine = new Engine(loc + File.separator + "buffer", loc + File.separator + "db"); + engine.bufferTransportThreadSleepInMs = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS + + lag; engine.start(); Thread thread = new Thread(new Runnable() { @@ -202,7 +201,7 @@ public void run() { .get()); Threads.sleep(Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS); Assert.assertTrue(engine.bufferTransportThreadHasEverBeenRestarted - .get()); + .get()); engine.stop(); FileSystem.deleteDirectory(loc); } @@ -210,7 +209,6 @@ public void run() { done.set(true); Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = frequency; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = threshold; - Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = sleep; } } From 9ed18689a0e0df8fbf0a434633ee354092469e7a Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 13:20:33 -0400 Subject: [PATCH 048/100] Revert "speed up buffer transport if there are no reads" This reverts commit 09994bdb56dd5b5cc57d7388d344bece9188ebc5. Conflicts: concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java --- .../concourse/server/storage/Engine.java | 12 ++++---- .../concourse/server/storage/temp/Buffer.java | 29 ------------------- .../concourse/server/storage/temp/Limbo.java | 10 ------- .../concourse/server/storage/EngineTest.java | 10 ++++--- 4 files changed, 12 insertions(+), 49 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index b6789e5c51..7794773225 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -146,10 +146,12 @@ public final class Engine extends BufferedStore implements // testing /** - * If this value is > 0, then we will sleep for this amount instead of what - * the buffer suggests. This is mainly used for testing. + * The number of milliseconds that the {@link BufferTransportThread} sleeps + * after each transport in order to avoid CPU thrashing. */ - protected int bufferTransportThreadSleepInMs = 0; // visible for testing + protected static int BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = 100; // visible + // for + // testing /** * A flag to indicate that the {@link BufferTransportThrread} has appeared @@ -808,9 +810,7 @@ public void run() { try { // NOTE: This thread needs to sleep for a small amount of // time to avoid thrashing - int sleep = bufferTransportThreadSleepInMs > 0 ? bufferTransportThreadSleepInMs - : buffer.getDesiredTransportSleepTimeInMs(); - Thread.sleep(sleep); + Thread.sleep(BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS); bufferTransportThreadLastWakeUp.set(Time.now()); } catch (InterruptedException e) { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 71f3156b35..1c3045fa51 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -243,22 +242,6 @@ public void remove() { */ private int transportRate = 1; - /** - * The maximum number of milliseconds to sleep between transport cycles. - */ - private static final int MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 100; - - /** - * The minimum number of milliseconds to sleep between transport cycles. - */ - private static final int MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 5; - - /** - * The number of milliseconds to sleep between transport cycles. - */ - private final AtomicInteger transportThreadSleepTimeInMs = new AtomicInteger( - MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); - /** * The multiplier that is used when increasing the rate of transport. */ @@ -476,11 +459,6 @@ public String getBackingStore() { return directory; } - @Override - public int getDesiredTransportSleepTimeInMs() { - return transportThreadSleepTimeInMs.get(); - } - /** * Return the timestamp of the most recent data transport from the Buffer. * @@ -723,12 +701,6 @@ public void transport(PermanentStore destination) { timeOfLastTransport.set(Time.now()); transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE : (transportRate * transportRateMultiplier); - transportThreadSleepTimeInMs - .addAndGet(-MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); - if(transportThreadSleepTimeInMs.get() < MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS) { - transportThreadSleepTimeInMs - .set(MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); - } } finally { transportLock.writeLock().unlock(); @@ -851,7 +823,6 @@ private void removePage() { */ private void scaleBackTransportRate() { transportRate = 1; - transportThreadSleepTimeInMs.set(MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java index f361cf0b5f..92fd935580 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java @@ -619,16 +619,6 @@ public void waitUntilTransportable() { // the appropriate conditions. } - /** - * Return the number of milliseconds that this store desires any back to - * back transport requests to pause in between. - * - * @return the pause time - */ - public int getDesiredTransportSleepTimeInMs() { - return 0; - } - @Override protected Map> doExplore(long timestamp, String key, Operator operator, TObject... values) { diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java index e28d3c53d9..1e8c8af938 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java @@ -166,21 +166,22 @@ public void testBufferTransportBlockingIfWritesAreNotWithinThreshold() { engine.stop(); FileSystem.deleteDirectory(loc); } - + @Test public void testBufferTransportThreadWillRestartIfHung() { int frequency = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS; int threshold = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS; + int sleep = Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS; final AtomicBoolean done = new AtomicBoolean(false); try { Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = 100; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = 500; int lag = 5000; + Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS + + lag; String loc = TestData.DATA_DIR + File.separator + Time.now(); final Engine engine = new Engine(loc + File.separator + "buffer", loc + File.separator + "db"); - engine.bufferTransportThreadSleepInMs = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS - + lag; engine.start(); Thread thread = new Thread(new Runnable() { @@ -201,7 +202,7 @@ public void run() { .get()); Threads.sleep(Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS); Assert.assertTrue(engine.bufferTransportThreadHasEverBeenRestarted - .get()); + .get()); engine.stop(); FileSystem.deleteDirectory(loc); } @@ -209,6 +210,7 @@ public void run() { done.set(true); Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = frequency; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = threshold; + Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = sleep; } } From 3f54ed6920fb23cd6058d12b67b64685741a7f35 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 13:26:21 -0400 Subject: [PATCH 049/100] Revert "Revert "speed up buffer transport if there are no reads"" This reverts commit e42ea73c2ec305d6c5e484960b656f81a722ad06. Conflicts: concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java --- .../concourse/server/storage/Engine.java | 12 ++++---- .../concourse/server/storage/temp/Buffer.java | 29 +++++++++++++++++++ .../concourse/server/storage/temp/Limbo.java | 10 +++++++ .../concourse/server/storage/EngineTest.java | 10 +++---- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 7794773225..b6789e5c51 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -146,12 +146,10 @@ public final class Engine extends BufferedStore implements // testing /** - * The number of milliseconds that the {@link BufferTransportThread} sleeps - * after each transport in order to avoid CPU thrashing. + * If this value is > 0, then we will sleep for this amount instead of what + * the buffer suggests. This is mainly used for testing. */ - protected static int BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = 100; // visible - // for - // testing + protected int bufferTransportThreadSleepInMs = 0; // visible for testing /** * A flag to indicate that the {@link BufferTransportThrread} has appeared @@ -810,7 +808,9 @@ public void run() { try { // NOTE: This thread needs to sleep for a small amount of // time to avoid thrashing - Thread.sleep(BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS); + int sleep = bufferTransportThreadSleepInMs > 0 ? bufferTransportThreadSleepInMs + : buffer.getDesiredTransportSleepTimeInMs(); + Thread.sleep(sleep); bufferTransportThreadLastWakeUp.set(Time.now()); } catch (InterruptedException e) { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 1c3045fa51..71f3156b35 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -242,6 +243,22 @@ public void remove() { */ private int transportRate = 1; + /** + * The maximum number of milliseconds to sleep between transport cycles. + */ + private static final int MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 100; + + /** + * The minimum number of milliseconds to sleep between transport cycles. + */ + private static final int MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS = 5; + + /** + * The number of milliseconds to sleep between transport cycles. + */ + private final AtomicInteger transportThreadSleepTimeInMs = new AtomicInteger( + MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + /** * The multiplier that is used when increasing the rate of transport. */ @@ -459,6 +476,11 @@ public String getBackingStore() { return directory; } + @Override + public int getDesiredTransportSleepTimeInMs() { + return transportThreadSleepTimeInMs.get(); + } + /** * Return the timestamp of the most recent data transport from the Buffer. * @@ -701,6 +723,12 @@ public void transport(PermanentStore destination) { timeOfLastTransport.set(Time.now()); transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE : (transportRate * transportRateMultiplier); + transportThreadSleepTimeInMs + .addAndGet(-MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + if(transportThreadSleepTimeInMs.get() < MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS) { + transportThreadSleepTimeInMs + .set(MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + } } finally { transportLock.writeLock().unlock(); @@ -823,6 +851,7 @@ private void removePage() { */ private void scaleBackTransportRate() { transportRate = 1; + transportThreadSleepTimeInMs.set(MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java index 92fd935580..f361cf0b5f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java @@ -619,6 +619,16 @@ public void waitUntilTransportable() { // the appropriate conditions. } + /** + * Return the number of milliseconds that this store desires any back to + * back transport requests to pause in between. + * + * @return the pause time + */ + public int getDesiredTransportSleepTimeInMs() { + return 0; + } + @Override protected Map> doExplore(long timestamp, String key, Operator operator, TObject... values) { diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java index 1e8c8af938..e28d3c53d9 100644 --- a/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/storage/EngineTest.java @@ -166,22 +166,21 @@ public void testBufferTransportBlockingIfWritesAreNotWithinThreshold() { engine.stop(); FileSystem.deleteDirectory(loc); } - + @Test public void testBufferTransportThreadWillRestartIfHung() { int frequency = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS; int threshold = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS; - int sleep = Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS; final AtomicBoolean done = new AtomicBoolean(false); try { Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = 100; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = 500; int lag = 5000; - Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS - + lag; String loc = TestData.DATA_DIR + File.separator + Time.now(); final Engine engine = new Engine(loc + File.separator + "buffer", loc + File.separator + "db"); + engine.bufferTransportThreadSleepInMs = Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS + + lag; engine.start(); Thread thread = new Thread(new Runnable() { @@ -202,7 +201,7 @@ public void run() { .get()); Threads.sleep(Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS); Assert.assertTrue(engine.bufferTransportThreadHasEverBeenRestarted - .get()); + .get()); engine.stop(); FileSystem.deleteDirectory(loc); } @@ -210,7 +209,6 @@ public void run() { done.set(true); Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_FREQUENCY_IN_MILLISECONDS = frequency; Engine.BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS = threshold; - Engine.BUFFER_TRANSPORT_THREAD_SLEEP_TIME_IN_MILLISECONDS = sleep; } } From 8ea6a830561564829c9ec039282882dc7b4396d3 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 16:06:41 -0400 Subject: [PATCH 050/100] no need for atomic integer --- .../concourse/server/storage/temp/Buffer.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 71f3156b35..cd3f93e808 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -35,7 +35,6 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -256,8 +255,7 @@ public void remove() { /** * The number of milliseconds to sleep between transport cycles. */ - private final AtomicInteger transportThreadSleepTimeInMs = new AtomicInteger( - MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + private int transportThreadSleepTimeInMs = MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS; /** * The multiplier that is used when increasing the rate of transport. @@ -478,7 +476,7 @@ public String getBackingStore() { @Override public int getDesiredTransportSleepTimeInMs() { - return transportThreadSleepTimeInMs.get(); + return transportThreadSleepTimeInMs; } /** @@ -723,11 +721,9 @@ public void transport(PermanentStore destination) { timeOfLastTransport.set(Time.now()); transportRate = transportRate >= MAX_TRANSPORT_RATE ? MAX_TRANSPORT_RATE : (transportRate * transportRateMultiplier); - transportThreadSleepTimeInMs - .addAndGet(-MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); - if(transportThreadSleepTimeInMs.get() < MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS) { - transportThreadSleepTimeInMs - .set(MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + --transportThreadSleepTimeInMs; + if(transportThreadSleepTimeInMs < MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS) { + transportThreadSleepTimeInMs = MIN_TRANSPORT_THREAD_SLEEP_TIME_IN_MS; } } finally { @@ -851,7 +847,7 @@ private void removePage() { */ private void scaleBackTransportRate() { transportRate = 1; - transportThreadSleepTimeInMs.set(MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS); + transportThreadSleepTimeInMs = MAX_TRANSPORT_THREAD_SLEEP_TIME_IN_MS; } /** From 0f88b550a3ac0fddd95c810e81d9116252562fa0 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 24 Oct 2014 16:33:00 -0400 Subject: [PATCH 051/100] only lock mutable blocks --- .../concourse/server/storage/db/Block.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index c117402b2f..de4b8fc0ce 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -40,6 +40,7 @@ import org.cinchapi.concourse.annotate.PackagePrivate; import org.cinchapi.concourse.server.GlobalState; +import org.cinchapi.concourse.server.concurrent.Locks; import org.cinchapi.concourse.server.io.Byteable; import org.cinchapi.concourse.server.io.ByteableCollections; import org.cinchapi.concourse.server.io.Byteables; @@ -169,7 +170,7 @@ public static String getId(String filename) { * mutable until a call to {@link #sync()} stores it to disk. */ protected transient boolean mutable; - + /** * The master lock for {@link #write} and {@link #read}. DO NOT use this * lock directly. @@ -319,7 +320,7 @@ public int hashCode() { */ public Revision insert(L locator, K key, V value, long version, Action type) throws IllegalStateException { - write.lock(); + Locks.lockIfCondition(write, mutable); try { Preconditions.checkState(mutable, "Cannot modify a block that is not mutable"); @@ -339,7 +340,7 @@ public Revision insert(L locator, K key, V value, long version, return revision; } finally { - write.unlock(); + Locks.unlockIfCondition(write, mutable); } } @@ -357,12 +358,12 @@ public Revision insert(L locator, K key, V value, long version, * @return {@code true} if it is possible that relevant revisions exists */ public boolean mightContain(L locator, K key, V value) { - read.lock(); + Locks.lockIfCondition(read, mutable); try { return filter.mightContain(locator, key, value); } finally { - read.unlock(); + Locks.unlockIfCondition(read, mutable); } } @@ -395,12 +396,12 @@ public void seek(L locator, Record record) { @Override public int size() { - read.lock(); + Locks.lockIfCondition(read, mutable); try { return size; } finally { - read.unlock(); + Locks.unlockIfCondition(read, mutable); } } @@ -410,7 +411,7 @@ public int size() { */ @Override public void sync() { - write.lock(); + Locks.lockIfCondition(write, mutable); try { if(size > 0) { Preconditions.checkState(mutable, @@ -430,7 +431,7 @@ public void sync() { throw Throwables.propagate(e); } finally { - write.unlock(); + Locks.unlockIfCondition(write, mutable); } } @@ -442,7 +443,7 @@ public String toString() { @Override public void copyToByteBuffer(ByteBuffer buffer) { - read.lock(); + Locks.lockIfCondition(read, mutable); try { L locator = null; K key = null; @@ -496,7 +497,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { } } finally { - read.unlock(); + Locks.unlockIfCondition(read, mutable); } } @@ -511,7 +512,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { * @return a string dump */ protected String dump() { - read.lock(); + Locks.lockIfCondition(read, mutable); try { StringBuilder sb = new StringBuilder(); sb.append("Dump for " + getClass().getSimpleName() + " " + id); @@ -539,7 +540,7 @@ protected String dump() { return sb.toString(); } finally { - read.unlock(); + Locks.unlockIfCondition(read, mutable); } } @@ -573,7 +574,7 @@ protected abstract Revision makeRevision(L locator, K key, * @param byteables */ private void seek(Record record, Byteable... byteables) { - read.lock(); + Locks.lockIfCondition(read, mutable); try { if(filter.mightContain(byteables)) { if(mutable) { @@ -619,7 +620,7 @@ else if(processing) { } } finally { - read.unlock(); + Locks.unlockIfCondition(read, mutable); } } From aead5d3ce1a209393123d8e016ec186d9bce382e Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 25 Oct 2014 13:56:39 -0400 Subject: [PATCH 052/100] dont sanitize the environment name if the engine already exists --- .../concourse/server/ConcourseServer.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 4467c5848e..a454102d5f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -1058,7 +1058,20 @@ private Engine getEngine() { * @return the Engine */ private Engine getEngine(String env) { - env = Environments.sanitize(env); + Engine engine = engines.get(env); + if(engine == null) { + env = Environments.sanitize(env); + return getEngineUnsafe(env); + } + return engine; + } + + /** + * Return the {@link Engine} that is associated with {@code env} without + * performing any sanitzation on the name. If such an Engine does not exist, + * create a new one and add it to the collection. + */ + private Engine getEngineUnsafe(String env) { Engine engine = engines.get(env); if(engine == null) { engine = new Engine(bufferStore + File.separator + env, dbStore From 104bef69985896162c9e1703dfac8581d4d9bfba Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 25 Oct 2014 16:51:14 -0400 Subject: [PATCH 053/100] add another check to see if the transport was interrupted from the Engine stopping --- .../java/org/cinchapi/concourse/server/storage/Engine.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index b6789e5c51..37397eb92d 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -786,6 +786,11 @@ public BufferTransportThread() { @Override public void run() { while (running) { + if(Thread.interrupted()) { // the thread has been + // interrupted from the Engine + // stopping + break; + } if(getBufferTransportThreadIdleTimeInMs() > BUFFER_TRANSPORT_THREAD_ALLOWABLE_INACTIVITY_THRESHOLD_IN_MILLISECONDS) { // If there have been no transports within the last second // then make this thread block until the buffer is From a0215cde658613e274a0ae081e6307480d84785d Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 25 Oct 2014 17:03:07 -0400 Subject: [PATCH 054/100] don't confuse stopping the server with interrupting a hung transport thread --- .../concourse/server/storage/Engine.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index 37397eb92d..c4cf58df61 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -819,13 +819,18 @@ public void run() { bufferTransportThreadLastWakeUp.set(Time.now()); } catch (InterruptedException e) { - Logger.warn( - "The data transport thread been sleeping for over " - + "{} milliseconds even though there is work to do. " - + "An attempt has been made to restart the stalled " - + "process.", - BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS); - bufferTransportThreadHasEverBeenRestarted.set(true); + if(getBufferTransportThreadIdleTimeInMs() > BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS) { + Logger.warn( + "The data transport thread been sleeping for over " + + "{} milliseconds even though there is work to do. " + + "An attempt has been made to restart the stalled " + + "process.", + BUFFER_TRANSPORT_THREAD_HUNG_DETECTION_THRESOLD_IN_MILLISECONDS); + bufferTransportThreadHasEverBeenRestarted.set(true); + } + else { + Thread.currentThread().interrupt(); + } } } } From aaa5055234dc5ac48d0df326b2a415535938eb8a Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 25 Oct 2014 19:26:54 -0400 Subject: [PATCH 055/100] Lower compile threshold for big performance boost Conflicts: concourse-server/conf/.concourse.conf concourse-server/launch/Start Concourse.launch --- concourse-server/conf/.concourse.conf | 3 +++ concourse-server/launch/Start Concourse.launch | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/concourse-server/conf/.concourse.conf b/concourse-server/conf/.concourse.conf index d32d160133..dd7ef4decc 100644 --- a/concourse-server/conf/.concourse.conf +++ b/concourse-server/conf/.concourse.conf @@ -76,6 +76,9 @@ wrapper.java.additional.2=-Dcom.sun.management.jmxremote.port=9010 wrapper.java.additional.3=-Dcom.sun.management.jmxremote.local.only=false wrapper.java.additional.4=-Dcom.sun.management.jmxremote.authenticate=false wrapper.java.additional.5=-Dcom.sun.management.jmxremote.ssl=false +wrapper.java.additional.6=-XX:+UseThreadPriorities +wrapper.java.additional.7=-XX:ThreadPriorityPolicy=42 +wrapper.java.additional.8=-XX:CompileThreshold=500 # Initial Java Heap Size (in MB) wrapper.java.initmemory=512 diff --git a/concourse-server/launch/Start Concourse.launch b/concourse-server/launch/Start Concourse.launch index 8e2b1ff490..e154d7c652 100644 --- a/concourse-server/launch/Start Concourse.launch +++ b/concourse-server/launch/Start Concourse.launch @@ -7,10 +7,11 @@ - + + - + From 070fac40e16117138656c3d8954fc9bff37ae58c Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 25 Oct 2014 19:52:57 -0400 Subject: [PATCH 056/100] add lazy caching for Text and string Tokens Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java --- .../server/concurrent/RangeLockService.java | 4 +- .../concourse/server/concurrent/Token.java | 24 +++ .../cinchapi/concourse/server/model/Text.java | 31 +++- .../server/storage/AtomicOperation.java | 12 +- .../concourse/server/storage/Engine.java | 4 +- .../server/storage/cache/LazyCache.java | 146 ++++++++++++++++++ .../concourse/server/storage/db/Database.java | 24 +-- .../concourse/server/storage/temp/Limbo.java | 4 +- .../concourse/server/storage/temp/Write.java | 10 +- 9 files changed, 229 insertions(+), 30 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/LazyCache.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index 3a20c82572..0863686bed 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -143,7 +143,7 @@ public ReadLock getReadLock(RangeToken token) { */ public ReadLock getReadLock(String key, Operator operator, TObject... values) { - return getReadLock(Text.wrap(key), operator, + return getReadLock(Text.wrapCached(key), operator, Transformers.transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class)); } @@ -190,7 +190,7 @@ public WriteLock getWriteLock(RangeToken token) { * @return the WriteLock */ public WriteLock getWriteLock(String key, TObject value) { - return getWriteLock(Text.wrap(key), Value.wrap(value)); + return getWriteLock(Text.wrapCached(key), Value.wrap(value)); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 38946e80b2..0c1af7374b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -26,6 +26,7 @@ import java.nio.ByteBuffer; import org.cinchapi.concourse.server.io.Byteable; +import org.cinchapi.concourse.server.storage.cache.LazyCache; import org.cinchapi.concourse.util.ByteBuffers; import org.cinchapi.concourse.util.TArrays; @@ -62,6 +63,29 @@ public static Token wrap(Object... objects) { return new Token(TArrays.hash(objects)); } + /** + * Return a {@link Token} that wraps the specified {@code key}. This method + * takes advantage of caching since the keys are often to be reused + * frequently. + * + * @param key + * @return the Token + */ + public static Token wrap(String key) { + Token token = cache.get(key); + if(token == null) { + token = new Token(TArrays.hash(key)); + cache.put(key, token); + } + return token; + } + + /** + * The cache of string tokens that represent record keys. + */ + private static final LazyCache cache = LazyCache + .withExpectedSize(5000); + /** * The sequence of bytes is a 128-bit (16 byte) hash. */ diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index 9b13fb5596..ab7efa4591 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -30,6 +30,7 @@ import javax.annotation.concurrent.Immutable; import org.cinchapi.concourse.server.io.Byteable; +import org.cinchapi.concourse.server.storage.cache.LazyCache; import org.cinchapi.concourse.util.ByteBuffers; /** @@ -65,6 +66,31 @@ public static Text wrap(String string) { return new Text(string); } + /** + * Return Text that is backed by {@code string}. It is possible that the + * object will be a cached instance. This should only be called when + * wrapping record keys since they are expected to be used often. + * + * @param string + * @return the Text + */ + public static Text wrapCached(String string) { + Text text = cache.get(string); + if(text == null) { + text = new Text(string); + cache.put(string, text); + } + return text; + } + + /** + * The cache that holds the objects created from the + * {@link #wrapCached(String)} method. This is primary used for string keys + * since those are expected to be used often. + */ + private static final LazyCache cache = LazyCache + .withExpectedSize(5000); + /** * Represents an empty text string. */ @@ -124,7 +150,10 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { synchronized (mutex) { - bytes = ByteBuffers.fromString(text); + if(bytes == null) { // must check again to prevent duplicate + // copy if there is a race condition + bytes = ByteBuffers.fromString(text); + } } } return ByteBuffers.asReadOnlyBuffer(bytes); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index 303e428d71..85b3dc19b5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -209,8 +209,8 @@ public boolean add(String key, TObject value, long record) Token.wrap(key, record), this); intentions .add(new KeyInRecordLockIntention(key, record, LockType.WRITE)); - intentions - .add(new RangeLockIntention(Text.wrap(key), Value.wrap(value))); + intentions.add(new RangeLockIntention(Text.wrapCached(key), Value + .wrap(value))); return super.add(key, value, record); } @@ -338,8 +338,8 @@ public boolean remove(String key, TObject value, long record) Token.wrap(key, record), this); intentions .add(new KeyInRecordLockIntention(key, record, LockType.WRITE)); - intentions - .add(new RangeLockIntention(Text.wrap(key), Value.wrap(value))); + intentions.add(new RangeLockIntention(Text.wrapCached(key), Value + .wrap(value))); return super.remove(key, value, record); } @@ -416,10 +416,10 @@ protected Map> doExplore(String key, Operator operator, TObject... values) { checkState(); ((Compoundable) destination).addVersionChangeListener(RangeToken - .forReading(Text.wrap(key), operator, Transformers + .forReading(Text.wrapCached(key), operator, Transformers .transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class)), this); - intentions.add(new RangeLockIntention(Text.wrap(key), operator, + intentions.add(new RangeLockIntention(Text.wrapCached(key), operator, Transformers.transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class))); return super.doExplore(key, operator, values); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java index c4cf58df61..049f83119b 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/Engine.java @@ -713,7 +713,7 @@ private boolean addUnsafe(String key, TObject value, long record) { notifyVersionChange(Token.wrap(key, record)); notifyVersionChange(Token.wrap(record)); notifyVersionChange(Token.wrap(key)); - notifyVersionChange(RangeToken.forWriting(Text.wrap(key), + notifyVersionChange(RangeToken.forWriting(Text.wrapCached(key), Value.wrap(value))); return true; } @@ -760,7 +760,7 @@ private boolean removeUnsafe(String key, TObject value, long record) { notifyVersionChange(Token.wrap(key, record)); notifyVersionChange(Token.wrap(record)); notifyVersionChange(Token.wrap(key)); - notifyVersionChange(RangeToken.forWriting(Text.wrap(key), + notifyVersionChange(RangeToken.forWriting(Text.wrapCached(key), Value.wrap(value))); return true; } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/LazyCache.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/LazyCache.java new file mode 100644 index 0000000000..467af5552f --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/LazyCache.java @@ -0,0 +1,146 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.storage.cache; + +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.annotation.Nullable; + +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +/** + * A {@link LazyCache} is a simple and eventually consistent cache: + * {@link #put(Object, Object) Put} items into the collection and + * eventually a call to {@link #get(Object)} will reflect the update. + *

+ * The primary purpose of a {@link LazyCache} is to accommodate situations where + * caching helps performance, but retrieving cached data is not critical for + * correctness (i.e. worst case having duplicate instances of data won't lead to + * bad results). In particular this is designed to avoid overhead with placing + * new items into the cache when there is a cache miss. + *

+ *

+ * When placing items into the cache, the method returns immediately and the + * cache addition is scheduled to run in the background at some later time. So + * this cache only guarantees that it will eventually make updates and calls to + * retrieve items may report stale data for some time. Therefore, this cache is + * not a good candidate for data that changes frequently. It's best used as a + * sort of intern pool for immutable objects. + *

+ * + * @author jnelson + */ +public class LazyCache { + + /** + * Return a {@link LazyCache} that is initially sized to accommodate the + * specified number of elements. + * + * @param size + * @return the LazyCache + */ + public static LazyCache withExpectedSize(int size) { + return new LazyCache(Maps. newHashMapWithExpectedSize(size)); + } + + /** + * The service that handles placing items into the cache. + */ + private final ExecutorService executor; + + /** + * The internal data structure the holds the cached content. + */ + private final Map internal; + + /** + * Construct a new instance + * + * @param initialSize + */ + private LazyCache(Map internal) { + this.internal = internal; + executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("lazy-cache-" + System.identityHashCode(this)) + .build()); + } + + /** + * Return the value, if any, that is associated with the {@code key} in this + * cache. Recent updates to the cache are not guaranteed to be reflected. + * + * @param key + * @return the value associated with {@code key} at the time of invocation, + * or {@code null} if no such value exists + */ + @Nullable + public V get(K key) { + return internal.get(key); + } + + /** + * Associate {@code key} with {@code value} in the cache. This update will + * eventually take effect. + * + * @param key + * @param value + */ + public void put(K key, V value) { + executor.execute(new PutRunnable(key, value)); + } + + /** + * The {@link Runnable} that is passed to the {@link #executor} to add items + * to the cache. + * + * @author jnelson + */ + private final class PutRunnable implements Runnable { + + private final K key; + private final V value; + + /** + * Construct a new instance. + * + * @param key + * @param value + */ + public PutRunnable(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public void run() { + internal.put(key, value); + } + + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Database.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Database.java index f4fe753a29..99eb988aeb 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Database.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Database.java @@ -252,7 +252,7 @@ public Map audit(long record) { @Override public Map audit(String key, long record) { - Text key0 = Text.wrap(key); + Text key0 = Text.wrapCached(key); return getPrimaryRecord(PrimaryKey.wrap(record), key0).audit(key0); } @@ -275,7 +275,7 @@ public Map> browse(long record, long timestamp) { @Override public Map> browse(String key) { return Transformers.transformTreeMapSet( - getSecondaryRecord(Text.wrap(key)).browse(), + getSecondaryRecord(Text.wrapCached(key)).browse(), Functions.VALUE_TO_TOBJECT, Functions.PRIMARY_KEY_TO_LONG, TObjectSorter.INSTANCE); } @@ -283,7 +283,7 @@ public Map> browse(String key) { @Override public Map> browse(String key, long timestamp) { return Transformers.transformTreeMapSet( - getSecondaryRecord(Text.wrap(key)).browse(timestamp), + getSecondaryRecord(Text.wrapCached(key)).browse(timestamp), Functions.VALUE_TO_TOBJECT, Functions.PRIMARY_KEY_TO_LONG, TObjectSorter.INSTANCE); } @@ -291,7 +291,7 @@ public Map> browse(String key, long timestamp) { @Override public Map> doExplore(String key, Operator operator, TObject... values) { - SecondaryRecord record = getSecondaryRecord(Text.wrap(key)); + SecondaryRecord record = getSecondaryRecord(Text.wrapCached(key)); Map> map = record.explore(operator, Transformers.transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class)); @@ -303,7 +303,7 @@ public Map> doExplore(String key, Operator operator, @Override public Map> doExplore(long timestamp, String key, Operator operator, TObject... values) { - SecondaryRecord record = getSecondaryRecord(Text.wrap(key)); + SecondaryRecord record = getSecondaryRecord(Text.wrapCached(key)); Map> map = record.explore(timestamp, operator, Transformers.transformArray(values, Functions.TOBJECT_TO_VALUE, Value.class)); @@ -337,7 +337,7 @@ public String dump(String id) { @Override public Set fetch(String key, long record) { - Text key0 = Text.wrap(key); + Text key0 = Text.wrapCached(key); return Transformers.transformSet( getPrimaryRecord(PrimaryKey.wrap(record), key0).fetch(key0), Functions.VALUE_TO_TOBJECT); @@ -345,7 +345,7 @@ public Set fetch(String key, long record) { @Override public Set fetch(String key, long record, long timestamp) { - Text key0 = Text.wrap(key); + Text key0 = Text.wrapCached(key); return Transformers.transformSet( getPrimaryRecord(PrimaryKey.wrap(record), key0).fetch(key0, timestamp), Functions.VALUE_TO_TOBJECT); @@ -385,19 +385,19 @@ public long getVersion(String key) { // NOTE: We must consult the SecondaryRecord over the SearchRecord // because ALL writes for a key are secondary indexed whereas only text // writes are search indexed. - return getSecondaryRecord(Text.wrap(key)).getVersion(); + return getSecondaryRecord(Text.wrapCached(key)).getVersion(); } @Override public long getVersion(String key, long record) { - return getPrimaryRecord(PrimaryKey.wrap(record), Text.wrap(key)) + return getPrimaryRecord(PrimaryKey.wrap(record), Text.wrapCached(key)) .getVersion(); } @Override public Set search(String key, String query) { return Transformers.transformSet( - getSearchRecord(Text.wrap(key), Text.wrap(query)).search( + getSearchRecord(Text.wrapCached(key), Text.wrap(query)).search( Text.wrap(query)), Functions.PRIMARY_KEY_TO_LONG); } @@ -448,14 +448,14 @@ public void triggerSync() { @Override public boolean verify(String key, TObject value, long record) { - Text key0 = Text.wrap(key); + Text key0 = Text.wrapCached(key); return getPrimaryRecord(PrimaryKey.wrap(record), key0).verify(key0, Value.wrap(value)); } @Override public boolean verify(String key, TObject value, long record, long timestamp) { - Text key0 = Text.wrap(key); + Text key0 = Text.wrapCached(key); return getPrimaryRecord(PrimaryKey.wrap(record), key0).verify(key0, Value.wrap(value), timestamp); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java index f361cf0b5f..4292de4c19 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java @@ -420,7 +420,7 @@ public long getVersion(String key) { Iterator it = reverseIterator(); while (it.hasNext()) { Write write = it.next(); - if(write.getKey().equals(Text.wrap(key))) { + if(write.getKey().equals(Text.wrapCached(key))) { return write.getVersion(); } } @@ -435,7 +435,7 @@ public long getVersion(String key, long record) { Write write = it.next(); if(record == write.getRecord().longValue() && (Strings.isNullOrEmpty(key) || write.getKey().equals( - Text.wrap(key)))) { + Text.wrapCached(key)))) { return write.getVersion(); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java index 12a679a591..a2cdc6bae7 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java @@ -59,7 +59,7 @@ public final class Write implements Byteable, Versioned { * @return the Write */ public static Write add(String key, TObject value, long record) { - return new Write(Action.ADD, Text.wrap(key), Value.wrap(value), + return new Write(Action.ADD, Text.wrapCached(key), Value.wrap(value), PrimaryKey.wrap(record), Time.now()); } @@ -95,8 +95,8 @@ public static Write fromByteBuffer(ByteBuffer bytes) { * @return the Write */ public static Write notStorable(String key, TObject value, long record) { - return new Write(Action.COMPARE, Text.wrap(key), Value.wrap(value), - PrimaryKey.wrap(record), NO_VERSION); + return new Write(Action.COMPARE, Text.wrapCached(key), + Value.wrap(value), PrimaryKey.wrap(record), NO_VERSION); } /** @@ -109,8 +109,8 @@ public static Write notStorable(String key, TObject value, long record) { * @return the Write */ public static Write remove(String key, TObject value, long record) { - return new Write(Action.REMOVE, Text.wrap(key), Value.wrap(value), - PrimaryKey.wrap(record), Time.now()); + return new Write(Action.REMOVE, Text.wrapCached(key), + Value.wrap(value), PrimaryKey.wrap(record), Time.now()); } /** From b23eb264b0c04b7886ace37a31e96e5e4d4a87d2 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 26 Oct 2014 07:17:00 -0400 Subject: [PATCH 057/100] remove ByteBufferOutputStream because its super inefficient --- .../server/io/ByteBufferOutputStream.java | 321 ------------------ .../server/io/ByteableCollections.java | 28 +- 2 files changed, 16 insertions(+), 333 deletions(-) delete mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteBufferOutputStream.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteBufferOutputStream.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteBufferOutputStream.java deleted file mode 100644 index 396c4258e3..0000000000 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteBufferOutputStream.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package org.cinchapi.concourse.server.io; - -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel.MapMode; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; - -import com.google.common.collect.Lists; - -/** - * This class implements a concurrent output stream in which data is - * written into a byte buffer. The stream automatically grows as data as written - * to it. The data can be retrieved using the {@link #toByteBuffer()}, - * {@link #toDirectByteBuffer()} , and {@link #toMappedByteBuffer(String, long)} - * methods. - *

- * Closing a {@code ByteBufferOutputStream} has no effect. The methods in this - * class can be called after the stream has been closed without generating an - * {@code IOException}. - *

- * - * @author jnelson - */ -public class ByteBufferOutputStream extends OutputStream { - - /** - * We use an ArrayList to model a dynamically growing byte buffer output - * stream. - */ - private final ArrayList stream; - - /** - * Construct a new byte buffer output stream. - */ - public ByteBufferOutputStream() { - this(32); - } - - /** - * Construct a new byte buffer output stream with an initial capacity of the - * specified {@code size} in bytes. - * - * @param size - */ - public ByteBufferOutputStream(int size) { - this.stream = Lists.newArrayListWithCapacity(size); - } - - /** - * Closing a ByteBufferOutputStream has no effect. The methods in - * this class can be called after the stream has been closed without - * generating an IOException. - */ - @Override - public void close() {} - - /** - * Return the current size of the stream - * - * @return the number of valid bytes in the stream - */ - public int size() { - return stream.size(); - } - - /** - * Create a newly allocated ByteBuffer that contains the content of the - * stream. The ByteBuffer will have a capacity and limit of {@link #size()} - * and a position of 0. - * - * @return the current content of the output stream as a ByteBuffer - */ - public ByteBuffer toByteBuffer() { - return toByteBuffer(ByteBuffer.allocate(size())); - } - - /** - * Create a newly allocated DirectByteBuffer that contains the content of - * the - * stream. The ByteBuffer will have a capacity and limit of {@link #size()} - * and a position of 0. - * - * @return the current content of the output stream as a ByteBuffer - */ - public ByteBuffer toDirectByteBuffer() { - return toByteBuffer(ByteBuffer.allocateDirect(size())); - } - - /** - * Create a newly allocated MappedByteBuffer that contains the content of - * the stream. The ByteBuffer will have a capacity and limit of - * {@link #size()} and a position of 0. - * - * @return the current content of the output stream as a ByteBuffer - */ - public MappedByteBuffer toMappedByteBuffer(String file, long position) { - return (MappedByteBuffer) toByteBuffer(FileSystem.map(file, - MapMode.READ_WRITE, position, size())); - } - - /** - * Write a byte that represents {@code value} to the stream. - * - * @param value - */ - public void write(boolean value) { - write(value ? (byte) 1 : (byte) 0); - } - - /** - * Write {@code value} to the stream. - * - * @param value - */ - public void write(byte value) { - stream.add(value); - } - - @Override - public void write(byte[] bytes) { - for (byte b : bytes) { - write(b); - } - } - - @Override - public void write(byte[] b, int off, int len) { - throw new UnsupportedOperationException(); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - public void write(Byteable value) { - write(value.getBytes()); - } - - /** - * Write the remaining bytes from {@code buffer} to the stream. - * - * @param buffer - */ - public void write(ByteBuffer buffer) { - while (buffer.hasRemaining()) { - write(buffer.get()); - } - - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - public void write(double value) { - write(Double.doubleToRawLongBits(value)); - } - - /** - * Write the bytes for the enum ordinal of {@code value} to the stream. - * - * @param value - */ - public void write(Enum value) { - write(value.ordinal()); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - public void write(float value) { - write(Float.floatToRawIntBits(value)); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - @Override - public void write(int value) { - write((byte) (value >> 24)); - write((byte) (value >> 16)); - write((byte) (value >> 8)); - write((byte) (value)); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - public void write(long value) { - write((byte) (value >> 56)); - write((byte) (value >> 48)); - write((byte) (value >> 40)); - write((byte) (value >> 32)); - write((byte) (value >> 24)); - write((byte) (value >> 16)); - write((byte) (value >> 8)); - write((byte) (value)); - } - - /** - * Write the bytes for {@code value} to the stream. This method will check - * to see if {@code value} is an instance of a primitive type and write the - * bytes accordingly. If {@code value} is not a primitive type, then a UTF-8 - * encoded byte array from the toString() method will be written to the - * stream. - * - * @param value - */ - public void write(Object value) { - if(value instanceof Byte) { - write((byte) value); - } - else if(value instanceof Integer) { - write((int) value); - } - else if(value instanceof Boolean) { - write((boolean) value); - } - else if(value instanceof Long) { - write((long) value); - } - else if(value instanceof Float) { - write((float) value); - } - else if(value instanceof Double) { - write((double) value); - } - else { - write(value.toString()); - } - } - - /** - * UTF-8 encode {@code value} and write the bytes to the stream. - * - * @param value - */ - public void write(String value) { - write(value, StandardCharsets.UTF_8); - } - - /** - * Encode {@code value} using {@code charset} and write the bytes to the - * stream. - * - * @param value - * @param charset - */ - public void write(String value, Charset charset) { - write(value.getBytes(charset)); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - */ - public void write(Collection value) { - write(ByteableCollections.toByteBuffer(value)); - } - - /** - * Write the bytes for {@code value} to the stream. - * - * @param value - * @param sizePerElement - */ - public void write(Collection value, int sizePerElement) { - write(ByteableCollections.toByteBuffer(value, sizePerElement)); - } - - /** - * Write the contents of the stream to {@code buffer}. - * - * @param buffer - * @return {@code buffer} - */ - private ByteBuffer toByteBuffer(ByteBuffer buffer) { - for (byte b : stream) { - buffer.put(b); - } - buffer.rewind(); - return buffer; - } - -} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java index e80f248e55..bdd50c33f4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java @@ -73,13 +73,17 @@ public static Iterator iterator(ByteBuffer bytes, */ public static ByteBuffer toByteBuffer( Collection collection) { - ByteBufferOutputStream out = new ByteBufferOutputStream(); + int size = 0; for (Byteable object : collection) { - out.write(object.size()); - out.write(object); + size += (object.size() + 4); } - out.close(); - return out.toByteBuffer(); + ByteBuffer buffer = ByteBuffer.allocate(size); + for (Byteable object : collection) { + buffer.putInt(object.size()); + buffer.put(object.getBytes()); + } + buffer.rewind(); + return buffer; } /** @@ -92,19 +96,19 @@ public static ByteBuffer toByteBuffer( */ public static ByteBuffer toByteBuffer( Collection collection, int sizePerElement) { - ByteBufferOutputStream out = new ByteBufferOutputStream(); - out.write(collection.size()); - out.write(sizePerElement); + int size = (collection.size() * sizePerElement) + 8; + ByteBuffer buffer = ByteBuffer.allocate(size); + buffer.putInt(collection.size()); + buffer.putInt(sizePerElement); for (Byteable object : collection) { Preconditions.checkArgument(object.size() == sizePerElement, "'%s' must be '%s' bytes but it is " + "actually '%s' bytes", object, sizePerElement, object.size()); - - out.write(object.getBytes()); + buffer.put(object.getBytes()); } - out.close(); - return out.toByteBuffer(); + buffer.rewind(); + return buffer; } /** From 120731445a4c5f485006b6619b5c534f94bc2fde Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 26 Oct 2014 07:28:30 -0400 Subject: [PATCH 058/100] don't create an intermediate buffer when serializing byteable collections --- .../org/cinchapi/concourse/server/io/ByteableCollections.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java index bdd50c33f4..c78895ac03 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java @@ -80,7 +80,7 @@ public static ByteBuffer toByteBuffer( ByteBuffer buffer = ByteBuffer.allocate(size); for (Byteable object : collection) { buffer.putInt(object.size()); - buffer.put(object.getBytes()); + object.copyToByteBuffer(buffer); } buffer.rewind(); return buffer; @@ -105,7 +105,7 @@ public static ByteBuffer toByteBuffer( "'%s' must be '%s' bytes but it is " + "actually '%s' bytes", object, sizePerElement, object.size()); - buffer.put(object.getBytes()); + object.copyToByteBuffer(buffer); } buffer.rewind(); return buffer; From 1f2e942f45de193e9ecf0d7a54554815e1f8cb5d Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 26 Oct 2014 08:16:10 -0400 Subject: [PATCH 059/100] remove unnecessary calls to check if a key is contained in a map before just getting the value --- .../cinchapi/common/util/NonBlockingHashMultimap.java | 8 ++------ .../cinchapi/concourse/server/storage/db/Record.java | 10 ++++++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java index 6213908386..6f0788e0f6 100644 --- a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java +++ b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java @@ -165,12 +165,8 @@ public Collection> entries() { */ @Override public Set get(K key) { - if(map.containsKey(key)) { - return map.get(key); - } - else { - return emptySet; - } + Set values = map.get(key); + return values != null ? values : emptySet; } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java index 79015be2b6..707fd15a2f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java @@ -380,8 +380,9 @@ protected Set describe(long timestamp) { protected Set get(K key) { read.lock(); try { - return present.containsKey(key) ? Collections - .unmodifiableSet(present.get(key)) : emptyValues; + Set values = present.get(key); + return values != null ? Collections.unmodifiableSet(values) + : emptyValues; } finally { read.unlock(); @@ -400,9 +401,10 @@ protected Set get(K key, long timestamp) { read.lock(); try { Set values = emptyValues; - if(history.containsKey(key)) { + List> stored = history.get(key); + if(stored != null) { values = Sets.newLinkedHashSet(); - Iterator> it = history.get(key).iterator(); + Iterator> it = stored.iterator(); while (it.hasNext()) { Revision revision = it.next(); if(revision.getVersion() <= timestamp) { From 0298c9d49d57e58ce609fa277ee3c9c302fdcfbd Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 26 Oct 2014 08:54:28 -0400 Subject: [PATCH 060/100] use backport of java 8 concurrenthashmap --- .../cinchapi/concourse/server/concurrent/LockService.java | 5 +++-- .../concourse/server/concurrent/RangeLockService.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java index 99ece5846a..cf2ec3cfcb 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/LockService.java @@ -23,11 +23,12 @@ */ package org.cinchapi.concourse.server.concurrent; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import org.cinchapi.vendor.jsr166e.ConcurrentHashMapV8; + import com.google.common.base.Objects; import com.google.common.collect.Multiset; @@ -93,7 +94,7 @@ public WriteLock getWriteLock(Token token) { /** * A cache of locks that have been requested. */ - private final ConcurrentHashMap locks = new ConcurrentHashMap(); + private final ConcurrentHashMapV8 locks = new ConcurrentHashMapV8(); private LockService() {/* noop */} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java index 0863686bed..f9a36008d4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeLockService.java @@ -25,7 +25,6 @@ import java.util.Iterator; import java.util.Map.Entry; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; @@ -36,6 +35,7 @@ import org.cinchapi.concourse.thrift.Operator; import org.cinchapi.concourse.thrift.TObject; import org.cinchapi.concourse.util.Transformers; +import org.cinchapi.vendor.jsr166e.ConcurrentHashMapV8; import com.google.common.base.Objects; import com.google.common.base.Preconditions; @@ -110,7 +110,7 @@ public WriteLock getWriteLock(RangeToken token) { /** * A cache of locks that have been requested. */ - private final ConcurrentHashMap locks = new ConcurrentHashMap(); + private final ConcurrentHashMapV8 locks = new ConcurrentHashMapV8(); /** * Return the ReadLock that is identified by {@code token}. Every caller From fdb7813b1bc436df51716da9d5323e050b88c222 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 26 Oct 2014 19:06:30 -0400 Subject: [PATCH 061/100] make AccessManager comply with new Byteable method --- .../java/org/cinchapi/concourse/security/AccessManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java index d2ed9fd6de..11ab5e82ff 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java @@ -682,6 +682,11 @@ public String toString() { return sb.toString(); } + @Override + public void copyToByteBuffer(ByteBuffer buffer) { + buffer.put(getBytes()); + } + } } From 2449b857227b2d9644630379f0aad410456ec1fa Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 2 Nov 2014 19:03:38 -0500 Subject: [PATCH 062/100] copy write data directly to mappedbytebuffer --- .../org/cinchapi/concourse/server/storage/temp/Buffer.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index cd3f93e808..0474e4d32c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -975,11 +975,8 @@ public void append(Write write) throws CapacityException { try { if(content.remaining() >= write.size() + 4) { index(write); - ByteBuffer bytes = ByteBuffer.allocate(write.size()); - write.copyToByteBuffer(bytes); - bytes.rewind(); content.putInt(write.size()); - content.put(bytes); + write.copyToByteBuffer(content); content.force(); } else { From 5538da45723ce51f4fe7381f76a83dcbfcfdf650 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Tue, 28 Oct 2014 07:53:35 -0400 Subject: [PATCH 063/100] use prefix operator for variable increment Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/LoggingBloomFilter.java --- .../cinchapi/common/util/NonBlockingHashMultimap.java | 2 +- .../java/org/cinchapi/concourse/lang/Expression.java | 2 +- .../org/cinchapi/concourse/server/ConcourseServer.java | 2 +- .../cinchapi/concourse/server/storage/BaseStore.java | 4 ++-- .../concourse/server/storage/db/SearchBlock.java | 6 +++--- .../concourse/server/storage/db/SearchRecord.java | 2 +- .../cinchapi/concourse/server/storage/temp/Buffer.java | 6 +++--- .../main/java/org/cinchapi/concourse/util/TLists.java | 4 ++-- .../java/org/cinchapi/concourse/util/TStrings.java | 4 ++-- .../cinchapi/vendor/com/davekoelle/AlphanumSorter.java | 8 ++++---- .../main/java/org/cinchapi/concourse/Concourse.java | 2 +- .../java/org/cinchapi/concourse/ConnectionPool.java | 2 +- .../java/org/cinchapi/concourse/util/ByteBuffers.java | 2 +- .../cinchapi/concourse/util/RandomStringGenerator.java | 10 +++++----- .../org/cinchapi/concourse/util/TLinkedTableMap.java | 4 ++-- .../java/org/cinchapi/concourse/util/Transformers.java | 2 +- .../java/org/cinchapi/concourse/util/Versions.java | 2 +- 17 files changed, 32 insertions(+), 32 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java index 6f0788e0f6..92405c1f0a 100644 --- a/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java +++ b/concourse-server/src/main/java/org/cinchapi/common/util/NonBlockingHashMultimap.java @@ -205,7 +205,7 @@ public boolean put(K key, V value) { map.put(key, values); } if(values.add(value)) { - totalSize++; + ++totalSize; return true; } else { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/lang/Expression.java b/concourse-server/src/main/java/org/cinchapi/concourse/lang/Expression.java index d915893a77..dfadd6fa7a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/lang/Expression.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/lang/Expression.java @@ -149,7 +149,7 @@ public long getTimestampRaw() { */ public TObject[] getValuesRaw() { TObject[] values = new TObject[getValues().size()]; - for (int i = 0; i < values.length; i++) { + for (int i = 0; i < values.length; ++i) { values[i] = getValues().get(i).getValue(); } return values; diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index a454102d5f..33d8202589 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -1165,7 +1165,7 @@ private AtomicOperation updateChronologizeResultSet(String key, try { Map newResult = operation.audit(key, record); if(newResult.size() > history.size()) { - for (int i = history.size(); i < newResult.size(); i++) { + for (int i = history.size(); i < newResult.size(); ++i) { Long timestamp = Iterables.get( (Iterable) newResult.keySet(), i); Set values = operation.fetch(key, record); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/BaseStore.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/BaseStore.java index fe3b7bb371..efccb2545c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/BaseStore.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/BaseStore.java @@ -49,7 +49,7 @@ public final Set describe(long record, long timestamp) { @Override public final Map> explore(long timestamp, String key, Operator operator, TObject... values) { - for (int i = 0; i < values.length; i++) { + for (int i = 0; i < values.length; ++i) { values[i] = Stores.normalizeValue(operator, values[i]); } operator = Stores.normalizeOperator(operator); @@ -59,7 +59,7 @@ public final Map> explore(long timestamp, String key, @Override public final Map> explore(String key, Operator operator, TObject... values) { - for (int i = 0; i < values.length; i++) { + for (int i = 0; i < values.length; ++i) { values[i] = Stores.normalizeValue(operator, values[i]); } operator = Stores.normalizeOperator(operator); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchBlock.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchBlock.java index 82bafe27dc..405090d08f 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchBlock.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchBlock.java @@ -110,7 +110,7 @@ public final void insert(Text key, Value value, PrimaryKey record, for (String tok : toks) { executor.submit(getRunnable(key, tok, pos, record, version, type)); - pos++; + ++pos; } executor.shutdown(); try { @@ -176,8 +176,8 @@ public void run() { if(STOPWORDS.contains(term)) { return; } - for (int i = 0; i < term.length(); i++) { - for (int j = i + 1; j < term.length() + 1; j++) { + for (int i = 0; i < term.length(); ++i) { + for (int j = i + 1; j < term.length() + 1; ++j) { String substring = term.substring(i, j).trim(); if(!Strings.isNullOrEmpty(substring) && !STOPWORDS.contains(substring) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchRecord.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchRecord.java index 3a13f58db8..7ac18ddd14 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchRecord.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SearchRecord.java @@ -93,7 +93,7 @@ public Set search(Text query) { // When skipping a stop word, we must record an offset to // correctly determine if the next term match is in the // correct relative position to the previous term match - offset++; + ++offset; continue; } Set positions = get(Text.wrap(tok)); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 0474e4d32c..4fb93ba851 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -1082,8 +1082,8 @@ public Write next() { "A write has been removed from the Page"); } Write next = writes[index]; - index++; - distance++; + ++index; + ++distance; return next; } finally { @@ -1206,7 +1206,7 @@ public Write next() { public void remove() { Locks.lockIfCondition(pageLock.writeLock(), this == currentPage); try { - head++; + ++head; } finally { Locks.lockIfCondition(pageLock.writeLock(), this == currentPage); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TLists.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TLists.java index 14a29e02dd..f4b632ccd0 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TLists.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TLists.java @@ -44,10 +44,10 @@ public final class TLists { public static void retainIntersection(List... lists) { Preconditions.checkArgument(lists.length > 0); List intersection = lists[0]; - for (int i = 1; i < lists.length; i++) { + for (int i = 1; i < lists.length; ++i) { intersection.retainAll(lists[i]); } - for (int i = 1; i < lists.length; i++) { + for (int i = 1; i < lists.length; ++i) { lists[i].retainAll(intersection); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TStrings.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TStrings.java index 9a8eebb822..b2c7ab5762 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TStrings.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TStrings.java @@ -48,8 +48,8 @@ public final class TStrings { */ public static Set getAllSubStrings(String string) { Set result = Sets.newHashSet(); - for (int i = 0; i < string.length(); i++) { - for (int j = i + 1; j <= string.length(); j++) { + for (int i = 0; i < string.length(); ++i) { + for (int j = i + 1; j <= string.length(); ++j) { String substring = string.substring(i, j).trim(); if(!Strings.isNullOrEmpty(substring)) { result.add(substring); diff --git a/concourse-server/src/main/java/org/cinchapi/vendor/com/davekoelle/AlphanumSorter.java b/concourse-server/src/main/java/org/cinchapi/vendor/com/davekoelle/AlphanumSorter.java index 11eceef61d..abd9804336 100644 --- a/concourse-server/src/main/java/org/cinchapi/vendor/com/davekoelle/AlphanumSorter.java +++ b/concourse-server/src/main/java/org/cinchapi/vendor/com/davekoelle/AlphanumSorter.java @@ -37,14 +37,14 @@ private final String getChunk(String s, int slength, int marker) { StringBuilder chunk = new StringBuilder(); char c = s.charAt(marker); chunk.append(c); - marker++; + ++marker; if(isDigit(c)) { while (marker < slength) { c = s.charAt(marker); if(!isDigit(c)) break; chunk.append(c); - marker++; + ++marker; } } else { @@ -53,7 +53,7 @@ private final String getChunk(String s, int slength, int marker) { if(isDigit(c)) break; chunk.append(c); - marker++; + ++marker; } } return chunk.toString(); @@ -85,7 +85,7 @@ public int compare(String s1, String s2) { result = thisChunkLength - thatChunk.length(); // If equal, the first different number counts if(result == 0) { - for (int i = 0; i < thisChunkLength; i++) { + for (int i = 0; i < thisChunkLength; ++i) { result = thisChunk.charAt(i) - thatChunk.charAt(i); if(result != 0) { return result; diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index bddde1ea67..b85d94cce1 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -1477,7 +1477,7 @@ public Map> chronologize(final String key, entry = Iterables.get(chronology.entrySet(), index - 1); result.put(entry.getKey(), entry.getValue()); } - for (int i = index; i < chronology.size(); i++) { + for (int i = index; i < chronology.size(); ++i) { entry = Iterables.get(chronology.entrySet(), i); if(entry.getKey().getMicros() >= end.getMicros()) { break; diff --git a/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java b/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java index 80c04c4f2b..ba003fef59 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java +++ b/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java @@ -347,7 +347,7 @@ protected ConnectionPool(String host, int port, String username, String password, String environment, int poolSize) { this.connections = buildCache(poolSize); this.numAvailableConnections = new AtomicInteger(poolSize); - for (int i = 0; i < poolSize; i++) { + for (int i = 0; i < poolSize; ++i) { connections.put(Concourse.connect(host, port, username, password, environment), new AtomicBoolean(false)); } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java index 6e2ba13b6f..20cc9be79e 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ByteBuffers.java @@ -346,7 +346,7 @@ public static byte[] toByteArray(ByteBuffer buffer) { private static final ConcurrentLinkedQueue DECODERS = new ConcurrentLinkedQueue(); static { try { - for (int i = 0; i < NUM_DECODERS; i++) { + for (int i = 0; i < NUM_DECODERS; ++i) { DECODERS.add(StandardCharsets.UTF_8.newDecoder()); } } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/RandomStringGenerator.java b/concourse/src/main/java/org/cinchapi/concourse/util/RandomStringGenerator.java index cb27e4d7c8..720c0059c4 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/RandomStringGenerator.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/RandomStringGenerator.java @@ -48,10 +48,10 @@ public class RandomStringGenerator { private static final Character[] letters = new Character[26]; static { - for (int i = 0; i < digits.length; i++) { + for (int i = 0; i < digits.length; ++i) { digits[i] = (char) ('0' + i); } - for (int i = 0; i < letters.length; i++) { + for (int i = 0; i < letters.length; ++i) { letters[i] = (char) ('a' + i); } } @@ -91,7 +91,7 @@ public String nextString() { public String nextString(int length) { Character[] spaces = new Character[random .nextInt(MAX_DISTRIBUTION_OF_SPACE_CHARS)]; - for (int i = 0; i < spaces.length; i++) { + for (int i = 0; i < spaces.length; ++i) { spaces[i] = spaceChar; } ArrayList source = new ArrayList(); @@ -119,7 +119,7 @@ public String nextStringAllowDigits() { public String nextStringAllowDigits(int length) { Character[] spaces = new Character[random .nextInt(MAX_DISTRIBUTION_OF_SPACE_CHARS)]; - for (int i = 0; i < spaces.length; i++) { + for (int i = 0; i < spaces.length; ++i) { spaces[i] = spaceChar; } ArrayList source = new ArrayList(); @@ -144,7 +144,7 @@ private String build(int length, List source) { "Cannot generate a string with fewer than 1 characters."); } StringBuilder builder = new StringBuilder(); - for (int i = 0; i < length; i++) { + for (int i = 0; i < length; ++i) { int index = random.nextInt(source.size()); builder.append(source.get(index)); } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/TLinkedTableMap.java b/concourse/src/main/java/org/cinchapi/concourse/util/TLinkedTableMap.java index 2a27db4809..5957dc3267 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/TLinkedTableMap.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/TLinkedTableMap.java @@ -143,7 +143,7 @@ public String toString() { format += "%-" + entry.getValue() + "s | "; total += entry.getValue() + 3; header[i] = entry.getKey(); - i++; + ++i; } format += "%n"; String hr = Strings.padEnd("+", rowLength + 3 + total, '-'); @@ -159,7 +159,7 @@ public String toString() { i = 1; for (C column : columns.keySet()) { rowdata[i] = get(row).get(column); - i++; + ++i; } sb.append(String.format(format, rowdata)); } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java b/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java index c4bff0f678..9b147c84bc 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java @@ -57,7 +57,7 @@ public final class Transformers { public static V[] transformArray(F[] original, Function function, Class vClass) { V[] transformed = (V[]) Array.newInstance(vClass, original.length); - for (int i = 0; i < original.length; i++) { + for (int i = 0; i < original.length; ++i) { F item = original[i]; transformed[i] = function.apply(item); } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/Versions.java b/concourse/src/main/java/org/cinchapi/concourse/util/Versions.java index 5e42074f98..b74ea158fc 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/Versions.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/Versions.java @@ -93,7 +93,7 @@ public static long toLongRepresentation(String version, Character.forDigit(0, 10)))); } long sum = 0; - for (int i = 0; i < parts.size(); i++) { + for (int i = 0; i < parts.size(); ++i) { sum += parts.get(i) * Math.pow(10, (n * parts.size() - (i + 1))); } return sum; From 433bf60419fec6f81ab245765b983a653049cf1c Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 5 Nov 2014 18:04:14 -0500 Subject: [PATCH 064/100] abstract away mappedbytebuffer unammping so reflection overhead is minimal --- .../concourse/server/io/Cleaners.java | 55 +++++++++++++++++++ .../concourse/server/io/FileSystem.java | 13 +---- 2 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java new file mode 100644 index 0000000000..52620aaaaa --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java @@ -0,0 +1,55 @@ +package org.cinchapi.concourse.server.io; + +import java.lang.reflect.Method; +import java.nio.MappedByteBuffer; + +/** + * An abstraction for the reflection hack that allows us to use internal JVM + * cleaners to free memory "directly". + * + * @author jnelson + */ +public final class Cleaners { + + /** + * Force clean the {@code buffer} and unmap it from memory, if possible. + * This method is not guaranteed to work and can cause bad things to + * happen, so use with caution. + *

+ * Inspired by + * http://stackoverflow.com/a/19447758/1336833} + *

+ * + * @param buffer + */ + public static void freeMappedByteBuffer(MappedByteBuffer buffer) { + if(supported) { + try { + sunMiscCleanerCleanMethod + .invoke(directByteBufferGetCleanerMethod.invoke(buffer)); + } + catch (Exception e) {} + } + } + + // Reflection goodness + private static Method sunMiscCleanerCleanMethod; + private static Method directByteBufferGetCleanerMethod; + private static boolean supported; + static { + try { + directByteBufferGetCleanerMethod = Class.forName( + "java.nio.DirectByteBuffer").getMethod("cleaner"); + directByteBufferGetCleanerMethod.setAccessible(true); + sunMiscCleanerCleanMethod = Class.forName("sun.misc.Cleaner") + .getMethod("clean"); + sunMiscCleanerCleanMethod.setAccessible(true); + supported = true; + } + catch (Exception e) { + supported = false; + } + } + + private Cleaners() {/* noop */} +} \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java index 9b3c3251a3..2c48fbe2b5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java @@ -26,7 +26,6 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; @@ -295,7 +294,7 @@ public static void replaceFile(String original, String replacement) { throw Throwables.propagate(e); } } - + /** * Attempt to force the unmapping of {@code buffer}. This method should be * used with EXTREME CAUTION. If {@code buffer} is used @@ -303,16 +302,8 @@ public static void replaceFile(String original, String replacement) { * * @param buffer */ - // http://stackoverflow.com/a/19447758/1336833 public static void unmap(MappedByteBuffer buffer) { - try { - Method cleaner = buffer.getClass().getMethod("cleaner"); - cleaner.setAccessible(true); - Method clean = Class.forName("sun.misc.Cleaner").getMethod("clean"); - clean.setAccessible(true); - clean.invoke(cleaner.invoke(buffer)); - } - catch (Exception e) {/* noop */} + Cleaners.freeMappedByteBuffer(buffer); } private FileSystem() {} From d555c2d45d9f8c5a8ac65deff2e064e11c7332d6 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 5 Nov 2014 18:04:57 -0500 Subject: [PATCH 065/100] add copyright --- .../concourse/server/io/Cleaners.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java index 52620aaaaa..0dc21ab7f3 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Cleaners.java @@ -1,3 +1,26 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package org.cinchapi.concourse.server.io; import java.lang.reflect.Method; From ebf90f0007864e1a1e125c37e83e4b3a4b73d882 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 6 Nov 2014 10:16:01 -0500 Subject: [PATCH 066/100] change method name from copyToByteBuffer to copyTo Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/security/LegacyAccessManager.java --- .../concourse/server/concurrent/RangeToken.java | 4 ++-- .../cinchapi/concourse/server/concurrent/Token.java | 2 +- .../org/cinchapi/concourse/server/io/Byteable.java | 4 ++-- .../concourse/server/io/ByteableCollections.java | 4 ++-- .../org/cinchapi/concourse/server/io/Composite.java | 4 ++-- .../cinchapi/concourse/server/model/Position.java | 6 +++--- .../cinchapi/concourse/server/model/PrimaryKey.java | 4 ++-- .../org/cinchapi/concourse/server/model/Text.java | 2 +- .../org/cinchapi/concourse/server/model/Value.java | 4 ++-- .../concourse/server/storage/AtomicOperation.java | 6 +++--- .../cinchapi/concourse/server/storage/db/Block.java | 6 +++--- .../concourse/server/storage/db/BlockIndex.java | 12 ++++++------ .../concourse/server/storage/db/Revision.java | 10 +++++----- .../concourse/server/storage/temp/Buffer.java | 2 +- .../concourse/server/storage/temp/Write.java | 10 +++++----- 15 files changed, 40 insertions(+), 40 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java index 963378724c..00d95c73fd 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/RangeToken.java @@ -128,10 +128,10 @@ private static ByteBuffer serialize(Text key, Operator operator, ByteBuffer bytes = ByteBuffer.allocate(size); bytes.put(operator != null ? (byte) operator.ordinal() : NULL_OPERATOR); bytes.putInt(key.size()); - key.copyToByteBuffer(bytes); + key.copyTo(bytes); for (Value value : values) { bytes.putInt(value.size()); - value.copyToByteBuffer(bytes); + value.copyTo(bytes); } bytes.rewind(); return bytes; diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java index 0c1af7374b..20adad7ea6 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/Token.java @@ -130,7 +130,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { ByteBuffers.copyAndRewindSource(bytes, buffer); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java index 77c75646be..380c58c874 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteable.java @@ -70,7 +70,7 @@ public interface Byteable { * doing any unnecessary intermediate copying. So, if the binary * representation for this object depends on that of another Byteable, then * the implementation of this method should gather those bytes using the - * {@link #copyToByteBuffer(ByteBuffer)} method for the other Byteable. + * {@link #copyTo(ByteBuffer)} method for the other Byteable. *

*

* DO NOT make any modifications to {@code buffer} other @@ -81,7 +81,7 @@ public interface Byteable { * * @param buffer */ - public void copyToByteBuffer(ByteBuffer buffer); + public void copyTo(ByteBuffer buffer); /** * Returns a byte sequence that represents this object. diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java index c78895ac03..63a0a43fb2 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/ByteableCollections.java @@ -80,7 +80,7 @@ public static ByteBuffer toByteBuffer( ByteBuffer buffer = ByteBuffer.allocate(size); for (Byteable object : collection) { buffer.putInt(object.size()); - object.copyToByteBuffer(buffer); + object.copyTo(buffer); } buffer.rewind(); return buffer; @@ -105,7 +105,7 @@ public static ByteBuffer toByteBuffer( "'%s' must be '%s' bytes but it is " + "actually '%s' bytes", object, sizePerElement, object.size()); - object.copyToByteBuffer(buffer); + object.copyTo(buffer); } buffer.rewind(); return buffer; diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index e76594a03d..be911a59dd 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -94,7 +94,7 @@ private Composite(Byteable... byteables) { } bytes = ByteBuffer.allocate(size); for (Byteable byteable : byteables) { - byteable.copyToByteBuffer(bytes); + byteable.copyTo(bytes); } bytes.rewind(); } @@ -130,7 +130,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { ByteBuffers.copyAndRewindSource(bytes, buffer); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java index e1e6177db7..41371dc75a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Position.java @@ -149,7 +149,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); @@ -189,7 +189,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { // NOTE: Storing the index as an int instead of some size aware // variable length is probably overkill since most indexes will be // smaller than Byte.MAX_SIZE or Short.MAX_SIZE, but having variable @@ -199,7 +199,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { // Position is constant so we won't need to store the overall size // prior to the Position to deserialize it, which is actually more // space efficient. - primaryKey.copyToByteBuffer(buffer); + primaryKey.copyTo(buffer); buffer.putInt(index); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java index f673667534..519151e1c3 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/PrimaryKey.java @@ -138,7 +138,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(SIZE); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); @@ -169,7 +169,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.putLong(data); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java index ab7efa4591..3b7db39439 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Text.java @@ -175,7 +175,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { if(bytes == null) { ByteBuffers.putString(text, buffer); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java index e0b9682fa5..d67d2599a5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/model/Value.java @@ -213,7 +213,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); @@ -266,7 +266,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.put((byte) data.getType().ordinal()); buffer.put(data.bufferForData()); } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java index 85b3dc19b5..8883880098 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/AtomicOperation.java @@ -620,15 +620,15 @@ public ByteBuffer getBytes() { // happens if the AtomicOperation is not aborted before an attempt // to commit, so its best to not create a copy if we don't have to ByteBuffer bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); return bytes; } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.put((byte) type.ordinal()); - token.copyToByteBuffer(buffer); + token.copyTo(buffer); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index de4b8fc0ce..b11545e3a9 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -284,7 +284,7 @@ public ByteBuffer getBytes() { read.lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); return bytes; } @@ -442,7 +442,7 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { Locks.lockIfCondition(read, mutable); try { L locator = null; @@ -450,7 +450,7 @@ public void copyToByteBuffer(ByteBuffer buffer) { int position = 0; for (Revision revision : revisions) { buffer.putInt(revision.size()); - revision.copyToByteBuffer(buffer); + revision.copyTo(buffer); position = buffer.position() - revision.size() - 4; /* * States that trigger this condition to be true: diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java index 34698c0144..e23d829a38 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/BlockIndex.java @@ -143,7 +143,7 @@ public ByteBuffer getBytes() { masterLock.readLock().lock(); try { ByteBuffer bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); return bytes; } @@ -282,13 +282,13 @@ public void sync() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { Preconditions.checkState(mutable); masterLock.readLock().lock(); try { for (Entry entry : entries.values()) { buffer.putInt(entry.size()); - entry.copyToByteBuffer(buffer); + entry.copyTo(buffer); } } finally { @@ -390,7 +390,7 @@ public Entry(Composite key) { @Override public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); return bytes; } @@ -446,10 +446,10 @@ public int size() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.putInt(start); buffer.putInt(end); - key.copyToByteBuffer(buffer); + key.copyTo(buffer); } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java index b099fece7f..dba9c3265e 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java @@ -245,7 +245,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); @@ -314,18 +314,18 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.put((byte) type.ordinal()); buffer.putLong(version); if(xLocatorSize() == VARIABLE_SIZE) { buffer.putInt(locator.size()); } - locator.copyToByteBuffer(buffer); + locator.copyTo(buffer); if(xKeySize() == VARIABLE_SIZE) { buffer.putInt(key.size()); } - key.copyToByteBuffer(buffer); - value.copyToByteBuffer(buffer); + key.copyTo(buffer); + value.copyTo(buffer); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 4fb93ba851..0739e4fe17 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -976,7 +976,7 @@ public void append(Write write) throws CapacityException { if(content.remaining() >= write.size() + 4) { index(write); content.putInt(write.size()); - write.copyToByteBuffer(content); + write.copyTo(content); content.force(); } else { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java index a2cdc6bae7..0d579f338a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Write.java @@ -207,7 +207,7 @@ public boolean equals(Object obj) { public ByteBuffer getBytes() { if(bytes == null) { bytes = ByteBuffer.allocate(size()); - copyToByteBuffer(bytes); + copyTo(bytes); bytes.rewind(); } return ByteBuffers.asReadOnlyBuffer(bytes); @@ -295,13 +295,13 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.putInt(key.size()); buffer.put((byte) type.ordinal()); buffer.putLong(version); - record.copyToByteBuffer(buffer); - key.copyToByteBuffer(buffer); - value.copyToByteBuffer(buffer); + record.copyTo(buffer); + key.copyTo(buffer); + value.copyTo(buffer); } } From 4613699750b263376111a0e4f1b668d90a207ce5 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 6 Nov 2014 14:47:08 -0500 Subject: [PATCH 067/100] adding a BlockingExecutorService and logic to reuse thread with ConcourseExecutors utils so we minimize the thread overhead related to block syncing --- .../concurrent/BlockingExecutorService.java | 171 ++++++++++++++++ .../server/concurrent/ConcourseExecutors.java | 96 +++++---- .../BlockingExecutorServiceTest.java | 183 ++++++++++++++++++ 3 files changed, 398 insertions(+), 52 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorService.java create mode 100644 concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorServiceTest.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorService.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorService.java new file mode 100644 index 0000000000..2a180247c5 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorService.java @@ -0,0 +1,171 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.concurrent; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.annotation.concurrent.ThreadSafe; + +import org.cinchapi.concourse.time.Time; + +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +/** + * A {@link BlockingExecutorService} is a wrapper around a normal + * ExecutorService that allows caller threads to specify a batch of tasks to + * complete before proceeding. + *

+ * This service is designed to be used by multiple concurrent threads. While + * each call to the {@link #execute(Callable...) execute} methods will block the + * calling thread until all the specified tasks have completed, there are no + * guarantees made about the order in which tasks will execute. That is because + * all tasks execute asynchronously (just as they would in most other + * ExecutorServices), but this wrapper has logic to make the calling thread + * block until all of the tasks it cares about have executed. + *

+ *

+ * Since tasks execute asynchronously, threads do not interfere with one + * another. It is possible for thread A to submit tasks to the service before + * thread B but have thread B's tasks finish before thread A's. + *

+ *

+ * NOTE: All the threads used by this service are daemon + * threads, so they don't need to be stopped explicitly. The downside to this is + * that it is possible for the JVM to shutdown while this service is in the + * middle of executing task. This shouldn't be a problem in reality, since + * calling threads block while tasks are executing. + *

+ * + * @author jnelson + */ +@ThreadSafe +public class BlockingExecutorService { + + /** + * Return a new {@link BlockingExecutorService} that uses a system specified + * thread name prefix. + * + * @return the BlockingExecutorService + */ + public static BlockingExecutorService create() { + return create("blocking-executor-service-" + Time.now()); + } + + /** + * Return a new {@link BlockingExecutorService} that uses the specified + * {@code threadNamePrefix}. + * + * @param threadNamePrefix + * @return the BlockingExecutorService + */ + public static BlockingExecutorService create(String threadNamePrefix) { + return new BlockingExecutorService(threadNamePrefix); + } + + /** + * Spin (and therefore block the current thread) until all the tasks + * represented by the {@code futures} are done and the results are + * available. + * + * @param futures + */ + private static void waitForCompletion(Future... futures) { + boolean spin = true; + while (spin) { + spin = false; + for (Future future : futures) { + if(!future.isDone()) { + spin = true; + break; + } + } + } + } + + /** + * The underlying Executor that actually accomplishes task execution. + */ + private final ExecutorService executor; + + /** + * Construct a new instance. + * + * @param threadNamePrefix + */ + private BlockingExecutorService(String threadNamePrefix) { + this.executor = Executors + .newCachedThreadPool(new ThreadFactoryBuilder().setDaemon(true) + .setNameFormat(threadNamePrefix + "-%d").build()); + } + + /** + * Execute all the tasks in the {@code batch} and block until all of them + * complete. Afterwards, return the {@link Future} objects that represent + * the results of execution. The results are returned in the same order that + * the tasks are specified. + *

+ * Please note that this method will block the calling thread until all the + * tasks in the batch have completed, but it has no affect on other threads. + * Therefore, it is possible that another thread can submit a batch of tasks + * to the service and have them finish before the current thread's task do. + *

+ * + * @param batch + * @return the Future results of each task in the batch + */ + public List> execute(Callable... batch) { + Future[] futures = new Future[batch.length]; + for (int i = 0; i < futures.length; ++i) { + futures[i] = executor.submit(batch[i]); + } + waitForCompletion(futures); + return Lists.newArrayList(futures); + } + + /** + * Execute all the tasks in the {@code batch} and block until all of them + * complete. + *

+ * Please note that this method will block the calling thread until all the + * tasks in the batch have completed, but it has no affect on other threads. + * Therefore, it is possible that another thread can submit a batch of tasks + * to the service and have them finish before the current thread's task do. + *

+ * + * @param batch + * @return the Future results of each task in the batch + */ + public void execute(Runnable... batch) { + Future[] futures = new Future[batch.length]; + for (int i = 0; i < futures.length; ++i) { + futures[i] = executor.submit(batch[i]); + } + waitForCompletion(futures); + } +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/ConcourseExecutors.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/ConcourseExecutors.java index 052174fd3e..55819599f4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/ConcourseExecutors.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/concurrent/ConcourseExecutors.java @@ -24,14 +24,15 @@ package org.cinchapi.concourse.server.concurrent; import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; import org.cinchapi.concourse.annotate.UtilityClass; import org.cinchapi.concourse.util.Logger; +import com.google.common.collect.Maps; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** @@ -43,26 +44,25 @@ public final class ConcourseExecutors { /** - * Catches exceptions thrown from pooled threads. For the Database, - * exceptions will occur in the event that an attempt is made to write a - * duplicate non-offset write when the system shuts down in the middle of a - * buffer flush. Those exceptions can be ignored, so we catch them here and - * print log statements. + * Create an {@link ExecutorService} thread pool with enough threads to + * execute {@code commands} and block until all the tasks have completed. + * + * @param threadNamePrefix + * @param commands */ - private static final UncaughtExceptionHandler uncaughtExceptionHandler; - static { - uncaughtExceptionHandler = new UncaughtExceptionHandler() { - - @Override - public void uncaughtException(Thread t, Throwable e) { - Logger.warn("Uncaught exception in thread '{}'. This possibly " - + "indicates that the system shutdown prematurely " - + "during a buffer transport operation.", t); - Logger.warn("", e); - - } - - }; + public static void executeAndAwaitTermination(String threadNamePrefix, + Runnable... commands) { + BlockingExecutorService executor = executors.get(threadNamePrefix); + if(executor == null) { + executor = BlockingExecutorService.create(threadNamePrefix); + executors.put(threadNamePrefix, executor); + } + executor.execute(commands); + } + + public static ExecutorService newCachedThreadPool(String threadNamePrefix) { + return Executors + .newCachedThreadPool(getThreadFactory(threadNamePrefix)); } /** @@ -78,11 +78,6 @@ public static ExecutorService newThreadPool(int num, String threadNamePrefix) { getThreadFactory(threadNamePrefix)); } - public static ExecutorService newCachedThreadPool(String threadNamePrefix) { - return Executors - .newCachedThreadPool(getThreadFactory(threadNamePrefix)); - } - /** * Return a thread factory that will name all threads using * {@code threadNamePrefix}. @@ -97,37 +92,34 @@ private static ThreadFactory getThreadFactory(String threadNamePrefix) { } /** - * Create an {@link ExecutorService} thread pool with enough threads to - * execute {@code commands} and block until all the tasks have completed. - * - * @param threadNamePrefix - * @param commands + * A cache of ExecutorServices that are associated with a given + * threadNamePrefix. */ - public static void executeAndAwaitTermination(String threadNamePrefix, - Runnable... commands) { - executeAndAwaitTermination( - newThreadPool(commands.length, threadNamePrefix), commands); - } + private static final Map executors = Maps + .newHashMap(); /** - * Execute {@code commands} using {@code executor} and block until all - * tasks have completed. - * - * @param executor - * @param commands + * Catches exceptions thrown from pooled threads. For the Database, + * exceptions will occur in the event that an attempt is made to write a + * duplicate non-offset write when the system shuts down in the middle of a + * buffer flush. Those exceptions can be ignored, so we catch them here and + * print log statements. */ - public static void executeAndAwaitTermination(ExecutorService executor, - Runnable... commands) { - for (Runnable command : commands) { - executor.execute(command); - } - executor.shutdown(); - try { - executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); // effectively - // wait - // forever... - } - catch (InterruptedException e) {/* noop */} + private static final UncaughtExceptionHandler uncaughtExceptionHandler; + + static { + uncaughtExceptionHandler = new UncaughtExceptionHandler() { + + @Override + public void uncaughtException(Thread t, Throwable e) { + Logger.warn("Uncaught exception in thread '{}'. This possibly " + + "indicates that the system shutdown prematurely " + + "during a buffer transport operation.", t); + Logger.warn("", e); + + } + + }; } private ConcourseExecutors() {/* utility-class */} diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorServiceTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorServiceTest.java new file mode 100644 index 0000000000..6a16a4b810 --- /dev/null +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/concurrent/BlockingExecutorServiceTest.java @@ -0,0 +1,183 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.concurrent; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.cinchapi.concourse.ConcourseBaseTest; +import org.cinchapi.concourse.time.Time; +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link BlockingExecutorService}. + * + * @author jnelson + */ +public class BlockingExecutorServiceTest extends ConcourseBaseTest { + + private BlockingExecutorService service = null; + + @Override + public void beforeEachTest() { + service = BlockingExecutorService.create(); + } + + @Test + public void testServiceDoesntPreventJVMShutdown() { + service.execute(new Runnable() { + + @Override + public void run() { + for (int i = 0; i < 100; i++) {} + + } + + }); + } + + @Test + public void testCallingThreadIsBlockedUntilTaskCompletes() { + long ts = Time.now(); + service.execute(new Runnable() { + + @Override + public void run() { + Threads.sleep(100); + + } + + }); + long elapsed = Time.now() - ts; + Assert.assertTrue(TimeUnit.MILLISECONDS.convert(elapsed, + TimeUnit.MILLISECONDS) >= 100); + } + + @Test + public void testCallingThreadIsBlockedUntilAllTaskCompletes() { + long ts = Time.now(); + service.execute(new Runnable() { + + @Override + public void run() { + Threads.sleep(100); + + } + + }, new Runnable() { + + @Override + public void run() { + Threads.sleep(50); + + } + + }, new Runnable() { + + @Override + public void run() { + Threads.sleep(150); + + } + + }); + long elapsed = Time.now() - ts; + Assert.assertTrue(TimeUnit.MILLISECONDS.convert(elapsed, + TimeUnit.MICROSECONDS) >= 150); + Assert.assertTrue(TimeUnit.MILLISECONDS.convert(elapsed, + TimeUnit.MICROSECONDS) <= 300); + } + + @Test + public void testThreadIsNotAffectedByBlockingInAnotherThread() { + final AtomicBoolean aDone = new AtomicBoolean(false); + final AtomicBoolean aSelfDone = new AtomicBoolean(false); + final AtomicBoolean bDone = new AtomicBoolean(false); + Thread a = new Thread(new Runnable() { + + @Override + public void run() { + service.execute(new Runnable() { + + @Override + public void run() { + while (!aDone.get()) { + continue; + } + aSelfDone.set(true); + + } + + }); + + } + + }); + + Thread b = new Thread(new Runnable() { + + @Override + public void run() { + service.execute(new Runnable() { + + @Override + public void run() { + Threads.sleep(100); + + } + + }, new Runnable() { + + @Override + public void run() { + Threads.sleep(50); + + } + + }, new Runnable() { + + @Override + public void run() { + Threads.sleep(150); + + } + + }); + bDone.set(true); + + } + + }); + a.start(); + b.start(); + Threads.sleep(300); // b should take no longer than this + Assert.assertTrue(bDone.get()); + Assert.assertFalse(aSelfDone.get()); + aDone.set(true); + Threads.sleep(10); + Assert.assertTrue(aSelfDone.get()); + } + +} From 02c5eb2a351f8468d289f98f5d62c122fad14900 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 7 Nov 2014 13:11:12 -0500 Subject: [PATCH 068/100] cache byteable object constructors and static factories --- .../concourse/server/io/Byteables.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteables.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteables.java index 49ab4ee833..d4e61a6980 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteables.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Byteables.java @@ -29,10 +29,12 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; +import java.util.Map; import org.cinchapi.concourse.util.ByteBuffers; import com.google.common.base.Throwables; +import com.google.common.collect.Maps; /** * Contains static factory methods to construct {@link Byteable} objects from @@ -68,13 +70,17 @@ public abstract class Byteables { * @param classObj * @return an instance of {@code classObj} read from {@code bytes} */ + @SuppressWarnings("unchecked") public static T read(ByteBuffer bytes, Class classObj) { try { - Constructor constructor = classObj - .getDeclaredConstructor(ByteBuffer.class); - constructor.setAccessible(true); + Constructor constructor = (Constructor) constructorCache + .get(classObj); + if(constructor == null) { + constructor = classObj.getDeclaredConstructor(ByteBuffer.class); + constructor.setAccessible(true); + constructorCache.put(classObj, constructor); + } return constructor.newInstance(bytes); - } catch (ReflectiveOperationException e) { throw Throwables.propagate(e); @@ -147,8 +153,11 @@ public static T read(ByteBuffer bytes, final String className) { @SuppressWarnings("unchecked") public static T readStatic(ByteBuffer bytes, Class classObj) { try { - Method method = classObj.getMethod("fromByteBuffer", - ByteBuffer.class); + Method method = staticFactoryCache.get(classObj); + if(method == null) { + method = classObj.getMethod("fromByteBuffer", ByteBuffer.class); + staticFactoryCache.put(classObj, method); + } return (T) method.invoke(null, bytes); } catch (ReflectiveOperationException e) { @@ -213,4 +222,16 @@ public static void write(Byteable object, FileChannel channel) { } + /** + * Cache of constructors that are captured using reflection. + */ + private static final Map, Constructor> constructorCache = Maps + .newIdentityHashMap(); + + /** + * Cache of static factory methods that are captured using reflection. + */ + private static final Map, Method> staticFactoryCache = Maps + .newIdentityHashMap(); + } From b3c8e2ed9de49207c4f4ef9d9bd7daa66cc1b4cd Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 7 Nov 2014 21:06:04 -0500 Subject: [PATCH 069/100] cache CapacityException --- .../cinchapi/concourse/server/storage/temp/Buffer.java | 4 ++-- .../concourse/server/storage/temp/CapacityException.java | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 0739e4fe17..846e410dbf 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -980,7 +980,7 @@ public void append(Write write) throws CapacityException { content.force(); } else { - throw new CapacityException(); + throw CapacityException.INSTANCE; } } finally { @@ -1334,7 +1334,7 @@ private void index(Write write) throws CapacityException { ++size; } else { - throw new CapacityException(); + throw CapacityException.INSTANCE; } } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/CapacityException.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/CapacityException.java index 5a4c5a9acd..fcb279ac9c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/CapacityException.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/CapacityException.java @@ -35,6 +35,14 @@ @PackagePrivate class CapacityException extends RuntimeException { + /** + * A cached instance of the exception that can be used to avoid the overhead + * that is traditionally associated with creating new exceptions. This is + * okay to do since this exception is used as a state signaler and does not + * really imply any sort of error case. + */ + static final CapacityException INSTANCE = new CapacityException(); + private static final long serialVersionUID = 1L; } From 85da7b41e2da8cae35c5a0932b0fbc41091d788b Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 8 Nov 2014 11:11:33 -0500 Subject: [PATCH 070/100] better buffer page bloom filter sizing --- .../cinchapi/concourse/server/storage/temp/Buffer.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 846e410dbf..4fefcbb3ab 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -267,6 +267,13 @@ public void remove() { */ private static int MAX_TRANSPORT_RATE = 8192; + /** + * The number of slots to put in each Page's bloom filter. We want this + * small enough to have few hash functions, but large enough so that the + * bloom filter does not become saturated. + */ + private static int PER_PAGE_BLOOM_FILTER_CAPACITY = GlobalState.BUFFER_PAGE_SIZE / 10; + /** * Construct a Buffer that is backed by the default location, which is * {@link GlobalState#BUFFER_DIRECTORY}. @@ -880,7 +887,7 @@ private class Page implements Iterator, Iterable { * or not. */ private final BloomFilter filter = BloomFilter - .create(GlobalState.BUFFER_PAGE_SIZE); + .create(PER_PAGE_BLOOM_FILTER_CAPACITY); /** * The append-only buffer that contains the content of the backing file. From 6c58a2be51c805108ee1c676e9ccafd5c51ada84 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 8 Nov 2014 15:09:40 -0500 Subject: [PATCH 071/100] support for caching composites in hopes of improving write performance --- .../concourse/server/io/Composite.java | 44 ++++++- .../server/storage/cache/BloomFilter.java | 123 +++++++++++------- .../concourse/server/storage/temp/Buffer.java | 6 +- 3 files changed, 116 insertions(+), 57 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index be911a59dd..bd661f81bf 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -23,7 +23,11 @@ */ package org.cinchapi.concourse.server.io; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; + import java.nio.ByteBuffer; +import java.util.Arrays; import javax.annotation.concurrent.Immutable; @@ -42,15 +46,35 @@ public final class Composite implements Byteable { /** - * Return a Token for the list of {@code byteables}. + * Return a Composite for the list of {@code byteables}. * * @param byteables - * @return the Token + * @return the Composite */ public static Composite create(Byteable... byteables) { return new Composite(byteables); } + /** + * Create a Composite for the list of {@code byteables} with support for + * caching. Cached Composites are not guaranteed to perfectly match up with + * the list of byteables (because hash collisions can occur) so it is only + * advisable to use this method of creation when precision is not a + * requirement. + * + * @param byteables + * @return the Composite + */ + public static Composite createCached(Byteable... byteables) { + int hashCode = Arrays.hashCode(byteables); + Composite composite = CACHE.get(hashCode); + if(composite == null) { + composite = create(byteables); + CACHE.put(hashCode, composite); + } + return composite; + } + /** * Return the Token encoded in {@code bytes} so long as those * bytes adhere to the format specified by the {@link #getBytes()} method. @@ -66,6 +90,12 @@ public static Composite fromByteBuffer(ByteBuffer bytes) { return Byteables.read(bytes, Composite.class); } + /** + * A cache of Composite. Each composite is associated with the cumulative + * hashcode of all the things that went into the composite. + */ + private final static TIntObjectMap CACHE = new TIntObjectHashMap(); + private final ByteBuffer bytes; /** @@ -99,6 +129,11 @@ private Composite(Byteable... byteables) { bytes.rewind(); } + @Override + public void copyTo(ByteBuffer buffer) { + ByteBuffers.copyAndRewindSource(bytes, buffer); + } + @Override public boolean equals(Object obj) { if(obj instanceof Composite) { @@ -129,9 +164,4 @@ public String toString() { .toString(); } - @Override - public void copyTo(ByteBuffer buffer) { - ByteBuffers.copyAndRewindSource(bytes, buffer); - } - } \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index 2e526d659a..c224789db5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -57,7 +57,7 @@ public class BloomFilter implements Syncable { /** * Create a new BloomFilter with enough capacity for - * {@code expectedInsertions}. + * {@code expectedInsertions} but cannot be synced to disk. *

* Note that overflowing a BloomFilter with significantly more elements than * specified, will result in its saturation, and a sharp deterioration of @@ -65,17 +65,16 @@ public class BloomFilter implements Syncable { * {@link BloomFilter#create(com.google.common.hash.Funnel, int)}) *

* - * @param file * @param expectedInsertions * @return the BloomFilter */ - public static BloomFilter create(String file, int expectedInsertions) { - return new BloomFilter(file, expectedInsertions); + public static BloomFilter create(int expectedInsertions) { + return new BloomFilter(null, expectedInsertions); } /** * Create a new BloomFilter with enough capacity for - * {@code expectedInsertions} but cannot be synced to disk. + * {@code expectedInsertions}. *

* Note that overflowing a BloomFilter with significantly more elements than * specified, will result in its saturation, and a sharp deterioration of @@ -83,11 +82,12 @@ public static BloomFilter create(String file, int expectedInsertions) { * {@link BloomFilter#create(com.google.common.hash.Funnel, int)}) *

* + * @param file * @param expectedInsertions * @return the BloomFilter */ - public static BloomFilter create(int expectedInsertions) { - return new BloomFilter(null, expectedInsertions); + public static BloomFilter create(String file, int expectedInsertions) { + return new BloomFilter(file, expectedInsertions); } /** @@ -136,6 +136,11 @@ protected ObjectStreamClass readClassDescriptor() } } + /** + * The file where the content is stored. + */ + private String file; + /** * Lock used to ensure the object is ThreadSafe. This lock provides access * to a masterLock.readLock()() and masterLock.writeLock()(). @@ -148,9 +153,16 @@ protected ObjectStreamClass readClassDescriptor() private final com.google.common.hash.BloomFilter source; /** - * The file where the content is stored. + * Construct a new instance. + * + * @param file + * @param source */ - private String file; + private BloomFilter(String file, + com.google.common.hash.BloomFilter source) { + this.source = source; + this.file = file; + } /** * Construct a new instance. @@ -166,54 +178,44 @@ private BloomFilter(String file, int expectedInsertions) { } /** - * Construct a new instance. + * Return true if an element made up of {@code byteables} might have been + * put in this filter or false if this is definitely not the case. * - * @param file - * @param source + * @param byteables + * @return {@code true} if {@code byteables} might exist */ - private BloomFilter(String file, - com.google.common.hash.BloomFilter source) { - this.source = source; - this.file = file; - } - - @Override - // NOTE: It seems counter intuitive, but we take a read lock in this - // method instead of a write lock so that readers can concurrently use - // the bloom filter in memory while the content is being written to - // disk. - public void sync() { - Preconditions.checkState(file != null, "Cannot sync a " - + "BloomFilter that does not have an associated file"); - FileChannel channel = FileSystem.getFileChannel(file); - long stamp = lock.tryOptimisticRead(); - Serializables.write(source, channel); // CON-164 - if(!lock.validate(stamp)) { - stamp = lock.readLock(); - try { - channel.position(0); - Serializables.write(source, channel); // CON-164 - } - catch (IOException e) { - throw Throwables.propagate(e); - } - finally { - lock.unlockRead(stamp); - } - } - FileSystem.closeFileChannel(channel); + public boolean mightContain(Byteable... byteables) { + Composite composite = Composite.create(byteables); + return mightContain(composite); } /** - * Return true if an element made up of {@code byteables} might have been - * put in this filter or false if this is definitely not the case. + * Return true if an element made up of a cached copy of the + * {@code byteables} might have been put in this filter or false if this is + * definitely not the case. + *

+ * Since caching is involved, this method is more prone to false positives + * than the {@link #mightContain(Byteable...)} alternative, but it will + * never return false negatives. + *

* * @param byteables * @return {@code true} if {@code byteables} might exist */ - public boolean mightContain(Byteable... byteables) { + public boolean mightContainCached(Byteable... byteables) { + Composite composite = Composite.createCached(byteables); + return mightContain(composite); + } + + /** + * Check the backing bloom filter to see if the composite might have been + * added. + * + * @param composite + * @return {@code true} if the composite might exist + */ + private boolean mightContain(Composite composite) { long stamp = lock.tryOptimisticRead(); - Composite composite = Composite.create(byteables); boolean mightContain = source.mightContain(composite); if(!lock.validate(stamp)) { stamp = lock.readLock(); @@ -253,4 +255,31 @@ public boolean put(Byteable... byteables) { lock.unlockWrite(stamp); } } + + @Override + // NOTE: It seems counter intuitive, but we take a read lock in this + // method instead of a write lock so that readers can concurrently use + // the bloom filter in memory while the content is being written to + // disk. + public void sync() { + Preconditions.checkState(file != null, "Cannot sync a " + + "BloomFilter that does not have an associated file"); + FileChannel channel = FileSystem.getFileChannel(file); + long stamp = lock.tryOptimisticRead(); + Serializables.write(source, channel); // CON-164 + if(!lock.validate(stamp)) { + stamp = lock.readLock(); + try { + channel.position(0); + Serializables.write(source, channel); // CON-164 + } + catch (IOException e) { + throw Throwables.propagate(e); + } + finally { + lock.unlockRead(stamp); + } + } + FileSystem.closeFileChannel(channel); + } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 4fefcbb3ab..2196341433 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -1158,17 +1158,17 @@ public boolean mightContain(Write write) { Locks.lockIfCondition(pageLock.readLock(), this == currentPage); try { Type valueType = write.getValue().getType(); - if(filter.mightContain(write.getRecord(), write.getKey(), + if(filter.mightContainCached(write.getRecord(), write.getKey(), write.getValue())) { return true; } else if(valueType == Type.STRING) { - return filter.mightContain(write.getRecord(), write + return filter.mightContainCached(write.getRecord(), write .getKey(), Value.wrap(Convert.javaToThrift(Tag .create((String) write.getValue().getObject())))); } else if(valueType == Type.TAG) { - return filter.mightContain( + return filter.mightContainCached( write.getRecord(), write.getKey(), Value.wrap(Convert.javaToThrift(write.getValue() From e0e480d3cbb12480cadef605da74a147e2639381 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 8 Nov 2014 18:50:57 -0500 Subject: [PATCH 072/100] improve documentation for concourse.prefs --- concourse-server/conf/concourse.prefs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/concourse-server/conf/concourse.prefs b/concourse-server/conf/concourse.prefs index 9902a237c1..c1aa6185b4 100644 --- a/concourse-server/conf/concourse.prefs +++ b/concourse-server/conf/concourse.prefs @@ -9,10 +9,9 @@ # DEFAULT: {$user.home}/concourse/buffer #buffer_directory = /var/lib/concourse/buffer -# The size for each page in the Buffer. During reads, Buffer pages are -# individually locked, so it is desirable to have several smaller pages as -# opposed to few larger ones. Nevertheless, be sure to balance the desire to -# maximize lock granularity with the risks of having too many open buffer files +# The size for each page in the Buffer. It is generally desirable to have +# several smaller pages in the Buffer to maximize read and indexing throughput +# but this should be baanced with the risks of having too many open buffer files # which may lead to more frequent and less efficient disk I/O. # # DEFAULT: 8KB @@ -33,7 +32,7 @@ # The default environment that is automatically loaded when the server # starts and is used whenever a client does not specify an environment -# for a connection. +# when connecting. #default_environment = default # Determine whether log messages should also be printed to the console (STDOUT) @@ -49,6 +48,17 @@ # the initial and maximum heap sizes to the specified value, so there # must be enough system memory available for Concourse Server to start. # +# Be careful and avoid setting the heap size too large because this may +# cause longer garbage collection (gc) pauses or interfere with the ability +# of Concourse Server to memory map (mmap) certain data files. We +# recommend the following sizing guidelines: +# +# SYSTEM MEMORY | Recommended heap_size +# ----------------------------------------------------------- +# Less than 2GB | 1/2 system memory +# 2GB to 4GB | 1GB +# Greater than 4GB | 1/4 system memory, but not more than 8GB +# # DEFAULT: 1GB #heap_size = 1GB From 8586b6c8ee2b4039b92b81fc17ab99702dae1b73 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 12 Nov 2014 06:50:08 -0500 Subject: [PATCH 073/100] fix bug that made it possible for erroneous results to happen when using the BloomFilter#mightContainCached method --- .../server/storage/cache/BloomFilter.java | 92 +++++++++++++------ .../concourse/server/storage/temp/Buffer.java | 2 +- 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index c224789db5..cf99ec3dd4 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -196,7 +196,8 @@ public boolean mightContain(Byteable... byteables) { *

* Since caching is involved, this method is more prone to false positives * than the {@link #mightContain(Byteable...)} alternative, but it will - * never return false negatives. + * never return false negatives as long as the bits were added with the + * {@code #putCached(Byteable...)} method. *

* * @param byteables @@ -208,34 +209,34 @@ public boolean mightContainCached(Byteable... byteables) { } /** - * Check the backing bloom filter to see if the composite might have been - * added. + *

+ * Copied from {@link BloomFilter#put(Object)}. + *

+ * Puts {@link byteables} into this BloomFilter as a single element. + * Ensures that subsequent invocations of {@link #mightContain(Byteable...)} + * with the same elements will always return true. * - * @param composite - * @return {@code true} if the composite might exist + * @param byteables + * @return {@code true} if the filter's bits changed as a result of this + * operation. If the bits changed, this is definitely the first time + * {@code byteables} have been added to the filter. If the bits + * haven't changed, this might be the first time they have been + * added. Note that put(t) always returns the opposite result to + * what mightContain(t) would have returned at the time it is + * called. */ - private boolean mightContain(Composite composite) { - long stamp = lock.tryOptimisticRead(); - boolean mightContain = source.mightContain(composite); - if(!lock.validate(stamp)) { - stamp = lock.readLock(); - try { - mightContain = source.mightContain(composite); - } - finally { - lock.unlockRead(stamp); - } - } - return mightContain; + public boolean put(Byteable... byteables) { + return put(Composite.create(byteables)); } /** *

* Copied from {@link BloomFilter#put(Object)}. *

- * Puts {@link byteables} into this BloomFilter as a single element. - * Ensures that subsequent invocations of {@link #mightContain(Byteable...)} - * with the same elements will always return true. + * Puts {@link byteables} into this BloomFilter as a single element with + * support for caching Ensures that subsequent invocations of + * {@link #mightContainCached(Byteable...)} with the same elements will + * always return true. * * @param byteables * @return {@code true} if the filter's bits changed as a result of this @@ -246,14 +247,8 @@ private boolean mightContain(Composite composite) { * what mightContain(t) would have returned at the time it is * called. */ - public boolean put(Byteable... byteables) { - long stamp = lock.writeLock(); - try { - return source.put(Composite.create(byteables)); - } - finally { - lock.unlockWrite(stamp); - } + public boolean putCached(Byteable... byteables) { + return put(Composite.createCached(byteables)); } @Override @@ -282,4 +277,43 @@ public void sync() { } FileSystem.closeFileChannel(channel); } + + /** + * Check the backing bloom filter to see if the composite might have been + * added. + * + * @param composite + * @return {@code true} if the composite might exist + */ + private boolean mightContain(Composite composite) { + long stamp = lock.tryOptimisticRead(); + boolean mightContain = source.mightContain(composite); + if(!lock.validate(stamp)) { + stamp = lock.readLock(); + try { + mightContain = source.mightContain(composite); + } + finally { + lock.unlockRead(stamp); + } + } + return mightContain; + } + + /** + * Place the {@code composite} in the backing bloom filter. + * + * @param composite + * @return {@code true} if the bits have changed as a result of the addition + * of the {@code composite} + */ + private boolean put(Composite composite) { + long stamp = lock.writeLock(); + try { + return source.put(composite); + } + finally { + lock.unlockWrite(stamp); + } + } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java index 2196341433..967dc0f815 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Buffer.java @@ -1336,7 +1336,7 @@ private void index(Write write) throws CapacityException { // The individual Write components are added instead of the // entire Write so that version information is not factored into // the bloom filter hashing - filter.put(write.getRecord(), write.getKey(), write.getValue()); + filter.putCached(write.getRecord(), write.getKey(), write.getValue()); writes[size] = write; ++size; } From 656313830c77a4c7ca47b095c8bbfb4c501a71fa Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 12 Nov 2014 11:14:58 -0500 Subject: [PATCH 074/100] use built in hashmap for composite cache --- .../java/org/cinchapi/concourse/server/io/Composite.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java index bd661f81bf..602085769a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/Composite.java @@ -23,17 +23,16 @@ */ package org.cinchapi.concourse.server.io; -import gnu.trove.map.TIntObjectMap; -import gnu.trove.map.hash.TIntObjectHashMap; - import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Map; import javax.annotation.concurrent.Immutable; import org.cinchapi.concourse.annotate.DoNotInvoke; import org.cinchapi.concourse.util.ByteBuffers; +import com.google.common.collect.Maps; import com.google.common.hash.Hashing; /** @@ -94,7 +93,7 @@ public static Composite fromByteBuffer(ByteBuffer bytes) { * A cache of Composite. Each composite is associated with the cumulative * hashcode of all the things that went into the composite. */ - private final static TIntObjectMap CACHE = new TIntObjectHashMap(); + private final static Map CACHE = Maps.newHashMap(); private final ByteBuffer bytes; From 989a4b02282e1078be8ce38e39b910ea39f208fd Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 12 Nov 2014 16:31:08 -0500 Subject: [PATCH 075/100] Use soft reference to keep revisions in recently synced blocks in memory until there is memory pressure --- .../concourse/server/storage/db/Block.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java index b11545e3a9..7c6b87f90a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Block.java @@ -25,6 +25,7 @@ import java.io.File; import java.io.IOException; +import java.lang.ref.SoftReference; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; @@ -227,6 +228,14 @@ public static String getId(String filename) { @Nullable private TreeMultiset> revisions; + /** + * A soft reference to the {@link #revisions} that may stay in + * memory after the Block has been synced. The GC is encouraged to clear + * this reference in response to memory pressure at which point disk seeks + * will be performed in the {@link #seek(Record, Byteable...)} method. + */ + private final SoftReference>> softRevisions; + /** * The running size of the Block. This number only refers to the size of the * Revisions that are stored in the block file. The size for the filter and @@ -265,6 +274,8 @@ protected Block(String id, String directory, boolean diskLoad) { this.index = BlockIndex.create(directory + File.separator + id + INDEX_NAME_EXTENSION, EXPECTED_INSERTIONS); } + this.softRevisions = new SoftReference>>( + revisions); } @Override @@ -577,7 +588,8 @@ private void seek(Record record, Byteable... byteables) { Locks.lockIfCondition(read, mutable); try { if(filter.mightContain(byteables)) { - if(mutable) { + TreeMultiset> revisions = softRevisions.get(); + if(revisions != null) { Iterator> it = revisions.iterator(); boolean processing = false; // Since the revisions are // sorted, I can toggle this From 48eafa79f8c96b08c281037db78928fbf65f774f Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 12 Nov 2014 21:46:04 -0500 Subject: [PATCH 076/100] random changes to try and make find faster --- .../server/storage/db/SecondaryRecord.java | 30 +++++---- .../concourse/util/MultimapViews.java | 64 +++++++++++++++++++ .../org/cinchapi/concourse/util/TMaps.java | 58 +++++++++++++++++ .../cinchapi/concourse/thrift/TObject.java | 3 +- .../cinchapi/concourse/util/Transformers.java | 10 +-- 5 files changed, 146 insertions(+), 19 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/util/TMaps.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java index 700f17385f..63bd94b0ac 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java @@ -38,12 +38,11 @@ import org.cinchapi.concourse.server.model.Text; import org.cinchapi.concourse.server.model.Value; import org.cinchapi.concourse.thrift.Operator; +import org.cinchapi.concourse.util.MultimapViews; +import org.cinchapi.concourse.util.TMaps; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; -import com.google.common.collect.Multimaps; -import com.google.common.collect.SetMultimap; -import com.google.common.collect.TreeMultimap; /** * A grouping of data for efficient indirect queries. @@ -153,12 +152,15 @@ private Map> explore(boolean historical, long timestamp, Operator operator, Value... values) { /* Authorized */ read.lock(); try { - SetMultimap data = TreeMultimap.create(); + Map> data = Maps.newHashMap(); // sorting at + // the end is + // generally + // faster Value value = values[0]; if(operator == Operator.EQUALS) { for (PrimaryKey record : historical ? get(value, timestamp) : get(value)) { - data.put(record, value); + MultimapViews.putInSet(data, record, value); } } else if(operator == Operator.NOT_EQUALS) { @@ -167,7 +169,7 @@ else if(operator == Operator.NOT_EQUALS) { if(!value.equals(stored)) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -179,7 +181,7 @@ else if(operator == Operator.GREATER_THAN) { if(!historical || stored.compareTo(value) > 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -191,7 +193,7 @@ else if(operator == Operator.GREATER_THAN_OR_EQUALS) { if(!historical || stored.compareTo(value) >= 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -203,7 +205,7 @@ else if(operator == Operator.LESS_THAN) { if(!historical || stored.compareTo(value) < 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -215,7 +217,7 @@ else if(operator == Operator.LESS_THAN_OR_EQUALS) { if(!historical || stored.compareTo(value) <= 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -231,7 +233,7 @@ else if(operator == Operator.BETWEEN) { .compareTo(value2) < 0)) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -244,7 +246,7 @@ else if(operator == Operator.REGEX) { if(m.matches()) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -257,7 +259,7 @@ else if(operator == Operator.NOT_REGEX) { if(!m.matches()) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - data.put(record, stored); + MultimapViews.putInSet(data, record, stored); } } } @@ -265,7 +267,7 @@ else if(operator == Operator.NOT_REGEX) { else { throw new UnsupportedOperationException(); } - return Multimaps.asMap(data); + return TMaps.asSortedMap(data); } finally { read.unlock(); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java new file mode 100644 index 0000000000..f860e14a14 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java @@ -0,0 +1,64 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Sets; + +/** + * A collection of utility methods that abstracts adding and removing elements + * to a map of keys to a collection of values (e.g. checks will be performed to + * see if a key has an associated set before adding and empty sets will be + * cleared on value removal, etc). This class is meant to be used + * over Guava multimaps for performance gains. + * + * @author jnelson + */ +public final class MultimapViews { + + /** + * Associates the {@code value} with the {@code key} in the {@code map} if + * it does not already exist. + * + * @param map + * @param key + * @param value + * @return {@code true} if the new associated is added, {@code false} + * otherwise + */ + public static , V> boolean putInSet( + Map> map, K key, V value) { + Set set = map.get(key); + if(set == null) { + set = Sets.newLinkedHashSet(); + map.put(key, set); + } + return set.add(value); + } + + private MultimapViews() {/* noop */} + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TMaps.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TMaps.java new file mode 100644 index 0000000000..ad019df030 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TMaps.java @@ -0,0 +1,58 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Map based utility methods that are not found in the Guava + * {@link com.google.collect.Maps} utility class. + * + * @author jnelson + */ +public final class TMaps { + + /** + * Return a map that has all the data in {@code map} in naturally sorted + * order. + * + * @param map + * @return the sorted map + */ + public static , V> SortedMap asSortedMap( + Map map) { + if(map instanceof SortedMap) { + return (SortedMap) map; + } + else { + return new TreeMap(map); + } + + } + + private TMaps() {/* noop */} + +} diff --git a/concourse/src/main/java/org/cinchapi/concourse/thrift/TObject.java b/concourse/src/main/java/org/cinchapi/concourse/thrift/TObject.java index 97f74c662e..976d51dc35 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/thrift/TObject.java +++ b/concourse/src/main/java/org/cinchapi/concourse/thrift/TObject.java @@ -197,7 +197,8 @@ public Type getType() { @Override public int hashCode() { - return Objects.hash(data, getInternalType()); + return Arrays.hashCode(new int[] { data.hashCode(), + getInternalType().ordinal() }); } /** diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java b/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java index 9b147c84bc..14f123bda6 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java +++ b/concourse/src/main/java/org/cinchapi/concourse/util/Transformers.java @@ -127,13 +127,14 @@ public static Map> transformMapSet( */ public static Set transformSet(Set original, Function function) { - Set transformed = Sets.newLinkedHashSet(); + Set transformed = Sets.newLinkedHashSetWithExpectedSize(original + .size()); for (F item : original) { transformed.add(function.apply(item)); } return transformed; } - + /** * Transform the keys in {@code original} with the {@code keys} function * and each of the values with the {@code values} function and return the @@ -151,8 +152,9 @@ public static Set transformSet(Set original, * @return the transformed TreeMap */ public static Map> transformTreeMapSet( - Map> original, Function keys, - Function values, final Comparator sorter) { + Map> original, Function keys, + Function values, + final Comparator sorter) { Map> transformed = Maps.newTreeMap(sorter); for (Map.Entry> entry : original.entrySet()) { transformed.put(keys.apply(entry.getKey()), From 045c7f14bf9e95e6339ec5e905ceef565c9b958c Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 14 Nov 2014 08:32:59 -0500 Subject: [PATCH 077/100] Add compact revision representation to try to reduce memory bloat --- .../server/storage/db/CompactRevision.java | 139 ++++++++++++++++++ .../server/storage/db/PrimaryRecord.java | 11 +- .../concourse/server/storage/db/Record.java | 20 +-- .../concourse/server/storage/db/Revision.java | 11 ++ 4 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/CompactRevision.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/CompactRevision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/CompactRevision.java new file mode 100644 index 0000000000..d22532422d --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/CompactRevision.java @@ -0,0 +1,139 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.storage.db; + +import javax.annotation.concurrent.Immutable; + +import org.cinchapi.concourse.server.storage.Action; +import org.cinchapi.concourse.server.storage.Versioned; + +/** + * A compact form of a {@link Revision} that is appropriate to store within a + * {@link Record records} collection of history in order to save space. + *

+ * Compared to a normal Revision, this is more memory efficient because it only + * contains the value, version and type involved with the revision. This is okay + * to do because it is assumed that the housing Record has information about the + * locator and key. + *

+ * + * @author jnelson + */ +@Immutable +class CompactRevision> implements Versioned { + + /** + * A field indicating the action performed to generate this Revision. This + * information is recorded so that we can efficiently purge history while + * maintaining consistent state. + */ + private final Action type; + + /** + * The tertiary component that typically represents the payload for what + * this Revision represents. + */ + private final V value; + + /** + * The unique version that identifies this Revision. Versions are assumed to + * be an atomically increasing values (i.e. timestamps). + */ + private final long version; + + /** + * Construct a new instance. + * + * @param value + * @param timestamp + * @param type + */ + CompactRevision(V value, long version, Action type) { /* package-private */ + this.value = value; + this.version = version; + this.type = type; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if(obj instanceof CompactRevision) { + return value.equals(((CompactRevision) obj).value); + } + else { + return false; + } + } + + /** + * Return the {@link #type} associated with this Revision. + * + * @return the type + */ + public Action getType() { + return type; + } + + /** + * Return the {@link #value} associated with this Revision. + * + * @return the value + */ + public V getValue() { + return value; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean isStorable() { + return false; + } + + @Override + public String toString() { + return type + " " + value + " AT " + version; + } + + /** + * Return a string description that is compatible with a full + * {@link Revision} based on the {@code locator} and {@code key}. + * + * @return toString output + */ + public , K extends Comparable> String toString( + L locator, K key) { + return type + " " + key + " AS " + value + " IN " + locator + " AT " + + version; + } + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/PrimaryRecord.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/PrimaryRecord.java index a0c1666b6e..1b6dbc8301 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/PrimaryRecord.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/PrimaryRecord.java @@ -96,14 +96,13 @@ public Map audit(Text key) { read.lock(); try { Map audit = Maps.newLinkedHashMap(); - List> revisions = history - .get(key); /* Authorized */ + List> revisions = history.get(key); /* Authorized */ if(revisions != null) { - Iterator> it = revisions - .iterator(); + Iterator> it = revisions.iterator(); while (it.hasNext()) { - Revision revision = it.next(); - audit.put(revision.getVersion(), revision.toString()); + CompactRevision revision = it.next(); + audit.put(revision.getVersion(), + revision.toString(locator, key)); } } return audit; diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java index 707fd15a2f..aaacec388c 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Record.java @@ -163,7 +163,7 @@ public static SecondaryRecord createSecondaryRecordPartial(Text key, * number of times that the value appears beforehand at determine * if the mapping existed or not. */ - protected final transient HashMap>> history = Maps + protected final transient HashMap>> history = Maps .newHashMap(); /** @@ -174,7 +174,7 @@ public static SecondaryRecord createSecondaryRecordPartial(Text key, /** * The locator used to identify this Record. */ - private final L locator; + protected final L locator; /** * The key used to identify this Record. This value is {@code null} unless @@ -273,16 +273,19 @@ public void append(Revision revision) { } // Update history index - List> revisions = history.get(revision.getKey()); + List> revisions = history.get(revision.getKey()); if(revisions == null) { revisions = Lists.newArrayList(); history.put(revision.getKey(), revisions); } - revisions.add(revision); + revisions.add(revision.compact()); // Update metadata version = Math.max(version, revision.getVersion()); + // Make revision eligible for GC + revision = null; + } finally { write.unlock(); @@ -381,8 +384,7 @@ protected Set get(K key) { read.lock(); try { Set values = present.get(key); - return values != null ? Collections.unmodifiableSet(values) - : emptyValues; + return values != null ? values : emptyValues; } finally { read.unlock(); @@ -401,12 +403,12 @@ protected Set get(K key, long timestamp) { read.lock(); try { Set values = emptyValues; - List> stored = history.get(key); + List> stored = history.get(key); if(stored != null) { values = Sets.newLinkedHashSet(); - Iterator> it = stored.iterator(); + Iterator> it = stored.iterator(); while (it.hasNext()) { - Revision revision = it.next(); + CompactRevision revision = it.next(); if(revision.getVersion() <= timestamp) { if(revision.getType() == Action.ADD) { values.add(revision.getValue()); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java index dba9c3265e..24cdbf5ef0 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/Revision.java @@ -206,6 +206,17 @@ protected Revision(L locator, K key, V value, long version, Action type) { + key.size() + value.size(); } + /** + * Return a {@link CompactRevision} that is appropriate to store in a + * {@link Record records} history collection. After calling this method, it + * is okay to set this instance equal to {@code null}. + * + * @return the compact form of this Revision. + */ + public CompactRevision compact() { + return new CompactRevision(value, version, type); + } + /** * {@inheritDoc} *

From 7fd976b44a8f5c94d6316f3039a3303744691a14 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 14 Nov 2014 08:35:11 -0500 Subject: [PATCH 078/100] potential optimizations --- .../server/storage/db/SecondaryRecord.java | 26 ++++++++--------- .../concourse/server/storage/temp/Limbo.java | 18 +++++------- .../concourse/util/MultimapViews.java | 28 ++++++++++++++++--- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java index 63bd94b0ac..7b7c8416f1 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/db/SecondaryRecord.java @@ -39,7 +39,6 @@ import org.cinchapi.concourse.server.model.Value; import org.cinchapi.concourse.thrift.Operator; import org.cinchapi.concourse.util.MultimapViews; -import org.cinchapi.concourse.util.TMaps; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; @@ -152,15 +151,12 @@ private Map> explore(boolean historical, long timestamp, Operator operator, Value... values) { /* Authorized */ read.lock(); try { - Map> data = Maps.newHashMap(); // sorting at - // the end is - // generally - // faster + Map> data = Maps.newHashMap(); Value value = values[0]; if(operator == Operator.EQUALS) { for (PrimaryKey record : historical ? get(value, timestamp) : get(value)) { - MultimapViews.putInSet(data, record, value); + MultimapViews.put(data, record, value); } } else if(operator == Operator.NOT_EQUALS) { @@ -169,7 +165,7 @@ else if(operator == Operator.NOT_EQUALS) { if(!value.equals(stored)) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -181,7 +177,7 @@ else if(operator == Operator.GREATER_THAN) { if(!historical || stored.compareTo(value) > 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -193,7 +189,7 @@ else if(operator == Operator.GREATER_THAN_OR_EQUALS) { if(!historical || stored.compareTo(value) >= 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -205,7 +201,7 @@ else if(operator == Operator.LESS_THAN) { if(!historical || stored.compareTo(value) < 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -217,7 +213,7 @@ else if(operator == Operator.LESS_THAN_OR_EQUALS) { if(!historical || stored.compareTo(value) <= 0) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -233,7 +229,7 @@ else if(operator == Operator.BETWEEN) { .compareTo(value2) < 0)) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -246,7 +242,7 @@ else if(operator == Operator.REGEX) { if(m.matches()) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -259,7 +255,7 @@ else if(operator == Operator.NOT_REGEX) { if(!m.matches()) { for (PrimaryKey record : historical ? get(stored, timestamp) : get(stored)) { - MultimapViews.putInSet(data, record, stored); + MultimapViews.put(data, record, stored); } } } @@ -267,7 +263,7 @@ else if(operator == Operator.NOT_REGEX) { else { throw new UnsupportedOperationException(); } - return TMaps.asSortedMap(data); + return data; } finally { read.unlock(); diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java index 4292de4c19..8e2eaca0c7 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Limbo.java @@ -33,6 +33,8 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; +import org.cinchapi.concourse.util.MultimapViews; +import org.cinchapi.concourse.util.TMaps; import org.cinchapi.concourse.util.TStrings; import org.cinchapi.concourse.server.model.TObjectSorter; import org.cinchapi.concourse.server.model.Text; @@ -343,19 +345,13 @@ long record = write.getRecord().longValue(); if(write.getVersion() <= timestamp) { if(write.getKey().toString().equals(key) && matches(write.getValue(), operator, values)) { - Set v = context.get(record); - if(v == null) { - v = Sets.newHashSet(); - context.put(record, v); - } if(write.getType() == Action.ADD) { - v.add(write.getValue().getTObject()); + MultimapViews.put(context, record, write.getValue() + .getTObject()); } else { - v.remove(write.getValue().getTObject()); - if(v.isEmpty()) { - context.remove(record); - } + MultimapViews.remove(context, record, write.getValue() + .getTObject()); } } } @@ -363,7 +359,7 @@ && matches(write.getValue(), operator, values)) { break; } } - return context; + return TMaps.asSortedMap(context); } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java index f860e14a14..a161a9e597 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/MultimapViews.java @@ -49,16 +49,36 @@ public final class MultimapViews { * @return {@code true} if the new associated is added, {@code false} * otherwise */ - public static , V> boolean putInSet( - Map> map, K key, V value) { + public static , V> boolean put(Map> map, + K key, V value) { Set set = map.get(key); if(set == null) { - set = Sets.newLinkedHashSet(); + set = Sets.newHashSet(); map.put(key, set); } return set.add(value); } + /** + * Remove the {@code value} from the collection mapped from {@code key} in + * the {@code map}. If the {@code value} is the only one associated with the + * {@code key} in the map then clear the {@code key}. + * + * @param map + * @param key + * @param value + * @return {@code true} if the value existed and is removed, {@code false} + * otherwise + */ + public static , V> boolean remove( + Map> map, K key, V value) { + Set set = map.get(key); + if(set != null && set.size() == 1) { + map.remove(key); + } + return set.remove(value); + } + private MultimapViews() {/* noop */} - + } From 311403c85a6f560daacef8ece520b06e453c1a56 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 19 Nov 2014 08:45:26 -0500 Subject: [PATCH 079/100] fix bug --- .../java/org/cinchapi/concourse/security/AccessManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java index 11ab5e82ff..12f59ed0c1 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java @@ -131,6 +131,7 @@ protected static boolean isSecuredPassword(ByteBuffer password) { // visible private static ByteBuffer decodeHex(String hex) { return ByteBuffer.wrap(BaseEncoding.base16().decode(hex)); } + /** * Encode the {@code bytes} as a hexadecimal string. * @@ -141,6 +142,7 @@ private static String encodeHex(ByteBuffer bytes) { bytes.rewind(); return BaseEncoding.base16().encode(ByteBuffers.toByteArray(bytes)); } + // The AccessManager keeps track of user sessions and their associated // AccessTokens. An AccessToken is valid for a limited amount of time. private static final int ACCESS_TOKEN_TTL = 24; @@ -683,8 +685,9 @@ public String toString() { } @Override - public void copyToByteBuffer(ByteBuffer buffer) { + public void copyTo(ByteBuffer buffer) { buffer.put(getBytes()); + } } From 1264dafe8961b0ddb1ffbcb64cb5417076c102ee Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Wed, 19 Nov 2014 20:54:49 -0500 Subject: [PATCH 080/100] add method to list subdirectories in a directory --- .../concourse/server/io/FileSystem.java | 55 ++++++++++++++++--- .../concourse/server/io/FileSystemTest.java | 54 ++++++++++++++++++ 2 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 concourse-server/src/test/java/org/cinchapi/concourse/server/io/FileSystemTest.java diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java index 2c48fbe2b5..cff33fd894 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java @@ -36,23 +36,28 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.Collection; +import java.util.Set; import org.cinchapi.concourse.util.Logger; import com.google.common.base.Throwables; +import com.google.common.collect.Sets; /** * Interface to the underlying filesystem which provides methods to perform file * based operations without having to deal with the annoyance of checked - * exceptions. Using this class will help produce more streamlined and readable - * code. + * exceptions or the awkward {@link Path} API. Using this class will help + * produce more streamlined and readable code. * * @author jnelson */ public final class FileSystem { /** - * Close {@code channel}. + * Close the {@code channel} without throwing a checked exception. If, for + * some reason, this can't be done the underlying IOException will be + * re-thrown as a runtime exception. * * @param channel */ @@ -81,9 +86,11 @@ public static void copyBytes(String from, String to) { } /** - * Delete {@code directory} + * Delete {@code directory}. If files are added to the directory while its + * being deleted, this method will make a best effort to delete those files + * as well. * - * @param path + * @param directory */ public static void deleteDirectory(String directory) { try (DirectoryStream stream = Files.newDirectoryStream(Paths @@ -112,7 +119,7 @@ public static void deleteDirectory(String directory) { } /** - * Delete {@code file}. + * Delete the {@code file}. * * @param file */ @@ -126,7 +133,8 @@ public static void deleteFile(String file) { } /** - * Return the random access {@link FileChannel} for {@code file}. + * Return the random access {@link FileChannel} for {@code file}. The + * channel will be opened for reading and writing. * * @param file * @return the FileChannel for {@code file} @@ -171,6 +179,24 @@ public static String getSimpleName(String filename) { .split(File.separator))[placeholder.length - 1]; } + /** + * Look through {@code dir} and return all the sub directories. + * + * @param dir + * @return the sub directories under {@code dir}. + */ + public static Collection getSubDirs(String dir) { + File directory = new File(dir); + File[] files = directory.listFiles(); + Set subDirs = Sets.newHashSet(); + for (File file : files) { + if(Files.isDirectory(Paths.get(file.getAbsolutePath()))) { + subDirs.add(file.getName()); + } + } + return subDirs; + } + /** * Return {@code true} in the filesystem contains {@code dir} and it is * a directory. @@ -195,6 +221,21 @@ public static boolean hasFile(String file) { return Files.exists(path) && !Files.isDirectory(path); } + /** + * Create a valid path that contains sepearators in the appropriate places + * by joining all the {@link parts} together with the {@link File#separator} + * + * @param parts + * @return the path + */ + public static String makePath(String... parts) { + StringBuilder path = new StringBuilder(); + for (String part : parts) { + path.append(part).append(File.separator); + } + return path.toString(); + } + /** * Return a {@link MappedByteBuffer} for {@code file} in {@code mode} * starting at {@code position} and continuing for {@code size} bytes. This diff --git a/concourse-server/src/test/java/org/cinchapi/concourse/server/io/FileSystemTest.java b/concourse-server/src/test/java/org/cinchapi/concourse/server/io/FileSystemTest.java new file mode 100644 index 0000000000..22b49bf835 --- /dev/null +++ b/concourse-server/src/test/java/org/cinchapi/concourse/server/io/FileSystemTest.java @@ -0,0 +1,54 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.io; + +import java.util.Set; + +import org.cinchapi.concourse.ConcourseBaseTest; +import org.cinchapi.concourse.util.TestData; +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.collect.Sets; + +/** + * Unit tests for the {@link FileSystem} utility class + * + * @author jnelson + */ +public class FileSystemTest extends ConcourseBaseTest { + + @Test + public void testGetSubDirs() { + String dir = TestData.getTemporaryTestDir(); + Set subdirs = Sets.newHashSet(); + for (int i = 0; i < TestData.getScaleCount(); i++) { + String subdir = TestData.getString(); + FileSystem.mkdirs(FileSystem.makePath(dir, subdir)); + subdirs.add(subdir); + } + Assert.assertEquals(subdirs, FileSystem.getSubDirs(dir)); + } + +} From 5a6defb311b25f212637beffc995c86fe6165d88 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 20 Nov 2014 16:19:32 -0500 Subject: [PATCH 081/100] return set instead of collection --- .../main/java/org/cinchapi/concourse/server/io/FileSystem.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java index cff33fd894..281997e225 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/io/FileSystem.java @@ -36,7 +36,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; -import java.util.Collection; import java.util.Set; import org.cinchapi.concourse.util.Logger; @@ -185,7 +184,7 @@ public static String getSimpleName(String filename) { * @param dir * @return the sub directories under {@code dir}. */ - public static Collection getSubDirs(String dir) { + public static Set getSubDirs(String dir) { File directory = new File(dir); File[] files = directory.listFiles(); Set subDirs = Sets.newHashSet(); From cb01b8c26e2d2ad39731b28e48c1d18398c1bcc5 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 22 Nov 2014 08:04:20 -0500 Subject: [PATCH 082/100] fix bug that occured when trying to do managed logins --- .../concourse/server/ConcourseServer.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index 33d8202589..f294416522 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -655,15 +655,15 @@ record = Time.now(); @Override @ManagedOperation public boolean login(byte[] username, byte[] password) { - // NOTE: Any existing sessions for the user will be invalidated. try { AccessToken token = login(ByteBuffer.wrap(username), - ByteBuffer.wrap(password), null); // TODO get real - // env + ByteBuffer.wrap(password)); username = null; password = null; if(token != null) { - logout(token, null); // TODO get real env + logout(token, null); // NOTE: managed operations don't actually + // need an access token, so we expire it + // immediately return true; } else { @@ -1146,6 +1146,21 @@ private boolean isValidLink(Link link, long record) { return link.longValue() != record; } + /** + * A version of the login routine that handles the case when no environment + * has been specified. The is most common when authenticating a user for + * managed operations. + * + * @param username + * @param password + * @return the access token + * @throws TException + */ + private AccessToken login(ByteBuffer username, ByteBuffer password) + throws TException { + return login(username, password, DEFAULT_ENVIRONMENT); + } + /** * Start an {@link AtomicOperation} with {@code store} as the destination * and do the work to update chronologized values in {@code key} in From 68f9c70a8bf0e1e7ed076d20d0f1edb3266ece60 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 20 Nov 2014 18:40:41 -0500 Subject: [PATCH 083/100] reference commit for envtool cli with script and CLI class This reverts commit 6f5b244526617cc8f8ebfa20fab1f9454b06ad10. --- concourse-server/scripts/envtool | 8 ++ .../concourse/server/ConcourseServer.java | 6 ++ .../concourse/server/cli/EnvToolCli.java | 87 +++++++++++++++++++ .../server/jmx/ConcourseServerMXBean.java | 12 +++ .../cinchapi/concourse/util/TCollections.java | 18 ++++ 5 files changed, 131 insertions(+) create mode 100755 concourse-server/scripts/envtool create mode 100644 concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java diff --git a/concourse-server/scripts/envtool b/concourse-server/scripts/envtool new file mode 100755 index 0000000000..3f22046d8c --- /dev/null +++ b/concourse-server/scripts/envtool @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +# This config will setup all the enviornment variables and check that +# pthats are proper +. "`dirname "$0"`/.env" + +# run the program +exec $JAVACMD -classpath "$CLASSPATH" org.cinchapi.concourse.server.cli.EnvToolCli "$@" \ No newline at end of file diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index f294416522..e7fe0f9b49 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -652,6 +652,12 @@ record = Time.now(); } } + @Override + public Set listAllEnvironments() { + return TSets.intersection(FileSystem.getSubDirs(BUFFER_DIRECTORY), + FileSystem.getSubDirs(DATABASE_DIRECTORY)); + } + @Override @ManagedOperation public boolean login(byte[] username, byte[] password) { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java new file mode 100644 index 0000000000..e24b8fb901 --- /dev/null +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java @@ -0,0 +1,87 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.server.cli; + +import org.cinchapi.concourse.server.jmx.ConcourseServerMXBean; +import org.cinchapi.concourse.util.TCollections; + +/** + * A tool that is used to manage the environments in Concourse Server. + * + * @author jnelson + */ +public class EnvToolCli extends ManagedOperationCli { + + /** + * Run the program... + * + * @param args + */ + public static void main(String... args) { + EnvToolCli cli = new EnvToolCli(args); + cli.run(); + } + + /** + * Construct a new instance. + * + * @param options + * @param args + */ + public EnvToolCli(String[] args) { + super(new EnvToolOptions(), args); + } + + @Override + protected void doTask(ConcourseServerMXBean bean) { + System.out.println(TCollections.toOrderedListString(bean + .listAllEnvironments())); + + } + + /** + * The options that can be passed to the main method of this script. + * + * @author jnelson + */ + private static final class EnvToolOptions extends Options {} // NOTE: not + // extending + // EnvironmentOptions + // because any + // management + // options that + // we take on + // an + // environment + // (i.e. + // delete) + // should be + // specified as + // --delete + // + // instead of + // --delete -e + // + +} diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java index b0038366b3..4c8481b5f6 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java @@ -23,6 +23,8 @@ */ package org.cinchapi.concourse.server.jmx; +import java.util.Set; + import javax.management.MXBean; import org.cinchapi.concourse.server.GlobalState; @@ -115,6 +117,16 @@ public interface ConcourseServerMXBean { @ManagedOperation public boolean hasUser(byte[] username); + /** + * Return the names of all the environments that exist within Concourse + * Server. An environment is said to exist if at least one user has + * established a connection to that environment. + * + * @return a set containing all of the environments + */ + @ManagedOperation + public Set listAllEnvironments(); + /** * Return {@code true} if {@code username} and {@code password} is a valid * combination to login to the server for the purpose of performing a diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TCollections.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TCollections.java index 983ec4759a..207cfa15f8 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TCollections.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TCollections.java @@ -71,5 +71,23 @@ public static T getRandomElement(Collection collection) { return Iterables.get(collection, Math.abs(Random.getInt()) % size); } + /** + * Return a string that displays the {@code collection} as an order + * numerical list. + * + * @param collection + * @return the string output + */ + public static String toOrderedListString(Iterable collection) { + StringBuilder sb = new StringBuilder(); + int i = 1; + for (T item : collection) { + sb.append(i).append(". ").append(item) + .append(System.getProperty("line.separator")); + ++i; + } + return sb.toString(); + } + private TCollections() {/* noop */} } From 9164fb4041dba50f7d656a221c7df82b30b3ee01 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 22 Nov 2014 08:17:08 -0500 Subject: [PATCH 084/100] Fix the return type of the listAllEnvironments method and add some context to the output from the cli --- .../org/cinchapi/concourse/server/ConcourseServer.java | 8 +++++--- .../org/cinchapi/concourse/server/cli/EnvToolCli.java | 6 ++---- .../concourse/server/jmx/ConcourseServerMXBean.java | 4 +--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index e7fe0f9b49..f947782024 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -84,6 +84,7 @@ import org.cinchapi.concourse.util.Convert.ResolvableLink; import org.cinchapi.concourse.util.Environments; import org.cinchapi.concourse.util.Logger; +import org.cinchapi.concourse.util.TCollections; import org.cinchapi.concourse.util.TLinkedHashMap; import org.cinchapi.concourse.util.TSets; import org.cinchapi.concourse.util.Version; @@ -653,9 +654,10 @@ record = Time.now(); } @Override - public Set listAllEnvironments() { - return TSets.intersection(FileSystem.getSubDirs(BUFFER_DIRECTORY), - FileSystem.getSubDirs(DATABASE_DIRECTORY)); + public String listAllEnvironments() { + return TCollections.toOrderedListString(TSets.intersection( + FileSystem.getSubDirs(BUFFER_DIRECTORY), + FileSystem.getSubDirs(DATABASE_DIRECTORY))); } @Override diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java index e24b8fb901..cfdedbc8de 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/EnvToolCli.java @@ -24,7 +24,6 @@ package org.cinchapi.concourse.server.cli; import org.cinchapi.concourse.server.jmx.ConcourseServerMXBean; -import org.cinchapi.concourse.util.TCollections; /** * A tool that is used to manage the environments in Concourse Server. @@ -55,9 +54,8 @@ public EnvToolCli(String[] args) { @Override protected void doTask(ConcourseServerMXBean bean) { - System.out.println(TCollections.toOrderedListString(bean - .listAllEnvironments())); - + System.out.println("These are the environments in Concourse Server:"); + System.out.println(bean.listAllEnvironments()); } /** diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java index 4c8481b5f6..8a3db1bae2 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java @@ -23,8 +23,6 @@ */ package org.cinchapi.concourse.server.jmx; -import java.util.Set; - import javax.management.MXBean; import org.cinchapi.concourse.server.GlobalState; @@ -125,7 +123,7 @@ public interface ConcourseServerMXBean { * @return a set containing all of the environments */ @ManagedOperation - public Set listAllEnvironments(); + public String listAllEnvironments(); /** * Return {@code true} if {@code username} and {@code password} is a valid From e9b001e66ec6952474468fe070dd2290be294745 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 4 Dec 2014 06:26:39 -0500 Subject: [PATCH 085/100] call logout on exit from client --- .../main/java/org/cinchapi/concourse/Concourse.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index b85d94cce1..348235b697 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -1604,8 +1604,14 @@ public Set call() throws Exception { @Override public void exit() { - client.getInputProtocol().getTransport().close(); - client.getOutputProtocol().getTransport().close(); + try { + client.logout(creds, environment); + client.getInputProtocol().getTransport().close(); + client.getOutputProtocol().getTransport().close(); + } + catch (Exception e) { + throw Throwables.propagate(e); + } } @Override From c70626efcf2c4b4623d9ddf2faeb34d3f7021b2f Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 4 Dec 2014 10:22:25 -0500 Subject: [PATCH 086/100] add option to useradmin CLI to list the current sessions --- .../concourse/server/ConcourseServer.java | 5 +++++ .../concourse/server/cli/ManageUsersCli.java | 20 ++++++++++++------- .../server/jmx/ConcourseServerMXBean.java | 11 +++++++++- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java index f947782024..069e96a3d5 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/ConcourseServer.java @@ -660,6 +660,11 @@ public String listAllEnvironments() { FileSystem.getSubDirs(DATABASE_DIRECTORY))); } + @Override + public String listAllUserSessions() { + return TCollections.toOrderedListString(manager.describeAllAccessTokens()); + } + @Override @ManagedOperation public boolean login(byte[] username, byte[] password) { diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/ManageUsersCli.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/ManageUsersCli.java index b7baa50283..f1265bc0f6 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/ManageUsersCli.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/cli/ManageUsersCli.java @@ -81,6 +81,10 @@ else if(opts.revoke) { bean.revoke(username); System.out.println("Consider it done."); } + else if(opts.listSessions) { + System.out.println("Current User Sessions:"); + System.out.println(bean.listAllUserSessions()); + } else if(!Strings.isNullOrEmpty(opts.addingUsername)) { if(bean.hasUser(opts.addingUsername.getBytes())) { console.readLine(opts.addingUsername + " already exists. " @@ -141,16 +145,15 @@ else if(!Strings.isNullOrEmpty(opts.deletingUsername)) { throw Throwables.propagate(e); } } - + @Override protected boolean isReadyToRun() { MyOptions opts = (MyOptions) options; - return super.isReadyToRun() && - (opts.grant - || opts.revoke - || !Strings.isNullOrEmpty(opts.addingUsername) - || !Strings.isNullOrEmpty(opts.deletingUsername) - || !Strings.isNullOrEmpty(opts.editingUsername)); + return super.isReadyToRun() + && (opts.grant || opts.revoke || opts.listSessions + || !Strings.isNullOrEmpty(opts.addingUsername) + || !Strings.isNullOrEmpty(opts.deletingUsername) || !Strings + .isNullOrEmpty(opts.editingUsername)); } /** @@ -178,6 +181,9 @@ private static class MyOptions extends Options { @Parameter(names = { "-np", "--new-password" }, description = "Password of new user to add/edit.") public String newPassword; + @Parameter(names = { "--list-sessions" }, description = "List the user sessions that are currently active") + public boolean listSessions = false; + } } diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java index 8a3db1bae2..3e3a533786 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/jmx/ConcourseServerMXBean.java @@ -120,11 +120,20 @@ public interface ConcourseServerMXBean { * Server. An environment is said to exist if at least one user has * established a connection to that environment. * - * @return a set containing all of the environments + * @return a string containing all of the environments */ @ManagedOperation public String listAllEnvironments(); + /** + * Return a description of all the currently active user sessions within + * Concourse Server. + * + * @return a string containing all the user sessions + */ + @ManagedOperation + public String listAllUserSessions(); + /** * Return {@code true} if {@code username} and {@code password} is a valid * combination to login to the server for the purpose of performing a From 17e360d50598777cc96db19fc517f6cac871f900 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 6 Dec 2014 17:00:21 -0500 Subject: [PATCH 087/100] improved implementation for connection pools --- .../concourse/CachedConnectionPoolTest.java | 2 +- .../concourse/CachedConnectionPool.java | 64 ++------- .../cinchapi/concourse/ConnectionPool.java | 106 ++++++++------- .../concourse/FixedConnectionPool.java | 22 ++- .../util/ConcurrentLoadingQueue.java | 127 ++++++++++++++++++ .../util/ConcurrentLoadingQueueTest.java | 100 ++++++++++++++ 6 files changed, 315 insertions(+), 106 deletions(-) create mode 100644 concourse/src/main/java/org/cinchapi/concourse/util/ConcurrentLoadingQueue.java create mode 100644 concourse/src/test/java/org/cinchapi/concourse/util/ConcurrentLoadingQueueTest.java diff --git a/concourse-integration-tests/src/test/java/org/cinchapi/concourse/CachedConnectionPoolTest.java b/concourse-integration-tests/src/test/java/org/cinchapi/concourse/CachedConnectionPoolTest.java index 2aa4d08178..1c4e546d33 100644 --- a/concourse-integration-tests/src/test/java/org/cinchapi/concourse/CachedConnectionPoolTest.java +++ b/concourse-integration-tests/src/test/java/org/cinchapi/concourse/CachedConnectionPoolTest.java @@ -28,7 +28,7 @@ import org.junit.Assert; import org.junit.Test; -import com.beust.jcommander.internal.Lists; +import com.google.common.collect.Lists; /** * Unit tests for {@link CachedConnectionPool}. diff --git a/concourse/src/main/java/org/cinchapi/concourse/CachedConnectionPool.java b/concourse/src/main/java/org/cinchapi/concourse/CachedConnectionPool.java index ca5d493ea4..f096b6407f 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/CachedConnectionPool.java +++ b/concourse/src/main/java/org/cinchapi/concourse/CachedConnectionPool.java @@ -23,13 +23,9 @@ */ package org.cinchapi.concourse; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalNotification; +import java.util.Queue; +import java.util.concurrent.Callable; +import org.cinchapi.concourse.util.ConcurrentLoadingQueue; /** * A {@link ConnectionPool} that has no limit on the number of connections it @@ -40,12 +36,6 @@ */ class CachedConnectionPool extends ConnectionPool { - // Each connection will, if not active, will automatically expire. These - // units are chosen to correspond to the AccessToken TTL that is defined in - // {@link AccessManager}. - private static final int CONNECTION_TTL = 24; - private static final TimeUnit CONNECTION_TTL_UNIT = TimeUnit.HOURS; - // Connection Info private final String host; private final int port; @@ -88,47 +78,21 @@ protected CachedConnectionPool(String host, int port, String username, } @Override - public boolean hasAvailableConnection() { - return true; - } + protected Queue buildQueue(int size) { + return ConcurrentLoadingQueue.create(new Callable() { - @Override - public Concourse request() { - try { - return super.request(); - } - catch (IllegalStateException e) { - Concourse connection = Concourse.connect(host, port, username, - password, environment); - connections.put(connection, new AtomicBoolean(true)); - return connection; - } + @Override + public Concourse call() throws Exception { + return Concourse.connect(host, port, username, password, + environment); + } + + }); } @Override - protected Cache buildCache(int size) { - return CacheBuilder - .newBuilder() - .initialCapacity(size) - .expireAfterWrite(CONNECTION_TTL, CONNECTION_TTL_UNIT) - .removalListener( - new RemovalListener() { - - @Override - public void onRemoval( - RemovalNotification notification) { - if(notification.getValue().get()) { // ensure - // that - // active - // connections - // don't get - // dropped - connections.put(notification.getKey(), - notification.getValue()); - } - } - - }).build(); + protected Concourse getConnection() { + return available.poll(); } } diff --git a/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java b/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java index ba003fef59..81ad54f1c7 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java +++ b/concourse/src/main/java/org/cinchapi/concourse/ConnectionPool.java @@ -23,15 +23,16 @@ */ package org.cinchapi.concourse; +import java.util.Queue; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - import javax.annotation.concurrent.ThreadSafe; import org.cinchapi.concourse.config.ConcourseClientPreferences; import com.google.common.base.Preconditions; -import com.google.common.cache.Cache; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** *

@@ -303,15 +304,14 @@ public static ConnectionPool newFixedConnectionPool(String host, int port, private static final String DEFAULT_PREFS_FILE = "concourse_client.prefs"; /** - * A mapping from connection to a flag indicating if the connection is - * active (e.g. taken). + * A FIFO queue of connections that are available to be leased. */ - protected final Cache connections; + protected final Queue available; /** - * The number of connections that are currently available; + * The connections that have been leased from this pool. */ - private AtomicInteger numAvailableConnections; + private final Set leased; /** * A flag to indicate if the pool is currently open and operational. @@ -345,14 +345,15 @@ protected ConnectionPool(String host, int port, String username, */ protected ConnectionPool(String host, int port, String username, String password, String environment, int poolSize) { - this.connections = buildCache(poolSize); - this.numAvailableConnections = new AtomicInteger(poolSize); + this.available = buildQueue(poolSize); + this.leased = Sets.newSetFromMap(Maps + . newConcurrentMap()); for (int i = 0; i < poolSize; ++i) { - connections.put(Concourse.connect(host, port, username, password, - environment), new AtomicBoolean(false)); + available.offer(Concourse.connect(host, port, username, password, + environment)); } // Ensure that the client connections are forced closed when the JVM is - // shutdown in case the user does not properly shutdown the pool + // shutdown in case the user does not properly close the pool Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override @@ -384,8 +385,8 @@ public void close() throws Exception { * @return {@code true} if there are one or more available connections */ public boolean hasAvailableConnection() { - Preconditions.checkState(open.get(), "Connection pool is closed"); - return numAvailableConnections.get() > 0; + verifyOpenState(); + return available.peek() != null; } /** @@ -394,19 +395,10 @@ public boolean hasAvailableConnection() { * @param connection */ public void release(Concourse connection) { - Preconditions.checkState(open.get(), "Connection pool is closed"); - Preconditions.checkArgument( - connections.getIfPresent(connection) != null, - "Cannot release the connection because it " - + "was not previously requested from this pool"); - if(connections.getIfPresent(connection).compareAndSet(true, false)) { - numAvailableConnections.incrementAndGet(); - } - else { - throw new IllegalStateException( - "This is an unreachable state that is obviously " - + "reachable, indicating a bug in the code"); - } + verifyOpenState(); + verifyValidOrigin(connection); + available.offer(connection); + leased.remove(connection); } /** @@ -416,35 +408,35 @@ public void release(Concourse connection) { * @return a connection */ public Concourse request() { - Preconditions.checkState(open.get(), "Connection pool is closed"); - while (!hasAvailableConnection()) { - continue; - } - for (Concourse connection : connections.asMap().keySet()) { - if(connections.getIfPresent(connection).compareAndSet(false, true)) { - numAvailableConnections.decrementAndGet(); - return connection; - } - } - throw new IllegalStateException( - "This is an unreachable state that is obviously " - + "reachable, indicating a bug in the code"); + verifyOpenState(); + Concourse connection = getConnection(); + leased.add(connection); + return connection; } /** - * Return the {@link Cache} that will hold the connections. + * Return the {@link Queue} that will hold the connections. * * @param size * * @return the connections cache */ - protected abstract Cache buildCache(int size); + protected abstract Queue buildQueue(int size); + + /** + * Get a connection from the queue of {@code available} ones. The subclass + * should use the correct method depending upon whether this method should + * block or not. + * + * @return the connection + */ + protected abstract Concourse getConnection(); /** * Exit all the connections managed by the pool. */ private void exitAllConnections() { - for (Concourse concourse : connections.asMap().keySet()) { + for (Concourse concourse : available) { concourse.exit(); } } @@ -455,12 +447,28 @@ private void exitAllConnections() { * @return {@code true} if the pool can be closed */ private boolean isCloseable() { - for (AtomicBoolean bool : connections.asMap().values()) { - if(bool.get()) { - return false; - } + return leased.isEmpty(); + } + + /** + * Ensure that the connection pool is open. If it is not, throw an + * IllegalStateException. + */ + private void verifyOpenState() { + Preconditions.checkState(open.get(), "Connection pool is closed"); + } + + /** + * Verify that the {@code connection} was leased from this pool. + * + * @param connection + */ + private void verifyValidOrigin(Concourse connection) { + if(!leased.contains(connection)) { + throw new IllegalArgumentException( + "Cannot release the connection because it " + + "was not previously requested from this pool"); } - return true; } } diff --git a/concourse/src/main/java/org/cinchapi/concourse/FixedConnectionPool.java b/concourse/src/main/java/org/cinchapi/concourse/FixedConnectionPool.java index 6e7cae2f0c..d3eff7c7da 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/FixedConnectionPool.java +++ b/concourse/src/main/java/org/cinchapi/concourse/FixedConnectionPool.java @@ -23,10 +23,10 @@ */ package org.cinchapi.concourse; -import java.util.concurrent.atomic.AtomicBoolean; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import com.google.common.base.Throwables; /** * A {@link ConnectionPool} with a fixed number of connections. If all the @@ -67,8 +67,18 @@ protected FixedConnectionPool(String host, int port, String username, } @Override - protected Cache buildCache(int size) { - return CacheBuilder.newBuilder().initialCapacity(size).build(); + protected Queue buildQueue(int size) { + return new ArrayBlockingQueue(size); + } + + @Override + protected Concourse getConnection() { + try { + return ((BlockingQueue) available).take(); + } + catch (InterruptedException e) { + throw Throwables.propagate(e); + } } } diff --git a/concourse/src/main/java/org/cinchapi/concourse/util/ConcurrentLoadingQueue.java b/concourse/src/main/java/org/cinchapi/concourse/util/ConcurrentLoadingQueue.java new file mode 100644 index 0000000000..1da420368c --- /dev/null +++ b/concourse/src/main/java/org/cinchapi/concourse/util/ConcurrentLoadingQueue.java @@ -0,0 +1,127 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentLinkedQueue; + +import javax.annotation.concurrent.ThreadSafe; + +import com.google.common.base.Throwables; + +/** + * A {@link ConcurrentLinkedQueue} that uses a specified supplier + * {@link Callable} to dynamically loading elements into the queue when a read + * request is made and the queue is empty. + * + * @author jnelson + */ +@ThreadSafe +public class ConcurrentLoadingQueue extends ConcurrentLinkedQueue { + + /** + * Return a new {@link ConcurrentLoadingQueue} that uses the + * {@code supplier} to populate the queue on demand. + * + * @param supplier + * @return the ConcurrentLoadingQueue + */ + public static ConcurrentLoadingQueue create(Callable supplier) { + return new ConcurrentLoadingQueue(supplier); + } + + /** + * Return a new {@link ConcurrentLoadingQueue} that initially contains the + * elements of the given {@code collection} in traversal order of the + * collection's iterator and uses the {@code supplier} to populate the queue + * on demand. + * + * @param collection + * @param supplier + * @return the ConcurrentLoadingQueue + */ + public static ConcurrentLoadingQueue create( + Collection collection, Callable supplier) { + ConcurrentLoadingQueue queue = create(supplier); + for (E element : collection) { + queue.offer(element); + } + return queue; + } + + private static final long serialVersionUID = 1L; + + /** + * The function that supplies elements to the queue when it is empty. + */ + private final Callable supplier; + + /** + * Construct a new instance. + * + * @param supplier + */ + private ConcurrentLoadingQueue(Callable supplier) { + this.supplier = supplier; + } + + @Override + public E peek() { + E element = super.peek(); + if(element == null) { + loadElement(); + return super.peek(); + } + else { + return element; + } + } + + @Override + public E poll() { + E element = super.poll(); + if(element == null) { + loadElement(); + return super.poll(); + } + else { + return element; + } + } + + /** + * Load a new element and place it into the queue. + */ + private void loadElement() { + try { + E element = supplier.call(); + offer(element); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + +} diff --git a/concourse/src/test/java/org/cinchapi/concourse/util/ConcurrentLoadingQueueTest.java b/concourse/src/test/java/org/cinchapi/concourse/util/ConcurrentLoadingQueueTest.java new file mode 100644 index 0000000000..d6018ec10d --- /dev/null +++ b/concourse/src/test/java/org/cinchapi/concourse/util/ConcurrentLoadingQueueTest.java @@ -0,0 +1,100 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 Jeff Nelson, Cinchapi Software Collective + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.cinchapi.concourse.util; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.collect.Lists; + +/** + * Unit tests for {@link ConcurrentLoadingQueue}. + * + * @author jnelson + */ +public class ConcurrentLoadingQueueTest { + + @Test + public void testDynamicLoading() { + final AtomicInteger counter = new AtomicInteger(); + ConcurrentLoadingQueue queue = ConcurrentLoadingQueue + .create(new Callable() { + + @Override + public Integer call() throws Exception { + return counter.incrementAndGet(); + } + + }); + Assert.assertEquals(1, (int) queue.peek()); + Assert.assertEquals(1, (int) queue.peek()); + Assert.assertEquals(1, (int) queue.poll()); + Assert.assertEquals(2, (int) queue.poll()); + } + + @Test + public void testDynamicLoadingWithInitialValues() { + int count = Random.getScaleCount(); + final AtomicInteger counter = new AtomicInteger(count - 1); + List initial = Lists.newArrayList(); + for (int i = 0; i < count; i++) { + initial.add(i); + } + ConcurrentLoadingQueue queue = ConcurrentLoadingQueue.create( + initial, new Callable() { + + @Override + public Integer call() throws Exception { + return counter.incrementAndGet(); + } + + }); + for (int i = 0; i < count * 2; i++) { + Assert.assertEquals(i, (int) queue.poll()); + } + } + + @Test + public void testDynamicLoadOnlyWhenNecessary(){ + ConcurrentLoadingQueue queue = ConcurrentLoadingQueue.create(new Callable(){ + + @Override + public Integer call() throws Exception { + return 1; + } + + }); + queue.offer(100); + queue.offer(200); + Assert.assertEquals(100, (int) queue.peek()); + Assert.assertEquals(100, (int) queue.poll()); + Assert.assertEquals(200, (int) queue.poll()); + Assert.assertEquals(1, (int) queue.poll()); + } + +} From 6d0e489786eeb19930e43417656ce9fc9f0b2eb2 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 11 Dec 2014 21:18:36 -0500 Subject: [PATCH 088/100] perform cleanup even when test fails --- .../java/org/cinchapi/concourse/ConcourseIntegrationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/concourse-integration-tests/src/test/java/org/cinchapi/concourse/ConcourseIntegrationTest.java b/concourse-integration-tests/src/test/java/org/cinchapi/concourse/ConcourseIntegrationTest.java index 2074e16537..1908c68492 100644 --- a/concourse-integration-tests/src/test/java/org/cinchapi/concourse/ConcourseIntegrationTest.java +++ b/concourse-integration-tests/src/test/java/org/cinchapi/concourse/ConcourseIntegrationTest.java @@ -100,6 +100,8 @@ protected void failed(Throwable t, Description description) { System.out.println("---"); System.out.println(Variables.dump()); System.out.println(""); + stop(); + afterEachTest(); } @Override From 74a1d28d8775b695acdf1c475d2f3fbe9334e1b9 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Thu, 11 Dec 2014 21:18:58 -0500 Subject: [PATCH 089/100] handle corner case where the client is forced to exit due to a security change Conflicts: concourse/src/main/java/org/cinchapi/concourse/Concourse.java --- .../src/main/java/org/cinchapi/concourse/Concourse.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java index 348235b697..e6a1cda773 100644 --- a/concourse/src/main/java/org/cinchapi/concourse/Concourse.java +++ b/concourse/src/main/java/org/cinchapi/concourse/Concourse.java @@ -49,6 +49,7 @@ import org.cinchapi.concourse.thrift.ConcourseService; import org.cinchapi.concourse.thrift.Operator; import org.cinchapi.concourse.thrift.TObject; +import org.cinchapi.concourse.thrift.TSecurityException; import org.cinchapi.concourse.thrift.TTransactionException; import org.cinchapi.concourse.thrift.TransactionToken; import org.cinchapi.concourse.time.Time; @@ -1609,6 +1610,13 @@ public void exit() { client.getInputProtocol().getTransport().close(); client.getOutputProtocol().getTransport().close(); } + catch (TSecurityException | TTransportException e) { + // Handle corner case where the client is existing because of + // (or after the occurence of) a password change, which means it + // can't perform a traditional logout. Its worth nothing that + // we're okay with this scenario because a password change will + // delete all previously issued tokens. + } catch (Exception e) { throw Throwables.propagate(e); } From 63247d64524524ebbbb71d3181e2ead6f4560b40 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 06:00:58 -0500 Subject: [PATCH 090/100] remove warning --- .../cinchapi/concourse/server/storage/cache/BloomFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java index cf99ec3dd4..20c776aaea 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/cache/BloomFilter.java @@ -96,7 +96,7 @@ public static BloomFilter create(String file, int expectedInsertions) { * @param file * @return the BloomFilter */ - @SuppressWarnings({ "unchecked", "resource" }) + @SuppressWarnings({ "unchecked" }) public static BloomFilter open(String file) { try { final AtomicBoolean upgrade = new AtomicBoolean(false); From b877c17e7986f0bb3a7ca3725d89d65718e97abd Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 06:11:33 -0500 Subject: [PATCH 091/100] place the pref handling logic before the core tanuki logic --- concourse-server/scripts/concourse | 177 +++++++++++++++-------------- 1 file changed, 91 insertions(+), 86 deletions(-) diff --git a/concourse-server/scripts/concourse b/concourse-server/scripts/concourse index b082fd6e86..35e82a9113 100755 --- a/concourse-server/scripts/concourse +++ b/concourse-server/scripts/concourse @@ -65,9 +65,9 @@ PIDFILE_CHECK_PID=true # initialization tasks and it may be desirable to wait a few seconds # before returning. For example, to delay the invocation of following # startup scripts. Setting WAIT_AFTER_STARTUP to a positive number will -# cause the start command to delay for the indicated period of time +# cause the start command to delay for the indicated period of time # (in seconds). -# +# WAIT_AFTER_STARTUP=0 # If set, wait for the wrapper to report that the daemon has started @@ -108,13 +108,13 @@ PLIST_DOMAIN=org.tanukisoftware.wrapper # appropriate for your application. They should remain commented. # chkconfig: 2345 20 80 # description: @app.long.name@ - + # Initialization block for the install_initd and remove_initd scripts used by # SUSE linux distributions. ### BEGIN INIT INFO # Provides: @app.name@ # Required-Start: $local_fs $network $syslog -# Should-Start: +# Should-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 @@ -134,6 +134,35 @@ PLIST_DOMAIN=org.tanukisoftware.wrapper # 3) RUN_LEVEL, to specify a general run level. RUN_LEVEL=20 +# The following block contains custom logic to dynamically pull attributes from +# the concourse.prefs file and make analogous changes to the tanuki wrapper +# configuration file. +#----------------------------------------------------------------------------- + +# Pull jmx_port from concourse.prefs +JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` +if [ -z "$JMX_PREF" ]; then + JMX_PREF="9010" +fi +perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF + +# Pull heap_size from concourse.prefs +HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` +HEAP=${HEAP_PREF//[!0-9]/} +if [[ $HEAP_PREF == *g* ]] + then + HEAP=$(($HEAP * 1024)) +fi +if [ -z "$HEAP" ]; then + HEAP="1024" +fi +MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF +perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF + # Do not modify anything beyond this point #----------------------------------------------------------------------------- @@ -274,7 +303,7 @@ case "$DIST_OS" in # HP-UX needs the XPG4 version of ps (for -o args) DIST_OS="hpux" UNIX95="" - export UNIX95 + export UNIX95 ;; 'darwin') DIST_OS="macosx" @@ -296,7 +325,7 @@ then then DIST_BITS="32" else - if [ "X`sysctl -n hw.cpu64bit_capable`" == "X1" ] + if [ "X`sysctl -n hw.cpu64bit_capable`" == "X1" ] then DIST_BITS="64" else @@ -362,7 +391,7 @@ else DIST_BITS="64" else DIST_BITS="32" - fi + fi ;; 'sun4u' | 'sparcv9' | 'sparc') DIST_ARCH="sparc" @@ -381,10 +410,10 @@ else DIST_BITS="64" ;; armv*) - if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then + if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then DIST_ARCH="armel" DIST_BITS="32" - else + else DIST_ARCH="armhf" DIST_BITS="32" fi @@ -409,33 +438,9 @@ else ECHOOPT="" fi -# Pull jmx_port from concourse.prefs -JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` -if [ -z "$JMX_PREF" ]; then - JMX_PREF="9010" -fi -perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF - -# Pull heap_size from concourse.prefs -HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` -HEAP=${HEAP_PREF//[!0-9]/} -if [[ $HEAP_PREF == *g* ]] - then - HEAP=$(($HEAP * 1024)) -fi -if [ -z "$HEAP" ]; then - HEAP="1024" -fi -MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF -perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF - gettext() { "$WRAPPER_CMD" --translate "$1" "$WRAPPER_CONF" 2>/dev/null - if [ $? != 0 ] ; then + if [ $? != 0 ] ; then echo "$1" fi } @@ -463,7 +468,7 @@ then chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null fi if [ -x "$WRAPPER_TEST_CMD" ] - then + then WRAPPER_CMD="$WRAPPER_TEST_CMD" else outputFile "$WRAPPER_TEST_CMD" @@ -478,7 +483,7 @@ then chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null fi if [ -x "$WRAPPER_TEST_CMD" ] - then + then WRAPPER_CMD="$WRAPPER_TEST_CMD" else outputFile "$WRAPPER_TEST_CMD" @@ -493,7 +498,7 @@ then chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null fi if [ -x "$WRAPPER_TEST_CMD" ] - then + then WRAPPER_CMD="$WRAPPER_TEST_CMD" else outputFile "$WRAPPER_TEST_CMD" @@ -651,7 +656,7 @@ checkUser() { then if [ "X$1" != "X" ] then - # Resolve the primary group + # Resolve the primary group RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1` if [ "X$RUN_AS_GROUP" = "X" ] then @@ -720,7 +725,7 @@ getpid() { 'freebsd') pidtest=`$PSEXE -p $pid -o args | tail -1` if [ "X$pidtest" = "XCOMMAND" ] - then + then pidtest="" fi ;; @@ -820,7 +825,7 @@ getstatus() { then STATUS="Unknown" fi - + JAVASTATUS= if [ -f "$JAVASTATUSFILE" ] then @@ -868,7 +873,7 @@ testpid() { launchdtrap() { stopit - exit + exit } waitforwrapperstop() { @@ -881,9 +886,9 @@ waitforwrapperstop() { launchinternal() { getpid - trap launchdtrap TERM + trap launchdtrap TERM if [ "X$pid" = "X" ] - then + then prepAdditionalParams "$@" # The string passed to eval must handles spaces in paths correctly. @@ -921,7 +926,7 @@ console() { waitforjavastartup() { getstatus eval echo $ECHOOPT `gettext 'Waiting for $APP_LONG_NAME...'` - + # Wait until the timeout or we have something besides Unknown. counter=15 while [ "$JAVASTATUS" = "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do @@ -930,8 +935,8 @@ waitforjavastartup() { getstatus counter=`expr $counter - 1` done - - if [ -n "$WAIT_FOR_STARTED_TIMEOUT" ] ; then + + if [ -n "$WAIT_FOR_STARTED_TIMEOUT" ] ; then counter=$WAIT_FOR_STARTED_TIMEOUT else counter=120 @@ -946,13 +951,13 @@ waitforjavastartup() { echo "" fi } - + startwait() { if [ $WAIT_FOR_STARTED_STATUS = true ] then waitforjavastartup fi - # Sleep for a few seconds to allow for intialization if required + # Sleep for a few seconds to allow for intialization if required # then test to make sure we're still running. # i=0 @@ -972,7 +977,7 @@ startwait() { else eval echo `gettext ' running: PID:$pid'` fi - else + else echo "" fi } @@ -980,17 +985,17 @@ startwait() { macosxstart() { # The daemon has been installed. eval echo `gettext 'Starting $APP_LONG_NAME. Detected Mac OSX and installed launchd daemon.'` - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then + if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then eval echo `gettext 'Must be root to perform this action.'` exit 1 fi - + getpid if [ "X$pid" != "X" ] ; then eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi - + # If the daemon was just installed, it may not be loaded. LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` if [ "X${LOADED_PLIST}" = "X" ] ; then @@ -1001,26 +1006,26 @@ macosxstart() { if [ "X$pid" == "X" ] ; then launchctl start ${APP_PLIST_BASE} fi - + startwait } upstartstart() { # The daemon has been installed. eval echo `gettext 'Starting $APP_LONG_NAME. Detected Linux and installed upstart.'` - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then + if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then eval echo `gettext 'Must be root to perform this action.'` exit 1 fi - + getpid if [ "X$pid" != "X" ] ; then eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi - + /sbin/start ${APP_NAME} - + startwait } @@ -1038,13 +1043,13 @@ start() { eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi - + startwait } - + stopit() { # $1 exit if down flag - + eval echo `gettext 'Stopping $APP_LONG_NAME...'` getpid if [ "X$pid" = "X" ] @@ -1109,7 +1114,7 @@ stopit() { fi fi } - + pause() { eval echo `gettext 'Pausing $APP_LONG_NAME.'` } @@ -1138,7 +1143,7 @@ status() { installUpstart() { eval echo `gettext ' Installing the $APP_LONG_NAME daemon using upstart..'` - if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then + if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then eval echo `gettext ' a custom upstart conf file ${APP_NAME}.install found'` cp "${REALDIR}/${APP_NAME}.install" "/etc/init/${APP_NAME}.conf" else @@ -1154,7 +1159,7 @@ installUpstart() { } installdaemon() { - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then + if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then eval echo `gettext 'Must be root to perform this action.'` exit 1 else @@ -1176,14 +1181,14 @@ installdaemon() { fi elif [ "$DIST_OS" = "linux" ] ; then if [ -f /etc/redhat-release -o -f /etc/redhat_version -o -f /etc/fedora-release ] ; then - eval echo `gettext 'Detected RHEL or Fedora:'` + eval echo `gettext 'Detected RHEL or Fedora:'` if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" -o -f "/etc/init/${APP_NAME}.conf" ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` exit 1 else if [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then installUpstart - else + else eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" /sbin/chkconfig --add "$APP_NAME" @@ -1208,7 +1213,7 @@ installdaemon() { else if [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then installUpstart - else + else eval echo `gettext ' Installing the $APP_LONG_NAME daemon using init.d..'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" update-rc.d "$APP_NAME" defaults @@ -1232,8 +1237,8 @@ installdaemon() { ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" fi fi - elif [ "$DIST_OS" = "hpux" ] ; then - eval echo `gettext 'Detected HP-UX:'` + elif [ "$DIST_OS" = "hpux" ] ; then + eval echo `gettext 'Detected HP-UX:'` if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` exit 1 @@ -1257,7 +1262,7 @@ installdaemon() { exit 1 else eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - if [ -n "`/usr/sbin/lsitab install_assist`" ] ; then + if [ -n "`/usr/sbin/lsitab install_assist`" ] ; then eval echo `gettext ' The task /usr/sbin/install_assist was found in the inittab, this might cause problems for all subsequent tasks to launch at this process is known to block the init task. Please make sure this task is not needed anymore and remove/deactivate it.'` fi for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do @@ -1268,7 +1273,7 @@ installdaemon() { /usr/sbin/mkitab "$APP_NAME":2:once:"/usr/bin/startsrc -s \"${APP_NAME}\" >/dev/console 2>&1" fi elif [ "$DIST_OS" = "freebsd" ] ; then - eval echo `gettext 'Detected FreeBSD:'` + eval echo `gettext 'Detected FreeBSD:'` if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` exit 1 @@ -1360,7 +1365,7 @@ installdaemon() { } removedaemon() { - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then + if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then eval echo `gettext 'Must be root to perform this action.'` exit 1 else @@ -1458,7 +1463,7 @@ removedaemon() { if [ -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then /usr/sbin/rmitab $APP_NAME /usr/bin/rmssys -s $APP_NAME - fi + fi else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 @@ -1634,7 +1639,7 @@ docommand() { fi console "$@" ;; - + 'start') if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then macosxstart @@ -1648,12 +1653,12 @@ docommand() { start "$@" fi ;; - + 'stop') checkUser "" "$COMMAND" stopit "0" ;; - + 'restart') checkUser touchlock "$COMMAND" if [ ! -n "$FIXED_COMMAND" ] ; then @@ -1662,7 +1667,7 @@ docommand() { stopit "0" start "$@" ;; - + 'condrestart') checkUser touchlock "$COMMAND" if [ ! -n "$FIXED_COMMAND" ] ; then @@ -1671,7 +1676,7 @@ docommand() { stopit "1" start "$@" ;; - + 'pause') if [ -n "$PAUSABLE" ] then @@ -1680,7 +1685,7 @@ docommand() { showUsage "$COMMAND" fi ;; - + 'resume') if [ -n "$PAUSABLE" ] then @@ -1689,36 +1694,36 @@ docommand() { showUsage "$COMMAND" fi ;; - + 'status') status ;; - + 'install') installdaemon ;; - + 'remove') removedaemon ;; - + 'dump') checkUser "" "$COMMAND" dump ;; - + 'start_msg') # Internal command called by launchd on HP-UX. checkUser "" "$COMMAND" startmsg ;; - + 'stop_msg') # Internal command called by launchd on HP-UX. checkUser "" "$COMMAND" stopmsg ;; - + 'launchdinternal' | 'upstartinternal') if [ ! "$DIST_OS" = "macosx" -o ! -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then checkUser touchlock "$@" @@ -1730,7 +1735,7 @@ docommand() { fi launchinternal "$@" ;; - + *) showUsage "$COMMAND" ;; From 1fce74de9455e980d9af26ec83ebff9a808337ec Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 06:24:02 -0500 Subject: [PATCH 092/100] upgrade taunki wrapper to version 3.5.26 to take advantage of OS X yosemite bug fix --- concourse-server/scripts/concourse | 2740 +++++++++++++---------- concourse-server/wrapperlib/wrapper.jar | Bin 121317 -> 123204 bytes 2 files changed, 1502 insertions(+), 1238 deletions(-) diff --git a/concourse-server/scripts/concourse b/concourse-server/scripts/concourse index 35e82a9113..6078586770 100755 --- a/concourse-server/scripts/concourse +++ b/concourse-server/scripts/concourse @@ -16,7 +16,7 @@ #----------------------------------------------------------------------------- # These settings can be modified to fit the needs of your application -# Optimized for use with version 3.5.24 of the Wrapper. +# Optimized for use with version 3.5.26 of the Wrapper. # IMPORTANT - Please always stop and uninstall an application before making # any changes to this file. Failure to do so could remove the @@ -94,15 +94,32 @@ DETAIL_STATUS=false # port needs to be allocated prior to the user being changed. #RUN_AS_USER= +# Set the full path to the 'su' command (substitute user). +# NOTE - In case 'su' is not in the PATH, you can set the absolute path here, +# for example: +# SU_BIN=/bin/su +# NOTE - For Red Hat, the script will use '/sbin/runuser' if it is present and +# ignore the value of SU_BIN. +SU_BIN=su + +# Set option for 'su'. +# In case the user set in RUN_AS_USER has no bash set, the 'su' command will fail. +# The workaround for GNU/Linux system is to specify which bash to use with +# the '-s' option. +#SU_OPTS="-s /bin/bash" + # By default we show a detailed usage block. Uncomment to show brief usage. #BRIEF_USAGE=true # flag for using upstart when installing (rather than init.d rc.d) USE_UPSTART= +# OS service management tool: flag for using systemd when installing +USE_SYSTEMD= + # When installing on On Mac OSX platforms, the following domain will be used to # prefix the plist file name. -PLIST_DOMAIN=org.tanukisoftware.wrapper +PLIST_DOMAIN=org.cinchapi.concourse # The following two lines are used by the chkconfig command. Change as is # appropriate for your application. They should remain commented. @@ -166,62 +183,84 @@ perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g # Do not modify anything beyond this point #----------------------------------------------------------------------------- +# check if we are running under Cygwin terminal. +# Note: on some OS's (for example Solaris, MacOS), -o is not a valid parameter +# and it shows an error message. We redirect stderr to null so the error message +# doesn't show up. +CYGWIN=`uname -o 2>/dev/null` +if [ "$CYGWIN" = "Cygwin" ] + then + eval echo `gettext 'This script is not compatible with Cygwin. Please use the Wrapper batch files to control the Wrapper.'` + exit 1 +fi + if [ -n "$FIXED_COMMAND" ] -then - COMMAND="$FIXED_COMMAND" + then + COMMAND="$FIXED_COMMAND" else - COMMAND="$1" + COMMAND="$1" fi +# check if there is a parameter "sysd" +SYSD= +if [ $# -gt 1 ] ; then + if [ $2 = "sysd" ] ; then + SYSD=1 + fi +fi + +# default location for the service file +SYSTEMD_SERVICE_FILE="/etc/systemd/system/$APP_NAME.service" + # Required for HP-UX Startup if [ `uname -s` = "HP-UX" -o `uname -s` = "HP-UX64" ] ; then - PATH=$PATH:/usr/bin + PATH=$PATH:/usr/bin fi # Get the fully qualified path to the script case $0 in - /*) - SCRIPT="$0" - ;; - *) - PWD=`pwd` - SCRIPT="$PWD/$0" - ;; + /*) + SCRIPT="$0" + ;; + *) + PWD=`pwd` + SCRIPT="$PWD/$0" + ;; esac # Resolve the true real path without any sym links. CHANGED=true while [ "X$CHANGED" != "X" ] do - # Change spaces to ":" so the tokens can be parsed. - SAFESCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'` - # Get the real path to this script, resolving any symbolic links - TOKENS=`echo $SAFESCRIPT | sed -e 's;/; ;g'` - REALPATH= - for C in $TOKENS; do - # Change any ":" in the token back to a space. - C=`echo $C | sed -e 's;:; ;g'` - REALPATH="$REALPATH/$C" - # If REALPATH is a sym link, resolve it. Loop for nested links. - while [ -h "$REALPATH" ] ; do - LS="`ls -ld "$REALPATH"`" - LINK="`expr "$LS" : '.*-> \(.*\)$'`" - if expr "$LINK" : '/.*' > /dev/null; then - # LINK is absolute. - REALPATH="$LINK" - else - # LINK is relative. - REALPATH="`dirname "$REALPATH"`""/$LINK" - fi - done + # Change spaces to ":" so the tokens can be parsed. + SAFESCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'` + # Get the real path to this script, resolving any symbolic links + TOKENS=`echo $SAFESCRIPT | sed -e 's;/; ;g'` + REALPATH= + for C in $TOKENS; do + # Change any ":" in the token back to a space. + C=`echo $C | sed -e 's;:; ;g'` + REALPATH="$REALPATH/$C" + # If REALPATH is a sym link, resolve it. Loop for nested links. + while [ -h "$REALPATH" ] ; do + LS="`ls -ld "$REALPATH"`" + LINK="`expr "$LS" : '.*-> \(.*\)$'`" + if expr "$LINK" : '/.*' > /dev/null; then + # LINK is absolute. + REALPATH="$LINK" + else + # LINK is relative. + REALPATH="`dirname "$REALPATH"`""/$LINK" + fi done + done - if [ "$REALPATH" = "$SCRIPT" ] + if [ "$REALPATH" = "$SCRIPT" ] then - CHANGED="" - else - SCRIPT="$REALPATH" - fi + CHANGED="" + else + SCRIPT="$REALPATH" + fi done # Get the location of the script. @@ -233,26 +272,20 @@ REALDIR=`cd "${REALDIR}"; pwd` # the working directory is later changed. FIRST_CHAR=`echo $PIDDIR | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] -then - PIDDIR=$REALDIR/$PIDDIR + then + PIDDIR=$REALDIR/$PIDDIR fi # Same test for WRAPPER_CMD FIRST_CHAR=`echo $WRAPPER_CMD | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] -then - WRAPPER_CMD=$REALDIR/$WRAPPER_CMD + then + WRAPPER_CMD=$REALDIR/$WRAPPER_CMD fi # Same test for WRAPPER_CONF FIRST_CHAR=`echo $WRAPPER_CONF | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] -then - WRAPPER_CONF=$REALDIR/$WRAPPER_CONF -fi -# Same test for PREFS -FIRST_CHAR=`echo $PREFS | cut -c1,1` -if [ "$FIRST_CHAR" != "/" ] -then - PREFS=$REALDIR/$PREFS + then + WRAPPER_CONF=$REALDIR/$WRAPPER_CONF fi # Process ID @@ -266,192 +299,238 @@ LOCKFILE="$LOCKDIR/$APP_NAME" pid="" # Resolve the location of the 'ps' command -PSEXE="/usr/ucb/ps" - if [ ! -x "$PSEXE" ] +PS_BIN="/usr/ucb/ps" +if [ ! -x "$PS_BIN" ] + then + PS_BIN="/usr/bin/ps" + if [ ! -x "$PS_BIN" ] then - PSEXE="/usr/bin/ps" - if [ ! -x "$PSEXE" ] - then - PSEXE="/bin/ps" - if [ ! -x "$PSEXE" ] - then - eval echo `gettext 'Unable to locate "ps".'` - eval echo `gettext 'Please report this message along with the location of the command on your system.'` - exit 1 - fi - fi + PS_BIN="/bin/ps" + if [ ! -x "$PS_BIN" ] + then + eval echo `gettext 'Unable to locate "ps".'` + eval echo `gettext 'Please report this message along with the location of the command on your system.'` + exit 1 fi + fi +fi -TREXE="/usr/bin/tr" -if [ ! -x "$TREXE" ] -then - TREXE="/bin/tr" - if [ ! -x "$TREXE" ] +TR_BIN="/usr/bin/tr" +if [ ! -x "$TR_BIN" ] + then + TR_BIN="/bin/tr" + if [ ! -x "$TR_BIN" ] then - eval echo `gettext 'Unable to locate "tr".'` - eval echo `gettext 'Please report this message along with the location of the command on your system.'` - exit 1 - fi + eval echo `gettext 'Unable to locate "tr".'` + eval echo `gettext 'Please report this message along with the location of the command on your system.'` + exit 1 + fi fi # Resolve the os -DIST_OS=`uname -s | $TREXE "[A-Z]" "[a-z]" | $TREXE -d ' '` +DIST_OS=`uname -s | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` case "$DIST_OS" in - 'sunos') - DIST_OS="solaris" - ;; - 'hp-ux' | 'hp-ux64') - # HP-UX needs the XPG4 version of ps (for -o args) - DIST_OS="hpux" - UNIX95="" - export UNIX95 - ;; - 'darwin') - DIST_OS="macosx" - ;; - 'unix_sv') - DIST_OS="unixware" - ;; - 'os/390') - DIST_OS="zos" - ;; + 'sunos') + DIST_OS="solaris" + ;; + 'hp-ux' | 'hp-ux64') + # HP-UX needs the XPG4 version of ps (for -o args) + DIST_OS="hpux" + UNIX95="" + export UNIX95 + ;; + 'darwin') + DIST_OS="macosx" + ;; + 'unix_sv') + DIST_OS="unixware" + ;; + 'os/390') + DIST_OS="zos" + ;; esac +# Compare Versions $1<$2=0, $1==$2=1, $1>$2=2 +compareVersions () { + if [ "$1" = "$2" ] + then + return 1 + else + local i=1 + while true + do + local v1=`echo "$1" | cut -d '.' -f $i` + local v2=`echo "$2" | cut -d '.' -f $i` + if [ "X$v1" = "X" ] + then + if [ "X$v2" = "X" ] + then + return 1 + fi + v1="0" + elif [ "X$v2" = "X" ] + then + v2="0" + fi + if [ $v1 -lt $v2 ] + then + return 0 + elif [ $v1 -gt $v2 ] + then + return 2 + fi + i=`expr $i + 1` + done + fi +} + # Resolve the architecture if [ "$DIST_OS" = "macosx" ] -then - OS_VER=`sw_vers | grep 'ProductVersion:' | grep -o '[0-9]*\.[0-9]*\.[0-9]*\|[0-9]*\.[0-9]*'` - DIST_ARCH="universal" - if [[ "$OS_VER" < "10.5.0" ]] + then + OS_VER=`sw_vers | grep 'ProductVersion:' | grep -o '[0-9]*\.[0-9]*\.[0-9]*\|[0-9]*\.[0-9]*'` + DIST_ARCH="universal" + compareVersions "$OS_VER" "10.5.0" + if [[ $? < 1 ]] then - DIST_BITS="32" + DIST_BITS="32" + KEY_KEEP_ALIVE="OnDemand" + else + KEY_KEEP_ALIVE="KeepAlive" + if [ "X`/usr/sbin/sysctl -n hw.cpu64bit_capable`" == "X1" ] + then + DIST_BITS="64" else - if [ "X`sysctl -n hw.cpu64bit_capable`" == "X1" ] - then - DIST_BITS="64" - else - DIST_BITS="32" - fi + DIST_BITS="32" fi - APP_PLIST_BASE=${PLIST_DOMAIN}.${APP_NAME} - APP_PLIST=${APP_PLIST_BASE}.plist + fi + APP_PLIST_BASE=${PLIST_DOMAIN}.${APP_NAME} + APP_PLIST=${APP_PLIST_BASE}.plist else + if [ "$DIST_OS" = "linux" ] + then DIST_ARCH= - DIST_ARCH=`uname -p 2>/dev/null | $TREXE "[A-Z]" "[a-z]" | $TREXE -d ' '` - if [ "X$DIST_ARCH" = "X" ] + else + DIST_ARCH=`uname -p 2>/dev/null | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` + fi + if [ "X$DIST_ARCH" = "X" ] then - DIST_ARCH="unknown" - fi - if [ "$DIST_ARCH" = "unknown" ] + DIST_ARCH="unknown" + fi + if [ "$DIST_ARCH" = "unknown" ] then - DIST_ARCH=`uname -m 2>/dev/null | $TREXE "[A-Z]" "[a-z]" | $TREXE -d ' '` + DIST_ARCH=`uname -m 2>/dev/null | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` + fi + case "$DIST_ARCH" in + 'athlon' | 'i386' | 'i486' | 'i586' | 'i686') + DIST_ARCH="x86" + if [ "${DIST_OS}" = "solaris" ] ; then + DIST_BITS=`isainfo -b` + else + DIST_BITS="32" fi - case "$DIST_ARCH" in - 'athlon' | 'i386' | 'i486' | 'i586' | 'i686') - DIST_ARCH="x86" - if [ "${DIST_OS}" = "solaris" ] ; then - DIST_BITS=`isainfo -b` - else - DIST_BITS="32" - fi - ;; - 'amd64' | 'x86_64') - DIST_ARCH="x86" - DIST_BITS="64" - ;; - 'ia32') - DIST_ARCH="ia" - DIST_BITS="32" - ;; - 'ia64' | 'ia64n' | 'ia64w') - DIST_ARCH="ia" - DIST_BITS="64" - ;; - 'ip27') - DIST_ARCH="mips" - DIST_BITS="32" - ;; - 'power' | 'powerpc' | 'power_pc' | 'ppc64') - if [ "${DIST_ARCH}" = "ppc64" ] ; then - DIST_BITS="64" - else - DIST_BITS="32" - fi - DIST_ARCH="ppc" - if [ "${DIST_OS}" = "aix" ] ; then - if [ `getconf KERNEL_BITMODE` -eq 64 ]; then - DIST_BITS="64" - else - DIST_BITS="32" - fi - fi - ;; - 'pa_risc' | 'pa-risc') - DIST_ARCH="parisc" - if [ `getconf KERNEL_BITS` -eq 64 ]; then - DIST_BITS="64" - else - DIST_BITS="32" - fi - ;; - 'sun4u' | 'sparcv9' | 'sparc') - DIST_ARCH="sparc" - DIST_BITS=`isainfo -b` - ;; - '9000/800' | '9000/785') - DIST_ARCH="parisc" - if [ `getconf KERNEL_BITS` -eq 64 ]; then - DIST_BITS="64" - else - DIST_BITS="32" - fi - ;; - '2064' | '2066' | '2084' | '2086' | '2094' | '2096' | '2097' | '2098' | '2817') - DIST_ARCH="390" - DIST_BITS="64" - ;; - armv*) - if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then - DIST_ARCH="armel" - DIST_BITS="32" - else - DIST_ARCH="armhf" - DIST_BITS="32" - fi - ;; - esac + ;; + 'amd64' | 'x86_64') + DIST_ARCH="x86" + DIST_BITS="64" + ;; + 'ia32') + DIST_ARCH="ia" + DIST_BITS="32" + ;; + 'ia64' | 'ia64n' | 'ia64w') + DIST_ARCH="ia" + DIST_BITS="64" + ;; + 'ip27') + DIST_ARCH="mips" + DIST_BITS="32" + ;; + 'power' | 'powerpc' | 'power_pc' | 'ppc64') + if [ "${DIST_ARCH}" = "ppc64" ] ; then + DIST_BITS="64" + else + DIST_BITS="32" + fi + DIST_ARCH="ppc" + if [ "${DIST_OS}" = "aix" ] ; then + if [ `getconf KERNEL_BITMODE` -eq 64 ]; then + DIST_BITS="64" + else + DIST_BITS="32" + fi + fi + ;; + 'pa_risc' | 'pa-risc') + DIST_ARCH="parisc" + if [ `getconf KERNEL_BITS` -eq 64 ]; then + DIST_BITS="64" + else + DIST_BITS="32" + fi + ;; + 'sun4u' | 'sparcv9' | 'sparc') + DIST_ARCH="sparc" + DIST_BITS=`isainfo -b` + ;; + '9000/800' | '9000/785') + DIST_ARCH="parisc" + if [ `getconf KERNEL_BITS` -eq 64 ]; then + DIST_BITS="64" + else + DIST_BITS="32" + fi + ;; + '2064' | '2066' | '2084' | '2086' | '2094' | '2096' | '2097' | '2098' | '2817') + DIST_ARCH="390" + DIST_BITS="64" + ;; + armv*) + if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then + DIST_ARCH="armel" + DIST_BITS="32" + else + DIST_ARCH="armhf" + DIST_BITS="32" + fi + ;; + esac fi # OSX always places Java in the same location so we can reliably set JAVA_HOME if [ "$DIST_OS" = "macosx" ] -then - if [ -z "$JAVA_HOME" ]; then - JAVA_HOME=`/usr/libexec/java_home`; export JAVA_HOME + then + if [ -z "$JAVA_HOME" ]; then + if [ -x /usr/libexec/java_home ]; then + JAVA_HOME=`/usr/libexec/java_home`; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi + fi fi # Test Echo ECHOTEST=`echo -n "x"` if [ "$ECHOTEST" = "x" ] -then - ECHOOPT="-n " + then + ECHOOPT="-n " else - ECHOOPT="" + ECHOOPT="" fi + gettext() { - "$WRAPPER_CMD" --translate "$1" "$WRAPPER_CONF" 2>/dev/null - if [ $? != 0 ] ; then - echo "$1" - fi + "$WRAPPER_CMD" --translate "$1" "$WRAPPER_CONF" 2>/dev/null + if [ $? != 0 ] ; then + echo "$1" + fi } outputFile() { - if [ -f "$1" ] + if [ -f "$1" ] then - eval echo `gettext ' $1 Found but not executable.'`; - else - echo " $1" - fi + eval echo `gettext ' $1 Found but not executable.'`; + else + echo " $1" + fi } # Decide on the wrapper binary to use. @@ -461,1285 +540,1470 @@ outputFile() { # look for the default. WRAPPER_TEST_CMD="" if [ -f "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BITS" ] -then - WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BITS" - if [ ! -x "$WRAPPER_TEST_CMD" ] + then + WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BITS" + if [ ! -x "$WRAPPER_TEST_CMD" ] then - chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null - fi - if [ -x "$WRAPPER_TEST_CMD" ] + chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null + fi + if [ -x "$WRAPPER_TEST_CMD" ] then - WRAPPER_CMD="$WRAPPER_TEST_CMD" - else - outputFile "$WRAPPER_TEST_CMD" - WRAPPER_TEST_CMD="" - fi + WRAPPER_CMD="$WRAPPER_TEST_CMD" + else + outputFile "$WRAPPER_TEST_CMD" + WRAPPER_TEST_CMD="" + fi fi if [ -f "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" -a -z "$WRAPPER_TEST_CMD" ] -then - WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" - if [ ! -x "$WRAPPER_TEST_CMD" ] + then + WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" + if [ ! -x "$WRAPPER_TEST_CMD" ] then - chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null - fi - if [ -x "$WRAPPER_TEST_CMD" ] + chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null + fi + if [ -x "$WRAPPER_TEST_CMD" ] then - WRAPPER_CMD="$WRAPPER_TEST_CMD" - else - outputFile "$WRAPPER_TEST_CMD" - WRAPPER_TEST_CMD="" - fi + WRAPPER_CMD="$WRAPPER_TEST_CMD" + else + outputFile "$WRAPPER_TEST_CMD" + WRAPPER_TEST_CMD="" + fi fi if [ -f "$WRAPPER_CMD" -a -z "$WRAPPER_TEST_CMD" ] -then - WRAPPER_TEST_CMD="$WRAPPER_CMD" - if [ ! -x "$WRAPPER_TEST_CMD" ] + then + WRAPPER_TEST_CMD="$WRAPPER_CMD" + if [ ! -x "$WRAPPER_TEST_CMD" ] then - chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null - fi - if [ -x "$WRAPPER_TEST_CMD" ] + chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null + fi + if [ -x "$WRAPPER_TEST_CMD" ] then - WRAPPER_CMD="$WRAPPER_TEST_CMD" - else - outputFile "$WRAPPER_TEST_CMD" - WRAPPER_TEST_CMD="" - fi + WRAPPER_CMD="$WRAPPER_TEST_CMD" + else + outputFile "$WRAPPER_TEST_CMD" + WRAPPER_TEST_CMD="" + fi fi if [ -z "$WRAPPER_TEST_CMD" ] -then - eval echo `gettext 'Unable to locate any of the following binaries:'` - outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BITS" - if [ ! "$DIST_BITS" = "32" ] + then + eval echo `gettext 'Unable to locate any of the following binaries:'` + outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$DIST_BITS" + if [ ! "$DIST_BITS" = "32" ] then - outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" - fi - outputFile "$WRAPPER_CMD" + outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" + fi + outputFile "$WRAPPER_CMD" - exit 1 + exit 1 fi # Build the nice clause if [ "X$PRIORITY" = "X" ] -then - CMDNICE="" + then + CMDNICE="" else - CMDNICE="nice -$PRIORITY" + CMDNICE="nice -$PRIORITY" fi # Build the anchor file clause. if [ "X$IGNORE_SIGNALS" = "X" ] -then - ANCHORPROP= - IGNOREPROP= + then + ANCHORPROP= + IGNOREPROP= else - ANCHORPROP=wrapper.anchorfile=\"$ANCHORFILE\" - IGNOREPROP=wrapper.ignore_signals=TRUE + ANCHORPROP=wrapper.anchorfile=\"$ANCHORFILE\" + IGNOREPROP=wrapper.ignore_signals=TRUE fi # Build the status file clause. if [ "X$DETAIL_STATUS$WAIT_FOR_STARTED_STATUS" = "X" ] -then - STATUSPROP= + then + STATUSPROP= else - STATUSPROP="wrapper.statusfile=\"$STATUSFILE\" wrapper.java.statusfile=\"$JAVASTATUSFILE\"" + STATUSPROP="wrapper.statusfile=\"$STATUSFILE\" wrapper.java.statusfile=\"$JAVASTATUSFILE\"" fi # Build the command file clause. if [ -n "$PAUSABLE" ] -then - COMMANDPROP="wrapper.commandfile=\"$COMMANDFILE\" wrapper.pausable=TRUE" + then + COMMANDPROP="wrapper.commandfile=\"$COMMANDFILE\" wrapper.pausable=TRUE" else - COMMANDPROP= + COMMANDPROP= fi if [ ! -n "$WAIT_FOR_STARTED_STATUS" ] -then - WAIT_FOR_STARTED_STATUS=true + then + WAIT_FOR_STARTED_STATUS=true fi if [ $WAIT_FOR_STARTED_STATUS = true ] ; then - DETAIL_STATUS=true + DETAIL_STATUS=true fi # Build the lock file clause. Only create a lock file if the lock directory exists on this platform. LOCKPROP= if [ -d $LOCKDIR ] -then - if [ -w $LOCKDIR ] + then + if [ -w $LOCKDIR ] then - LOCKPROP=wrapper.lockfile=\"$LOCKFILE\" - fi + LOCKPROP=wrapper.lockfile=\"$LOCKFILE\" + fi fi # Decide on run levels. RUN_LEVEL_S_DIST_OS_TMP=`eval "echo \$\{RUN_LEVEL_S_${DIST_OS}\}"` RUN_LEVEL_S_DIST_OS=`eval "echo ${RUN_LEVEL_S_DIST_OS_TMP}"` if [ "X${RUN_LEVEL_S_DIST_OS}" != "X" ] ; then - APP_RUN_LEVEL_S=${RUN_LEVEL_S_DIST_OS} + APP_RUN_LEVEL_S=${RUN_LEVEL_S_DIST_OS} elif [ "X$RUN_LEVEL_S" != "X" ] ; then - APP_RUN_LEVEL_S=$RUN_LEVEL_S + APP_RUN_LEVEL_S=$RUN_LEVEL_S else - APP_RUN_LEVEL_S=$RUN_LEVEL + APP_RUN_LEVEL_S=$RUN_LEVEL fi APP_RUN_LEVEL_S_CHECK=`echo "$APP_RUN_LEVEL_S" | sed "s/[(0-9)*]/0/g"` if [ "X${APP_RUN_LEVEL_S_CHECK}" != "X00" ] ; then - eval echo `gettext 'Run level \"${APP_RUN_LEVEL_S}\" must be numeric and have a length of two \(00-99\).'` - exit 1; + eval echo `gettext 'Run level \"${APP_RUN_LEVEL_S}\" must be numeric and have a length of two \(00-99\).'` + exit 1; fi RUN_LEVEL_K_DIST_OS_TMP=`eval "echo \$\{RUN_LEVEL_K_${DIST_OS}\}"` RUN_LEVEL_K_DIST_OS=`eval "echo ${RUN_LEVEL_K_DIST_OS_TMP}"` if [ "X${RUN_LEVEL_K_DIST_OS}" != "X" ] ; then - APP_RUN_LEVEL_K=${RUN_LEVEL_K_DIST_OS} + APP_RUN_LEVEL_K=${RUN_LEVEL_K_DIST_OS} elif [ "X$RUN_LEVEL_K" != "X" ] ; then - APP_RUN_LEVEL_K=$RUN_LEVEL_K + APP_RUN_LEVEL_K=$RUN_LEVEL_K else - APP_RUN_LEVEL_K=$RUN_LEVEL + APP_RUN_LEVEL_K=$RUN_LEVEL fi APP_RUN_LEVEL_K_CHECK=`echo "$APP_RUN_LEVEL_K" | sed "s/[(0-9)*]/0/g"` if [ "X${APP_RUN_LEVEL_K_CHECK}" != "X00" ] ; then - eval echo `gettext 'Run level \"${APP_RUN_LEVEL_K}\" must be numeric and have a length of two \(00-99\).'` - exit 1; + eval echo `gettext 'Run level \"${APP_RUN_LEVEL_K}\" must be numeric and have a length of two \(00-99\).'` + exit 1; fi prepAdditionalParams() { - ADDITIONAL_PARA="" - if [ -n "$PASS_THROUGH" ] ; then - ADDITIONAL_PARA="--" - fi - while [ -n "$1" ] ; do - ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" - shift - done + ADDITIONAL_PARA="" + if [ -n "$PASS_THROUGH" ] ; then + ADDITIONAL_PARA="--" + fi + while [ -n "$1" ] ; do + ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" + shift + done } checkUser() { - # $1 touchLock flag - # $2.. [command] args + # $1 touchLock flag + # $2.. [command] args - # Check the configured user. If necessary rerun this script as the desired user. - if [ "X$RUN_AS_USER" != "X" ] + # Check the configured user. If necessary rerun this script as the desired user. + if [ "X$RUN_AS_USER" != "X" ] then - # Resolve the location of the 'id' command - IDEXE="/usr/xpg4/bin/id" - if [ ! -x "$IDEXE" ] + # Resolve the location of the 'id' command + ID_BIN="/usr/xpg4/bin/id" + if [ ! -x "$ID_BIN" ] + then + ID_BIN="/usr/bin/id" + if [ ! -x "$ID_BIN" ] then - IDEXE="/usr/bin/id" - if [ ! -x "$IDEXE" ] - then - eval echo `gettext 'Unable to locate "id".'` - eval echo `gettext 'Please report this message along with the location of the command on your system.'` - exit 1 - fi - fi + eval echo `gettext 'Unable to locate "id".'` + eval echo `gettext 'Please report this message along with the location of the command on your system.'` + exit 1 + fi + fi - if [ "`$IDEXE -u -n`" = "$RUN_AS_USER" ] - then - # Already running as the configured user. Avoid password prompts by not calling su. - RUN_AS_USER="" - fi + if [ "`$ID_BIN -u -n`" = "$RUN_AS_USER" ] + then + # Already running as the configured user. Avoid password prompts by not calling su. + RUN_AS_USER="" fi - if [ "X$RUN_AS_USER" != "X" ] + fi + if [ "X$RUN_AS_USER" != "X" ] then - if [ "`$IDEXE -u -n "$RUN_AS_USER" 2>/dev/null`" != "$RUN_AS_USER" ] - then - eval echo `gettext 'User $RUN_AS_USER does not exist.'` - exit 1 - fi + if [ "`$ID_BIN -u -n "$RUN_AS_USER" 2>/dev/null`" != "$RUN_AS_USER" ] + then + eval echo `gettext 'User $RUN_AS_USER does not exist.'` + exit 1 + fi - # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be - # able to create the lock file. The Wrapper will be able to update this file once it - # is created but will not be able to delete it on shutdown. If $1 is set then - # the lock file should be created for the current command - if [ "X$LOCKPROP" != "X" ] + # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be + # able to create the lock file. The Wrapper will be able to update this file once it + # is created but will not be able to delete it on shutdown. If $1 is set then + # the lock file should be created for the current command + if [ "X$LOCKPROP" != "X" ] + then + if [ "X$1" != "X" ] then - if [ "X$1" != "X" ] - then - # Resolve the primary group - RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1` - if [ "X$RUN_AS_GROUP" = "X" ] - then - RUN_AS_GROUP=$RUN_AS_USER - fi - touch $LOCKFILE - chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE - fi + # Resolve the primary group + RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1` + if [ "X$RUN_AS_GROUP" = "X" ] + then + RUN_AS_GROUP=$RUN_AS_USER fi + touch $LOCKFILE + chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE + fi + fi - # Still want to change users, recurse. This means that the user will only be - # prompted for a password once. Variables shifted by 1 - shift + # Still want to change users, recurse. This means that the user will only be + # prompted for a password once. Variables shifted by 1 + shift - # Wrap the parameters so they can be passed. - ADDITIONAL_PARA="" - while [ -n "$1" ] ; do - ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" - shift - done + # Wrap the parameters so they can be passed. + ADDITIONAL_PARA="" + while [ -n "$1" ] ; do + ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" + shift + done - # Use "runuser" if this exists. runuser should be used on RedHat in preference to su. - # - if test -f "/sbin/runuser" - then - /sbin/runuser - $RUN_AS_USER -c "\"$REALPATH\" $ADDITIONAL_PARA" - else - su - $RUN_AS_USER -c "\"$REALPATH\" $ADDITIONAL_PARA" - fi - RUN_AS_USER_EXITCODE=$? - # Now that we are the original user again, we may need to clean up the lock file. - if [ "X$LOCKPROP" != "X" ] + # Use "runuser" if this exists. runuser should be used on RedHat in preference to su. + # + if test -f "/sbin/runuser" + then + /sbin/runuser - $RUN_AS_USER -c "\"$REALPATH\" $ADDITIONAL_PARA" + else + $SU_BIN - $RUN_AS_USER -c "\"$REALPATH\" $ADDITIONAL_PARA" $SU_OPTS + fi + RUN_AS_USER_EXITCODE=$? + + # we check if the previous command has failed + if [ $RUN_AS_USER_EXITCODE -ne 0 ] + then + eval echo `gettext 'Could not run the command using user \"$RUN_AS_USER\".'` + eval echo `gettext 'Advice: Make sure the user \"$RUN_AS_USER\" has a shell.'` + eval echo `gettext ' If user \"$RUN_AS_USER\" has a no shell, you can specify one using SU_OPTS if your platform support it.'` + eval echo `gettext ' For example, at the top of this script you can set: SU_OPTS=\"-s /bin/bash\".'` + eval echo `gettext ' Another workaround would be to use a OS service management tool if available on your platform.'` + eval echo `gettext ' OS service management tools supported by this script are described at the top of this script.'` + fi + + # Now that we are the original user again, we may need to clean up the lock file. + if [ "X$LOCKPROP" != "X" ] + then + getpid + if [ "X$pid" = "X" ] then - getpid - if [ "X$pid" = "X" ] - then - # Wrapper is not running so make sure the lock file is deleted. - if [ -f "$LOCKFILE" ] - then - rm "$LOCKFILE" - fi - fi + # Wrapper is not running so make sure the lock file is deleted. + if [ -f "$LOCKFILE" ] + then + rm "$LOCKFILE" fi - - exit $RUN_AS_USER_EXITCODE + fi fi + + exit $RUN_AS_USER_EXITCODE + fi } getpid() { - pid="" - if [ -f "$PIDFILE" ] + pid="" + if [ -f "$PIDFILE" ] then - if [ -r "$PIDFILE" ] + if [ -r "$PIDFILE" ] + then + pid=`cat "$PIDFILE"` + if [ "X$pid" != "X" ] then - pid=`cat "$PIDFILE"` - if [ "X$pid" != "X" ] - then - if [ "X$PIDFILE_CHECK_PID" != "X" ] - then - # It is possible that 'a' process with the pid exists but that it is not the - # correct process. This can happen in a number of cases, but the most - # common is during system startup after an unclean shutdown. - # The ps statement below looks for the specific wrapper command running as - # the pid. If it is not found then the pid file is considered to be stale. - case "$DIST_OS" in - 'freebsd') - pidtest=`$PSEXE -p $pid -o args | tail -1` - if [ "X$pidtest" = "XCOMMAND" ] - then - pidtest="" - fi - ;; - 'macosx') - pidtest=`$PSEXE -ww -p $pid -o command | grep -F "$WRAPPER_CMD" | tail -1` - ;; - 'solaris') - if [ -f "/usr/bin/pargs" ] - then - pidtest=`pargs $pid | fgrep "$WRAPPER_CMD" | tail -1` - else - case "$PSEXE" in - '/usr/ucb/ps') - pidtest=`$PSEXE -auxww $pid | fgrep "$WRAPPER_CMD" | tail -1` - ;; - '/usr/bin/ps') - TRUNCATED_CMD=`$PSEXE -o comm -p $pid | tail -1` - COUNT=`echo $TRUNCATED_CMD | wc -m` - COUNT=`echo ${COUNT}` - COUNT=`expr $COUNT - 1` - TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` - pidtest=`$PSEXE -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` - ;; - '/bin/ps') - TRUNCATED_CMD=`$PSEXE -o comm -p $pid | tail -1` - COUNT=`echo $TRUNCATED_CMD | wc -m` - COUNT=`echo ${COUNT}` - COUNT=`expr $COUNT - 1` - TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` - pidtest=`$PSEXE -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` - ;; - *) - echo "Unsupported ps command $PSEXE" - exit 1 - ;; - esac - fi - ;; - 'hpux') - pidtest=`$PSEXE -p $pid -x -o args | grep -F "$WRAPPER_CMD" | tail -1` - ;; - *) - pidtest=`$PSEXE -p $pid -o args | grep -F "$WRAPPER_CMD" | tail -1` - ;; - esac - else - # Check to see whether the pid exists as a running process, but in this mode, don't check what that pid is. - case "$DIST_OS" in - 'solaris') - case "$PSEXE" in - '/usr/ucb/ps') - pidtest=`$PSEXE $pid | grep "$pid" | awk '{print $1}' | tail -1` - ;; - '/usr/bin/ps') - pidtest=`$PSEXE -p $pid -o pid | grep "$pid" | tail -1` - ;; - '/bin/ps') - pidtest=`$PSEXE -p $pid -o pid | grep "$pid" | tail -1` - ;; - *) - echo "Unsupported ps command $PSEXE" - exit 1 - ;; - esac - ;; - *) - pidtest=`$PSEXE -p $pid -o pid | grep "$pid" | tail -1` - ;; - esac - fi - - if [ "X$pidtest" = "X" ] - then - # This is a stale pid file. - rm -f "$PIDFILE" - eval echo `gettext 'Removed stale pid file: $PIDFILE'` - pid="" - fi + if [ "X$PIDFILE_CHECK_PID" != "X" ] + then + # It is possible that 'a' process with the pid exists but that it is not the + # correct process. This can happen in a number of cases, but the most + # common is during system startup after an unclean shutdown. + # The ps statement below looks for the specific wrapper command running as + # the pid. If it is not found then the pid file is considered to be stale. + case "$DIST_OS" in + 'freebsd') + pidtest=`$PS_BIN -p $pid -o args | tail -1` + if [ "X$pidtest" = "XCOMMAND" ] + then + pidtest="" fi + ;; + 'macosx') + pidtest=`$PS_BIN -ww -p $pid -o command | grep -F "$WRAPPER_CMD" | tail -1` + ;; + 'solaris') + if [ -f "/usr/bin/pargs" ] + then + pidtest=`pargs $pid | fgrep "$WRAPPER_CMD" | tail -1` + else + case "$PS_BIN" in + '/usr/ucb/ps') + pidtest=`$PS_BIN -auxww $pid | fgrep "$WRAPPER_CMD" | tail -1` + ;; + '/usr/bin/ps') + TRUNCATED_CMD=`$PS_BIN -o comm -p $pid | tail -1` + COUNT=`echo $TRUNCATED_CMD | wc -m` + COUNT=`echo ${COUNT}` + COUNT=`expr $COUNT - 1` + TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` + pidtest=`$PS_BIN -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` + ;; + '/bin/ps') + TRUNCATED_CMD=`$PS_BIN -o comm -p $pid | tail -1` + COUNT=`echo $TRUNCATED_CMD | wc -m` + COUNT=`echo ${COUNT}` + COUNT=`expr $COUNT - 1` + TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` + pidtest=`$PS_BIN -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` + ;; + *) + echo "Unsupported ps command $PS_BIN" + exit 1 + ;; + esac + fi + ;; + 'hpux') + pidtest=`$PS_BIN -p $pid -x -o args | grep -F "$WRAPPER_CMD" | tail -1` + ;; + *) + pidtest=`$PS_BIN -p $pid -o args | grep -F "$WRAPPER_CMD" | tail -1` + ;; + esac else - eval echo `gettext 'Cannot read $PIDFILE.'` - exit 1 + # Check to see whether the pid exists as a running process, but in this mode, don't check what that pid is. + case "$DIST_OS" in + 'solaris') + case "$PS_BIN" in + '/usr/ucb/ps') + pidtest=`$PS_BIN $pid | grep "$pid" | awk '{print $1}' | tail -1` + ;; + '/usr/bin/ps') + pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` + ;; + '/bin/ps') + pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` + ;; + *) + echo "Unsupported ps command $PS_BIN" + exit 1 + ;; + esac + ;; + *) + pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` + ;; + esac + fi + + if [ "X$pidtest" = "X" ] + then + # This is a stale pid file. + rm -f "$PIDFILE" + eval echo `gettext 'Removed stale pid file: $PIDFILE'` + pid="" fi + fi + else + eval echo `gettext 'Cannot read $PIDFILE.'` + exit 1 fi + fi } getstatus() { - STATUS= - if [ -f "$STATUSFILE" ] + STATUS= + if [ -f "$STATUSFILE" ] then - if [ -r "$STATUSFILE" ] - then - STATUS=`cat "$STATUSFILE"` - fi + if [ -r "$STATUSFILE" ] + then + STATUS=`cat "$STATUSFILE"` fi - if [ "X$STATUS" = "X" ] + fi + if [ "X$STATUS" = "X" ] then - STATUS="Unknown" - fi + STATUS="Unknown" + fi - JAVASTATUS= - if [ -f "$JAVASTATUSFILE" ] + JAVASTATUS= + if [ -f "$JAVASTATUSFILE" ] then - if [ -r "$JAVASTATUSFILE" ] - then - JAVASTATUS=`cat "$JAVASTATUSFILE"` - fi + if [ -r "$JAVASTATUSFILE" ] + then + JAVASTATUS=`cat "$JAVASTATUSFILE"` fi - if [ "X$JAVASTATUS" = "X" ] + fi + if [ "X$JAVASTATUS" = "X" ] then - JAVASTATUS="Unknown" - fi + JAVASTATUS="Unknown" + fi } testpid() { - case "$DIST_OS" in - 'solaris') - case "$PSEXE" in - '/usr/ucb/ps') - pid=`$PSEXE $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` - ;; - '/usr/bin/ps') - pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` - ;; - '/bin/ps') - pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` - ;; - *) - echo "Unsupported ps command $PSEXE" - exit 1 - ;; - esac - ;; - *) - pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` 2>/dev/null - ;; + case "$DIST_OS" in + 'solaris') + case "$PS_BIN" in + '/usr/ucb/ps') + pid=`$PS_BIN $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` + ;; + '/usr/bin/ps') + pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` + ;; + '/bin/ps') + pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` + ;; + *) + echo "Unsupported ps command $PS_BIN" + exit 1 + ;; esac - if [ "X$pid" = "X" ] + ;; + *) + pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` 2>/dev/null + ;; + esac + if [ "X$pid" = "X" ] then - # Process is gone so remove the pid file. - rm -f "$PIDFILE" - pid="" - fi + # Process is gone so remove the pid file. + rm -f "$PIDFILE" + pid="" + fi } launchdtrap() { - stopit - exit + stopit + exit } waitforwrapperstop() { + getpid + while [ "X$pid" != "X" ] ; do + sleep 1 getpid - while [ "X$pid" != "X" ] ; do - sleep 1 - getpid - done + done } launchinternal() { - getpid - trap launchdtrap TERM - if [ "X$pid" = "X" ] + getpid + trap launchdtrap TERM + if [ "X$pid" = "X" ] then - prepAdditionalParams "$@" + prepAdditionalParams "$@" - # The string passed to eval must handles spaces in paths correctly. - COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.24 $ADDITIONAL_PARA" - eval $COMMAND_LINE - else - eval echo `gettext '$APP_LONG_NAME is already running.'` - exit 1 - fi - # launchd expects that this script stay up and running so we need to do our own monitoring of the Wrapper process. - if [ $WAIT_FOR_STARTED_STATUS = true ] + # The string passed to eval must handles spaces in paths correctly. + COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.26 $ADDITIONAL_PARA" + eval $COMMAND_LINE + else + eval echo `gettext '$APP_LONG_NAME is already running.'` + exit 1 + fi + # launchd expects that this script stay up and running so we need to do our own monitoring of the Wrapper process. + if [ $WAIT_FOR_STARTED_STATUS = true ] then - waitforwrapperstop - fi + waitforwrapperstop + fi } console() { - eval echo `gettext 'Running $APP_LONG_NAME...'` - getpid - if [ "X$pid" = "X" ] + eval echo `gettext 'Running $APP_LONG_NAME...'` + getpid + if [ "X$pid" = "X" ] then - trap '' 3 + trap '' 3 - prepAdditionalParams "$@" + prepAdditionalParams "$@" - # The string passed to eval must handles spaces in paths correctly. - COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" $ANCHORPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.24 $ADDITIONAL_PARA" - eval $COMMAND_LINE - else - eval echo `gettext '$APP_LONG_NAME is already running.'` - exit 1 - fi + # The string passed to eval must handles spaces in paths correctly. + COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" $ANCHORPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.26 $ADDITIONAL_PARA" + eval $COMMAND_LINE + else + eval echo `gettext '$APP_LONG_NAME is already running.'` + exit 1 + fi } waitforjavastartup() { + getstatus + eval echo $ECHOOPT `gettext 'Waiting for $APP_LONG_NAME...'` + + # Wait until the timeout or we have something besides Unknown. + counter=15 + while [ "$JAVASTATUS" = "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do + echo $ECHOOPT"." + sleep 1 getstatus - eval echo $ECHOOPT `gettext 'Waiting for $APP_LONG_NAME...'` - - # Wait until the timeout or we have something besides Unknown. - counter=15 - while [ "$JAVASTATUS" = "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do - echo $ECHOOPT"." - sleep 1 - getstatus - counter=`expr $counter - 1` - done - - if [ -n "$WAIT_FOR_STARTED_TIMEOUT" ] ; then - counter=$WAIT_FOR_STARTED_TIMEOUT - else - counter=120 - fi - while [ "$JAVASTATUS" != "STARTED" -a "$JAVASTATUS" != "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do - echo $ECHOOPT"." - sleep 1 - getstatus - counter=`expr $counter - 1` - done - if [ "X$ECHOOPT" != "X" ] ; then - echo "" - fi + counter=`expr $counter - 1` + done + + if [ -n "$WAIT_FOR_STARTED_TIMEOUT" ] ; then + counter=$WAIT_FOR_STARTED_TIMEOUT + else + counter=120 + fi + while [ "$JAVASTATUS" != "STARTED" -a "$JAVASTATUS" != "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do + echo $ECHOOPT"." + sleep 1 + getstatus + counter=`expr $counter - 1` + done + if [ "X$ECHOOPT" != "X" ] ; then + echo "" + fi } startwait() { - if [ $WAIT_FOR_STARTED_STATUS = true ] + if [ $WAIT_FOR_STARTED_STATUS = true ] then - waitforjavastartup - fi - # Sleep for a few seconds to allow for intialization if required - # then test to make sure we're still running. - # - i=0 - while [ $i -lt $WAIT_AFTER_STARTUP ] - do - sleep 1 - echo $ECHOOPT"." - i=`expr $i + 1` - done - if [ $WAIT_AFTER_STARTUP -gt 0 -o $WAIT_FOR_STARTED_STATUS = true ] + waitforjavastartup + fi + # Sleep for a few seconds to allow for intialization if required + # then test to make sure we're still running. + # + i=0 + while [ $i -lt $WAIT_AFTER_STARTUP ] + do + sleep 1 + echo $ECHOOPT"." + i=`expr $i + 1` + done + if [ $WAIT_AFTER_STARTUP -gt 0 -o $WAIT_FOR_STARTED_STATUS = true ] then - getpid - if [ "X$pid" = "X" ] - then - eval echo `gettext ' WARNING: $APP_LONG_NAME may have failed to start.'` - exit 1 - else - eval echo `gettext ' running: PID:$pid'` - fi + getpid + if [ "X$pid" = "X" ] + then + eval echo `gettext ' WARNING: $APP_LONG_NAME may have failed to start.'` + exit 1 else - echo "" + eval echo `gettext ' running: PID:$pid'` fi + else + echo "" + fi } -macosxstart() { - # The daemon has been installed. - eval echo `gettext 'Starting $APP_LONG_NAME. Detected Mac OSX and installed launchd daemon.'` - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then - eval echo `gettext 'Must be root to perform this action.'` - exit 1 - fi +mustBeRootOrExit() { + if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then + eval echo `gettext 'Must be root to perform this action.'` + exit 1 + fi +} - getpid - if [ "X$pid" != "X" ] ; then - eval echo `gettext '$APP_LONG_NAME is already running.'` - exit 1 - fi - # If the daemon was just installed, it may not be loaded. - LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` - if [ "X${LOADED_PLIST}" = "X" ] ; then - launchctl load /Library/LaunchDaemons/${APP_PLIST} - fi - # If launchd is set to run the daemon already at Load, we don't need to call start - getpid - if [ "X$pid" == "X" ] ; then - launchctl start ${APP_PLIST_BASE} - fi +macosxStart() { + # The daemon has been installed. + eval echo `gettext 'Starting $APP_LONG_NAME. Detected Mac OSX and installed launchd daemon.'` + mustBeRootOrExit - startwait + getpid + if [ "X$pid" != "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is already running.'` + exit 1 + fi + + # If the daemon was just installed, it may not be loaded. + LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` + if [ "X${LOADED_PLIST}" = "X" ] ; then + launchctl load /Library/LaunchDaemons/${APP_PLIST} + fi + # If launchd is set to run the daemon already at Load, we don't need to call start + getpid + if [ "X$pid" = "X" ] ; then + launchctl start ${APP_PLIST_BASE} + fi + + startwait +} + +macosxStop() { + # The daemon should be running. + eval echo `gettext 'Stopping $APP_LONG_NAME...'` + mustBeRootOrExit + + getpid + if [ "X$pid" = "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is not running.'` + exit 1 + else + launchctl stop ${APP_PLIST_BASE} + fi +} + +macosxRestart() { + # The daemon should be running. + eval echo `gettext 'Restarting $APP_LONG_NAME...'` + mustBeRootOrExit + + getpid + if [ "X$pid" = "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is not running.'` + exit 1 + else + launchctl unload "/Library/LaunchDaemons/${APP_PLIST}" + launchctl load "/Library/LaunchDaemons/${APP_PLIST}" + fi + + startwait } upstartstart() { - # The daemon has been installed. - eval echo `gettext 'Starting $APP_LONG_NAME. Detected Linux and installed upstart.'` - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then - eval echo `gettext 'Must be root to perform this action.'` - exit 1 - fi + # The daemon has been installed. + eval echo `gettext 'Starting $APP_LONG_NAME. Detected Linux and installed upstart.'` + mustBeRootOrExit - getpid - if [ "X$pid" != "X" ] ; then - eval echo `gettext '$APP_LONG_NAME is already running.'` - exit 1 - fi + getpid + if [ "X$pid" != "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is already running.'` + exit 1 + fi + + /sbin/start ${APP_NAME} + + startwait +} + +upstartStop() { + # The daemon has been installed. + eval echo `gettext 'Stopping $APP_LONG_NAME...'` + mustBeRootOrExit + + getpid + if [ "X$pid" = "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is not running.'` + exit 1 + fi + + /sbin/stop ${APP_NAME} +} + +upstartRestart() { + # The daemon has been installed. + eval echo `gettext 'Restarting $APP_LONG_NAME...'` + mustBeRootOrExit + + getpid + if [ "X$pid" = "X" ] ; then + eval echo `gettext '$APP_LONG_NAME is not running.'` + exit 1 + fi + + /sbin/restart ${APP_NAME} + + startwait +} + +systemdInstall() { + eval echo `gettext ' Installing the $APP_LONG_NAME daemon using systemd...'` + if [ -f "${REALDIR}/${APP_NAME}.service" ] ; then + eval echo `gettext ' a custom service file ${APP_NAME}.service found'` + cp "${REALDIR}/${APP_NAME}.service" "${SYSTEMD_SERVICE_FILE}" + else + eval echo `gettext ' creating default service file...'` + echo "[Unit]" > "${SYSTEMD_SERVICE_FILE}" + echo "Description=${APP_LONG_NAME}" >> "${SYSTEMD_SERVICE_FILE}" + echo "After=syslog.target" >> "${SYSTEMD_SERVICE_FILE}" + echo "" >> "${SYSTEMD_SERVICE_FILE}" + echo "[Service]" >> "${SYSTEMD_SERVICE_FILE}" + echo "Type=forking" >> "${SYSTEMD_SERVICE_FILE}" + echo "ExecStart=${REALPATH} start sysd" >> "${SYSTEMD_SERVICE_FILE}" + echo "ExecStop=${REALPATH} stop sysd" >> "${SYSTEMD_SERVICE_FILE}" + echo "" >> "${SYSTEMD_SERVICE_FILE}" + echo "[Install]" >> "${SYSTEMD_SERVICE_FILE}" + echo "WantedBy=multi-user.target" >> "${SYSTEMD_SERVICE_FILE}" + + systemctl daemon-reload + systemctl enable "${APP_NAME}" + fi +} + +systemdStart() { + # check if the service file is present + if [ -f "${SYSTEMD_SERVICE_FILE}" ] ; then + eval echo `gettext 'Reading file ${SYSTEMD_SERVICE_FILE}'` + else + eval echo `gettext 'No service file detected. Did you install the service?'` + exit 1 + fi + + systemctl start $APP_NAME + if [ $? -ne 0 ] ; then + eval echo `gettext 'Failed to start service $APP_NAME'` + exit 1 + fi - /sbin/start ${APP_NAME} + startwait +} + +systemdStop() { + systemctl stop $APP_NAME + if [ $? -ne 0 ] ; then + eval echo `gettext 'Failed to stop $APP_NAME'` + exit 1 + fi +} + +systemdRestart() { + systemctl restart $APP_NAME + if [ $? -ne 0 ] ; then + eval echo `gettext 'Failed to restart $APP_NAME'` + exit 1 + fi + + startwait +} - startwait +systemdRemove() { + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon from systemd...'` + systemctl disable $APP_NAME + rm "/etc/systemd/system/${APP_NAME}.service" + systemctl daemon-reload } start() { - eval echo `gettext 'Starting $APP_LONG_NAME...'` - getpid - if [ "X$pid" = "X" ] + eval echo `gettext 'Starting $APP_LONG_NAME...'` + getpid + if [ "X$pid" = "X" ] then - prepAdditionalParams "$@" + prepAdditionalParams "$@" - # The string passed to eval must handles spaces in paths correctly. - COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.24 $ADDITIONAL_PARA" - eval $COMMAND_LINE - else - eval echo `gettext '$APP_LONG_NAME is already running.'` - exit 1 - fi + # The string passed to eval must handles spaces in paths correctly. + COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\" wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.26 $ADDITIONAL_PARA" + eval $COMMAND_LINE + else + eval echo `gettext '$APP_LONG_NAME is already running.'` + exit 1 + fi - startwait + startwait } stopit() { - # $1 exit if down flag + # $1 exit if down flag - eval echo `gettext 'Stopping $APP_LONG_NAME...'` - getpid - if [ "X$pid" = "X" ] + eval echo `gettext 'Stopping $APP_LONG_NAME...'` + getpid + if [ "X$pid" = "X" ] then - eval echo `gettext '$APP_LONG_NAME was not running.'` - if [ "X$1" = "X1" ] + eval echo `gettext '$APP_LONG_NAME was not running.'` + if [ "X$1" = "X1" ] + then + exit 1 + fi + else + if [ "X$IGNORE_SIGNALS" = "X" ] + then + # Running so try to stop it. + kill $pid + if [ $? -ne 0 ] then - exit 1 - fi + # An explanation for the failure should have been given + eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` + exit 1 + fi else - if [ "X$IGNORE_SIGNALS" = "X" ] + rm -f "$ANCHORFILE" + if [ -f "$ANCHORFILE" ] then - # Running so try to stop it. - kill $pid - if [ $? -ne 0 ] - then - # An explanation for the failure should have been given - eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` - exit 1 - fi - else - rm -f "$ANCHORFILE" - if [ -f "$ANCHORFILE" ] - then - # An explanation for the failure should have been given - eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` - exit 1 - fi - fi + # An explanation for the failure should have been given + eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` + exit 1 + fi + fi - # We can not predict how long it will take for the wrapper to - # actually stop as it depends on settings in wrapper.conf. - # Loop until it does. - savepid=$pid + # We can not predict how long it will take for the wrapper to + # actually stop as it depends on settings in wrapper.conf. + # Loop until it does. + savepid=$pid + CNT=0 + TOTCNT=0 + while [ "X$pid" != "X" ] + do + # Show a waiting message every 5 seconds. + if [ "$CNT" -lt "5" ] + then + CNT=`expr $CNT + 1` + else + eval echo `gettext 'Waiting for $APP_LONG_NAME to exit...'` CNT=0 - TOTCNT=0 - while [ "X$pid" != "X" ] - do - # Show a waiting message every 5 seconds. - if [ "$CNT" -lt "5" ] - then - CNT=`expr $CNT + 1` - else - eval echo `gettext 'Waiting for $APP_LONG_NAME to exit...'` - CNT=0 - fi - TOTCNT=`expr $TOTCNT + 1` + fi + TOTCNT=`expr $TOTCNT + 1` - sleep 1 + sleep 1 - testpid - done + testpid + done - pid=$savepid - testpid - if [ "X$pid" != "X" ] - then - eval echo `gettext 'Failed to stop $APP_LONG_NAME.'` - exit 1 - else - eval echo `gettext 'Stopped $APP_LONG_NAME.'` - fi + pid=$savepid + testpid + if [ "X$pid" != "X" ] + then + eval echo `gettext 'Failed to stop $APP_LONG_NAME.'` + exit 1 + else + eval echo `gettext 'Stopped $APP_LONG_NAME.'` fi + fi } pause() { - eval echo `gettext 'Pausing $APP_LONG_NAME.'` + eval echo `gettext 'Pausing $APP_LONG_NAME.'` } resume() { - eval echo `gettext 'Resuming $APP_LONG_NAME.'` + eval echo `gettext 'Resuming $APP_LONG_NAME.'` } status() { - getpid - if [ "X$pid" = "X" ] + getpid + if [ "X$pid" = "X" ] then - eval echo `gettext '$APP_LONG_NAME is not running.'` - exit 1 + eval echo `gettext '$APP_LONG_NAME is not running.'` + exit 1 + else + if [ "X$DETAIL_STATUS" = "X" ] + then + eval echo `gettext '$APP_LONG_NAME is running: PID:$pid'` else - if [ "X$DETAIL_STATUS" = "X" ] - then - eval echo `gettext '$APP_LONG_NAME is running: PID:$pid'` - else - getstatus - eval echo `gettext '$APP_LONG_NAME is running: PID:$pid, Wrapper:$STATUS, Java:$JAVASTATUS'` - fi - exit 0 + getstatus + eval echo `gettext '$APP_LONG_NAME is running: PID:$pid, Wrapper:$STATUS, Java:$JAVASTATUS'` fi + exit 0 + fi +} + +# Make sure APP_NAME is less than 14 characters, otherwise in AIX, the command +# "lsitab" will fail +validateAppNameLength() { + if [ ${#APP_NAME} -gt 14 ] ; then + eval echo `gettext ' APP_NAME (${APP_NAME}) must be less than 14 characters long'` + exit 1 + fi } installUpstart() { - eval echo `gettext ' Installing the $APP_LONG_NAME daemon using upstart..'` - if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then - eval echo `gettext ' a custom upstart conf file ${APP_NAME}.install found'` - cp "${REALDIR}/${APP_NAME}.install" "/etc/init/${APP_NAME}.conf" - else - eval echo `gettext ' creating default upstart conf file..'` - echo "# ${APP_NAME} - ${APP_LONG_NAME}" > "/etc/init/${APP_NAME}.conf" - echo "description \"${APP_LONG_NAME}\"" >> "/etc/init/${APP_NAME}.conf" - echo "author \"Tanuki Software Ltd. \"" >> "/etc/init/${APP_NAME}.conf" - echo "start on runlevel [2345]" >> "/etc/init/${APP_NAME}.conf" - echo "stop on runlevel [!2345]" >> "/etc/init/${APP_NAME}.conf" - echo "env LANG=${LANG}" >> "/etc/init/${APP_NAME}.conf" - echo "exec \"${REALPATH}\" upstartinternal" >> "/etc/init/${APP_NAME}.conf" - fi + eval echo `gettext ' Installing the $APP_LONG_NAME daemon using upstart..'` + if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then + eval echo `gettext ' a custom upstart conf file ${APP_NAME}.install found'` + cp "${REALDIR}/${APP_NAME}.install" "/etc/init/${APP_NAME}.conf" + else + eval echo `gettext ' creating default upstart conf file..'` + echo "# ${APP_NAME} - ${APP_LONG_NAME}" > "/etc/init/${APP_NAME}.conf" + echo "description \"${APP_LONG_NAME}\"" >> "/etc/init/${APP_NAME}.conf" + echo "author \"Tanuki Software Ltd. \"" >> "/etc/init/${APP_NAME}.conf" + echo "start on runlevel [2345]" >> "/etc/init/${APP_NAME}.conf" + echo "stop on runlevel [!2345]" >> "/etc/init/${APP_NAME}.conf" + echo "env LANG=${LANG}" >> "/etc/init/${APP_NAME}.conf" + echo "exec \"${REALPATH}\" upstartinternal" >> "/etc/init/${APP_NAME}.conf" + fi } installdaemon() { - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then - eval echo `gettext 'Must be root to perform this action.'` - exit 1 + mustBeRootOrExit + + APP_NAME_LOWER=`echo "$APP_NAME" | $TR_BIN "[A-Z]" "[a-z]"` + if [ "$DIST_OS" = "solaris" ] ; then + eval echo `gettext 'Detected Solaris:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 else - APP_NAME_LOWER=`echo "$APP_NAME" | $TREXE "[A-Z]" "[a-z]"` - if [ "$DIST_OS" = "solaris" ] ; then - eval echo `gettext 'Detected Solaris:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - ln -s "$REALPATH" "/etc/init.d/$APP_NAME" - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do - eval echo `gettext ' Removing unexpected file before proceeding: $i'` - rm -f $i - done - ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" - ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" - fi - elif [ "$DIST_OS" = "linux" ] ; then - if [ -f /etc/redhat-release -o -f /etc/redhat_version -o -f /etc/fedora-release ] ; then - eval echo `gettext 'Detected RHEL or Fedora:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" -o -f "/etc/init/${APP_NAME}.conf" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - if [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then - installUpstart - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - ln -s "$REALPATH" "/etc/init.d/$APP_NAME" - /sbin/chkconfig --add "$APP_NAME" - /sbin/chkconfig "$APP_NAME" on - fi - fi - elif [ -f /etc/SuSE-release ] ; then - eval echo `gettext 'Detected SuSE or SLES:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - ln -s "$REALPATH" "/etc/init.d/$APP_NAME" - insserv "/etc/init.d/$APP_NAME" - fi - elif [ -f /etc/lsb-release -o -f /etc/debian_version -o -f /etc/debian_release ] ; then - eval echo `gettext 'Detected Ubuntu or Debian:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" -o -f "/etc/init/${APP_NAME}.conf" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - if [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then - installUpstart - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon using init.d..'` - ln -s "$REALPATH" "/etc/init.d/$APP_NAME" - update-rc.d "$APP_NAME" defaults - fi - fi - else - eval echo `gettext 'Detected Linux:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - ln -s "$REALPATH" /etc/init.d/$APP_NAME - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do - eval echo `gettext ' Removing unexpected file before proceeding: $i'` - rm -f $i - done - ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" - ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" - ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" - ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" - fi - fi - elif [ "$DIST_OS" = "hpux" ] ; then - eval echo `gettext 'Detected HP-UX:'` - if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - ln -s "$REALPATH" "/sbin/init.d/$APP_NAME" - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do - eval echo `gettext ' Removing unexpected file before proceeding: $i'` - rm -f $i - done - ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" - ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" - fi - elif [ "$DIST_OS" = "aix" ] ; then - eval echo `gettext 'Detected AIX:'` - if [ -f "/etc/rc.d/init.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed as rc.d script.'` - exit 1 - elif [ -n "`/usr/sbin/lsitab $APP_NAME`" -a -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed as SRC service.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - if [ -n "`/usr/sbin/lsitab install_assist`" ] ; then - eval echo `gettext ' The task /usr/sbin/install_assist was found in the inittab, this might cause problems for all subsequent tasks to launch at this process is known to block the init task. Please make sure this task is not needed anymore and remove/deactivate it.'` - fi - for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do - eval echo `gettext ' Removing unexpected file before proceeding: $i'` - rm -f $i - done - /usr/bin/mkssys -s "$APP_NAME" -p "$REALPATH" -a "launchdinternal" -u 0 -f 9 -n 15 -S - /usr/sbin/mkitab "$APP_NAME":2:once:"/usr/bin/startsrc -s \"${APP_NAME}\" >/dev/console 2>&1" - fi - elif [ "$DIST_OS" = "freebsd" ] ; then - eval echo `gettext 'Detected FreeBSD:'` - if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - sed -i .bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf - if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then - ln -s "${REALDIR}/${APP_NAME}.install" "/etc/rc.d/$APP_NAME" - else - echo '#!/bin/sh' > "/etc/rc.d/$APP_NAME" - echo "#" >> "/etc/rc.d/$APP_NAME" - echo "# PROVIDE: $APP_NAME" >> "/etc/rc.d/$APP_NAME" - echo "# REQUIRE: NETWORKING" >> "/etc/rc.d/$APP_NAME" - echo "# KEYWORD: shutdown" >> "/etc/rc.d/$APP_NAME" - echo ". /etc/rc.subr" >> "/etc/rc.d/$APP_NAME" - echo "name=\"$APP_NAME\"" >> "/etc/rc.d/$APP_NAME" - echo "rcvar=\`set_rcvar\`" >> "/etc/rc.d/$APP_NAME" - echo "command=\"${REALPATH}\"" >> "/etc/rc.d/$APP_NAME" - echo 'start_cmd="${name}_start"' >> "/etc/rc.d/$APP_NAME" - echo 'load_rc_config $name' >> "/etc/rc.d/$APP_NAME" - echo 'status_cmd="${name}_status"' >> "/etc/rc.d/$APP_NAME" - echo 'stop_cmd="${name}_stop"' >> "/etc/rc.d/$APP_NAME" - echo "${APP_NAME}_status() {" >> "/etc/rc.d/$APP_NAME" - echo '${command} status' >> "/etc/rc.d/$APP_NAME" - echo '}' >> "/etc/rc.d/$APP_NAME" - echo "${APP_NAME}_stop() {" >> "/etc/rc.d/$APP_NAME" - echo '${command} stop' >> "/etc/rc.d/$APP_NAME" - echo '}' >> "/etc/rc.d/$APP_NAME" - echo "${APP_NAME}_start() {" >> "/etc/rc.d/$APP_NAME" - echo '${command} start' >> "/etc/rc.d/$APP_NAME" - echo '}' >> "/etc/rc.d/$APP_NAME" - echo 'run_rc_command "$1"' >> "/etc/rc.d/$APP_NAME" - fi - echo "${APP_NAME}_enable=\"YES\"" >> /etc/rc.conf - chmod 555 "/etc/rc.d/$APP_NAME" - fi - elif [ "$DIST_OS" = "macosx" ] ; then - eval echo `gettext 'Detected Mac OSX:'` - if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - if [ -f "${REALDIR}/${APP_PLIST}" ] ; then - ln -s "${REALDIR}/${APP_PLIST}" "/Library/LaunchDaemons/${APP_PLIST}" - else - echo "" > "/Library/LaunchDaemons/${APP_PLIST}" - echo "> "/Library/LaunchDaemons/${APP_PLIST}" - echo "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " Label" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " ${APP_PLIST_BASE}" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " ProgramArguments" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " ${REALPATH}" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " launchdinternal" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " OnDemand" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " RunAtLoad" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - if [ "X$RUN_AS_USER" != "X" ] ; then - echo " UserName" >> "/Library/LaunchDaemons/${APP_PLIST}" - echo " ${RUN_AS_USER}" >> "/Library/LaunchDaemons/${APP_PLIST}" - fi - echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" - echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" - fi - chmod 555 "/Library/LaunchDaemons/${APP_PLIST}" - fi - elif [ "$DIST_OS" = "zos" ] ; then - eval echo `gettext 'Detected z/OS:'` - if [ -f /etc/rc.bak ] ; then - eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` - exit 1 - else - eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` - cp /etc/rc /etc/rc.bak - sed "s:echo /etc/rc script executed, \`date\`::g" /etc/rc.bak > /etc/rc - echo "_BPX_JOBNAME='${APP_NAME}' \"${REALDIR}/${APP_NAME}\" start" >>/etc/rc - echo '/etc/rc script executed, `date`' >>/etc/rc - fi + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + ln -s "$REALPATH" "/etc/init.d/$APP_NAME" + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do + eval echo `gettext ' Removing unexpected file before proceeding: $i'` + rm -f $i + done + ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" + ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" + fi + elif [ "$DIST_OS" = "linux" ] ; then + if [ -f /etc/redhat-release -o -f /etc/redhat_version -o -f /etc/fedora-release ] ; then + eval echo `gettext 'Detected RHEL or Fedora:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" -o -f "/etc/init/${APP_NAME}.conf" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + elif [ -n "$USE_SYSTEMD" -a -d "/etc/systemd" ] ; then + systemdInstall + else + if [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then + installUpstart else - eval echo `gettext 'Install not currently supported for $DIST_OS'` - exit 1 + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + ln -s "$REALPATH" "/etc/init.d/$APP_NAME" + /sbin/chkconfig --add "$APP_NAME" + /sbin/chkconfig "$APP_NAME" on fi + fi + elif [ -f /etc/SuSE-release ] ; then + eval echo `gettext 'Detected SuSE or SLES:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + elif [ -n "$USE_SYSTEMD" -a -d "/etc/systemd" ] ; then + systemdInstall + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + ln -s "$REALPATH" "/etc/init.d/$APP_NAME" + insserv "/etc/init.d/$APP_NAME" + fi + elif [ -f /etc/lsb-release -o -f /etc/debian_version -o -f /etc/debian_release ] ; then + eval echo `gettext 'Detected Ubuntu or Debian:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" -o -f "/etc/init/${APP_NAME}.conf" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + if [ -n "$USE_SYSTEMD" -a -d "/etc/systemd" ] ; then + systemdInstall + elif [ -n "$USE_UPSTART" -a -d "/etc/init" ] ; then + installUpstart + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon using init.d..'` + ln -s "$REALPATH" "/etc/init.d/$APP_NAME" + update-rc.d "$APP_NAME" defaults + fi + fi + else + eval echo `gettext 'Detected Linux:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + ln -s "$REALPATH" /etc/init.d/$APP_NAME + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do + eval echo `gettext ' Removing unexpected file before proceeding: $i'` + rm -f $i + done + ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" + ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" + ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" + ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" + fi + fi + elif [ "$DIST_OS" = "hpux" ] ; then + eval echo `gettext 'Detected HP-UX:'` + if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + ln -s "$REALPATH" "/sbin/init.d/$APP_NAME" + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do + eval echo `gettext ' Removing unexpected file before proceeding: $i'` + rm -f $i + done + ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" + ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" fi + elif [ "$DIST_OS" = "aix" ] ; then + eval echo `gettext 'Detected AIX:'` + validateAppNameLength + if [ -f "/etc/rc.d/init.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed as rc.d script.'` + exit 1 + elif [ -n "`/usr/sbin/lsitab $APP_NAME`" -a -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed as SRC service.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + if [ -n "`/usr/sbin/lsitab install_assist`" ] ; then + eval echo `gettext ' The task /usr/sbin/install_assist was found in the inittab, this might cause problems for all subsequent tasks to launch at this process is known to block the init task. Please make sure this task is not needed anymore and remove/deactivate it.'` + fi + for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do + eval echo `gettext ' Removing unexpected file before proceeding: $i'` + rm -f $i + done + /usr/bin/mkssys -s "$APP_NAME" -p "$REALPATH" -a "launchdinternal" -u 0 -f 9 -n 15 -S + /usr/sbin/mkitab "$APP_NAME":2:once:"/usr/bin/startsrc -s \"${APP_NAME}\" >/dev/console 2>&1" + fi + elif [ "$DIST_OS" = "freebsd" ] ; then + eval echo `gettext 'Detected FreeBSD:'` + if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + sed -i .bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf + if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then + ln -s "${REALDIR}/${APP_NAME}.install" "/etc/rc.d/$APP_NAME" + else + echo '#!/bin/sh' > "/etc/rc.d/$APP_NAME" + echo "#" >> "/etc/rc.d/$APP_NAME" + echo "# PROVIDE: $APP_NAME" >> "/etc/rc.d/$APP_NAME" + echo "# REQUIRE: NETWORKING" >> "/etc/rc.d/$APP_NAME" + echo "# KEYWORD: shutdown" >> "/etc/rc.d/$APP_NAME" + echo ". /etc/rc.subr" >> "/etc/rc.d/$APP_NAME" + echo "name=\"$APP_NAME\"" >> "/etc/rc.d/$APP_NAME" + echo "rcvar=\`set_rcvar\`" >> "/etc/rc.d/$APP_NAME" + echo "command=\"${REALPATH}\"" >> "/etc/rc.d/$APP_NAME" + echo 'start_cmd="${name}_start"' >> "/etc/rc.d/$APP_NAME" + echo 'load_rc_config $name' >> "/etc/rc.d/$APP_NAME" + echo 'status_cmd="${name}_status"' >> "/etc/rc.d/$APP_NAME" + echo 'stop_cmd="${name}_stop"' >> "/etc/rc.d/$APP_NAME" + echo "${APP_NAME}_status() {" >> "/etc/rc.d/$APP_NAME" + echo '${command} status' >> "/etc/rc.d/$APP_NAME" + echo '}' >> "/etc/rc.d/$APP_NAME" + echo "${APP_NAME}_stop() {" >> "/etc/rc.d/$APP_NAME" + echo '${command} stop' >> "/etc/rc.d/$APP_NAME" + echo '}' >> "/etc/rc.d/$APP_NAME" + echo "${APP_NAME}_start() {" >> "/etc/rc.d/$APP_NAME" + echo '${command} start' >> "/etc/rc.d/$APP_NAME" + echo '}' >> "/etc/rc.d/$APP_NAME" + echo 'run_rc_command "$1"' >> "/etc/rc.d/$APP_NAME" + fi + echo "${APP_NAME}_enable=\"YES\"" >> /etc/rc.conf + chmod 555 "/etc/rc.d/$APP_NAME" + fi + elif [ "$DIST_OS" = "macosx" ] ; then + eval echo `gettext 'Detected Mac OSX:'` + if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + if [ -f "${REALDIR}/${APP_PLIST}" ] ; then + ln -s "${REALDIR}/${APP_PLIST}" "/Library/LaunchDaemons/${APP_PLIST}" + else + echo "" > "/Library/LaunchDaemons/${APP_PLIST}" + echo "> "/Library/LaunchDaemons/${APP_PLIST}" + echo "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " Label" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " ${APP_PLIST_BASE}" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " ProgramArguments" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " ${REALPATH}" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " launchdinternal" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " ${KEY_KEEP_ALIVE}" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " RunAtLoad" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + if [ "X$RUN_AS_USER" != "X" ] ; then + echo " UserName" >> "/Library/LaunchDaemons/${APP_PLIST}" + echo " ${RUN_AS_USER}" >> "/Library/LaunchDaemons/${APP_PLIST}" + fi + echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" + echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" + fi + chmod 555 "/Library/LaunchDaemons/${APP_PLIST}" + fi + elif [ "$DIST_OS" = "zos" ] ; then + eval echo `gettext 'Detected z/OS:'` + if [ -f /etc/rc.bak ] ; then + eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` + exit 1 + else + eval echo `gettext ' Installing the $APP_LONG_NAME daemon..'` + cp /etc/rc /etc/rc.bak + sed "s:echo /etc/rc script executed, \`date\`::g" /etc/rc.bak > /etc/rc + echo "_BPX_JOBNAME='${APP_NAME}' \"${REALDIR}/${APP_NAME}\" start" >>/etc/rc + echo '/etc/rc script executed, `date`' >>/etc/rc + fi + else + eval echo `gettext 'Install not currently supported for $DIST_OS'` + exit 1 + fi } removedaemon() { - if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then - eval echo `gettext 'Must be root to perform this action.'` + mustBeRootOrExit + + APP_NAME_LOWER=`echo "$APP_NAME" | $TR_BIN "[A-Z]" "[a-z]"` + if [ "$DIST_OS" = "solaris" ] ; then + eval echo `gettext 'Detected Solaris:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do + rm -f $i + done + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + elif [ "$DIST_OS" = "linux" ] ; then + if [ -f /etc/redhat-release -o -f /etc/redhat_version -o -f /etc/fedora-release ] ; then + eval echo `gettext 'Detected RHEL or Fedora:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + /sbin/chkconfig "$APP_NAME" off + /sbin/chkconfig --del "$APP_NAME" + rm -f "/etc/init.d/$APP_NAME" + elif [ -n "$USE_SYSTEMD" -a -f "${SYSTEMD_SERVICE_FILE}" ] ; then + systemdRemove + elif [ -f "/etc/init/${APP_NAME}.conf" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon from upstart...'` + rm "/etc/init/${APP_NAME}.conf" + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 + fi + elif [ -f /etc/SuSE-release ] ; then + eval echo `gettext 'Detected SuSE or SLES:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + insserv -r "/etc/init.d/$APP_NAME" + rm -f "/etc/init.d/$APP_NAME" + elif [ -n "$USE_SYSTEMD" -a -f "${SYSTEMD_SERVICE_FILE}" ] ; then + systemdRemove + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + elif [ -f /etc/lsb-release -o -f /etc/debian_version -o -f /etc/debian_release ] ; then + eval echo `gettext 'Detected Ubuntu or Debian:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon from init.d...'` + update-rc.d -f "$APP_NAME" remove + rm -f "/etc/init.d/$APP_NAME" + elif [ -n "$USE_SYSTEMD" -a -f "${SYSTEMD_SERVICE_FILE}" ] ; then + systemdRemove + elif [ -f "/etc/init/${APP_NAME}.conf" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon from upstart...'` + rm "/etc/init/${APP_NAME}.conf" + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi else - APP_NAME_LOWER=`echo "$APP_NAME" | $TREXE "[A-Z]" "[a-z]"` - if [ "$DIST_OS" = "solaris" ] ; then - eval echo `gettext 'Detected Solaris:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do - rm -f $i - done - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ "$DIST_OS" = "linux" ] ; then - if [ -f /etc/redhat-release -o -f /etc/redhat_version -o -f /etc/fedora-release ] ; then - eval echo `gettext 'Detected RHEL or Fedora:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - /sbin/chkconfig "$APP_NAME" off - /sbin/chkconfig --del "$APP_NAME" - rm -f "/etc/init.d/$APP_NAME" - elif [ -f "/etc/init/${APP_NAME}.conf" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon from upstart...'` - rm "/etc/init/${APP_NAME}.conf" - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ -f /etc/SuSE-release ] ; then - eval echo `gettext 'Detected SuSE or SLES:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - insserv -r "/etc/init.d/$APP_NAME" - rm -f "/etc/init.d/$APP_NAME" - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ -f /etc/lsb-release -o -f /etc/debian_version -o -f /etc/debian_release ] ; then - eval echo `gettext 'Detected Ubuntu or Debian:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon from init.d...'` - update-rc.d -f "$APP_NAME" remove - rm -f "/etc/init.d/$APP_NAME" - elif [ -f "/etc/init/${APP_NAME}.conf" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon from upstart...'` - rm "/etc/init/${APP_NAME}.conf" - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - else - eval echo `gettext 'Detected Linux:'` - if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do - rm -f $i - done - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - fi - elif [ "$DIST_OS" = "hpux" ] ; then - eval echo `gettext 'Detected HP-UX:'` - if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do - rm -f $i - done - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ "$DIST_OS" = "aix" ] ; then - eval echo `gettext 'Detected AIX:'` - if [ -f "/etc/rc.d/init.d/$APP_NAME" -o -L "/etc/rc.d/init.d/$APP_NAME" -o -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - if [ -f "/etc/rc.d/init.d/$APP_NAME" -o -L "/etc/rc.d/init.d/$APP_NAME" ] ; then - for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" "/etc/rc.d/init.d/$APP_NAME" 2>/dev/null` ; do - rm -f $i - done - fi - if [ -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then - /usr/sbin/rmitab $APP_NAME - /usr/bin/rmssys -s $APP_NAME - fi - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ "$DIST_OS" = "freebsd" ] ; then - eval echo `gettext 'Detected FreeBSD:'` - if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - for i in "/etc/rc.d/$APP_NAME" - do - rm -f $i - done - sed -i .bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ "$DIST_OS" = "macosx" ] ; then - eval echo `gettext 'Detected Mac OSX:'` - if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - # Make sure the plist is installed - LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` - if [ "X${LOADED_PLIST}" != "X" ] ; then - launchctl unload "/Library/LaunchDaemons/${APP_PLIST}" - fi - rm -f "/Library/LaunchDaemons/${APP_PLIST}" - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - elif [ "$DIST_OS" = "zos" ] ; then - eval echo `gettext 'Detected z/OS:'` - if [ -f /etc/rc.bak ] ; then - stopit "0" - eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` - cp /etc/rc /etc/rc.bak - sed "s/_BPX_JOBNAME=\'APP_NAME\'.*//g" /etc/rc.bak > /etc/rc - rm /etc/rc.bak - else - eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` - exit 1 - fi - else - eval echo `gettext 'Remove not currently supported for $DIST_OS'` - exit 1 - fi + eval echo `gettext 'Detected Linux:'` + if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do + rm -f $i + done + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + fi + elif [ "$DIST_OS" = "hpux" ] ; then + eval echo `gettext 'Detected HP-UX:'` + if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do + rm -f $i + done + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + elif [ "$DIST_OS" = "aix" ] ; then + eval echo `gettext 'Detected AIX:'` + validateAppNameLength + if [ -f "/etc/rc.d/init.d/$APP_NAME" -o -L "/etc/rc.d/init.d/$APP_NAME" -o -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + if [ -f "/etc/rc.d/init.d/$APP_NAME" -o -L "/etc/rc.d/init.d/$APP_NAME" ] ; then + for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" "/etc/rc.d/init.d/$APP_NAME" 2>/dev/null` ; do + rm -f $i + done + fi + if [ -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then + /usr/sbin/rmitab $APP_NAME + /usr/bin/rmssys -s $APP_NAME + fi + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + elif [ "$DIST_OS" = "freebsd" ] ; then + eval echo `gettext 'Detected FreeBSD:'` + if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + for i in "/etc/rc.d/$APP_NAME" + do + rm -f $i + done + sed -i .bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 fi + elif [ "$DIST_OS" = "macosx" ] ; then + eval echo `gettext 'Detected Mac OSX:'` + if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + # Make sure the plist is installed + LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` + if [ "X${LOADED_PLIST}" != "X" ] ; then + launchctl unload "/Library/LaunchDaemons/${APP_PLIST}" + fi + rm -f "/Library/LaunchDaemons/${APP_PLIST}" + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + elif [ "$DIST_OS" = "zos" ] ; then + eval echo `gettext 'Detected z/OS:'` + if [ -f /etc/rc.bak ] ; then + stopit "0" + eval echo `gettext ' Removing $APP_LONG_NAME daemon...'` + cp /etc/rc /etc/rc.bak + sed "s/_BPX_JOBNAME=\'APP_NAME\'.*//g" /etc/rc.bak > /etc/rc + rm /etc/rc.bak + else + eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` + exit 1 + fi + else + eval echo `gettext 'Remove not currently supported for $DIST_OS'` + exit 1 + fi } dump() { - eval echo `gettext 'Dumping $APP_LONG_NAME...'` - getpid - if [ "X$pid" = "X" ] + eval echo `gettext 'Dumping $APP_LONG_NAME...'` + getpid + if [ "X$pid" = "X" ] then - eval echo `gettext '$APP_LONG_NAME was not running.'` + eval echo `gettext '$APP_LONG_NAME was not running.'` + else + kill -3 $pid + + if [ $? -ne 0 ] + then + eval echo `gettext 'Failed to dump $APP_LONG_NAME.'` + exit 1 else - kill -3 $pid - - if [ $? -ne 0 ] - then - eval echo `gettext 'Failed to dump $APP_LONG_NAME.'` - exit 1 - else - eval echo `gettext 'Dumped $APP_LONG_NAME.'` - fi + eval echo `gettext 'Dumped $APP_LONG_NAME.'` fi + fi } # Used by HP-UX init scripts. startmsg() { - getpid - if [ "X$pid" = "X" ] + getpid + if [ "X$pid" = "X" ] then - eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Stopped'` + eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Stopped'` + else + if [ "X$DETAIL_STATUS" = "X" ] + then + eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Running'` else - if [ "X$DETAIL_STATUS" = "X" ] - then - eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Running'` - else - getstatus - eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` - fi + getstatus + eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` fi + fi } # Used by HP-UX init scripts. stopmsg() { - getpid - if [ "X$pid" = "X" ] + getpid + if [ "X$pid" = "X" ] then - eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Stopped'` + eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Stopped'` + else + if [ "X$DETAIL_STATUS" = "X" ] + then + eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Running'` else - if [ "X$DETAIL_STATUS" = "X" ] - then - eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Running'` - else - getstatus - eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` - fi + getstatus + eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` fi + fi } showUsage() { - # $1 bad command + # $1 bad command - if [ -n "$1" ] + if [ -n "$1" ] then - eval echo `gettext 'Unexpected command: $1'` - echo ""; - fi + eval echo `gettext 'Unexpected command: $1'` + echo ""; + fi - eval MSG=`gettext 'Usage: '` - if [ -n "$FIXED_COMMAND" ] ; then - if [ -n "$PASS_THROUGH" ] ; then - echo "${MSG} $0 {JavaAppArgs}" - else - echo "${MSG} $0" - fi + eval MSG=`gettext 'Usage: '` + if [ -n "$FIXED_COMMAND" ] ; then + if [ -n "$PASS_THROUGH" ] ; then + echo "${MSG} $0 {JavaAppArgs}" else - if [ -n "$PAUSABLE" ] ; then - if [ -n "$PASS_THROUGH" ] ; then - echo "${MSG} $0 [ console {JavaAppArgs} | start {JavaAppArgs} | stop | restart {JavaAppArgs} | condrestart {JavaAppArgs} | pause | resume | status | install | remove | dump ]" - else - echo "${MSG} $0 [ console | start | stop | restart | condrestart | pause | resume | status | install | remove | dump ]" - fi - else - if [ -n "$PASS_THROUGH" ] ; then - echo "${MSG} $0 [ console {JavaAppArgs} | start {JavaAppArgs} | stop | restart {JavaAppArgs} | condrestart {JavaAppArgs} | status | install | remove | dump ]" - else - echo "${MSG} $0 [ console | start | stop | restart | condrestart | status | install | remove | dump ]" - fi - fi + echo "${MSG} $0" + fi + else + if [ -n "$PAUSABLE" ] ; then + if [ -n "$PASS_THROUGH" ] ; then + echo "${MSG} $0 [ console {JavaAppArgs} | start {JavaAppArgs} | stop | restart {JavaAppArgs} | condrestart {JavaAppArgs} | pause | resume | status | install | remove | dump ]" + else + echo "${MSG} $0 [ console | start | stop | restart | condrestart | pause | resume | status | install | remove | dump ]" + fi + else + if [ -n "$PASS_THROUGH" ] ; then + echo "${MSG} $0 [ console {JavaAppArgs} | start {JavaAppArgs} | stop | restart {JavaAppArgs} | condrestart {JavaAppArgs} | status | install | remove | dump ]" + else + echo "${MSG} $0 [ console | start | stop | restart | condrestart | status | install | remove | dump ]" + fi fi + fi - if [ ! -n "$BRIEF_USAGE" ] + if [ ! -n "$BRIEF_USAGE" ] then - echo ""; - if [ ! -n "$FIXED_COMMAND" ] ; then - echo "`gettext 'Commands:'`" - echo "`gettext ' console Launch in the current console.'`" - echo "`gettext ' start Start in the background as a daemon process.'`" - echo "`gettext ' stop Stop if running as a daemon or in another console.'`" - echo "`gettext ' restart Stop if running and then start.'`" - echo "`gettext ' condrestart Restart only if already running.'`" - if [ -n "$PAUSABLE" ] ; then - echo "`gettext ' pause Pause if running.'`" - echo "`gettext ' resume Resume if paused.'`" - fi - echo "`gettext ' status Query the current status.'`" - echo "`gettext ' install Install to start automatically when system boots.'`" - echo "`gettext ' remove Uninstall.'`" - echo "`gettext ' dump Request a Java thread dump if running.'`" - echo ""; - fi - if [ -n "$PASS_THROUGH" ] ; then - echo "`gettext 'JavaAppArgs: Zero or more arguments which will be passed to the Java application.'`" - echo ""; - fi + echo ""; + if [ ! -n "$FIXED_COMMAND" ] ; then + echo "`gettext 'Commands:'`" + echo "`gettext ' console Launch in the current console.'`" + echo "`gettext ' start Start in the background as a daemon process.'`" + echo "`gettext ' stop Stop if running as a daemon or in another console.'`" + echo "`gettext ' restart Stop if running and then start.'`" + echo "`gettext ' condrestart Restart only if already running.'`" + if [ -n "$PAUSABLE" ] ; then + echo "`gettext ' pause Pause if running.'`" + echo "`gettext ' resume Resume if paused.'`" + fi + echo "`gettext ' status Query the current status.'`" + echo "`gettext ' install Install to start automatically when system boots.'`" + echo "`gettext ' remove Uninstall.'`" + echo "`gettext ' dump Request a Java thread dump if running.'`" + echo ""; fi + if [ -n "$PASS_THROUGH" ] ; then + echo "`gettext 'JavaAppArgs: Zero or more arguments which will be passed to the Java application.'`" + echo ""; + fi + fi - exit 1 + exit 1 } docommand() { - case "$COMMAND" in - 'console') - checkUser touchlock "$@" - if [ ! -n "$FIXED_COMMAND" ] ; then - shift - fi - console "$@" - ;; - - 'start') - if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then - macosxstart - elif [ "$DIST_OS" = "linux" -a -f "/etc/init/${APP_NAME}.conf" ] ; then - upstartstart - else - checkUser touchlock "$@" - if [ ! -n "$FIXED_COMMAND" ] ; then - shift - fi - start "$@" - fi - ;; - - 'stop') - checkUser "" "$COMMAND" - stopit "0" - ;; - - 'restart') - checkUser touchlock "$COMMAND" - if [ ! -n "$FIXED_COMMAND" ] ; then - shift - fi - stopit "0" - start "$@" - ;; - - 'condrestart') - checkUser touchlock "$COMMAND" - if [ ! -n "$FIXED_COMMAND" ] ; then - shift - fi - stopit "1" - start "$@" - ;; - - 'pause') - if [ -n "$PAUSABLE" ] - then - pause - else - showUsage "$COMMAND" - fi - ;; - - 'resume') - if [ -n "$PAUSABLE" ] - then - resume - else - showUsage "$COMMAND" - fi - ;; - - 'status') - status - ;; - - 'install') - installdaemon - ;; - - 'remove') - removedaemon - ;; - - 'dump') - checkUser "" "$COMMAND" - dump - ;; + case "$COMMAND" in + 'console') + checkUser touchlock "$@" + if [ ! -n "$FIXED_COMMAND" ] ; then + shift + fi + console "$@" + ;; + + 'start') + if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + macosxStart + elif [ "$DIST_OS" = "linux" -a -f "/etc/init/${APP_NAME}.conf" ] ; then + upstartstart + elif [ "$DIST_OS" = "linux" -a -n "$USE_SYSTEMD" -a -z "$SYSD" ] ; then + systemdStart + else + if [ -n "$SYSD" ] ; then + shift + fi - 'start_msg') - # Internal command called by launchd on HP-UX. - checkUser "" "$COMMAND" - startmsg - ;; + checkUser touchlock "$@" + if [ ! -n "$FIXED_COMMAND" ] ; then + shift + fi + start "$@" + fi + ;; + + 'stop') + if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + macosxStop + elif [ "$DIST_OS" = "linux" -a -f "/etc/init/${APP_NAME}.conf" ] ; then + upstartStop + elif [ "$DIST_OS" = "linux" -a -n "$USE_SYSTEMD" -a -z "$SYSD" ] ; then + systemdStop + else + checkUser "" "$COMMAND" + stopit "0" + fi + ;; + + 'restart') + if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + macosxRestart + elif [ "$DIST_OS" = "linux" -a -f "/etc/init/${APP_NAME}.conf" ] ; then + upstartRestart + elif [ "$DIST_OS" = "linux" -a -n "$USE_SYSTEMD" -a -z "$SYSD" ] ; then + systemdRestart + else + if [ -n "$SMF" ] ; then + shift + fi + checkUser touchlock "$COMMAND" + if [ ! -n "$FIXED_COMMAND" ] ; then + shift + fi + stopit "0" + start "$@" + fi + ;; - 'stop_msg') - # Internal command called by launchd on HP-UX. - checkUser "" "$COMMAND" - stopmsg - ;; + 'condrestart') + checkUser touchlock "$COMMAND" + if [ ! -n "$FIXED_COMMAND" ] ; then + shift + fi + stopit "1" + start "$@" + ;; + + 'pause') + if [ -n "$PAUSABLE" ] + then + pause + else + showUsage "$COMMAND" + fi + ;; - 'launchdinternal' | 'upstartinternal') - if [ ! "$DIST_OS" = "macosx" -o ! -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then - checkUser touchlock "$@" - fi - # Internal command called by launchd on Max OSX. - # We do not want to call checkUser here as it is handled in the launchd plist file. Doing it here would confuse launchd. - if [ ! -n "$FIXED_COMMAND" ] ; then - shift - fi - launchinternal "$@" - ;; + 'resume') + if [ -n "$PAUSABLE" ] + then + resume + else + showUsage "$COMMAND" + fi + ;; + + 'status') + status + ;; + + 'install') + installdaemon + ;; + + 'remove') + removedaemon + ;; + + 'dump') + checkUser "" "$COMMAND" + dump + ;; + + 'start_msg') + # Internal command called by launchd on HP-UX. + checkUser "" "$COMMAND" + startmsg + ;; + + 'stop_msg') + # Internal command called by launchd on HP-UX. + checkUser "" "$COMMAND" + stopmsg + ;; + + 'launchdinternal' | 'upstartinternal') + if [ ! "$DIST_OS" = "macosx" -o ! -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then + checkUser touchlock "$@" + fi + # Internal command called by launchd on Max OSX. + # We do not want to call checkUser here as it is handled in the launchd plist file. Doing it here would confuse launchd. + if [ ! -n "$FIXED_COMMAND" ] ; then + shift + fi + launchinternal "$@" + ;; - *) - showUsage "$COMMAND" - ;; - esac + *) + showUsage "$COMMAND" + ;; + esac } docommand "$@" diff --git a/concourse-server/wrapperlib/wrapper.jar b/concourse-server/wrapperlib/wrapper.jar index 407d90dcfafe641b40c655f2fa8007d47cd46063..3cd174de9ae6f4ced9018378cdb928c4729f9591 100644 GIT binary patch delta 74586 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�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 Nt&SVJ;1gsS`R6_7{NMe~jkpyRRXeJ3 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 From 9d2b39d648a50d9442b7c61a0602e1707ee172ef Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 10:11:39 -0500 Subject: [PATCH 093/100] upgrade native libraries and wrapper binaries for tanuki --- concourse-server/wrapper/wrapper-linux-x86-32 | Bin 278880 -> 290112 bytes concourse-server/wrapper/wrapper-linux-x86-64 | Bin 301320 -> 312520 bytes .../wrapper/wrapper-macosx-universal-32 | Bin 634780 -> 659392 bytes .../wrapper/wrapper-macosx-universal-64 | Bin 655544 -> 676056 bytes .../wrapperlib/libwrapper-linux-x86-32.so | Bin 33568 -> 35000 bytes .../wrapperlib/libwrapper-linux-x86-64.so | Bin 44448 -> 44456 bytes .../libwrapper-macosx-universal-32.jnilib | Bin 83228 -> 87360 bytes .../libwrapper-macosx-universal-64.jnilib | Bin 92624 -> 92656 bytes 8 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 concourse-server/wrapper/wrapper-linux-x86-32 mode change 100644 => 100755 concourse-server/wrapper/wrapper-macosx-universal-32 mode change 100644 => 100755 concourse-server/wrapperlib/libwrapper-linux-x86-32.so mode change 100644 => 100755 concourse-server/wrapperlib/libwrapper-linux-x86-64.so mode change 100644 => 100755 concourse-server/wrapperlib/libwrapper-macosx-universal-32.jnilib mode change 100644 => 100755 concourse-server/wrapperlib/libwrapper-macosx-universal-64.jnilib diff --git a/concourse-server/wrapper/wrapper-linux-x86-32 b/concourse-server/wrapper/wrapper-linux-x86-32 old mode 100644 new mode 100755 index f94fbfd09c2f1aa4bb89733e655446461c1bb822..10b0373584ee27ecdb74f9c1a321e00c06431af7 GIT binary patch literal 290112 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�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 JS1I@E{r@@C$?^aI diff --git a/concourse-server/wrapper/wrapper-linux-x86-64 b/concourse-server/wrapper/wrapper-linux-x86-64 index 71cacfb5f5fcaffd70081fd2cc5cee39dfdec815..43d5ec63ca849357e89f9207eb91914ac1b92871 100755 GIT binary patch literal 312520 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

TA&B95NqW+@P}B5Fj@U|)Q*o2NqC0u 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

c1*3qyzLSI^U}bkKt@L&Ay``qhLi~Z!zjLKA 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 F{|8Jf@rD2Z literal 301320 zcmd?SXyMOMlJKE_!PgR{db?Q{r zsim7cvpiQvIUI`pi&FlhkXk&(B_Q*!qHMfBTG0ICQqq)I{OzgqP)-9KBQSF|g9eOc;mcY1OQO zv(hdfc=@1VN*G8O`|AKSp zr|r|u8hO`xgdM`w2iJqR_u^1c%Z=+3Tr+U7yaRFZ zmt@|QD{;RV*AKYzalM9X3@-kb;#z~Nhy;EruCH({!DavM$3q&f2XIZsH38QxxSqh( z1J`z3{B6Wl%?JE4amjT(6Z9htkbV#0c{8p_xGu-l8P|Vt<>0yl*LAo)$MrKV{=UZ* z5Ko~$kK^$dT)DVb;_~8}hifXX^KkLkB>pNvyDzoqiMZcvJ&(kF1+MP6p2Ss)>jqqJ z;<^u)X<3S*;aUD21W|>n+yX0czY5nNT({v$#>L+pT;p(!B7t86t`uCi;(7~Le_Z^{ z!u2Aqmq}Q^+j0LDR{^e%ah;FrY+QM`_^ZHm39h-g_TUk3@_-DKXx->>oa9E%=} z`vL2Dwsp5}ui~Ku*CreI|LA^t7~l4wpS7?A>)sXjPS&%1JJWuMaE}kedr9<#(zE@z z01q8;J!1iP;{K8K%v*2iS$?bo@vQ~Cfcw2tU>7|1#dU^+4U?Yt;@O4k8e9u;*}pIC z2i)69&lgC~_G2L)Hdw?i>;AfRzZ&;qTwho;Z)Lc4+~1a-d*Zp#qQAp^nf3gXbq~Ka19(&dW2EOxt!JNv zrCQI1ggLF}=Om1e^>byXa(N@8Pd z9TMZAOM)Z1SGV$%=nUnI>go(fpXjveIG57YsV*fdx;i%c>Bed$@#49iq7~P@k5%_j zT&|=jB{6DgLOUfh!;}SLC~RNz`G8-Ic@uwY@t6AIkG9Enxe%9zi$B{w@53`BQXazf z2(HI)@%Myz6MxxI-+

&%<@yB>cxSabvKn{csRHRAdTmu)|I{MV26#5PLX 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

dCRizaW=6bGq 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 X8eDav=*o`DjoR|hMTw+cK1cq)psA3K diff --git a/concourse-server/wrapper/wrapper-macosx-universal-32 b/concourse-server/wrapper/wrapper-macosx-universal-32 old mode 100644 new mode 100755 index 5b75d2f8313f221aa948c43388bab3e66a924bfb..b730be2443cb6b5ebbf11750f7c4f262e4ca99f2 GIT binary patch literal 659392 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≦pf>1=p6I|l3!uB-Xa14n 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 delta 211758 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 uUPp+y5I=c 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 K;m#me=YIjDlejAY diff --git a/concourse-server/wrapper/wrapper-macosx-universal-64 b/concourse-server/wrapper/wrapper-macosx-universal-64 index 72296e00688a9e93500bd36ca2213334183124ff..d93ecfaa26ea841740359ee04c8f6751b1487466 100755 GIT binary patch literal 676056 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

4|?<36@;tBdE)M19w&xStsHopD`s-r(n@_oscY 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%0Di^ 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 delta 221572 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 L=iTmpmB#-ACcO|8 diff --git a/concourse-server/wrapperlib/libwrapper-linux-x86-32.so b/concourse-server/wrapperlib/libwrapper-linux-x86-32.so old mode 100644 new mode 100755 index 9bdb8fe8d46423db19138aeb468dabba9abd77f6..250886fb29dbe1586a4e336720a9ca6b91a3a603 GIT binary patch literal 35000 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%0ApvC zJNvM^IdYBh>OA)ms`}sNE835?pdHS2-=H13is$o<87KNP{s<-(9Vq`h)&PBNVxw~m z*z1k|sSD?9?g#Wceat$pH3z^2=&Wxt>Ueb?&-lvJXM)Ebhtz>}W_-?>vT5^j4Q4!! zu~4^}_+*`FXFslQ>dW|KJ+JfigNeUc=)=Bd?vzWKHZd`8x5q%#R{$Kkojzo_vo5SN zywux(iF2+~&fBiPS$7=Q$Tj+Or=J77W*nC7eb1#6c?ryaP5{Rt7`qsmgHgxpLVU$J zn4H{M7s@~`fG*=~(y|d4c>UJ~(H}qE7aBq8!#a0Cs*;Pu}STMTw^yU$|mM0&(7xnZDg_Bc|7K7yWZrXk2o&%;Cv{L zHZeM$D;8*HUW4Rk4%9YDZFt6FFJ#*Ush?ZGAFa3jj>4e9s+Si(uP`i9KbC*nKKug* zBBQu$nElrh@be_^250(r{?^1y-^*WmqHv57J=Tie4ozK|Q z@p~uu;U@K49+KOqqaWjdvCWHR`p?7?u>+6+pdU?4(f3@7#<%AF!nMHoGV77?$~a>D zH~@@k(}z4Po4$rNeLLqZ$}#$+pv-;yK#d0j^Kt?%M1Sf>`2+{RtU=1M&yzYa&KcV) z0A?=KgL+dB2jD_LB{~_61;32 z>Y+;+s;xHQkMXyo@m)X}W&i(pC00hXUVjaK^B1rKun+J7;3(iUAog1PCM4hnz!<tur5>-MhNgOj8a^10RT0h+$tlS?+DBaP#*olk+@f_?fWee1#U~K@f!GE-uc)X!!Lt z{*x@yR8Rbl5<21M7x^oA=Y28n^aAt%F!vGaq7{vz zz?A^jk&ii)z}#uBC|y8GkUU5}=0E~}cbPOVVgm2o%$qDTcM|5K%rPj7mT|07f3~Nd!zKe6fues)dhwH-jLw4PcA992yq`vk&>29#CLY zPjn+I_fF)aU9Cz5w3oR|AMQyFUrZ%zN-#{ohOY``!zsc2>^#ffdGJO9H1iZql literal 33568 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 literal 44448 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 UXtieBO%>m$`rk1@qnw8SAN>=&+yDRo diff --git a/concourse-server/wrapperlib/libwrapper-macosx-universal-32.jnilib b/concourse-server/wrapperlib/libwrapper-macosx-universal-32.jnilib old mode 100644 new mode 100755 index f25452c68df1ba0c6144ed60292e454fc5f6a04e..b06810697ccd976fc8fd6fa93bdb085ee58eb611 GIT binary patch delta 24822 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 BwOIfF delta 23447 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 H->~{0c~YY3 delta 26625 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 From 922b203ab9f0ad33dda5763d9f4b75a8822c2d3e Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 14:49:34 -0500 Subject: [PATCH 094/100] backport changes necessary to make session listing cli action work --- .../concourse/security/AccessManager.java | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java index 12f59ed0c1..21badb2a0a 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java @@ -27,13 +27,17 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.security.SecureRandom; +import java.text.MessageFormat; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.cinchapi.concourse.Timestamp; import org.cinchapi.concourse.annotate.Restricted; import org.cinchapi.concourse.server.io.Byteable; import org.cinchapi.concourse.server.io.ByteableCollections; @@ -42,6 +46,8 @@ import org.cinchapi.concourse.time.Time; import org.cinchapi.concourse.util.ByteBuffers; import org.cinchapi.concourse.util.TStrings; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; import static com.google.common.base.Preconditions.*; @@ -50,6 +56,7 @@ import com.google.common.base.Throwables; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; @@ -216,6 +223,20 @@ public void deauthorize(AccessToken token) { tokenManager.deleteToken(token); // the #tokenManager handles locking } + /** + * Return a list of strings, each of which describes a currently existing + * access token. + * + * @return a list of token descriptions + */ + public List describeAllAccessTokens() { + List sessions = Lists.newArrayList(); + for (AccessTokenWrapper token : tokenManager.tokens.asMap().values()) { + sessions.add(token.getDescription()); + } + return sessions; + } + /** * Grant access to the user identified by {@code username} with * {@code password}. @@ -505,6 +526,18 @@ public static AccessTokenWrapper create(AccessToken token, return new AccessTokenWrapper(token, username, timestamp); } + /** + * The formatter that is used to when constructing a human readable + * description of the access token. + */ + private static final DateTimeFormatter DATE_TIME_FORMATTER = new DateTimeFormatterBuilder() + .appendMonthOfYearShortText().appendLiteral(" ") + .appendDayOfMonth(1).appendLiteral(", ").appendYear(4, 4) + .appendLiteral(" at ").appendHourOfDay(1).appendLiteral(":") + .appendMinuteOfHour(2).appendLiteral(":") + .appendSecondOfMinute(2).appendLiteral(" ") + .appendHalfdayOfDayText().toFormatter(); + private final AccessToken token; private final String username; // hex private final long timestamp; @@ -541,6 +574,19 @@ public AccessToken getAccessToken() { return token; } + /** + * Return a human readable description of the access token. + * + * @return the description + */ + public String getDescription() { + return MessageFormat.format( + "{0} logged in since {1}", + ByteBuffers.getString(decodeHex(username)), + Timestamp.fromMicros(timestamp).getJoda() + .toString(DATE_TIME_FORMATTER)); + } + /** * Return the timestamp that is associated with the wrapped access * token. @@ -630,6 +676,12 @@ private Credentials(String username, String password, String salt) { this.salt = salt; } + @Override + public void copyTo(ByteBuffer buffer) { + buffer.put(getBytes()); + + } + @Override public ByteBuffer getBytes() { ByteBuffer bytes = ByteBuffer.allocate(size()); @@ -684,12 +736,6 @@ public String toString() { return sb.toString(); } - @Override - public void copyTo(ByteBuffer buffer) { - buffer.put(getBytes()); - - } - } } From 40a8afa0888a3887b2ef77d7689b3d3191dddb1b Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Fri, 12 Dec 2014 14:53:16 -0500 Subject: [PATCH 095/100] fix unit test compile error --- .../main/java/org/cinchapi/concourse/util/TestData.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/util/TestData.java b/concourse-server/src/main/java/org/cinchapi/concourse/util/TestData.java index 0b975631de..84f9d12a01 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/util/TestData.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/util/TestData.java @@ -53,6 +53,15 @@ public final class TestData extends Random { public static final String DATA_DIR = "test.out"; + + /** + * Return a temporary directory to use for testing files. + * + * @return the directory path + */ + public static String getTemporaryTestDir() { + return DATA_DIR + File.separator + Time.now(); + } public static PrimaryRevision getPrimaryRevision() { return Revision.createPrimaryRevision(getPrimaryKey(), getText(), From 44aae05f7877690521c7f883b58903f138e88872 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 13 Dec 2014 16:07:43 -0500 Subject: [PATCH 096/100] move pref reading block to a point after the path to the script is resolved --- concourse-server/scripts/concourse | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/concourse-server/scripts/concourse b/concourse-server/scripts/concourse index 6078586770..ec58adfbb1 100755 --- a/concourse-server/scripts/concourse +++ b/concourse-server/scripts/concourse @@ -151,35 +151,6 @@ PLIST_DOMAIN=org.cinchapi.concourse # 3) RUN_LEVEL, to specify a general run level. RUN_LEVEL=20 -# The following block contains custom logic to dynamically pull attributes from -# the concourse.prefs file and make analogous changes to the tanuki wrapper -# configuration file. -#----------------------------------------------------------------------------- - -# Pull jmx_port from concourse.prefs -JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` -if [ -z "$JMX_PREF" ]; then - JMX_PREF="9010" -fi -perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF - -# Pull heap_size from concourse.prefs -HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` -HEAP=${HEAP_PREF//[!0-9]/} -if [[ $HEAP_PREF == *g* ]] - then - HEAP=$(($HEAP * 1024)) -fi -if [ -z "$HEAP" ]; then - HEAP="1024" -fi -MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF -perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF - # Do not modify anything beyond this point #----------------------------------------------------------------------------- @@ -288,6 +259,35 @@ if [ "$FIRST_CHAR" != "/" ] WRAPPER_CONF=$REALDIR/$WRAPPER_CONF fi +# The following block contains custom logic to dynamically pull attributes from +# the concourse.prefs file and make analogous changes to the tanuki wrapper +# configuration file. +#----------------------------------------------------------------------------- + +# Pull jmx_port from concourse.prefs +JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` +if [ -z "$JMX_PREF" ]; then + JMX_PREF="9010" +fi +perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF + +# Pull heap_size from concourse.prefs +HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` +HEAP=${HEAP_PREF//[!0-9]/} +if [[ $HEAP_PREF == *g* ]] + then + HEAP=$(($HEAP * 1024)) +fi +if [ -z "$HEAP" ]; then + HEAP="1024" +fi +MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF +perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF + # Process ID ANCHORFILE="$PIDDIR/$APP_NAME.anchor" COMMANDFILE="$PIDDIR/$APP_NAME.command" From d5d6a89c31f0987921031cd6496728550b475d33 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 13 Dec 2014 16:15:41 -0500 Subject: [PATCH 097/100] another attempt to place pref parsing logic in correct location --- concourse-server/scripts/concourse | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/concourse-server/scripts/concourse b/concourse-server/scripts/concourse index ec58adfbb1..ee245f07ad 100755 --- a/concourse-server/scripts/concourse +++ b/concourse-server/scripts/concourse @@ -259,35 +259,6 @@ if [ "$FIRST_CHAR" != "/" ] WRAPPER_CONF=$REALDIR/$WRAPPER_CONF fi -# The following block contains custom logic to dynamically pull attributes from -# the concourse.prefs file and make analogous changes to the tanuki wrapper -# configuration file. -#----------------------------------------------------------------------------- - -# Pull jmx_port from concourse.prefs -JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` -if [ -z "$JMX_PREF" ]; then - JMX_PREF="9010" -fi -perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF - -# Pull heap_size from concourse.prefs -HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` -HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` -HEAP=${HEAP_PREF//[!0-9]/} -if [[ $HEAP_PREF == *g* ]] - then - HEAP=$(($HEAP * 1024)) -fi -if [ -z "$HEAP" ]; then - HEAP="1024" -fi -MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` -perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF -perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF - # Process ID ANCHORFILE="$PIDDIR/$APP_NAME.anchor" COMMANDFILE="$PIDDIR/$APP_NAME.command" @@ -516,6 +487,35 @@ else ECHOOPT="" fi +# The following block contains custom logic to dynamically pull attributes from +# the concourse.prefs file and make analogous changes to the tanuki wrapper +# configuration file. +#----------------------------------------------------------------------------- + +# Pull jmx_port from concourse.prefs +JMX_PREF=`cat $PREFS | grep -e '^jmx_port\s*=\s*[0-9]\{1,\}$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +JMX_CONF=`cat $WRAPPER_CONF | grep -e '-Dcom.sun.management.jmxremote.port\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f3 | tr -d ' '` +if [ -z "$JMX_PREF" ]; then + JMX_PREF="9010" +fi +perl -p -i -e "s/jmxremote.port=$JMX_CONF/jmxremote.port=$JMX_PREF/g" $WRAPPER_CONF + +# Pull heap_size from concourse.prefs +HEAP_PREF=`cat $PREFS | grep -e '^heap_size\s*=\s*[0-9]\{1,\}\(m\|M\|mb\|MB\|g\|G\|gb\|GB\)$' | head -n1 | cut -d'=' -f2 | tr -d ' '` +HEAP_PREF=`echo $HEAP_PREF | awk '{print tolower($0)}'` +HEAP=${HEAP_PREF//[!0-9]/} +if [[ $HEAP_PREF == *g* ]] + then + HEAP=$(($HEAP * 1024)) +fi +if [ -z "$HEAP" ]; then + HEAP="1024" +fi +MIN_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.initmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +MAX_HEAP=`cat $WRAPPER_CONF | grep -e 'wrapper.java.maxmemory\s*=\s*[0-9]\{1,\}' | head -n1 | cut -d'=' -f2` +perl -p -i -e "s/wrapper.java.initmemory=$MIN_HEAP/wrapper.java.initmemory=$HEAP/g" $WRAPPER_CONF +perl -p -i -e "s/wrapper.java.maxmemory=$MAX_HEAP/wrapper.java.maxmemory=$HEAP/g" $WRAPPER_CONF + gettext() { "$WRAPPER_CMD" --translate "$1" "$WRAPPER_CONF" 2>/dev/null From 95f3cbff43c5c148e11c2923c68e9fce529531ae Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sat, 13 Dec 2014 16:25:49 -0500 Subject: [PATCH 098/100] set location of prefs file relative to the real dir --- concourse-server/scripts/concourse | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/concourse-server/scripts/concourse b/concourse-server/scripts/concourse index ee245f07ad..dd2beee14c 100755 --- a/concourse-server/scripts/concourse +++ b/concourse-server/scripts/concourse @@ -258,6 +258,12 @@ if [ "$FIRST_CHAR" != "/" ] then WRAPPER_CONF=$REALDIR/$WRAPPER_CONF fi +# Same test for PREFS +FIRST_CHAR=`echo $PREFS | cut -c1,1` +if [ "$PREFS" != "/" ] + then + PREFS=$REALDIR/$PREFS +fi # Process ID ANCHORFILE="$PIDDIR/$APP_NAME.anchor" From 7f4e207215057db76f20723ccf9fcf292ff2d938 Mon Sep 17 00:00:00 2001 From: Jeff Nelson Date: Sun, 21 Dec 2014 07:38:39 -0500 Subject: [PATCH 099/100] sort access tokens when returning a description of them Conflicts: concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java --- .../concourse/security/AccessManager.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java index 21badb2a0a..c469f8166e 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/security/AccessManager.java @@ -29,6 +29,7 @@ import java.security.SecureRandom; import java.text.MessageFormat; import java.util.Iterator; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -60,6 +61,7 @@ import com.google.common.collect.Maps; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; +import com.google.common.primitives.Longs; /** * The {@link AccessManager} controls access to the server by keeping tracking @@ -231,12 +233,15 @@ public void deauthorize(AccessToken token) { */ public List describeAllAccessTokens() { List sessions = Lists.newArrayList(); + List tokens = Lists + .newArrayList(tokenManager.tokens.asMap().values()); + Collections.sort(tokens); for (AccessTokenWrapper token : tokenManager.tokens.asMap().values()) { sessions.add(token.getDescription()); } return sessions; } - + /** * Grant access to the user identified by {@code username} with * {@code password}. @@ -510,7 +515,8 @@ public boolean isValidToken(AccessToken token) { * * @author jnelson */ - private static class AccessTokenWrapper { + private static class AccessTokenWrapper implements + Comparable { /** * Create a new {@link AccessTokenWrapper} that wraps {@code token} for @@ -556,6 +562,11 @@ private AccessTokenWrapper(AccessToken token, String username, this.timestamp = timestamp; } + @Override + public int compareTo(AccessTokenWrapper o) { + return Longs.compare(timestamp, o.timestamp); + } + @Override public boolean equals(Object obj) { if(obj instanceof AccessTokenWrapper) { From ebfe90d1b47fb132c416b180855d18286ac19183 Mon Sep 17 00:00:00 2001 From: dubex Date: Mon, 29 Dec 2014 23:50:52 +0530 Subject: [PATCH 100/100] removed write lock from insert method in Queue --- .../org/cinchapi/concourse/server/storage/temp/Queue.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Queue.java b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Queue.java index f3903b8d07..16c04c9bd3 100644 --- a/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Queue.java +++ b/concourse-server/src/main/java/org/cinchapi/concourse/server/storage/temp/Queue.java @@ -71,13 +71,7 @@ public Iterator iterator() { @Override public boolean insert(Write write) { - writeLock.lock(); - try { - return writes.add(write); - } - finally { - writeLock.unlock(); - } + return writes.add(write); } @Override

6mdQI80WD08^}Y?ar=n&KaLEWchqc>6?WiO}Vv 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