diff --git a/akka-actor-tests/src/test/scala/akka/actor/ActorTimeoutSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ActorTimeoutSpec.scala
index 6800033d4c..61f71ad994 100644
--- a/akka-actor-tests/src/test/scala/akka/actor/ActorTimeoutSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/actor/ActorTimeoutSpec.scala
@@ -13,6 +13,7 @@ import akka.pattern.{ ask, AskTimeoutException }
class ActorTimeoutSpec extends AkkaSpec {
val testTimeout = 200.millis.dilated
+ val leeway = 500.millis.dilated
"An Actor-based Future" must {
@@ -20,13 +21,13 @@ class ActorTimeoutSpec extends AkkaSpec {
implicit val timeout = Timeout(testTimeout)
val echo = system.actorOf(Props.empty)
val f = (echo ? "hallo")
- intercept[AskTimeoutException] { Await.result(f, testTimeout * 2) }
+ intercept[AskTimeoutException] { Await.result(f, testTimeout + leeway) }
}
"use explicitly supplied timeout" in {
val echo = system.actorOf(Props.empty)
val f = echo.?("hallo")(testTimeout)
- intercept[AskTimeoutException] { Await.result(f, testTimeout * 2) }
+ intercept[AskTimeoutException] { Await.result(f, testTimeout + leeway) }
}
}
}
diff --git a/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala
index d205d1a1ad..e9e5f3129b 100644
--- a/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala
+++ b/akka-actor-tests/src/test/scala/akka/actor/SchedulerSpec.scala
@@ -33,7 +33,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
collectCancellable(system.scheduler.schedule(0 milliseconds, 50 milliseconds, tickActor, Tick))
// after max 1 second it should be executed at least the 3 times already
- assert(countDownLatch.await(1, TimeUnit.SECONDS))
+ assert(countDownLatch.await(2, TimeUnit.SECONDS))
val countDownLatch2 = new CountDownLatch(3)
@@ -43,14 +43,21 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
assert(countDownLatch2.await(2, TimeUnit.SECONDS))
}
- "should stop continuous scheduling if the receiving actor has been terminated" in {
+ "should stop continuous scheduling if the receiving actor has been terminated" taggedAs TimingTest in {
+ val actor = system.actorOf(Props(new Actor {
+ def receive = {
+ case x ⇒ testActor ! x
+ }
+ }))
+
// run immediately and then every 100 milliseconds
- collectCancellable(system.scheduler.schedule(0 milliseconds, 100 milliseconds, testActor, "msg"))
+ collectCancellable(system.scheduler.schedule(0 milliseconds, 100 milliseconds, actor, "msg"))
+ expectNoMsg(1 second)
// stop the actor and, hence, the continuous messaging from happening
- testActor ! PoisonPill
+ actor ! PoisonPill
- expectNoMsg(500 milliseconds)
+ expectMsg("msg")
}
"schedule once" in {
@@ -69,7 +76,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
countDownLatch.getCount must be(3)
// after 1 second the wait should fail
- assert(countDownLatch.await(1, TimeUnit.SECONDS) == false)
+ assert(countDownLatch.await(2, TimeUnit.SECONDS) == false)
// should still be 1 left
countDownLatch.getCount must be(1)
}
@@ -93,7 +100,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
assert(ticks.await(3, TimeUnit.SECONDS) == false) //No counting down should've been made
}
- "be cancellable during initial delay" in {
+ "be cancellable during initial delay" taggedAs TimingTest in {
val ticks = new AtomicInteger
val initialDelay = 200.milliseconds.dilated
@@ -108,7 +115,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
ticks.get must be(0)
}
- "be cancellable after initial delay" in {
+ "be cancellable after initial delay" taggedAs TimingTest in {
val ticks = new AtomicInteger
val initialDelay = 20.milliseconds.dilated
@@ -179,7 +186,7 @@ class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout
Await.ready(ticks, 3 seconds)
}
- "schedule with different initial delay and frequency" in {
+ "schedule with different initial delay and frequency" taggedAs TimingTest in {
val ticks = new TestLatch(3)
case object Msg
diff --git a/akka-actor/src/main/java/akka/jsr166y/ThreadLocalRandom.java b/akka-actor/src/main/java/akka/jsr166y/ThreadLocalRandom.java
new file mode 100644
index 0000000000..d2dbd58120
--- /dev/null
+++ b/akka-actor/src/main/java/akka/jsr166y/ThreadLocalRandom.java
@@ -0,0 +1,197 @@
+/*
+ * 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 akka.jsr166y;
+
+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)
+ * @throws IllegalArgumentException if least greater than or equal
+ * to bound
+ * @return the next value
+ */
+ 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/akka-actor/src/main/java/com/typesafe/config/ConfigValue.java b/akka-actor/src/main/java/com/typesafe/config/ConfigValue.java
index 903b8c8c8f..1f389be08f 100644
--- a/akka-actor/src/main/java/com/typesafe/config/ConfigValue.java
+++ b/akka-actor/src/main/java/com/typesafe/config/ConfigValue.java
@@ -18,7 +18,7 @@ package com.typesafe.config;
* interface is likely to grow new methods over time, so third-party
* implementations will break.
*/
-public interface ConfigValue extends ConfigMergeable, java.io.Serializable {
+public interface ConfigValue extends ConfigMergeable {
/**
* The origin of the value (file, line number, etc.), for debugging and
* error messages.
diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java
index 83d4ccdc44..642fb7ae26 100644
--- a/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java
+++ b/akka-actor/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java
@@ -3,6 +3,8 @@
*/
package com.typesafe.config.impl;
+import java.io.Serializable;
+
import com.typesafe.config.ConfigException;
import com.typesafe.config.ConfigMergeable;
import com.typesafe.config.ConfigOrigin;
@@ -16,7 +18,7 @@ import com.typesafe.config.ConfigValue;
* improperly-factored and non-modular code. Please don't add parent().
*
*/
-abstract class AbstractConfigValue implements ConfigValue, MergeableValue {
+abstract class AbstractConfigValue implements ConfigValue, MergeableValue, Serializable {
final private SimpleConfigOrigin origin;
diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMerge.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMerge.java
index 218aaca306..b64712ebd5 100644
--- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMerge.java
+++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMerge.java
@@ -3,6 +3,7 @@
*/
package com.typesafe.config.impl;
+import java.io.ObjectStreamException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -228,4 +229,14 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements
sb.append("# ) end of unresolved merge\n");
}
}
+
+ // This ridiculous hack is because some JDK versions apparently can't
+ // serialize an array, which is used to implement ArrayList and EmptyList.
+ // maybe
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627
+ private Object writeReplace() throws ObjectStreamException {
+ // switch to LinkedList
+ return new ConfigDelayedMerge(origin(),
+ new java.util.LinkedList(stack), ignoresFallbacks);
+ }
}
diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMergeObject.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMergeObject.java
index a9ff7bd3bf..c290c47fa5 100644
--- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMergeObject.java
+++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigDelayedMergeObject.java
@@ -3,6 +3,7 @@
*/
package com.typesafe.config.impl;
+import java.io.ObjectStreamException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -192,4 +193,14 @@ class ConfigDelayedMergeObject extends AbstractConfigObject implements
protected AbstractConfigValue peek(String key) {
throw notResolved();
}
+
+ // This ridiculous hack is because some JDK versions apparently can't
+ // serialize an array, which is used to implement ArrayList and EmptyList.
+ // maybe
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627
+ private Object writeReplace() throws ObjectStreamException {
+ // switch to LinkedList
+ return new ConfigDelayedMergeObject(origin(),
+ new java.util.LinkedList(stack), ignoresFallbacks);
+ }
}
diff --git a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java
index e882061f11..0906fd2168 100644
--- a/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java
+++ b/akka-actor/src/main/java/com/typesafe/config/impl/ConfigSubstitution.java
@@ -3,6 +3,7 @@
*/
package com.typesafe.config.impl;
+import java.io.ObjectStreamException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -284,4 +285,14 @@ final class ConfigSubstitution extends AbstractConfigValue implements
}
}
}
+
+ // This ridiculous hack is because some JDK versions apparently can't
+ // serialize an array, which is used to implement ArrayList and EmptyList.
+ // maybe
+ // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6446627
+ private Object writeReplace() throws ObjectStreamException {
+ // switch to LinkedList
+ return new ConfigSubstitution(origin(), new java.util.LinkedList