Skip to content

Commit 5198d8e

Browse files
committed
Remove computeIfAbsent from the warm path
1 parent 6f264af commit 5198d8e

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,4 +536,15 @@
536536
</Or>
537537
<Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE"/>
538538
</Match>
539+
540+
<!-- Intentional benign-race get-then-put on ConcurrentHashMap. SdkField instances are
541+
static final, and the registry always returns the same marshaller for a given
542+
(location, marshallingType) pair, so concurrent puts are idempotent. Using get()
543+
instead of computeIfAbsent() avoids the latter's bucket-level synchronization
544+
overhead on every call. -->
545+
<Match>
546+
<Class name="software.amazon.awssdk.protocols.json.internal.marshall.JsonProtocolMarshaller"/>
547+
<Method name="marshallFieldViaRegistry"/>
548+
<Bug pattern="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION"/>
549+
</Match>
539550
</FindBugsFilter>

core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,16 @@ private void marshallFieldViaRegistry(SdkField<?> field, Object val) {
422422
.marshall(val, marshallerContext, field.locationName(), (SdkField<Object>) field);
423423
return;
424424
}
425-
JsonMarshaller<Object> marshaller = MARSHALLER_CACHE.computeIfAbsent(field,
426-
f -> MARSHALLER_REGISTRY.getMarshaller(f.location(), f.marshallingType(), val));
425+
// Use get-before-put instead of computeIfAbsent. ConcurrentHashMap.get() is a single lock-free
426+
// volatile read, whereas computeIfAbsent() has additional overhead even on cache hits (bucket-level
427+
// synchronization bookkeeping). The benign-race on first access is safe: SdkField instances are
428+
// static final, and the registry always returns the same marshaller for a given (location, type) pair,
429+
// so concurrent puts are idempotent.
430+
JsonMarshaller<Object> marshaller = MARSHALLER_CACHE.get(field);
431+
if (marshaller == null) {
432+
marshaller = MARSHALLER_REGISTRY.getMarshaller(field.location(), field.marshallingType(), val);
433+
MARSHALLER_CACHE.put(field, marshaller);
434+
}
427435
marshaller.marshall(val, marshallerContext, field.locationName(), (SdkField<Object>) field);
428436
}
429437

0 commit comments

Comments
 (0)