Thread local resource management feature request/discussion

classic Classic list List threaded Threaded
22 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Thread local resource management feature request/discussion

Nitsan Wakart-2
Currently thread local resources can be managed using a ThreadLocal.
ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a
use case for this already inside the JDK with thread local direct byte
buffers. Currently the cleanup of the thread local byte buffers is
driven by finalization, this can result in issues in high thread churn
applications. With lifecycle awareness the buffers could be cleaned up
on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
   final static ThreadLocal<ByteBuffer> TL_BUFFER = new
ThreadLocal<ByteBuffer>() {
     @Override
     protected ByteBuffer initialValue() {
       return ByteBuffer.allocateDirect(4096);
     }
     @Override
     protected void discard(ByteBuffer bb) {
       ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
     }
   };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this
functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Dawid Weiss
Apache Lucene has a similar problem and a workaround here:
https://github.com/apache/lucene-solr/blob/master/lucene/core/src/java/org/apache/lucene/util/CloseableThreadLocal.java

Dawid

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]> wrote:

> Currently thread local resources can be managed using a ThreadLocal.
> ThreadLocals are initialized on 'discovery' and never explicitly discarded.
> I would like to have Thread lifecycle aware thread resources. There is a use
> case for this already inside the JDK with thread local direct byte buffers.
> Currently the cleanup of the thread local byte buffers is driven by
> finalization, this can result in issues in high thread churn applications.
> With lifecycle awareness the buffers could be cleaned up on thread exit.
> Here is a suggested enhancement to TL to allow this behavior:
>   final static ThreadLocal<ByteBuffer> TL_BUFFER = new
> ThreadLocal<ByteBuffer>() {
>     @Override
>     protected ByteBuffer initialValue() {
>       return ByteBuffer.allocateDirect(4096);
>     }
>     @Override
>     protected void discard(ByteBuffer bb) {
>       ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
>     }
>   };
>
> The discard method will be called on thread exit.
> Am I missing an already existing way of achieving the same? Does this
> functionality somehow not sit well with existing APIs?
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Viktor Klang
In reply to this post by Nitsan Wakart-2
Speaking of ThreadLocals, especially for TL propagation it would be splendid if one could get *ALL* TLs currently initialized for a given Thread instance.

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]> wrote:
Currently thread local resources can be managed using a ThreadLocal. ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a use case for this already inside the JDK with thread local direct byte buffers. Currently the cleanup of the thread local byte buffers is driven by finalization, this can result in issues in high thread churn applications. With lifecycle awareness the buffers could be cleaned up on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
  final static ThreadLocal<ByteBuffer> TL_BUFFER = new ThreadLocal<ByteBuffer>() {
    @Override
    protected ByteBuffer initialValue() {
      return ByteBuffer.allocateDirect(4096);
    }
    @Override
    protected void discard(ByteBuffer bb) {
      ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
    }
  };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

David Holmes-6

That would totally break encapsulation. The ThreadLocal variable belongs to the code in which it is declared and used. Some other piece of code should not be able to gather all such variables – just like you couldn’t (and shouldn’t) be able to gather a list of all (for example) static fields that a thread can access.

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Thursday, June 9, 2016 7:27 PM
To: Nitsan Wakart <[hidden email]>
Cc: concurrency-interest <[hidden email]>
Subject: Re: [concurrency-interest] Thread local resource management feature request/discussion

 

Speaking of ThreadLocals, especially for TL propagation it would be splendid if one could get *ALL* TLs currently initialized for a given Thread instance.

 

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]> wrote:

Currently thread local resources can be managed using a ThreadLocal. ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a use case for this already inside the JDK with thread local direct byte buffers. Currently the cleanup of the thread local byte buffers is driven by finalization, this can result in issues in high thread churn applications. With lifecycle awareness the buffers could be cleaned up on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
  final static ThreadLocal<ByteBuffer> TL_BUFFER = new ThreadLocal<ByteBuffer>() {
    @Override
    protected ByteBuffer initialValue() {
      return ByteBuffer.allocateDirect(4096);
    }
    @Override
    protected void discard(ByteBuffer bb) {
      ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
    }
  };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



 

--

Cheers,


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Viktor Klang


On Thu, Jun 9, 2016 at 11:37 AM, David Holmes <[hidden email]> wrote:

That would totally break encapsulation.


You mean like reflection and setAccessible(true)? ;)
 

