Skip to content

Commit 7c4bd23

Browse files
authored
Rework locking in DeserializerCache (#4456)
1 parent 5430443 commit 7c4bd23

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-databind
66

77
2.18.0 (not yet released)
88

9+
#4456: Rework locking in `DeserializerCache`
10+
(contributed by @pjfanning)
911
#4464: When `Include.NON_DEFAULT` setting is used, `isEmpty()` method is
1012
not called on the serializer
1113
(reported by Teodor D)

src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ public final class DeserializerCache
5454
protected final HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
5555
= new HashMap<JavaType, JsonDeserializer<Object>>(8);
5656

57-
5857
/**
5958
* We hold an explicit lock while creating deserializers to avoid creating duplicates.
6059
*/
@@ -166,7 +165,6 @@ public JsonDeserializer<Object> findValueDeserializer(DeserializationContext ctx
166165
throws JsonMappingException
167166
{
168167
Objects.requireNonNull(propertyType, "Null 'propertyType' passed");
169-
170168
JsonDeserializer<Object> deser = _findCachedDeserializer(propertyType);
171169
if (deser == null) {
172170
// If not, need to request factory to construct (or recycle)
@@ -255,26 +253,33 @@ protected JsonDeserializer<Object> _createAndCacheValueDeserializer(Deserializat
255253
// Only one thread to construct deserializers at any given point in time;
256254
// limitations necessary to ensure that only completely initialized ones
257255
// are visible and used.
258-
259-
try {
260-
_incompleteDeserializersLock.lock();
261-
262-
// Ok, then: could it be that due to a race condition, deserializer can now be found?
263-
JsonDeserializer<Object> deser = _findCachedDeserializer(type);
256+
final boolean isCustom = _hasCustomHandlers(type);
257+
if (!isCustom) {
258+
JsonDeserializer<Object> deser = _cachedDeserializers.get(type);
264259
if (deser != null) {
265260
return deser;
266261
}
262+
}
263+
_incompleteDeserializersLock.lock();
264+
try {
265+
if (!isCustom) {
266+
JsonDeserializer<Object> deser = _cachedDeserializers.get(type);
267+
if (deser != null) {
268+
return deser;
269+
}
270+
}
271+
// Ok, then: could it be that due to a race condition, deserializer can now be found?
267272
int count = _incompleteDeserializers.size();
268273
// Or perhaps being resolved right now?
269274
if (count > 0) {
270-
deser = _incompleteDeserializers.get(type);
275+
JsonDeserializer<Object> deser = _incompleteDeserializers.get(type);
271276
if (deser != null) {
272277
return deser;
273278
}
274279
}
275280
// Nope: need to create and possibly cache
276281
try {
277-
return _createAndCache2(ctxt, factory, type);
282+
return _createAndCache2(ctxt, factory, type, isCustom);
278283
} finally {
279284
// also: any deserializers that have been created are complete by now
280285
if (count == 0 && _incompleteDeserializers.size() > 0) {
@@ -289,10 +294,21 @@ protected JsonDeserializer<Object> _createAndCacheValueDeserializer(Deserializat
289294
/**
290295
* Method that handles actual construction (via factory) and caching (both
291296
* intermediate and eventual)
297+
*
298+
* @deprecated Since 2.18 use version of _createAndCache2 that takes `isCustom` flag
292299
*/
300+
@Deprecated // since 2.18, remove from 2.19
293301
protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
294302
DeserializerFactory factory, JavaType type)
295303
throws JsonMappingException
304+
{
305+
return _createAndCache2(ctxt, factory, type, _hasCustomHandlers(type));
306+
}
307+
308+
// @since 2.18
309+
protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
310+
DeserializerFactory factory, JavaType type, boolean isCustom)
311+
throws JsonMappingException
296312
{
297313
JsonDeserializer<Object> deser;
298314
try {
@@ -306,11 +322,11 @@ protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
306322
if (deser == null) {
307323
return null;
308324
}
309-
/* cache resulting deserializer? always true for "plain" BeanDeserializer
310-
* (but can be re-defined for sub-classes by using @JsonCachable!)
311-
*/
325+
// cache resulting deserializer? always true for "plain" BeanDeserializer
326+
// (but can be re-defined for sub-classes by using @JsonCachable!)
327+
312328
// 27-Mar-2015, tatu: As per [databind#735], avoid caching types with custom value desers
313-
boolean addToCache = !_hasCustomHandlers(type) && deser.isCachable();
329+
boolean addToCache = !isCustom && deser.isCachable();
314330

315331
/* we will temporarily hold on to all created deserializers (to
316332
* handle cyclic references, and possibly reuse non-cached
@@ -321,9 +337,8 @@ protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
321337
* either not add Lists or Maps, or clear references eagerly.
322338
* Let's actually do both; since both seem reasonable.
323339
*/
324-
/* Need to resolve? Mostly done for bean deserializers; required for
325-
* resolving cyclic references.
326-
*/
340+
// Need to resolve? Mostly done for bean deserializers; required for
341+
// resolving cyclic references.
327342
if (deser instanceof ResolvableDeserializer) {
328343
_incompleteDeserializers.put(type, deser);
329344
try {

0 commit comments

Comments
 (0)