RFR: 8209437: Mechanism for decoupling consumers and providers of behaviours
Erik Österlund
erik.osterlund at oracle.com
Wed Aug 22 08:43:30 UTC 2018
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