users@jersey.java.net

Re: [Jersey] SAXParserContextProvider is a contention point

From: Paul Sandoz <Paul.Sandoz_at_oracle.com>
Date: Wed, 25 Aug 2010 10:54:03 +0200

Hi Felix,

After some email exchange with one of your colleagues i understand the
problem now, i overlooked the "asynchronous" in your first email.

Because we create a new thread for every client request (the illusion
of async) a new parser instance will be created every time, hence the
contention you point out.

I think the right solution here is to use a proper underlying async
API, which obviously requires a little more work (and we may be
required to break some APIs, also filtering needs to be changed, i
wrote a proposal on this list a while ago on how to do that).

Another simpler interim solution is consider using of a thread pool. I
think the async handler impl:

     public Future<ClientResponse> handle(final ClientRequest request,
final FutureListener<ClientResponse> l) {
         Callable c = new Callable() {
             public Object call() throws Exception {
                 return getHeadHandler().handle(request);
             }
         };
         FutureTask<ClientResponse> ft = new
FutureTask<ClientResponse>(c) {
             @Override
             protected void done() {
                 try {
                     l.onComplete(this);
                 } catch (Throwable t) {
                     LOGGER.log(Level.SEVERE,
                             "Throwable caught on call to
ClientResponseListener.onComplete",
                             t);
                 }
             }
         };

         new Thread(ft).start();
         return ft;
     }

could use a pool and the done methods of "ft" could return the thread
to the pool. It does mean that the number of requests could be limited
waiting for a thread to become available (but there would i suspect be
some blocking lower down in the HttpURLConnection at some point when
enough threads are utilized as they will contend for IO/CPU resources).

Paul.


On Aug 19, 2010, at 6:29 PM, Paul Sandoz wrote:

