2010-10-26 12:49:25 +02:00
|
|
|
package akka.actor
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-06-20 15:11:32 -06:00
|
|
|
import org.scalatest.BeforeAndAfterEach
|
2011-11-23 15:15:44 +01:00
|
|
|
import akka.util.duration._
|
|
|
|
|
import java.util.concurrent.{ CountDownLatch, ConcurrentLinkedQueue, TimeUnit }
|
2012-01-13 16:38:33 +01:00
|
|
|
import akka.testkit._
|
2011-12-12 22:50:08 +01:00
|
|
|
import akka.dispatch.Await
|
2012-01-18 10:18:51 +01:00
|
|
|
import akka.pattern.ask
|
2012-01-13 16:38:33 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-21 17:01:22 +02:00
|
|
|
@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner])
|
2011-12-05 20:01:42 +01:00
|
|
|
class SchedulerSpec extends AkkaSpec with BeforeAndAfterEach with DefaultTimeout {
|
2011-11-10 11:53:36 +01:00
|
|
|
private val cancellables = new ConcurrentLinkedQueue[Cancellable]()
|
2011-07-12 12:17:32 +02:00
|
|
|
|
2011-11-10 11:53:36 +01:00
|
|
|
def collectCancellable(c: Cancellable): Cancellable = {
|
|
|
|
|
cancellables.add(c)
|
|
|
|
|
c
|
2011-07-12 12:17:32 +02:00
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
override def afterEach {
|
2011-11-10 11:53:36 +01:00
|
|
|
while (cancellables.peek() ne null) { Option(cancellables.poll()).foreach(_.cancel()) }
|
2010-08-24 23:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
"A Scheduler" must {
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
"schedule more than once" in {
|
|
|
|
|
case object Tick
|
|
|
|
|
val countDownLatch = new CountDownLatch(3)
|
2011-12-13 14:09:40 +01:00
|
|
|
val tickActor = system.actorOf(Props(new Actor {
|
2011-10-11 16:05:48 +02:00
|
|
|
def receive = { case Tick ⇒ countDownLatch.countDown() }
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-11-26 15:24:13 +01:00
|
|
|
// run every 50 milliseconds
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.schedule(0 milliseconds, 50 milliseconds, tickActor, Tick))
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
// after max 1 second it should be executed at least the 3 times already
|
|
|
|
|
assert(countDownLatch.await(1, TimeUnit.SECONDS))
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
val countDownLatch2 = new CountDownLatch(3)
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.schedule(0 milliseconds, 50 milliseconds)(countDownLatch2.countDown()))
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
// after max 1 second it should be executed at least the 3 times already
|
|
|
|
|
assert(countDownLatch2.await(2, TimeUnit.SECONDS))
|
|
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-11-25 17:56:14 +01:00
|
|
|
"should stop continuous scheduling if the receiving actor has been terminated" in {
|
|
|
|
|
// run immediately and then every 100 milliseconds
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.schedule(0 milliseconds, 100 milliseconds, testActor, "msg"))
|
2011-11-25 17:56:14 +01:00
|
|
|
|
|
|
|
|
// stop the actor and, hence, the continuous messaging from happening
|
2011-11-26 15:24:13 +01:00
|
|
|
testActor ! PoisonPill
|
2011-11-25 17:56:14 +01:00
|
|
|
|
2011-11-26 15:24:13 +01:00
|
|
|
expectNoMsg(500 milliseconds)
|
2011-11-25 17:56:14 +01:00
|
|
|
}
|
|
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
"schedule once" in {
|
|
|
|
|
case object Tick
|
|
|
|
|
val countDownLatch = new CountDownLatch(3)
|
2011-12-13 14:09:40 +01:00
|
|
|
val tickActor = system.actorOf(Props(new Actor {
|
2011-10-11 16:05:48 +02:00
|
|
|
def receive = { case Tick ⇒ countDownLatch.countDown() }
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-11-09 15:25:14 +01:00
|
|
|
|
2011-12-02 17:13:46 +01:00
|
|
|
// run after 300 millisec
|
|
|
|
|
collectCancellable(system.scheduler.scheduleOnce(300 milliseconds, tickActor, Tick))
|
|
|
|
|
collectCancellable(system.scheduler.scheduleOnce(300 milliseconds)(countDownLatch.countDown()))
|
|
|
|
|
|
|
|
|
|
// should not be run immediately
|
|
|
|
|
assert(countDownLatch.await(100, TimeUnit.MILLISECONDS) == false)
|
|
|
|
|
countDownLatch.getCount must be(3)
|
2011-10-11 16:05:48 +02:00
|
|
|
|
|
|
|
|
// after 1 second the wait should fail
|
2011-12-02 17:13:46 +01:00
|
|
|
assert(countDownLatch.await(1, TimeUnit.SECONDS) == false)
|
2011-10-11 16:05:48 +02:00
|
|
|
// should still be 1 left
|
2011-12-02 17:13:46 +01:00
|
|
|
countDownLatch.getCount must be(1)
|
2010-08-24 23:21:28 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
/**
|
|
|
|
|
* ticket #372
|
|
|
|
|
*/
|
|
|
|
|
"be cancellable" in {
|
|
|
|
|
object Ping
|
|
|
|
|
val ticks = new CountDownLatch(1)
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val actor = system.actorOf(Props(new Actor {
|
2011-10-11 16:05:48 +02:00
|
|
|
def receive = { case Ping ⇒ ticks.countDown() }
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
(1 to 10).foreach { i ⇒
|
2011-12-02 17:13:46 +01:00
|
|
|
val timeout = collectCancellable(system.scheduler.scheduleOnce(1 second, actor, Ping))
|
2011-11-09 15:25:14 +01:00
|
|
|
timeout.cancel()
|
2010-08-24 23:21:28 +02:00
|
|
|
}
|
2011-11-09 15:25:14 +01:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
assert(ticks.await(3, TimeUnit.SECONDS) == false) //No counting down should've been made
|
|
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
|
2012-01-13 16:38:33 +01:00
|
|
|
"be cancellable during initial delay" in {
|
|
|
|
|
val ticks = new AtomicInteger
|
|
|
|
|
|
|
|
|
|
val initialDelay = 200.milliseconds.dilated
|
|
|
|
|
val delay = 10.milliseconds.dilated
|
|
|
|
|
val timeout = collectCancellable(system.scheduler.schedule(initialDelay, delay) {
|
|
|
|
|
ticks.incrementAndGet()
|
|
|
|
|
})
|
|
|
|
|
10.milliseconds.dilated.sleep()
|
|
|
|
|
timeout.cancel()
|
|
|
|
|
(initialDelay + 100.milliseconds.dilated).sleep()
|
|
|
|
|
|
|
|
|
|
ticks.get must be(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"be cancellable after initial delay" in {
|
|
|
|
|
val ticks = new AtomicInteger
|
|
|
|
|
|
|
|
|
|
val initialDelay = 20.milliseconds.dilated
|
|
|
|
|
val delay = 200.milliseconds.dilated
|
|
|
|
|
val timeout = collectCancellable(system.scheduler.schedule(initialDelay, delay) {
|
|
|
|
|
ticks.incrementAndGet()
|
|
|
|
|
})
|
|
|
|
|
(initialDelay + 100.milliseconds.dilated).sleep()
|
|
|
|
|
timeout.cancel()
|
|
|
|
|
(delay + 100.milliseconds.dilated).sleep()
|
|
|
|
|
|
|
|
|
|
ticks.get must be(1)
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
/**
|
|
|
|
|
* ticket #307
|
|
|
|
|
*/
|
|
|
|
|
"pick up schedule after actor restart" in {
|
2011-12-05 20:01:42 +01:00
|
|
|
|
2011-10-11 16:05:48 +02:00
|
|
|
object Ping
|
|
|
|
|
object Crash
|
|
|
|
|
|
2011-12-14 12:39:27 +01:00
|
|
|
val restartLatch = new TestLatch
|
2011-12-19 15:05:33 +01:00
|
|
|
val pingLatch = new TestLatch(6)
|
2011-10-11 16:05:48 +02:00
|
|
|
|
2012-01-24 08:37:01 +01:00
|
|
|
val supervisor = system.actorOf(Props(new Supervisor(AllForOneStrategy(List(classOf[Exception]), 3, 1 second))))
|
2011-10-18 15:39:26 +02:00
|
|
|
val props = Props(new Actor {
|
2011-10-11 16:05:48 +02:00
|
|
|
def receive = {
|
|
|
|
|
case Ping ⇒ pingLatch.countDown()
|
|
|
|
|
case Crash ⇒ throw new Exception("CRASH")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def postRestart(reason: Throwable) = restartLatch.open
|
2011-10-18 15:39:26 +02:00
|
|
|
})
|
2011-12-12 22:50:08 +01:00
|
|
|
val actor = Await.result((supervisor ? props).mapTo[ActorRef], timeout.duration)
|
2011-10-11 16:05:48 +02:00
|
|
|
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.schedule(500 milliseconds, 500 milliseconds, actor, Ping))
|
2011-10-11 16:05:48 +02:00
|
|
|
// appx 2 pings before crash
|
2011-11-11 18:41:43 +01:00
|
|
|
EventFilter[Exception]("CRASH", occurrences = 1) intercept {
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.scheduleOnce(1000 milliseconds, actor, Crash))
|
2011-11-11 18:41:43 +01:00
|
|
|
}
|
2011-10-11 16:05:48 +02:00
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(restartLatch, 2 seconds)
|
2011-10-11 16:05:48 +02:00
|
|
|
// should be enough time for the ping countdown to recover and reach 6 pings
|
2011-12-20 10:58:17 +01:00
|
|
|
Await.ready(pingLatch, 5 seconds)
|
2011-10-11 16:05:48 +02:00
|
|
|
}
|
2011-11-22 15:26:21 +01:00
|
|
|
|
|
|
|
|
"never fire prematurely" in {
|
2011-12-19 15:05:33 +01:00
|
|
|
val ticks = new TestLatch(300)
|
2011-11-22 15:26:21 +01:00
|
|
|
|
|
|
|
|
case class Msg(ts: Long)
|
|
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val actor = system.actorOf(Props(new Actor {
|
2011-11-22 15:26:21 +01:00
|
|
|
def receive = {
|
|
|
|
|
case Msg(ts) ⇒
|
2011-11-24 14:30:03 +01:00
|
|
|
val now = System.nanoTime
|
2012-01-24 08:37:01 +01:00
|
|
|
// Make sure that no message has been dispatched before the scheduled time (10ms) has occurred
|
|
|
|
|
if (now - ts < 10.millis.toNanos) throw new RuntimeException("Interval is too small: " + (now - ts))
|
2011-11-22 15:26:21 +01:00
|
|
|
ticks.countDown()
|
|
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-11-22 15:26:21 +01:00
|
|
|
|
2011-11-24 09:54:08 +01:00
|
|
|
(1 to 300).foreach { i ⇒
|
2011-12-02 17:13:46 +01:00
|
|
|
collectCancellable(system.scheduler.scheduleOnce(10 milliseconds, actor, Msg(System.nanoTime)))
|
2011-11-22 15:26:21 +01:00
|
|
|
Thread.sleep(5)
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(ticks, 3 seconds)
|
2011-11-22 15:26:21 +01:00
|
|
|
}
|
2011-11-23 15:15:44 +01:00
|
|
|
|
|
|
|
|
"schedule with different initial delay and frequency" in {
|
2011-12-19 15:05:33 +01:00
|
|
|
val ticks = new TestLatch(3)
|
2011-11-23 15:15:44 +01:00
|
|
|
|
|
|
|
|
case object Msg
|
|
|
|
|
|
2011-12-13 14:09:40 +01:00
|
|
|
val actor = system.actorOf(Props(new Actor {
|
2011-11-23 15:15:44 +01:00
|
|
|
def receive = {
|
|
|
|
|
case Msg ⇒ ticks.countDown()
|
|
|
|
|
}
|
2011-12-13 14:09:40 +01:00
|
|
|
}))
|
2011-11-23 15:15:44 +01:00
|
|
|
|
|
|
|
|
val startTime = System.nanoTime()
|
2011-12-20 10:58:17 +01:00
|
|
|
val cancellable = system.scheduler.schedule(1 second, 300 milliseconds, actor, Msg)
|
2011-12-19 15:05:33 +01:00
|
|
|
Await.ready(ticks, 3 seconds)
|
2011-11-23 15:15:44 +01:00
|
|
|
val elapsedTimeMs = (System.nanoTime() - startTime) / 1000000
|
|
|
|
|
|
2011-12-19 16:30:15 +01:00
|
|
|
assert(elapsedTimeMs > 1600)
|
|
|
|
|
assert(elapsedTimeMs < 2000) // the precision is not ms exact
|
2011-11-23 15:15:44 +01:00
|
|
|
cancellable.cancel()
|
|
|
|
|
}
|
2010-08-24 23:21:28 +02:00
|
|
|
}
|
|
|
|
|
}
|