I have an issue in a Vapor code base that I’m migrating to async/await that I don’t know how to debug.
Long story short, some unit tests simply hang at the line where they wait on an actor. Setting a breakpoint or adding print statements in the actor methods/properties show that they are never called. Sorry that I can’t post some code to reproduce this issue, but I haven’t been able to properly isolate this and I don’t know how to debug this.
Does anybody have suggestions on how to debug such an issue? Can I somehow log all calls to an actor before the suspension point? Is there a way to know what threads/queues/tasks are waiting on an actor? Any other hints?
I don’t know if this is relevant, but since this is a Vapor code base, all this involves NIO event loops in the background.
Some more detail:
The unit tests use mock classes that conform to certain protocols. The tests do something that calls the mock (there are some async calls involved here, which makes isolation of the issue tricky), which would increment a counter named requestCount
in the mock. The unit tests would then check that the requestCount
matched the expected amount.
After the async/await migration, the respective protocols now have method requirements that are async
, which means the mock classes now also need to implement async
methods. Because the unit tests therefore are also async
, and the protocol methods might be called on any thread, I thought it might be a good idea to turn the mock classes into actors to ensure the mutations to its internal state are safe.
What’s funny is that triggering this issue can be fixed and “unfixed” by simple changes in the actor. For example, accessing the requestCount
property in the unit tests works fine or hangs, depending on how that property is declared. The call site is always await mock.requestCount
in all cases.
It works fine when declared like this:
actor MyMock {
var requestCount: Int = 0
}
It also works fine like this:
actor MyMock {
private var _requestCount: Int = 0
var requestCount: Int {
get {
return _requestCount
}
}
}
… but it hangs when I add the explicit async
to the computed property getter:
actor MyMock {
private var _requestCount: Int = 0
var requestCount: Int {
get async { // 💣
return _requestCount
}
}
}
Any suggestions on what I could try would be very welcome!