The ThreadLocal variable belongs to the code in which it is declared and used. Some other piece of code should not be able to gather all such variables – just like you couldn’t (and shouldn’t) be able to gather a list of all (for example) static fields that a thread can access.


You can already do this (collect which ThreadLocals are used) with instrumentation though.

Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).

 

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Thursday, June 9, 2016 7:27 PM
To: Nitsan Wakart <[hidden email]>
Cc: concurrency-interest <[hidden email]>
Subject: Re: [concurrency-interest] Thread local resource management feature request/discussion

 

Speaking of ThreadLocals, especially for TL propagation it would be splendid if one could get *ALL* TLs currently initialized for a given Thread instance.

 

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]> wrote:

Currently thread local resources can be managed using a ThreadLocal. ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a use case for this already inside the JDK with thread local direct byte buffers. Currently the cleanup of the thread local byte buffers is driven by finalization, this can result in issues in high thread churn applications. With lifecycle awareness the buffers could be cleaned up on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
  final static ThreadLocal<ByteBuffer> TL_BUFFER = new ThreadLocal<ByteBuffer>() {
    @Override
    protected ByteBuffer initialValue() {
      return ByteBuffer.allocateDirect(4096);
    }
    @Override
    protected void discard(ByteBuffer bb) {
      ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
    }
  };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



 

--

Cheers,




--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Jens Wilke
On Thursday 09 June 2016 11:48:12 Viktor Klang wrote:
> Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).

If you have a list of all thread locals you don't know which one to propagate either. What is the meaning of _thread local_ again?!

You can define a thread factory and everyone wanting to propagate thread related things, can register there.

Cheers,

Jens

--
"Everything superfluous is wrong!"

   // Jens Wilke - headissue GmbH - Germany
 \//  https://headissue.com
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Viktor Klang


On Jun 9, 2016 12:06 PM, "Jens Wilke" <[hidden email]> wrote:
>
> On Thursday 09 June 2016 11:48:12 Viktor Klang wrote:
> > Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).
>
> If you have a list of all thread locals you don't know which one to propagate either. What is the meaning of _thread local_ again?!

Would you mind elaborating a bit there--what exactly wouldn't work?

>
> You can define a thread factory and everyone wanting to propagate thread related things, can register there.

Doesn't work because A) legacy code/libraries and B) becomes end-user concern and C) how does the end User find and access all TLs needed and track whether new TLs have been introduced between lib upgrades?

Perhaps what is needed is a reboot: AsyncLocals and AsyncTraces to replace ThreadLocal and StackTrace(Elements) (since the Async versions subsume the thread locals if we can specify that ThreadLocal is an AsyncLocal which is pinned to a specific Async context--a thread instance)

>
> Cheers,
>
> Jens
>
> --
> "Everything superfluous is wrong!"
>
>    // Jens Wilke - headissue GmbH - Germany
>  \//  https://headissue.com


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

David M. Lloyd-3
In reply to this post by Nitsan Wakart-2
On 06/09/2016 04:04 AM, Nitsan Wakart wrote:
> Currently thread local resources can be managed using a ThreadLocal.
> ThreadLocals are initialized on 'discovery' and never explicitly discarded.
> I would like to have Thread lifecycle aware thread resources. There is a
> use case for this already inside the JDK with thread local direct byte
> buffers. Currently the cleanup of the thread local byte buffers is
> driven by finalization, this can result in issues in high thread churn
> applications. With lifecycle awareness the buffers could be cleaned up
> on thread exit.

A simple solution to this is to keep TLs private and only allow them to
be set in combination with a function or action that clears it on exit:

MyContextual c = ...something that keeps an internal TL...
MyResult r = c.doWith(a, b, (aa, ab) -> { ...TL is set only inside of
here... });

Lexical scoping means never leaking.  Adding support for recursive
doWith is easy too.

Granted it doesn't really solve 100% of use cases but it does help with
some.  I agree that *ThreadLocals should be allowed to self-clean on
thread exit; it's a bit crazy that they don't.  We (JBoss) use some
pretty hideous reflection hacks today to accomplish this.

> Here is a suggested enhancement to TL to allow this behavior:
>    final static ThreadLocal<ByteBuffer> TL_BUFFER = new
> ThreadLocal<ByteBuffer>() {
>      @Override
>      protected ByteBuffer initialValue() {
>        return ByteBuffer.allocateDirect(4096);
>      }
>      @Override
>      protected void discard(ByteBuffer bb) {
>        ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
>      }
>    };
>
> The discard method will be called on thread exit.
> Am I missing an already existing way of achieving the same? Does this
> functionality somehow not sit well with existing APIs?