> Hi Felix,
>
> The SAXParserContextProvider that extends
> ThreadLocalSingletonContextProvider only creates a new instance of
> SAXParserFactory per-thread (i.e. when a get is called on the thread
> local and the value has not been set), it will not be creating a new
> instance of SAXParserFactory every time one is created, so i would
> expect any contention to be limited to an initialization period.
>
> See:
>
> http://download.oracle.com/javase/6/docs/api/java/lang/ThreadLocal.html#initialValue%28%29
>
> Now that i think about it i am wondering why it is like this:
>
> protected ThreadLocalSingletonContextProvider(Class<T> t) {
> this.t = t;
> this.rf = new ThreadLocal<T>() {
> @Override
> protected synchronized T initialValue() {
> return getInstance();
> }
> };
> }
>
> and the synchronized is used. I think it was because i was not sure
> what would happen if two threads were executing the following at the
> same time:
>
> SAXParserFactory f = SAXParserFactory.newInstance();
>
>
> And that is the general why we keep instances of SAXParserFactory
> per-thread: we cannot guarantee that those implementations are
> thread safe when utilized to obtain SAXParser instances. The spec
> and JavaDoc are vague on the topic.
>
> You can override by registering your own InjectableProvider
> implementation that supports the SAXParserFactory type and
> registering that implementation as a provider (annotate with
> @Provider to identify and then register explicitly or via scanning).
>
> Paul.
>
>
> On Aug 19, 2010, at 5:06 PM, felix-etienne.trepanier_at_nokia.com wrote:
>
>> Hi,
>>
>> I’ve been using the jersey default client (new Client()) using the
>> asynchronous API on several thread simultaneously and noticed a
>> contention point (see stacktrace bellow). The
>> XMLRootElementProvider get a SAXParserContextProvider and used it
>> to read the XML stream. The implementation of the
>> SAXParserContextProvider is based on the
>> ThreadLocalSingletonContextProvider which synchronizes the
>> getInitialValue() but the implementation of
>> SAXParserContextProvider recreates a new SAXParserFactory every
>> time and this seems to be a costly operation. This causes contention.
>>
>> I’m trying to figure out how to inject another implementation of
>> the SAXParserContextProvider that would not synchronize the
>> creation of the SAXParserFactory. Pointers would be helpful.
>>
>> Thanks,
>>
>> Felix
>> …
>> at
>> java.lang.ClassLoader.getResourceAsStream(ClassLoader.java:1168)
>> at javax.xml.parsers.SecuritySupport
>> $4.run(SecuritySupport.java:96)
>> at java.security.AccessController.doPrivileged(Native Method)
>> at
>> javax
>> .xml
>> .parsers.SecuritySupport.getResourceAsStream(SecuritySupport.java:89)
>> at
>> javax
>> .xml
>> .parsers.FactoryFinder.findJarServiceProvider(FactoryFinder.java:250)
>> at javax.xml.parsers.FactoryFinder.find(FactoryFinder.java:
>> 223)
>> at
>> javax
>> .xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:128)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .impl
>> .provider
>> .xml
>> .SAXParserContextProvider.getInstance(SAXParserContextProvider.java:
>> 65)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .impl
>> .provider
>> .xml
>> .SAXParserContextProvider.getInstance(SAXParserContextProvider.java:
>> 51)
>> at
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>> $1.initialValue(ThreadLocalSingletonContextProvider.java:61)
>> - locked <0x00002aaab578ecd8> (a
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider$1)
>> at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:141)
>> at java.lang.ThreadLocal.get(ThreadLocal.java:131)
>> at
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>> $2.getValue(ThreadLocalSingletonContextProvider.java:74)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .impl
>> .provider
>> .entity.XMLRootElementProvider.readFrom(XMLRootElementProvider.java:
>> 110)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .provider
>> .jaxb
>> .AbstractRootElementProvider
>> .readFrom(AbstractRootElementProvider.java:106)
>> at
>> com
>> .sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:
>> 549)
>> at
>> com
>> .sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:
>> 502)
>> at com.sun.jersey.api.client.AsyncWebResource
>> $3.get(AsyncWebResource.java:706)
>> at
>> com
>> .sun
>> .jersey
>> .client
>> .impl
>> .async
>> .FutureClientResponseListener
>> .onComplete(FutureClientResponseListener.java:103)
>> at com.sun.jersey.api.client.AsyncWebResource
>> $6.done(AsyncWebResource.java:749)
>> at java.util.concurrent.FutureTask
>> $Sync.innerSet(FutureTask.java:251)
>> at java.util.concurrent.FutureTask
>> $Sync.innerRun(FutureTask.java:303)
>> at java.util.concurrent.FutureTask.run(FutureTask.java:138)
>> at java.lang.Thread.run(Thread.java:619)
>>
>> "Thread-42745" daemon prio=10 tid=0x00002aaafa221000 nid=0x9ca
>> waiting for monitor entry [0x00002aab74f8a000..0x00002aab74f8ab10]
>> java.lang.Thread.State: BLOCKED (on object monitor)
>> at
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>> $1.initialValue(ThreadLocalSingletonContextProvider.java:61)
>> - waiting to lock <0x00002aaab578ecd8> (a
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider$1)
>> at java.lang.ThreadLocal.setInitialValue(ThreadLocal.java:141)
>> at java.lang.ThreadLocal.get(ThreadLocal.java:131)
>> at
>> com
>> .sun
>> .jersey.core.impl.provider.xml.ThreadLocalSingletonContextProvider
>> $2.getValue(ThreadLocalSingletonContextProvider.java:74)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .impl
>> .provider
>> .entity.XMLRootElementProvider.readFrom(XMLRootElementProvider.java:
>> 110)
>> at
>> com
>> .sun
>> .jersey
>> .core
>> .provider
>> .jaxb
>> .AbstractRootElementProvider
>> .readFrom(AbstractRootElementProvider.java:106)
>> at
>> com
>> .sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:
>> 549)
>> at
>> com
>> .sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:
>> 502)
>> at com.sun.jersey.api.client.AsyncWebResource
>> $3.get(AsyncWebResource.java:706)
>> at
>> com
>> .sun
>> .jersey
>> .client
>> .impl
>> .async
>> .FutureClientResponseListener
>> .onComplete(FutureClientResponseListener.java:103)
>> at com.sun.jersey.api.client.AsyncWebResource
>> $6.done(AsyncWebResource.java:749)
>> at java.util.concurrent.FutureTask
>> $Sync.innerSet(FutureTask.java:251)
>> at java.util.concurrent.FutureTask
>> $Sync.innerRun(FutureTask.java:303)
>> at java.util.concurrent.FutureTask.run(FutureTask.java:138)
>> at java.lang.Thread.run(Thread.java:619)
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>