Skip to content

Conversation

@ktoso
Copy link
Member

@ktoso ktoso commented Sep 10, 2020

Motivation:

  • Fix flaky test in FAILED: test_interceptor_shouldInterceptSignals #614
  • the guard protecting "parent needs not watch child explicitly because child always reports termination back" in DeathWatch was in the wrong function -- fixed that
  • we double notified a parent with Termianted and ChildTerminated -- this is technically harmless but wrong (!), so added a test to notice and fix this as well (it's a result of the above wrong guard).

Result:

with terminationMessage: Message? = nil,
file: String = #file, line: UInt = #line
) -> Watchee where Watchee: DeathWatchable {
self.deathWatch.watch(watchee: watchee, with: terminationMessage, myself: self.myself, parent: self._parent, file: file, line: line)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • the parent is super unnecessary for ISSUING a watch;
    • we don't care about OUR parent, only if we are the parent of the being watched actor, as such we can pass myself (self in order to have _children to check them inside there)
  • it optionally necessary for HANDLING a watch.
    • it's not required, but we're being defensive now, even if someone sent us such system message we'd do the right thing

try postStop(context)
}
return .same // will be ignored
return .stop // will be ignored
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just cosmetic tbh


/// Performed by the sending side of "watch", therefore the `watcher` should equal `context.myself`
public mutating func watch<Watchee>(
mutating func watch<Watchee>(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does not have to be public

return
}

guard addressableWatchee.address != parent.address else {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whooop! this is in the wrong function, moved to becomeWatchedBy

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙀

// not yet watching, so let's add it:
self.watching[addressableWatchee] = OnTerminationMessage(customize: terminationMessage)

if !watcher.children.contains(identifiedBy: watchee.asAddressable.address) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we proactively avoid sending a message which would have been ignored anyway

/// and our direct message arrives first, before the watch at the destination, causing potentially confusing behavior
/// in some very ordering delicate testing scenarios.
public func forward<Message>(_ message: Message, to target: ActorRef<Message>, file: String = #file, line: UInt = #line) where Message: Codable {
self.internalRef.tell(ProbeCommands.forwardCommand(send: { () in target.tell(message, file: file, line: line) }))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really necessary here, since watching a dead actor works as well, but I wanted to make sure to see the exact ordering I'm testing.

terminated.address.path.shouldEqual(try! ActorPath._user.appending("parent").appending("stopper"))
terminated.existenceConfirmed.shouldBeTrue()
terminated.nodeTerminated.shouldBeFalse()
terminated.shouldBe(Signals.ChildTerminated.self)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which would have potentially failed and may have received Terminated before (!)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or rather, the expect no message would have gotten it)

let terminated = try p.expectMessage()
(terminated.address.name == "stopperOne" || terminated.address.name == "stopperTwo").shouldBeTrue()
try p.expectNoMessage(for: .milliseconds(100))
try p.expectNoMessage(for: .milliseconds(500))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be extra sure

}

func test_interceptor_shouldRemainWHenReturningStoppingWithPostStop() throws {
func test_interceptor_shouldRemainWhenReturningStoppingWithPostStop() throws {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was a typo

@ktoso ktoso requested a review from yim-lee September 10, 2020 08:55
Copy link
Member

@yim-lee yim-lee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great find!

return
}

guard addressableWatchee.address != parent.address else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙀

@ktoso ktoso merged commit 5f829e3 into apple:main Sep 11, 2020
@ktoso ktoso deleted the wip-interceptor-test branch September 11, 2020 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FAILED: test_interceptor_shouldInterceptSignals

2 participants