Merge pull request #339 from corda/aslemmer-fix-rpc-close-deliver-deadlock

rpc: Fix deadlock caused by deliver() and close()
This commit is contained in:
Andras Slemmer
2017-03-15 16:20:11 +00:00
committed by GitHub

View File

@ -284,7 +284,8 @@ class CordaRPCClientImpl(private val session: ClientSession,
private val rootShared = root.doOnUnsubscribe { close() }.share()
// This could be made more efficient by using a specialised IntMap
private val observables = HashMap<Int, Observable<Any>>()
// When handling this map we don't synchronise on [this], otherwise there is a race condition between close() and deliver()
private val observables = Collections.synchronizedMap(HashMap<Int, Observable<Any>>())
private var consumer: ClientConsumer? = null
@ -320,8 +321,8 @@ class CordaRPCClientImpl(private val session: ClientSession,
}
}
@Synchronized
fun getForHandle(handle: Int): Observable<Any> {
synchronized(observables) {
return observables.getOrPut(handle) {
/**
* Note that the order of bufferUntilSubscribed() -> dematerialize() is very important here.
@ -340,13 +341,14 @@ class CordaRPCClientImpl(private val session: ClientSession,
rootShared.filter { it.forHandle == handle }.map { it.what }.bufferUntilSubscribed().dematerialize<Any>().doOnSubscribe { refCountUp() }.doOnUnsubscribe { refCountDown() }.share()
}
}
}
private fun deliver(msg: ClientMessage) {
msg.acknowledge()
val kryo = createRPCKryo(observableSerializer = observableDeserializer)
val received: MarshalledObservation = msg.deserialize(kryo)
rpcLog.debug { "<- Observable [$rpcName] <- Received $received" }
synchronized(this) {
synchronized(observables) {
// Force creation of the buffer if it doesn't already exist.
getForHandle(received.forHandle)
root.onNext(received)