Handle race where broadcasthub consumer already cancelled (#23970)

* Handle race where broadcasthub consumer already cancelled #23205

* Don't use the same thread ec, import cleanup

* Complete feedback promise when async event has been executed #23953

* No need for double-deregister-guard after invokeWithFeedback was fixed.

* Test coverage for async callbacks #23953

* Updated docs on AsyncCallback #23953

* MiMa

* Completing a promise of scaladoc

* Improved test coverage and handling of async invokation failing or throwing

* MiMa

* Review updates
This commit is contained in:
Johan Andrén 2017-11-17 10:55:30 +01:00 committed by Patrik Nordwall
parent 26cd916d98
commit 3dc0adb4e9
9 changed files with 355 additions and 32 deletions

View file

@ -9,8 +9,11 @@ import akka.stream.stage._
import akka.stream._
import java.util.concurrent.ThreadLocalRandom
import akka.Done
import akka.annotation.InternalApi
import akka.util.OptionVal
import scala.concurrent.Promise
import scala.util.control.NonFatal
/**
@ -191,7 +194,7 @@ import scala.util.control.NonFatal
val log: LoggingAdapter,
val logics: Array[GraphStageLogic], // Array of stage logics
val connections: Array[GraphInterpreter.Connection],
val onAsyncInput: (GraphStageLogic, Any, (Any) Unit) Unit,
val onAsyncInput: (GraphStageLogic, Any, OptionVal[Promise[Done]], (Any) Unit) Unit,
val fuzzingMode: Boolean,
val context: ActorRef) {
@ -432,7 +435,7 @@ import scala.util.control.NonFatal
eventsRemaining
}
def runAsyncInput(logic: GraphStageLogic, evt: Any, handler: (Any) Unit): Unit =
def runAsyncInput(logic: GraphStageLogic, evt: Any, promise: OptionVal[Promise[Done]], handler: (Any) Unit): Unit =
if (!isStageCompleted(logic)) {
if (GraphInterpreter.Debug) println(s"$Name ASYNC $evt ($handler) [$logic]")
val currentInterpreterHolder = _currentInterpreter.get()
@ -440,9 +443,13 @@ import scala.util.control.NonFatal
currentInterpreterHolder(0) = this
try {
activeStage = logic
try handler(evt)
catch {
case NonFatal(ex) logic.failStage(ex)
try {
handler(evt)
if (promise.isDefined) promise.get.success(Done)
} catch {
case NonFatal(ex)
if (promise.isDefined) promise.get.failure(ex)
logic.failStage(ex)
}
afterStageHasRun(logic)
} finally currentInterpreterHolder(0) = previousInterpreter