RFR: 8209437: Mechanism for decoupling consumers and providers of behaviours

Roman Kennke rkennke at redhat.com
Wed Aug 22 09:51:06 UTC 2018

Maybe it would be good to start from the beginning. What problem are you
trying to solve and how to you propose to solve it. I don't yet have a
clear picture how everything fits together, but so far it smells a
little like an anti-pattern.

Thank you,

> Hi David,
> I don't know if this pattern does have a name. The closest resembling
> pattern is a chain of responsibility. But this is more like a layered or
> stacked chain of responsibility, with thread-local endpoints. It is a
> pattern I have used in many projects of my own, that I have always found
> useful.
> I will try to give a few examples.
> 1) Unloading nmethods in serial, parallel and concurrent contexts.
> This is for me the most pressing concrete use-case right now. Today we
> have one mechanism for serial nmethod unloading triggered by GC. We have
> another mechanism for parallel nmethod unloading, and now I'm building
> yet another one for concurrent unloading. What I want to do here is to
> rather than reinventing the wheel 3 times, find one nmethod unloading
> mechanism that can be called from serial, parallel and concurrent contexts.
> A preview (not necessarily final) of how this can be done with
> behaviours for unifying the serial and parallel cases can be found here:
> http://cr.openjdk.java.net/~eosterlund/concurrent_class_unloading/webrev.01_03_concurrent_do_unloading/
> Subsequently, the concurrent nmethod unloading mechanism will re-use the
> same nmethod cleaning code when we are soon introducing concurrent class
> unloading. A preview with said behaviour used (again, very much a work
> in progress) can be found here:
> http://cr.openjdk.java.net/~eosterlund/concurrent_class_unloading/webrev.01_05_class_unloading/
> In particular, I provide a new function on nmethod called is_unloading()
> that basically lazily computes if the nmethod has broken oops in it.
> Whoever calls it first computes if it is unloading, and subsequent calls
> use the cached value of what that computation was.
> In order to compute is_unloading() lazily, a BoolObjectClosure must be
> available answering the question "is this oop phantomly alive?". And I
> want this code to not care about howsoever the GC provides the answer in
> any given context.
> a) In the serial context, the CodeCache::do_unloading() call will
> locally provide the behaviour using its passed in OopClosure.
> b) In the parallel context, the VM thread sets up a local behaviour for
> this based on the is_alive closure used in the parallel cleanup task.
> The worker  threads can call do_unloading() on nmethods, because they
> inherit the context of the VM thread's local scope.
> c) In the concurrent context, the IC cache cleaning code will also be
> called by the sweeper and JavaThreads running concurrent to unloading.
> In this case, ZGC will provide a global phantom is_alive behaviour that
> can be called from all these contexts.
> So as you can see, the goal is to make the is_unloading() function that
> will be called in quite a few contexts not have to care about *how* the
> phantom is_alive behaviour is available in all contexts it is called
> from, similar in a way, to how we can call
> NativeAccess<ON_PHANTOM_OOP_REF> in many contexts without having to know
> how that behaviour is provided by the GC in that context.
> 2) Untangling shared GC code
> Different GCs have slightly different ways of tracking things like time
> and statistics due to different requirements. This sometimes makes it a
> nightmare to write code to be shared between different GCs, because
> anything called that wants to track some time or something, needs to be
> provided with GC-specific context information, using abstractions that
> are not shared. With this approach, it is much easier to decouple the
> callsite that tracks sime time or statistics, to the provider of context
> information required for it. In particular, we are considering using
> this for the ZStatCounter.
> I hope these concrete usages makes it seem more attractive. Again, I do
> not intend for this to be the silver bullet solution to be abused in all
> code with variation points.
> Thanks,
> /Erik
> On 2018-08-20 08:55, David Holmes wrote:
>> Hi Erik,
>> Sorry but I find this so abstract I can't quite determine what it is
>> exactly that you are doing. I suspect I know this pattern under some
>> other name/terminology but so far, just looking at the code, I can't
>> connect the dots. Could you give some concrete examples of usage please.
>> Thanks,
>> David
>> On 14/08/2018 3:05 AM, Erik Österlund wrote:
>>> Hi,
>>> Sometimes we find ourselves passing around context information about
>>> how to perform a certain operation (e.g. check if an oop is alive,
>>> logging or tracing, etc). Sometimes such behaviours are provided
>>> globally by a GC, and sometimes only in local scopes known by the GC.
>>> Sometimes it is even accessed from mutators.
>>> It would be great to have a general mechanism for decoupling how
>>> behaviours are provided, from the code that uses them.
>>> In particular, I will need this mechanism to build a new nmethod
>>> unloading mechanism for concurrent class unloading. Today we have a
>>> single threaded and a parallel nmethod unloading mechanism. Rather
>>> than introducing a third concurrent way of doing this, I would like
>>> to unify these mechanism into one mechanism that can be used in all
>>> three contexts. In order to get there, I need these utilities in
>>> order to not make a mess. I have a bunch of other use cases down the
>>> road as well.
>>> The ideas behind this mechanism are pretty straight forward.
>>> Behaviours are provided in different ways by "behaviour providers".
>>> The providers may be global and local, but come in a strict layering
>>> (each provider has a parent). So from a given callsite, there is a
>>> chain of responsibility with behaviour providers. You can use
>>> BehaviourMark to provide a behaviour locally in a certain scope.
>>> There are also global behaviours to which a GC at bootstrapping time
>>> can add behaviours. If no local behaviour was found, the global
>>> behaviours are checked as plan B. In order to speed up the walk, the
>>> scoped behaviour providers also come with a lazily populated
>>> behaviour provider cache that will take you straight to a given
>>> provider, effectively skipping through the search through the chain
>>> of responsibility.
>>> Bug:
>>> https://bugs.openjdk.java.net/browse/JDK-8209437
>>> Webrev:
>>> http://cr.openjdk.java.net/~eosterlund/8209437/webrev.00/
>>> Thanks,
>>> /Erik

More information about the hotspot-runtime-dev mailing list