I like this idea but some may balk at adding a virtual method to classes
which are meant to be extended.  Might make more sense to have a
Runnable cleanup action (or Consumer or whatever) that could be supplied
to the *ThreadLocal constructor.

--
- DML
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Jens Wilke
In reply to this post by Viktor Klang
On Thursday 09 June 2016 13:26:46 Viktor Klang wrote:
> On Jun 9, 2016 12:06 PM, "Jens Wilke" <[hidden email]> wrote:
> >
> > On Thursday 09 June 2016 11:48:12 Viktor Klang wrote:
> > > Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).
> >
> > If you have a list of all thread locals you don't know which one to propagate either. What is the meaning of _thread local_ again?!
>
> Would you mind elaborating a bit there--what exactly wouldn't work?

A thread local is local to one thread. Not local to one thread and others considering they have a common parent.

For example nested transactions: If you have a transaction context, you may want to propagate the transaction context. But you do do not necessarily want to copy the context instance and treat the two threads equally, because a tx.commit() in a child thread should not have the same effect as the tx.commit() in the parent thread.

Whether it needs to be really thread local, or whether it is context that needs to be propagated depends on the library and use case. Futhermore,
if you know it needs to be propagated, the "how" might differ as well.

> > You can define a thread factory and everyone wanting to propagate thread related things, can register there.
>
> Doesn't work because

> A) legacy code/libraries

Yes, but nothing will, since there is no standard what should happen. Propagating every TL might just create a big memory leak.

> and B) becomes end-user concern and

A thread local propagator can be a SPI and every library needing this can provide one.

> C) how does the end User find and access all TLs needed and track whether new TLs have been introduced between lib upgrades?

It does not and never should.

> Perhaps what is needed is a reboot: AsyncLocals and AsyncTraces to replace ThreadLocal and StackTrace(Elements) (since the Async versions subsume the thread locals if we can specify that ThreadLocal is an AsyncLocal which is pinned to a specific Async context--a thread instance)

What has async to do with thread locals? ;) Wheels can be used not just for cars.

But srsly, mind about the concepts you try to name. How can a local variable reference be asynchronous by itself?
Hope you don't mind the hint from someone who is not doing only async with TLs.

So maybe its an AutoPropagatedThreadLocal?!

But yes, stack traces is a pain point, too. Just trying to fix the JCache TCK because it is actually asserted that the identical exception instance from another thread is rethrown.

Cheers,

Jens

--
"Everything superfluous is wrong!"

   // Jens Wilke - headissue GmbH - Germany
 \//  https://headissue.com
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Viktor Klang


On Thu, Jun 9, 2016 at 2:18 PM, Jens Wilke <[hidden email]> wrote:
On Thursday 09 June 2016 13:26:46 Viktor Klang wrote:
> On Jun 9, 2016 12:06 PM, "Jens Wilke" <[hidden email]> wrote:
> >
> > On Thursday 09 June 2016 11:48:12 Viktor Klang wrote:
> > > Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).
> >
> > If you have a list of all thread locals you don't know which one to propagate either. What is the meaning of _thread local_ again?!
>
> Would you mind elaborating a bit there--what exactly wouldn't work?

A thread local is local to one thread. Not local to one thread and others considering they have a common parent.

For example nested transactions: If you have a transaction context, you may want to propagate the transaction context. But you do do not necessarily want to copy the context instance and treat the two threads equally, because a tx.commit() in a child thread should not have the same effect as the tx.commit() in the parent thread.

How would this differ with propagation?
 

Whether it needs to be really thread local, or whether it is context that needs to be propagated depends on the library and use case. Futhermore,
if you know it needs to be propagated, the "how" might differ as well.

Do you have a concrete example?
 

> > You can define a thread factory and everyone wanting to propagate thread related things, can register there.
>
> Doesn't work because

> A) legacy code/libraries

Yes, but nothing will, since there is no standard what should happen. Propagating every TL might just create a big memory leak.

This is interesting that you mention, because this is the problem Nitsan is trying to address with having "disposable TLs" (i.e. TLs already have issues with memory leakage and undesired memory retention. ClassLoader leakages most prominently)
 

> and B) becomes end-user concern and

