forked from akka/akka
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
=act #3562: Do not generate Terminated when unwatched in preRestart
- also avoid duplicate DWN for parent
- Loading branch information
Showing
3 changed files
with
105 additions
and
5 deletions.
There are no files selected for viewing
99 changes: 99 additions & 0 deletions
99
akka-actor-tests/src/test/scala/akka/actor/UidClashTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com> | ||
*/ | ||
package akka.actor | ||
|
||
import akka.testkit.{ TestProbe, AkkaSpec } | ||
import akka.actor.SupervisorStrategy.{ Restart, Stop } | ||
import akka.dispatch.sysmsg.SystemMessage | ||
import akka.event.EventStream | ||
import scala.util.control.NoStackTrace | ||
|
||
object UidClashTest { | ||
|
||
class TerminatedForNonWatchedActor extends Exception("Received Terminated for actor that was not actually watched") | ||
with NoStackTrace | ||
|
||
@volatile var oldActor: ActorRef = _ | ||
|
||
class EvilCollidingActorRef(override val provider: ActorRefProvider, | ||
override val path: ActorPath, | ||
val eventStream: EventStream) extends MinimalActorRef { | ||
|
||
//Ignore everything | ||
override def isTerminated(): Boolean = true | ||
override def sendSystemMessage(message: SystemMessage): Unit = () | ||
override def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit = () | ||
} | ||
|
||
def createCollidingRef(system: ActorSystem): ActorRef = | ||
new EvilCollidingActorRef(system.asInstanceOf[ActorSystemImpl].provider, oldActor.path, system.eventStream) | ||
|
||
case object PleaseRestart | ||
case object PingMyself | ||
case object RestartedSafely | ||
|
||
class RestartedActor extends Actor { | ||
|
||
def receive = { | ||
case PleaseRestart ⇒ throw new Exception("restart") | ||
case Terminated(ref) ⇒ throw new TerminatedForNonWatchedActor | ||
// This is the tricky part to make this test a positive one (avoid expectNoMsg). | ||
// Since anything enqueued in postRestart will arrive before the Terminated | ||
// the bug triggers, there needs to be a bounce: | ||
// 1. Ping is sent from postRestart to self | ||
// 2. As a response to pint, RestartedSafely is sent to self | ||
// 3a. if Terminated was enqueued during the restart procedure it will arrive before the RestartedSafely message | ||
// 3b. otherwise only the RestartedSafely message arrives | ||
case PingMyself ⇒ self ! RestartedSafely | ||
case RestartedSafely ⇒ context.parent ! RestartedSafely | ||
} | ||
|
||
override def preRestart(reason: Throwable, message: Option[Any]): Unit = { | ||
context.children foreach { child ⇒ | ||
oldActor = child | ||
context.unwatch(child) | ||
context.stop(child) | ||
} | ||
} | ||
|
||
override def preStart(): Unit = context.watch(context.actorOf(Props.empty, "child")) | ||
|
||
override def postRestart(reason: Throwable): Unit = { | ||
context.watch(createCollidingRef(context.system)) | ||
self ! PingMyself | ||
} // Simulate UID clash | ||
} | ||
|
||
class RestartingActor(probe: ActorRef) extends Actor { | ||
override val supervisorStrategy = OneForOneStrategy(loggingEnabled = false) { | ||
case _: TerminatedForNonWatchedActor ⇒ | ||
context.stop(self) | ||
Stop | ||
case _ ⇒ Restart | ||
} | ||
val theRestartedOne = context.actorOf(Props[RestartedActor], "theRestartedOne") | ||
|
||
def receive = { | ||
case PleaseRestart ⇒ theRestartedOne ! PleaseRestart | ||
case RestartedSafely ⇒ probe ! RestartedSafely | ||
} | ||
} | ||
|
||
} | ||
|
||
class UidClashTest extends AkkaSpec { | ||
import UidClashTest._ | ||
|
||
"The Terminated message for an old child stopped in preRestart" should { | ||
"not arrive after restart" in { | ||
val watcher = TestProbe() | ||
val topActor = system.actorOf(Props(classOf[RestartingActor], watcher.ref), "top") | ||
watcher.watch(topActor) | ||
|
||
topActor ! PleaseRestart | ||
watcher.expectMsg(RestartedSafely) | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters