diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt index 03cc1bec69..dce8fb99dd 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt @@ -10,7 +10,7 @@ import com.google.common.cache.RemovalCause import com.google.common.cache.RemovalListener import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.ThreadFactoryBuilder -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.crypto.random63BitValue import net.corda.core.getOrThrow import net.corda.core.internal.LazyPool diff --git a/core/src/main/kotlin/net/corda/core/Utils.kt b/core/src/main/kotlin/net/corda/core/Utils.kt index 94ef3ac8f5..11121de66c 100644 --- a/core/src/main/kotlin/net/corda/core/Utils.kt +++ b/core/src/main/kotlin/net/corda/core/Utils.kt @@ -25,12 +25,10 @@ import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutionException import java.util.concurrent.Future import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.ReentrantLock import java.util.zip.Deflater import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import java.util.zip.ZipOutputStream -import kotlin.concurrent.withLock // TODO: Review by EOY2016 if we ever found these utilities helpful. val Int.bd: BigDecimal get() = BigDecimal(this) @@ -163,33 +161,6 @@ fun logElapsedTime(label: String, logger: Logger? = null, body: () -> T): T fun Logger.logElapsedTime(label: String, body: () -> T): T = logElapsedTime(label, this, body) -/** - * A threadbox is a simple utility that makes it harder to forget to take a lock before accessing some shared state. - * Simply define a private class to hold the data that must be grouped under the same lock, and then pass the only - * instance to the ThreadBox constructor. You can now use the [locked] method with a lambda to take the lock in a - * way that ensures it'll be released if there's an exception. - * - * Note that this technique is not infallible: if you capture a reference to the fields in another lambda which then - * gets stored and invoked later, there may still be unsafe multi-threaded access going on, so watch out for that. - * This is just a simple guard rail that makes it harder to slip up. - * - * Example: - * - * private class MutableState { var i = 5 } - * private val state = ThreadBox(MutableState()) - * - * val ii = state.locked { i } - */ -class ThreadBox(val content: T, val lock: ReentrantLock = ReentrantLock()) { - inline fun locked(body: T.() -> R): R = lock.withLock { body(content) } - inline fun alreadyLocked(body: T.() -> R): R { - check(lock.isHeldByCurrentThread, { "Expected $lock to already be locked." }) - return body(content) - } - - fun checkNotLocked() = check(!lock.isHeldByCurrentThread) -} - /** * Given a path to a zip file, extracts it to the given directory. */ diff --git a/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt b/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt new file mode 100644 index 0000000000..eedd576694 --- /dev/null +++ b/core/src/main/kotlin/net/corda/core/internal/ThreadBox.kt @@ -0,0 +1,32 @@ +package net.corda.core.internal + +import java.util.concurrent.locks.ReentrantLock +import kotlin.concurrent.withLock + +/** + * A threadbox is a simple utility that makes it harder to forget to take a lock before accessing some shared state. + * Simply define a private class to hold the data that must be grouped under the same lock, and then pass the only + * instance to the ThreadBox constructor. You can now use the [locked] method with a lambda to take the lock in a + * way that ensures it'll be released if there's an exception. + * + * Note that this technique is not infallible: if you capture a reference to the fields in another lambda which then + * gets stored and invoked later, there may still be unsafe multi-threaded access going on, so watch out for that. + * This is just a simple guard rail that makes it harder to slip up. + * + * Example: + *``` + * private class MutableState { var i = 5 } + * private val state = ThreadBox(MutableState()) + * + * val ii = state.locked { i } + * ``` + */ +class ThreadBox(val content: T, val lock: ReentrantLock = ReentrantLock()) { + inline fun locked(body: T.() -> R): R = lock.withLock { body(content) } + inline fun alreadyLocked(body: T.() -> R): R { + check(lock.isHeldByCurrentThread, { "Expected $lock to already be locked." }) + return body(content) + } + + fun checkNotLocked(): Unit = check(!lock.isHeldByCurrentThread) +} diff --git a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt index b69906afbe..2ec91e357e 100644 --- a/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt +++ b/node/src/main/kotlin/net/corda/node/services/events/NodeSchedulerService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.events import com.google.common.util.concurrent.SettableFuture -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.contracts.SchedulableState import net.corda.core.contracts.ScheduledActivity import net.corda.core.contracts.ScheduledStateRef diff --git a/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt index cd09d34e3c..c41398a236 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/E2ETestKeyManagementService.kt @@ -1,6 +1,6 @@ package net.corda.node.services.keys -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.keys diff --git a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt index 19108a422c..d145003723 100644 --- a/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt +++ b/node/src/main/kotlin/net/corda/node/services/keys/PersistentKeyManagementService.kt @@ -1,6 +1,6 @@ package net.corda.node.services.keys -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.keys diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index 221460f952..a598dded88 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -7,6 +7,7 @@ import net.corda.core.* import net.corda.core.crypto.* import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA +import net.corda.core.internal.ThreadBox import net.corda.core.internal.div import net.corda.core.node.NodeInfo import net.corda.core.node.services.NetworkMapCache diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt index 81f9a47701..463a89002b 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/NodeMessagingClient.kt @@ -1,10 +1,10 @@ package net.corda.node.services.messaging import com.google.common.util.concurrent.ListenableFuture -import net.corda.core.ThreadBox import net.corda.core.andForget import net.corda.core.crypto.random63BitValue import net.corda.core.getOrThrow +import net.corda.core.internal.ThreadBox import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.RPCOps @@ -468,8 +468,8 @@ class NodeMessagingClient(override val config: NodeConfiguration, } private fun sendWithRetry(retryCount: Int, address: String, message: ClientMessage, retryId: Long) { - fun randomiseDuplicateId(message: ClientMessage) { - message.putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(UUID.randomUUID().toString())) + fun ClientMessage.randomiseDuplicateId() { + putStringProperty(HDR_DUPLICATE_DETECTION_ID, SimpleString(UUID.randomUUID().toString())) } log.trace { "Attempting to retry #$retryCount message delivery for $retryId" } @@ -479,7 +479,7 @@ class NodeMessagingClient(override val config: NodeConfiguration, return } - randomiseDuplicateId(message) + message.randomiseDuplicateId() state.locked { log.trace { "Retry #$retryCount sending message $message to $address for $retryId" } diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt index 7ee02601b5..b5d5aa729b 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapService.kt @@ -1,7 +1,7 @@ package net.corda.node.services.network import com.google.common.annotations.VisibleForTesting -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SignedData import net.corda.core.crypto.isFulfilledBy diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt index 2220d94a19..1cb7de5292 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapService.kt @@ -1,6 +1,6 @@ package net.corda.node.services.network -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.identity.PartyAndCertificate import net.corda.core.messaging.SingleMessageRecipient import net.corda.node.services.api.ServiceHubInternal diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt index b9c376b768..9b455f9544 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt @@ -1,6 +1,6 @@ package net.corda.node.services.persistence -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.crypto.SecureHash import net.corda.core.flows.StateMachineRunId diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/InMemoryStateMachineRecordedTransactionMappingStorage.kt b/node/src/main/kotlin/net/corda/node/services/persistence/InMemoryStateMachineRecordedTransactionMappingStorage.kt index 168fee3bc8..8c4a684a11 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/InMemoryStateMachineRecordedTransactionMappingStorage.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/InMemoryStateMachineRecordedTransactionMappingStorage.kt @@ -1,6 +1,6 @@ package net.corda.node.services.persistence -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.crypto.SecureHash import net.corda.core.flows.StateMachineRunId diff --git a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt index 3104a3b9b2..6ee71cb3c1 100644 --- a/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt +++ b/node/src/main/kotlin/net/corda/node/services/statemachine/StateMachineManager.kt @@ -8,7 +8,7 @@ import com.esotericsoftware.kryo.KryoException import com.google.common.collect.HashMultimap import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.MoreExecutors -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.crypto.SecureHash import net.corda.core.crypto.random63BitValue diff --git a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt index f6419d2c24..4f94975f5e 100644 --- a/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt +++ b/node/src/main/kotlin/net/corda/node/services/transactions/PersistentUniquenessProvider.kt @@ -1,6 +1,6 @@ package net.corda.node.services.transactions -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.contracts.StateRef import net.corda.core.crypto.SecureHash import net.corda.core.identity.Party diff --git a/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt b/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt index 929312f753..c7cb8b6647 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/HibernateVaultQueryImpl.kt @@ -1,6 +1,6 @@ package net.corda.node.services.vault -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.ContractState import net.corda.core.contracts.StateAndRef diff --git a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt index 773772ad5f..3a7dc5ad0e 100644 --- a/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt +++ b/node/src/main/kotlin/net/corda/node/services/vault/NodeVaultService.kt @@ -12,7 +12,7 @@ import io.requery.kotlin.notNull import io.requery.query.RowExpression import net.corda.contracts.asset.Cash import net.corda.contracts.asset.OnLedgerAsset -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.bufferUntilSubscribed import net.corda.core.contracts.* import net.corda.core.crypto.SecureHash diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index 01bcebfb17..d75ab5f5c8 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -8,7 +8,7 @@ import net.corda.contracts.Tenor import net.corda.contracts.math.CubicSplineInterpolator import net.corda.contracts.math.Interpolator import net.corda.contracts.math.InterpolatorFactory -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.contracts.Command import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.MerkleTreeException diff --git a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt index d75150b844..a056e03e7e 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -15,6 +15,7 @@ import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.appendToCommonName import net.corda.core.crypto.commonName import net.corda.core.identity.Party +import net.corda.core.internal.ThreadBox import net.corda.core.internal.div import net.corda.core.internal.times import net.corda.core.messaging.CordaRPCOps diff --git a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt index 2c9601b48f..92e420aa7d 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/node/InMemoryMessagingNetwork.kt @@ -3,7 +3,7 @@ package net.corda.testing.node import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.SettableFuture -import net.corda.core.ThreadBox +import net.corda.core.internal.ThreadBox import net.corda.core.crypto.X509Utilities import net.corda.core.getOrThrow import net.corda.core.messaging.AllPossibleRecipients