A thread local propagator can be a SPI and every library needing this can provide one.

Absolutely. So by Java 25 we might see broad adoption and all old dependencies will be phased out. Not really tracable :)
 

> C) how does the end User find and access all TLs needed and track whether new TLs have been introduced between lib upgrades?

It does not and never should.

I agree completely :)
 

> Perhaps what is needed is a reboot: AsyncLocals and AsyncTraces to replace ThreadLocal and StackTrace(Elements) (since the Async versions subsume the thread locals if we can specify that ThreadLocal is an AsyncLocal which is pinned to a specific Async context--a thread instance)

What has async to do with thread locals? ;) Wheels can be used not just for cars.

There *does* seem to be prior art here: http://blog.cincura.net/233526-asynclocal-t-in-net-46/
 

But srsly, mind about the concepts you try to name. How can a local variable reference be asynchronous by itself?
Hope you don't mind the hint from someone who is not doing only async with TLs.

My power screwdriver was sold as a screwdriver but it's pretty good at drilling too. :)
 

So maybe its an AutoPropagatedThreadLocal?!

…ContextFactoryMetaProviderServiceLocatorBean :)
 

But yes, stack traces is a pain point, too. Just trying to fix the JCache TCK because it is actually asserted that the identical exception instance from another thread is rethrown.

Don't get me started on StackTraceElement :)
 

Cheers,

Jens

--
"Everything superfluous is wrong!"

   // Jens Wilke - headissue GmbH - Germany
 \//  https://headissue.com



--
Cheers,

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

David M. Lloyd-3
In reply to this post by David M. Lloyd-3
On 06/09/2016 06:43 AM, Norman Maurer wrote:

> Hey David,
>
>
>> On 09 Jun 2016, at 13:37, David M. Lloyd <[hidden email]> wrote:
>>
>> On 06/09/2016 04:04 AM, Nitsan Wakart wrote:
>>> Currently thread local resources can be managed using a ThreadLocal.
>>> ThreadLocals are initialized on 'discovery' and never explicitly discarded.
>>> I would like to have Thread lifecycle aware thread resources. There is a
>>> use case for this already inside the JDK with thread local direct byte
>>> buffers. Currently the cleanup of the thread local byte buffers is
>>> driven by finalization, this can result in issues in high thread churn
>>> applications. With lifecycle awareness the buffers could be cleaned up
>>> on thread exit.
>>
>> A simple solution to this is to keep TLs private and only allow them to be set in combination with a function or action that clears it on exit:
>>
>> MyContextual c = ...something that keeps an internal TL...
>> MyResult r = c.doWith(a, b, (aa, ab) -> { ...TL is set only inside of here... });
>>
>> Lexical scoping means never leaking.  Adding support for recursive doWith is easy too.
>>
>> Granted it doesn't really solve 100% of use cases but it does help with some.  I agree that *ThreadLocals should be allowed to self-clean on thread exit; it's a bit crazy that they don't.  We (JBoss) use some pretty hideous reflection hacks today to accomplish this.
>
> Do you have a link to one of such “pretty hideous reflection hacks” ;) ?

Sure (though this just unreferences, it doesn't actually call remove() -
the idea being just to avoid memory leaks in the event that the Thread
reference is not released for long periods of time):

https://github.com/jbossas/jboss-threads/blob/master/src/main/java/org/jboss/threads/ThreadLocalResetter.java#L29

--
- DML
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

thurstonn
In reply to this post by Nitsan Wakart-2
And how would you propose dealing with the case where the ThreadLocal "referent" (ByteBuffer in your case) is strongly reachable outside the exiting thread's ThreadLocalMap?

Nitsan Wakart-2 wrote
Currently thread local resources can be managed using a ThreadLocal.
ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a
use case for this already inside the JDK with thread local direct byte
buffers. Currently the cleanup of the thread local byte buffers is
driven by finalization, this can result in issues in high thread churn
applications. With lifecycle awareness the buffers could be cleaned up
on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
   final static ThreadLocal<ByteBuffer> TL_BUFFER = new
ThreadLocal<ByteBuffer>() {
     @Override
     protected ByteBuffer initialValue() {
       return ByteBuffer.allocateDirect(4096);
     }
     @Override
     protected void discard(ByteBuffer bb) {
       ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
     }
   };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this
functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Jens Wilke
In reply to this post by Viktor Klang
On Thursday 09 June 2016 14:38:55 Viktor Klang wrote:

> > A thread local is local to one thread. Not local to one thread and others
> > considering they have a common parent.
> >
> > For example nested transactions: If you have a transaction context, you
> > may want to propagate the transaction context. But you do do not
> > necessarily want to copy the context instance and treat the two threads
> > equally, because a tx.commit() in a child thread should not have the same
> > effect as the tx.commit() in the parent thread.
>
> How would this differ with propagation?

If it's nested you may want to add a nesting level when propagating.

> > Whether it needs to be really thread local, or whether it is context that
> > needs to be propagated depends on the library and use case. Futhermore,
> > if you know it needs to be propagated, the "how" might differ as well.
> >
>
> Do you have a concrete example?

E.g. LongAdder, ConcurrentHashMap. Those use the thread local random's variables, which is
now directly in the thread, but conceptually its a thread local and backports exist using a thread local.
You don't want to be those values all the same....

> > But srsly, mind about the concepts you try to name. How can a local
> > variable reference be asynchronous by itself?
> > Hope you don't mind the hint from someone who is not doing only async with
> > TLs.
>
> My power screwdriver was sold as a screwdriver but it's pretty good at
> drilling too. :)

My power driller makes awkward holes, when I try to use it for screw driving and
the noise is just terrible. :)

> Absolutely. So by Java 25 we might see broad adoption and all old dependencies
> will be phased out. Not really tracable :)

Meanwhile I learned to be patient, Rome wasn't build in a day.

And every now and then I find out that something is fixed, where I did use workarounds
for decades.

Cheers,

Jens

--
"Everything superfluous is wrong!"

   // Jens Wilke - headissue GmbH - Germany
 \//  https://headissue.com
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Peter Levart
In reply to this post by Dawid Weiss



On 06/09/2016 11:21 AM, Dawid Weiss wrote:
Apache Lucene has a similar problem and a workaround here:
https://github.com/apache/lucene-solr/blob/master/lucene/core/src/java/org/apache/lucene/util/CloseableThreadLocal.java

Dawid

Hi Dawid,

As I understand this is a different problem. Lucene's CloseableThreadLocal tries to expedite cleanup of values associated with unreachable (and already GCed) ThreadLocal instances while the associated Thread(s) are still alive. If a thread exits and it's Thread object is GCed, then all thread local values associated with such thread can be GCed with the Thread in the same GC cycle. If OTOH a thread is still alive and a ThreadLocal object is GCed, then the value associated with such ThreadLocal object remains reachable until it is expunged from the ThreadLocalMap(s) reachable from live Thread objects, which happens only when they are accessed by some other ThreadLocal object. If they are not, such values can remain reachable until associated Thread(s) are GCed.

Fixing this (the case that lucene is trying to solve with CloseableThreadLocal) is not simple as it would require actions of some other (say cleanup) thread to do the expunging and that would require ThreadLocalMap to allow access from multiple threads which means it would require synchronization. Introducing synchronization in ThreadLocalMap would penalize normal access to ThreadLocalMap which currently does not need any synchronization.

I think the only "real" solution to this problem (and to some other problems with imprompt cleanup such as in ClassValue API) is adding Ephemeron support to JVM. I even created a prototype for that some time ago (with advice from Gil Tene who helped me specify the behavior):

    http://cr.openjdk.java.net/~plevart/misc/Ephemeron/webrev.jdk.02/
    http://cr.openjdk.java.net/~plevart/misc/Ephemeron/webrev.hotspot.02/

The interest to add such feature to JVM was not very big at that time. But that's probably just a result of people not being aware of what such feature could be used for. In ThreadLocal API, for example, an Ephemeron would be used as an Entry in ThreadLocalMap referenced from the Thread object as now. The key of such Ephemeron would be the ThreadLocal object and the value of such Ephemeron would be the associated value. If either of Thread or ThreadLocal object became unreachable, they would be GCed together with the associated value in the same GC cycle.

Regards, Peter

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart [hidden email] wrote:
Currently thread local resources can be managed using a ThreadLocal.
ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a use
case for this already inside the JDK with thread local direct byte buffers.
Currently the cleanup of the thread local byte buffers is driven by
finalization, this can result in issues in high thread churn applications.
With lifecycle awareness the buffers could be cleaned up on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
  final static ThreadLocal<ByteBuffer> TL_BUFFER = new
ThreadLocal<ByteBuffer>() {
    @Override
    protected ByteBuffer initialValue() {
      return ByteBuffer.allocateDirect(4096);
    }
    @Override
    protected void discard(ByteBuffer bb) {
      ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
    }
  };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this
functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Peter Levart
In reply to this post by Viktor Klang



On 06/09/2016 11:48 AM, Viktor Klang wrote:


On Thu, Jun 9, 2016 at 11:37 AM, David Holmes <[hidden email]> wrote:

That would totally break encapsulation.


You mean like reflection and setAccessible(true)? ;)
 

The ThreadLocal variable belongs to the code in which it is declared and used. Some other piece of code should not be able to gather all such variables – just like you couldn’t (and shouldn’t) be able to gather a list of all (for example) static fields that a thread can access.


You can already do this (collect which ThreadLocals are used) with instrumentation though.

Right now it's a real pain point with async programs to propagate ThreadLocals (since you don't know which thread locals to propagate).

An API that doesn't break encapsulation could be as follows:

public class Thread {
...
    /**
     * Exchanges thread-local values associated with current thread with the
     * values encapsulated in given {@link ThreadLocal.Context}.
     *
     * @param tlCtx the context with which to exchange thread-local values
     *              associated with current thread
     */
    public static void exchangeThreadLocalContext(ThreadLocal.Context tlCtx) {
        Objects.requireNonNull(tlCtx);
        Thread cur = Thread.currentThread();
        cur.threadLocals = tlCtx.mapRef.getAndSet(cur.threadLocals);
    }
}

public class ThreadLocal {
...
    /**
     * A snapshot of values that can be
     * {@link Thread#exchangeThreadLocalContext(Context) exchanged}
     * with thread-local values associated with current thread.
     */
    public static final class Context {
        final AtomicReference<ThreadLocalMap> mapRef = new AtomicReference<>();

        /**
         * Constructs an empty context with no values.
         */
        public Context() {
        }
    }
}



...but would require special permissions, as any code could otherwise "clear" thread-locals of current thread or install thread-locals exchanged with some other thread. The API makes sure that thread-local values remain accessible by a single thread.

Regards, Peter



 

 

David

 

From: Concurrency-interest [mailto:[hidden email]] On Behalf Of Viktor Klang
Sent: Thursday, June 9, 2016 7:27 PM
To: Nitsan Wakart <[hidden email]>
Cc: concurrency-interest <[hidden email]>
Subject: Re: [concurrency-interest] Thread local resource management feature request/discussion

 

Speaking of ThreadLocals, especially for TL propagation it would be splendid if one could get *ALL* TLs currently initialized for a given Thread instance.

 

On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]> wrote:

Currently thread local resources can be managed using a ThreadLocal. ThreadLocals are initialized on 'discovery' and never explicitly discarded.
I would like to have Thread lifecycle aware thread resources. There is a use case for this already inside the JDK with thread local direct byte buffers. Currently the cleanup of the thread local byte buffers is driven by finalization, this can result in issues in high thread churn applications. With lifecycle awareness the buffers could be cleaned up on thread exit.
Here is a suggested enhancement to TL to allow this behavior:
  final static ThreadLocal<ByteBuffer> TL_BUFFER = new ThreadLocal<ByteBuffer>() {
    @Override
    protected ByteBuffer initialValue() {
      return ByteBuffer.allocateDirect(4096);
    }
    @Override
    protected void discard(ByteBuffer bb) {
      ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
    }
  };

The discard method will be called on thread exit.
Am I missing an already existing way of achieving the same? Does this functionality somehow not sit well with existing APIs?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest



 

--

Cheers,




--
Cheers,


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest


_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Dawid Weiss
In reply to this post by Peter Levart
You are correct, Peter -- apologies for introducing some noise to the
thread. Your explanation and links are very helpful though.

Dawid

On Thu, Jun 9, 2016 at 10:35 PM, Peter Levart <[hidden email]> wrote:

>
>
> On 06/09/2016 11:21 AM, Dawid Weiss wrote:
>
> Apache Lucene has a similar problem and a workaround here:
> https://github.com/apache/lucene-solr/blob/master/lucene/core/src/java/org/apache/lucene/util/CloseableThreadLocal.java
>
> Dawid
>
>
> Hi Dawid,
>
> As I understand this is a different problem. Lucene's CloseableThreadLocal
> tries to expedite cleanup of values associated with unreachable (and already
> GCed) ThreadLocal instances while the associated Thread(s) are still alive.
> If a thread exits and it's Thread object is GCed, then all thread local
> values associated with such thread can be GCed with the Thread in the same
> GC cycle. If OTOH a thread is still alive and a ThreadLocal object is GCed,
> then the value associated with such ThreadLocal object remains reachable
> until it is expunged from the ThreadLocalMap(s) reachable from live Thread
> objects, which happens only when they are accessed by some other ThreadLocal
> object. If they are not, such values can remain reachable until associated
> Thread(s) are GCed.
>
> Fixing this (the case that lucene is trying to solve with
> CloseableThreadLocal) is not simple as it would require actions of some
> other (say cleanup) thread to do the expunging and that would require
> ThreadLocalMap to allow access from multiple threads which means it would
> require synchronization. Introducing synchronization in ThreadLocalMap would
> penalize normal access to ThreadLocalMap which currently does not need any
> synchronization.
>
> I think the only "real" solution to this problem (and to some other problems
> with imprompt cleanup such as in ClassValue API) is adding Ephemeron support
> to JVM. I even created a prototype for that some time ago (with advice from
> Gil Tene who helped me specify the behavior):
>
>     http://cr.openjdk.java.net/~plevart/misc/Ephemeron/webrev.jdk.02/
>     http://cr.openjdk.java.net/~plevart/misc/Ephemeron/webrev.hotspot.02/
>
> The interest to add such feature to JVM was not very big at that time. But
> that's probably just a result of people not being aware of what such feature
> could be used for. In ThreadLocal API, for example, an Ephemeron would be
> used as an Entry in ThreadLocalMap referenced from the Thread object as now.
> The key of such Ephemeron would be the ThreadLocal object and the value of
> such Ephemeron would be the associated value. If either of Thread or
> ThreadLocal object became unreachable, they would be GCed together with the
> associated value in the same GC cycle.
>
> Regards, Peter
>
>
> On Thu, Jun 9, 2016 at 11:04 AM, Nitsan Wakart <[hidden email]>
> wrote:
>
> Currently thread local resources can be managed using a ThreadLocal.
> ThreadLocals are initialized on 'discovery' and never explicitly discarded.
> I would like to have Thread lifecycle aware thread resources. There is a use
> case for this already inside the JDK with thread local direct byte buffers.
> Currently the cleanup of the thread local byte buffers is driven by
> finalization, this can result in issues in high thread churn applications.
> With lifecycle awareness the buffers could be cleaned up on thread exit.
> Here is a suggested enhancement to TL to allow this behavior:
>   final static ThreadLocal<ByteBuffer> TL_BUFFER = new
> ThreadLocal<ByteBuffer>() {
>     @Override
>     protected ByteBuffer initialValue() {
>       return ByteBuffer.allocateDirect(4096);
>     }
>     @Override
>     protected void discard(ByteBuffer bb) {
>       ((sun.nio.ch.DirectBuffer)bb).cleaner().clean();
>     }
>   };
>
> The discard method will be called on thread exit.
> Am I missing an already existing way of achieving the same? Does this
> functionality somehow not sit well with existing APIs?
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
>
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Nitsan Wakart-2
 > I think the only "real" solution to this problem (and to some other
problems with imprompt cleanup such as in ClassValue API) is adding
Ephemeron support to JVM.
I particularly do not want to have this handled on the GC schedule.
Thread exit is the right time to release a thread resources. A GC may
take place in the very distant future, but especially for thread
lifetime sort of resources this will typically mean waiting for the next
old GC. The resources cleared may not be heap pressure related but
perhaps lookup or external resource pools which would benefit from a
prompt release.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Nitsan Wakart-2
In reply to this post by Jens Wilke
I appreciate the interest folks, but you have diverted to a somewhat
different feature than the one I'm asking for.
The feature I'm after is simply a ThreadLocal exit hook. It is
notionally similar to the init hook already in place and does not entail
threads exposing their TL contents to other threads/code.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Nitsan Wakart-2
In reply to this post by thurstonn
 > And how would you propose dealing with the case where the ThreadLocal
"referent" (ByteBuffer in your case) is strongly reachable outside the
exiting thread's ThreadLocalMap?

I suggest we don't deal with it. If we limited ourselves to features
which cannot be abused we wouldn't have a programming language.
The method is optional, and the default implementation will be empty,
the same way init is.
IMO this is no more risky than leaving it to the user discretion to use
TL get() and at their discretion check/not check for null.
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: Thread local resource management feature request/discussion

Peter Levart
In reply to this post by Nitsan Wakart-2
Hi Nitsan,


On 06/10/2016 11:41 AM, Nitsan Wakart wrote:

> > I think the only "real" solution to this problem (and to some other
> problems with imprompt cleanup such as in ClassValue API) is adding
> Ephemeron support to JVM.
> I particularly do not want to have this handled on the GC schedule.
> Thread exit is the right time to release a thread resources. A GC may
> take place in the very distant future, but especially for thread
> lifetime sort of resources this will typically mean waiting for the
> next old GC. The resources cleared may not be heap pressure related
> but perhaps lookup or external resource pools which would benefit from
> a prompt release.

I was talking about the other issue that Lucene is trying to solve with
CloseableThreadLocal - the case where ThreadLocal instance is GCed, but
the Thread is still alive and keeps executing code. I agree with your
point in extending the ThreadLocal API to allow for a discard action. If
you have control over Thread creation (for example if you can supply
your own ThreadFactory to some executor) then you can do that on-top of
existing API. For example:


public abstract class DiscardableThreadLocal<T> extends ThreadLocal<T> {
     private static final ThreadLocal<Map<DiscardableThreadLocal<?>,
Boolean>> DTLS =
         new ThreadLocal<>() {
             @Override
             protected Map<DiscardableThreadLocal<?>, Boolean>
initialValue() {
                 return new IdentityHashMap<>();
             }
         };

     @Override
     protected final T initialValue() {
         T value = initialValueImpl();
         // register if not registered yet
         DTLS.get().putIfAbsent(this, Boolean.TRUE);
         return value;
     }

     @Override
     public void set(T value) {
         // register if not registered yet
         DTLS.get().putIfAbsent(this, Boolean.TRUE);
         super.set(value);
     }

     @Override
     public void remove() {
         super.remove();
         // deregister if registered
         DTLS.get().remove(this);
     }

     T getImpl() {
         return super.get();
     }

     // to be implemented by subclasses

     protected abstract T initialValueImpl();

     protected abstract void discardValue(T value);

     // a ThreadFactory that discards DiscardableThreadLocal(s) when
threads exit
     public static class DiscardingThreadFactory implements ThreadFactory {
         private final ThreadFactory factory;

         public DiscardingThreadFactory(ThreadFactory factory) {
             this.factory = factory;
         }

         @Override
         public Thread newThread(Runnable r) {
             return factory.newThread(() -> {
                 try {
                     r.run();
                 } finally {
                     for (DiscardableThreadLocal<?> dtl :
DTLS.get().keySet()) {
                         @SuppressWarnings("unchecked")
                         DiscardableThreadLocal<Object> dtlo =
(DiscardableThreadLocal) dtl;
                         dtlo.discardValue(dtlo.getImpl());
                     }
                     // release from exiting thread
                     DTLS.remove();
                 }
             });
         }
     }

     // example usage
     public static void main(String[] args) throws InterruptedException {
         DiscardableThreadLocal<String> tl = new
DiscardableThreadLocal<>() {
             @Override
             protected String initialValueImpl() {
                 return "garbage";
             }

             @Override
             protected void discardValue(String value) {
                 System.out.println("discarding: " + value);
             }
         };

         ExecutorService exe = Executors.newCachedThreadPool(
             new DiscardingThreadFactory(Executors.defaultThreadFactory()));

         exe.submit(() -> {
             System.out.println("observing: " + tl.get());
         });

         exe.shutdown();
         exe.awaitTermination(5, TimeUnit.SECONDS);
     }
}


... will print:

observing: garbage
discarding: garbage


Note that in order to guarantee that discardValue() gets called on
thread-exit for all initialized/set/but-not-removed associated values,
DiscardableThreadLocal(s) are retained for the lifetime of any thread,
created by DiscardingThreadFactory that associated a value in
combination with them. This is typically not a problem unless such
DiscardableThreadLocal subclasses also retain a ClassLoader you want to
be GCed before the mentioned threads exit.

This could be "fixed" by using a WeakHashMap instead of IdentityHashMap
in above code and ensuring that DiscardableThreadLocal subclasses don't
override equals/hashCode by making them final. This would nullify the
guarantee that discardValues() is called though, if
DiscardableThreadLocal instances are GCed before thread(s) exit.

Regards, Peter

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
12