This is an automated email from the ASF dual-hosted git repository.
tabish pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git
The following commit(s) were added to refs/heads/main by this push:
new d029fd463f ARTEMIS-5139 improve locking on TypedProperties
d029fd463f is described below
commit d029fd463f1f11be2ef8458050fcabf3a353e935
Author: Justin Bertram <[email protected]>
AuthorDate: Mon Feb 3 12:05:28 2025 -0600
ARTEMIS-5139 improve locking on TypedProperties
Back in 8e40b2d4f4f242271d3dfcda4f9b96d3f94cee1b thread safety was added
to TypedProperties by synchronizing all relevant methods. This was
simple and effective but suffers from performance issues in read-heavy
use-cases.
This commit improves the performance by using a read/write locking
mechanism so reads can execute concurrently.
No new tests were added since the original commit added tests to verify
thread safety.
---
.../artemis/utils/collections/TypedProperties.java | 659 +++++++++++++--------
.../artemis/core/message/impl/CoreMessage.java | 5 +-
2 files changed, 403 insertions(+), 261 deletions(-)
diff --git
a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/TypedProperties.java
b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/TypedProperties.java
index 04937af5c7..e2a367ab36 100644
---
a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/TypedProperties.java
+++
b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/collections/TypedProperties.java
@@ -25,6 +25,9 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -65,6 +68,7 @@ public class TypedProperties {
private int size;
private final Predicate<SimpleString> internalPropertyPredicate;
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
private boolean internalProperties;
private final Predicate<SimpleString> amqpPropertyPredicate;
private boolean amqpProperties;
@@ -84,28 +88,40 @@ public class TypedProperties {
}
/**
- * Return the number of properties
- * */
- public synchronized int size() {
- return properties == null ? 0 : properties.size();
+ * Return the number of properties
+ */
+ public int size() {
+ lock.readLock().lock();
+ try {
+ return properties == null ? 0 : properties.size();
+ } finally {
+ lock.readLock().unlock();
+ }
}
- public synchronized int getMemoryOffset() {
+ public int getMemoryOffset() {
// The estimate is basically the encode size + 2 object references for
each entry in the map
// Note we don't include the attributes or anything else since they
already included in the memory estimate
// of the ServerMessage
-
- return properties == null ? 0 : size + 2 * DataConstants.SIZE_INT *
properties.size();
+ lock.readLock().lock();
+ try {
+ return properties == null ? 0 : size + 2 * DataConstants.SIZE_INT *
properties.size();
+ } finally {
+ lock.readLock().unlock();
+ }
}
public TypedProperties(final TypedProperties other) {
- synchronized (other) {
+ other.lock.readLock().lock();
+ try {
properties = other.properties == null ? null : new
HashMap<>(other.properties);
size = other.size;
internalPropertyPredicate = other.internalPropertyPredicate;
internalProperties = other.internalProperties;
amqpPropertyPredicate = other.amqpPropertyPredicate;
amqpProperties = other.amqpProperties;
+ } finally {
+ other.lock.readLock().unlock();
}
}
@@ -330,58 +346,89 @@ public class TypedProperties {
return doRemoveProperty(key);
}
- public synchronized boolean containsProperty(final SimpleString key) {
- if (properties == null) {
- return false;
+ public boolean containsProperty(final SimpleString key) {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return false;
- } else {
- return properties.containsKey(key);
+ } else {
+ return properties.containsKey(key);
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized Set<SimpleString> getPropertyNames() {
- if (properties == null) {
- return Collections.emptySet();
- } else {
- return new HashSet<>(properties.keySet());
+
+ public Set<SimpleString> getPropertyNames() {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return Collections.emptySet();
+ } else {
+ return new HashSet<>(properties.keySet());
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized boolean clearInternalProperties() {
- return internalProperties && removeInternalProperties();
+ public boolean clearInternalProperties() {
+ lock.writeLock().lock();
+ try {
+ return internalProperties && removeInternalProperties();
+ } finally {
+ lock.writeLock().unlock();
+ }
}
- public synchronized boolean clearAMQPProperties() {
- return amqpProperties && removeAMQPProperties();
+ public boolean clearAMQPProperties() {
+ lock.writeLock().lock();
+ try {
+ return amqpProperties && removeAMQPProperties();
+ } finally {
+ lock.writeLock().unlock();
+ }
}
- private synchronized boolean removeInternalProperties() {
- if (internalPropertyPredicate == null) {
- return false;
- }
- if (properties == null) {
- return false;
- }
- if (properties.isEmpty()) {
- return false;
+ private boolean removeInternalProperties() {
+ lock.writeLock().lock();
+ try {
+ if (internalPropertyPredicate == null) {
+ return false;
+ }
+ if (properties == null) {
+ return false;
+ }
+ if (properties.isEmpty()) {
+ return false;
+ }
+ boolean removed = removePredicate(internalPropertyPredicate);
+ internalProperties = false;
+ return removed;
+ } finally {
+ lock.writeLock().unlock();
}
- boolean removed = removePredicate(internalPropertyPredicate);
- internalProperties = false;
- return removed;
}
- private synchronized boolean removeAMQPProperties() {
- if (amqpPropertyPredicate == null) {
- return false;
- }
- if (properties == null) {
- return false;
- }
- if (properties.isEmpty()) {
- return false;
+ private boolean removeAMQPProperties() {
+ lock.writeLock().lock();
+ try {
+ if (amqpPropertyPredicate == null) {
+ return false;
+ }
+ if (properties == null) {
+ return false;
+ }
+ if (properties.isEmpty()) {
+ return false;
+ }
+ boolean removed = removePredicate(amqpPropertyPredicate);
+ amqpProperties = false;
+ return removed;
+ } finally {
+ lock.writeLock().unlock();
}
- boolean removed = removePredicate(amqpPropertyPredicate);
- amqpProperties = false;
- return removed;
}
private boolean removePredicate(Predicate<SimpleString> predicate) {
@@ -402,21 +449,51 @@ public class TypedProperties {
return removed;
}
- public synchronized void forEachKey(Consumer<SimpleString> action) {
- if (properties != null) {
- properties.keySet().forEach(action::accept);
+ /**
+ * This method is read-only. Do not modify the TypedProperties using the
Consumer.
+ *
+ * @param action Consumer implementation that should not modify the
TypedProperties
+ */
+ public void forEachKey(Consumer<SimpleString> action) {
+ lock.readLock().lock();
+ try {
+ if (properties != null) {
+ properties.keySet().forEach(action::accept);
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized void forEach(BiConsumer<SimpleString, Object> action) {
- if (properties != null) {
- properties.forEach((k, v) -> action.accept(k, v.getValue()));
+ /**
+ * This method is read-only. Do not modify the TypedProperties using the
BiConsumer.
+ *
+ * @param action BiConsumer implementation that should not modify the
TypedProperties
+ */
+ public void forEach(BiConsumer<SimpleString, Object> action) {
+ lock.readLock().lock();
+ try {
+ if (properties != null) {
+ properties.forEach((k, v) -> action.accept(k, v.getValue()));
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- private synchronized void forEachInternal(BiConsumer<SimpleString,
PropertyValue> action) {
- if (properties != null) {
- properties.forEach(action::accept);
+ /**
+ * This method is read-only. Do not modify the TypedProperties using the
BiConsumer.
+ *
+ * @param action BiConsumer implementation that should not modify the
TypedProperties
+ */
+ private void forEachInternal(BiConsumer<SimpleString, PropertyValue>
action) {
+ lock.readLock().lock();
+ try {
+ if (properties != null) {
+ properties.forEach(action::accept);
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
@@ -493,87 +570,91 @@ public class TypedProperties {
return false;
}
- public synchronized void decode(final ByteBuf buffer,
- final TypedPropertiesDecoderPools
keyValuePools) {
- byte b = buffer.readByte();
- if (b == DataConstants.NULL) {
- properties = null;
- size = 0;
- } else {
- int numHeaders = buffer.readInt();
+ public void decode(final ByteBuf buffer, final TypedPropertiesDecoderPools
keyValuePools) {
+ lock.writeLock().lock();
+ try {
+ byte b = buffer.readByte();
+ if (b == DataConstants.NULL) {
+ properties = null;
+ size = 0;
+ } else {
+ int numHeaders = buffer.readInt();
- //optimize the case of no collisions to avoid any resize (it doubles
the map size!!!) when load factor is reached
- properties = new HashMap<>(numHeaders, 1.0f);
- size = 0;
+ //optimize the case of no collisions to avoid any resize (it
doubles the map size!!!) when load factor is reached
+ properties = new HashMap<>(numHeaders, 1.0f);
+ size = 0;
- for (int i = 0; i < numHeaders; i++) {
- final SimpleString key = SimpleString.readSimpleString(buffer,
keyValuePools == null ? null : keyValuePools.getPropertyKeysPool());
+ for (int i = 0; i < numHeaders; i++) {
+ final SimpleString key = SimpleString.readSimpleString(buffer,
keyValuePools == null ? null : keyValuePools.getPropertyKeysPool());
- byte type = buffer.readByte();
+ byte type = buffer.readByte();
- PropertyValue val;
+ PropertyValue val;
- switch (type) {
- case NULL: {
- val = NullValue.INSTANCE;
- doPutValue(key, val);
- break;
- }
- case CHAR: {
- val = new CharValue(buffer);
- doPutValue(key, val);
- break;
- }
- case BOOLEAN: {
- val = BooleanValue.of(buffer.readBoolean());
- doPutValue(key, val);
- break;
- }
- case BYTE: {
- val = ByteValue.valueOf(buffer.readByte());
- doPutValue(key, val);
- break;
- }
- case BYTES: {
- val = new BytesValue(buffer);
- doPutValue(key, val);
- break;
- }
- case SHORT: {
- val = new ShortValue(buffer);
- doPutValue(key, val);
- break;
- }
- case INT: {
- val = new IntValue(buffer);
- doPutValue(key, val);
- break;
- }
- case LONG: {
- val = new LongValue(buffer);
- doPutValue(key, val);
- break;
- }
- case FLOAT: {
- val = new FloatValue(buffer);
- doPutValue(key, val);
- break;
- }
- case DOUBLE: {
- val = new DoubleValue(buffer);
- doPutValue(key, val);
- break;
- }
- case STRING: {
- val = StringValue.readStringValue(buffer, keyValuePools ==
null ? null : keyValuePools.getPropertyValuesPool());
- doPutValue(key, val);
- break;
- }
- default: {
- throw ActiveMQUtilBundle.BUNDLE.invalidType(type);
+ switch (type) {
+ case NULL: {
+ val = NullValue.INSTANCE;
+ doPutValue(key, val);
+ break;
+ }
+ case CHAR: {
+ val = new CharValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case BOOLEAN: {
+ val = BooleanValue.of(buffer.readBoolean());
+ doPutValue(key, val);
+ break;
+ }
+ case BYTE: {
+ val = ByteValue.valueOf(buffer.readByte());
+ doPutValue(key, val);
+ break;
+ }
+ case BYTES: {
+ val = new BytesValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case SHORT: {
+ val = new ShortValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case INT: {
+ val = new IntValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case LONG: {
+ val = new LongValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case FLOAT: {
+ val = new FloatValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case DOUBLE: {
+ val = new DoubleValue(buffer);
+ doPutValue(key, val);
+ break;
+ }
+ case STRING: {
+ val = StringValue.readStringValue(buffer, keyValuePools
== null ? null : keyValuePools.getPropertyValuesPool());
+ doPutValue(key, val);
+ break;
+ }
+ default: {
+ throw ActiveMQUtilBundle.BUNDLE.invalidType(type);
+ }
}
}
}
+ } finally {
+ lock.writeLock().unlock();
}
}
@@ -581,144 +662,179 @@ public class TypedProperties {
decode(buffer, null);
}
- public synchronized int encode(final ByteBuf buffer) {
- final int encodedSize;
- // it's a trick to not pay the cost of buffer.writeIndex without
assertions enabled
- int writerIndex = 0;
- assert (writerIndex = buffer.writerIndex()) >= 0 : "Always true";
- if (properties == null || size == 0) {
- encodedSize = DataConstants.SIZE_BYTE;
- ensureExactWritable(buffer, encodedSize);
- buffer.writeByte(DataConstants.NULL);
- } else {
- encodedSize = DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + size;
- ensureExactWritable(buffer, encodedSize);
- buffer.writeByte(DataConstants.NOT_NULL);
-
- buffer.writeInt(properties.size());
-
- //uses internal iteration to allow inlining/loop unrolling
- properties.forEach((key, value) -> {
- final byte[] data = key.getData();
- buffer.writeInt(data.length);
- buffer.writeBytes(data);
- value.write(buffer);
- });
+ public int encode(final ByteBuf buffer) {
+ lock.readLock().lock();
+ try {
+ final int encodedSize;
+ // it's a trick to not pay the cost of buffer.writeIndex without
assertions enabled
+ int writerIndex = 0;
+ assert (writerIndex = buffer.writerIndex()) >= 0 : "Always true";
+ if (properties == null || size == 0) {
+ encodedSize = DataConstants.SIZE_BYTE;
+ ensureExactWritable(buffer, encodedSize);
+ buffer.writeByte(DataConstants.NULL);
+ } else {
+ encodedSize = DataConstants.SIZE_BYTE + DataConstants.SIZE_INT +
size;
+ ensureExactWritable(buffer, encodedSize);
+ buffer.writeByte(DataConstants.NOT_NULL);
+
+ buffer.writeInt(properties.size());
+
+ //uses internal iteration to allow inlining/loop unrolling
+ properties.forEach((key, value) -> {
+ final byte[] data = key.getData();
+ buffer.writeInt(data.length);
+ buffer.writeBytes(data);
+ value.write(buffer);
+ });
+ }
+ assert buffer.writerIndex() == (writerIndex + encodedSize) : "Bad
encode size estimation";
+ return encodedSize;
+ } finally {
+ lock.readLock().unlock();
}
- assert buffer.writerIndex() == (writerIndex + encodedSize) : "Bad encode
size estimation";
- return encodedSize;
}
- public synchronized int getEncodeSize() {
- if (properties == null || size == 0) {
- return DataConstants.SIZE_BYTE;
- } else {
- return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + size;
+ public int getEncodeSize() {
+ lock.readLock().lock();
+ try {
+ if (properties == null || size == 0) {
+ return DataConstants.SIZE_BYTE;
+ } else {
+ return DataConstants.SIZE_BYTE + DataConstants.SIZE_INT + size;
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized void clear() {
- if (properties != null) {
- properties.clear();
+ public void clear() {
+ lock.writeLock().lock();
+ try {
+ if (properties != null) {
+ properties.clear();
+ }
+ size = 0;
+ } finally {
+ lock.writeLock().unlock();
}
- size = 0;
}
@Override
- public synchronized String toString() {
- StringBuilder sb = new StringBuilder("TypedProperties[");
-
- if (properties != null) {
- Iterator<Entry<SimpleString, PropertyValue>> iter =
properties.entrySet().iterator();
-
- while (iter.hasNext()) {
- Entry<SimpleString, PropertyValue> iterItem = iter.next();
- sb.append(iterItem.getKey() + "=");
-
- // it seems weird but it's right!!
- // The first getValue is from the EntrySet
- // The second is to convert the PropertyValue into the actual value
- Object theValue = iterItem.getValue().getValue();
-
- if (theValue == null) {
- sb.append("NULL-value");
- } else if (theValue instanceof byte[] bytes) {
- sb.append("[" + ByteUtil.maxString(ByteUtil.bytesToHex(bytes,
2), 150) + "]");
-
- if (iterItem.getKey().toString().startsWith("_AMQ_ROUTE_TO")) {
- sb.append(", bytesAsLongs[");
- try {
- ByteBuffer buff = ByteBuffer.wrap(bytes);
- while (buff.hasRemaining()) {
- long bindingID = buff.getLong();
- sb.append(bindingID);
- if (buff.hasRemaining()) {
- sb.append(", ");
+ public String toString() {
+ lock.readLock().lock();
+ try {
+ StringBuilder sb = new StringBuilder("TypedProperties[");
+
+ if (properties != null) {
+ Iterator<Entry<SimpleString, PropertyValue>> iter =
properties.entrySet().iterator();
+
+ while (iter.hasNext()) {
+ Entry<SimpleString, PropertyValue> iterItem = iter.next();
+ sb.append(iterItem.getKey() + "=");
+
+ // it seems weird but it's right!!
+ // The first getValue is from the EntrySet
+ // The second is to convert the PropertyValue into the actual
value
+ Object theValue = iterItem.getValue().getValue();
+
+ if (theValue == null) {
+ sb.append("NULL-value");
+ } else if (theValue instanceof byte[] bytes) {
+ sb.append("[" +
ByteUtil.maxString(ByteUtil.bytesToHex(bytes, 2), 150) + "]");
+
+ if
(iterItem.getKey().toString().startsWith("_AMQ_ROUTE_TO")) {
+ sb.append(", bytesAsLongs[");
+ try {
+ ByteBuffer buff = ByteBuffer.wrap(bytes);
+ while (buff.hasRemaining()) {
+ long bindingID = buff.getLong();
+ sb.append(bindingID);
+ if (buff.hasRemaining()) {
+ sb.append(", ");
+ }
}
+ } catch (Throwable e) {
+ sb.append("error-converting-longs=" + e.getMessage());
}
- } catch (Throwable e) {
- sb.append("error-converting-longs=" + e.getMessage());
+ sb.append("]");
}
- sb.append("]");
+ } else {
+ sb.append(theValue.toString());
}
- } else {
- sb.append(theValue.toString());
- }
- if (iter.hasNext()) {
- sb.append(", ");
+ if (iter.hasNext()) {
+ sb.append(", ");
+ }
}
}
- }
- return sb.append("]").toString();
+ return sb.append("]").toString();
+ } finally {
+ lock.readLock().unlock();
+ }
}
- private synchronized void doPutValue(final SimpleString key, final
PropertyValue value) {
- if (!internalProperties && internalPropertyPredicate != null &&
internalPropertyPredicate.test(key)) {
- internalProperties = true;
- }
+ private void doPutValue(final SimpleString key, final PropertyValue value) {
+ lock.writeLock().lock();
+ try {
+ if (!internalProperties && internalPropertyPredicate != null &&
internalPropertyPredicate.test(key)) {
+ internalProperties = true;
+ }
- if (!amqpProperties && amqpPropertyPredicate != null &&
amqpPropertyPredicate.test(key)) {
- amqpProperties = true;
- }
+ if (!amqpProperties && amqpPropertyPredicate != null &&
amqpPropertyPredicate.test(key)) {
+ amqpProperties = true;
+ }
- if (properties == null) {
- properties = new HashMap<>();
- }
+ if (properties == null) {
+ properties = new HashMap<>();
+ }
- PropertyValue oldValue = properties.put(key, value);
- if (oldValue != null) {
- size += value.encodeSize() - oldValue.encodeSize();
- } else {
- size += SimpleString.sizeofString(key) + value.encodeSize();
+ PropertyValue oldValue = properties.put(key, value);
+ if (oldValue != null) {
+ size += value.encodeSize() - oldValue.encodeSize();
+ } else {
+ size += SimpleString.sizeofString(key) + value.encodeSize();
+ }
+ } finally {
+ lock.writeLock().unlock();
}
}
- private synchronized Object doRemoveProperty(final SimpleString key) {
- if (properties == null) {
- return null;
- }
+ private Object doRemoveProperty(final SimpleString key) {
+ lock.writeLock().lock();
+ try {
+ if (properties == null) {
+ return null;
+ }
- PropertyValue val = properties.remove(key);
- if (val == null) {
- return null;
- } else {
- size -= SimpleString.sizeofString(key) + val.encodeSize();
- return val.getValue();
+ PropertyValue val = properties.remove(key);
+ if (val == null) {
+ return null;
+ } else {
+ size -= SimpleString.sizeofString(key) + val.encodeSize();
+ return val.getValue();
+ }
+ } finally {
+ lock.writeLock().unlock();
}
}
- private synchronized Object doGetProperty(final SimpleString key) {
- if (properties == null) {
- return null;
- }
+ private Object doGetProperty(final SimpleString key) {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return null;
+ }
- PropertyValue val = properties.get(key);
- if (val == null) {
- return null;
- } else {
- return val.getValue();
+ PropertyValue val = properties.get(key);
+ if (val == null) {
+ return null;
+ } else {
+ return val.getValue();
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
@@ -1173,43 +1289,66 @@ public class TypedProperties {
}
}
- public synchronized boolean isEmpty() {
- if (properties == null) {
- return true;
- } else {
- return properties.isEmpty();
+ public boolean isEmpty() {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return true;
+ } else {
+ return properties.isEmpty();
+ }
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized Set<String> getMapNames() {
- if (properties == null) {
- return Collections.emptySet();
- } else {
- Set<String> names = new HashSet<>(properties.size());
- for (SimpleString name : properties.keySet()) {
- names.add(name.toString());
+ public Set<String> getMapNames() {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return Collections.emptySet();
+ } else {
+ Set<String> names = new HashSet<>(properties.size());
+ for (SimpleString name : properties.keySet()) {
+ names.add(name.toString());
+ }
+ return names;
}
- return names;
+ } finally {
+ lock.readLock().unlock();
}
}
- public synchronized Map<String, Object> getMap() {
- if (properties == null) {
- return Collections.emptyMap();
- } else {
- Map<String, Object> m = new HashMap<>(properties.size());
- for (Entry<SimpleString, PropertyValue> entry :
properties.entrySet()) {
- Object val = entry.getValue().getValue();
- if (val instanceof SimpleString simpleString) {
- m.put(entry.getKey().toString(), simpleString.toString());
- } else {
- m.put(entry.getKey().toString(), val);
+ public Map<String, Object> getMap() {
+ lock.readLock().lock();
+ try {
+ if (properties == null) {
+ return Collections.emptyMap();
+ } else {
+ Map<String, Object> m = new HashMap<>(properties.size());
+ for (Entry<SimpleString, PropertyValue> entry :
properties.entrySet()) {
+ Object val = entry.getValue().getValue();
+ if (val instanceof SimpleString simpleString) {
+ m.put(entry.getKey().toString(), simpleString.toString());
+ } else {
+ m.put(entry.getKey().toString(), val);
+ }
}
+ return m;
}
- return m;
+ } finally {
+ lock.readLock().unlock();
}
}
+ public Lock getReadLock() {
+ return lock.readLock();
+ }
+
+ public Lock getWriteLock() {
+ return lock.writeLock();
+ }
+
/**
* Helper for MapMessage#setObjectProperty(String, Object)
*
diff --git
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java
index a6f6d4e2a2..a7ce7775ca 100644
---
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java
+++
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/message/impl/CoreMessage.java
@@ -790,7 +790,8 @@ public class CoreMessage extends RefCountMessage implements
ICoreMessage {
Long.BYTES + // expiration
Long.BYTES + // timestamp
Byte.BYTES; // priority
- synchronized (properties) {
+ properties.getReadLock().lock();
+ try {
final int propertiesEncodeSize = properties.getEncodeSize();
final int totalEncodedSize = headersSize + propertiesEncodeSize;
ensureExactWritable(buffer, totalEncodedSize);
@@ -810,6 +811,8 @@ public class CoreMessage extends RefCountMessage implements
ICoreMessage {
assert buffer.writerIndex() == initialWriterIndex + headersSize :
"Bad Headers encode size estimation";
final int realPropertiesEncodeSize = properties.encode(buffer);
assert realPropertiesEncodeSize == propertiesEncodeSize :
"TypedProperties has a wrong encode size estimation or is being modified
concurrently";
+ } finally {
+ properties.getReadLock().unlock();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact