jruby/jruby — pull request #1391 by phs (2014-01-10 07:35:58+00:00)
Title: Phs/issue 1388 leaky thread locals

Thread-local and concurrent contexts can nearly reclaim thread locals. Java unfortunately offers no official way to clean up thread locals without having each thread that owns a copy execute `remove()`. When JRuby is embedded in an application as a plugin in a larger system, this can be hard or impossible to arrange. In this scenario `ConcurrentLocalContextProvider` and `ThreadSafeLocalContextProvider` are basically forced to leak their thread locals.

However, this does not mean they must leak the referenced `LocalContext` objects. If those could be reclaimed, then the lion's share of resources would be garbage-collectable, even if the thread locals themselves leak. Importantly, this would also allow the relevant jruby classloader to be collected to enable hot-unloading in embedding/plugin scenarios.

By wrapping each `LocalContext` in an explicit, mutable reference and tracking those references, the providers can still make the contexts unreachable without the help of other threads. This is what we do here.

In each provider:
* `ThreadLocal` becomes `ThreadLocal>`
* A `volatile` `ConcurrentLinkedQueue>>` is added to accumulate new `AtomicReference` instances as they are created
* `terminate()` walks this collection, cleaning up each context and setting its owning `AtomicReference` value to `null`.

The `volatile`, combined with setting the container to null, takes care of a race condition between `initialValue()` and `terminate()`. Weak references are used to prevent the collection from accumulating `LocalContext`s after the owning threads die.

`ConcurrentLocalContextProvider` has also become a subclass of `ThreadSafeLocalContextProvider`.

See #1388