Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-2661

MediaTypeMap shared across threads, but cache is not thread-safe

    Details

    • Type: Bug
    • Status: Resolved (View Workflow)
    • Priority: Critical
    • Resolution: Done
    • Affects Version/s: 4.5.5.Final
    • Fix Version/s: 4.5.8.Final
    • Component/s: jaxrs
    • Labels:
      None
    • Steps to Reproduce:
      Hide

      I expect this to be difficult to reproduce under controlled conditions.

      Show
      I expect this to be difficult to reproduce under controlled conditions.

      Description

      Under high contention during startup, we observe:

           Caused by: javax.ws.rs.ProcessingException: java.lang.NullPointerException: Cannot invoke "java.util.Map.put(Object, Object)" because "cache" is null
          	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:251)
          	at org.jboss.resteasy.specimpl.BuiltResponse.readEntity(BuiltResponse.java:88)
          	at org.jboss.resteasy.specimpl.AbstractBuiltResponse.readEntity(AbstractBuiltResponse.java:256)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.extractResult(ClientInvocation.java:163)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.lambda$getResponseTypeExtractor$6(ClientInvocation.java:613)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.lambda$asyncSubmit$10(ClientInvocation.java:756)
          	at org.jboss.resteasy.client.jaxrs.engines.jetty.JettyClientEngine$2.complete(JettyClientEngine.java:198)
          	at org.jboss.resteasy.client.jaxrs.engines.jetty.JettyClientEngine$2.onSuccess(JettyClientEngine.java:184)
          	... 24 more
          Caused by: java.lang.NullPointerException: Cannot invoke "java.util.Map.put(Object, Object)" because "cache" is null
          	at org.jboss.resteasy.core.MediaTypeMap.getPossible(MediaTypeMap.java:584)
          	at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.resolveMessageBodyReader(ResteasyProviderFactoryImpl.java:576)
          	at org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl.getClientMessageBodyReader(ResteasyProviderFactoryImpl.java:568)
          	at org.jboss.resteasy.core.interception.jaxrs.ClientReaderInterceptorContext.resolveReader(ClientReaderInterceptorContext.java:54)
          	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.getReader(AbstractReaderInterceptorContext.java:130)
          	at org.jboss.resteasy.core.interception.jaxrs.AbstractReaderInterceptorContext.proceed(AbstractReaderInterceptorContext.java:75)
          	at org.jboss.resteasy.client.jaxrs.internal.ClientResponse.readFrom(ClientResponse.java:214)

       

      ResteasyProviderFactoryImpl holds a ConcurrentMap with MediaTypeMap values.

      MediaTypeMap maintains a cache:

            if (useCache) {
               // don't care about variable volatility.  Really rare to add entries post boot
               Map<CachedMediaTypeAndClass, List<T>> cache = classCache;
               if (classCache == null) {
                  cache = new HashMap<>();
                  classCache = cache;
               }
               cache.put(cacheEntry, cached);
            } 

      Note in particular, classCache is read twice.  If the first read yields a null value, but a competing thread writes before the second read, you may end up storing into a null map.  I appreciate that this code probably wants to be fast rather than cache perfectly, but this is not thread-safe in a way that may crash rather than simply under-perform.

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  jim.ma Jim Ma
                  Reporter:
                  stevenschlansker Steven Schlansker
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  3 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: