Get libffi closures to cope with SELinux execmem/execmod

Boehm, Hans hans.boehm@hp.com
Fri Jan 26 21:13:00 GMT 2007


How about the following variant of (d):

f) Register all Java finalizable objects with a finalize_mark_proc that
traces the class, but nothing else?

This effectively states that object finalizers need the corresponding
classes to stay around until the object finalizer has completed.  It
requires that we invent a new finalize_mark_proc, which might seem a bit
ad hoc in the GC code.  But the addition is pretty trivial, and fits
well into the current framework.

I think the performance impact should also be trivial.  (I think I
measured that adding a finalizer currently adds a factor of 11 or so to
allocation overhead.  This might increase it to 12 or 13.  If you care,
you're already in trouble.)

This in some sense violated the Java requirement for unordered
finalization.  But I think that's completely undetectable to the client
code.  I don't think we can get finalization cycles in this manner.
(And even if it were, we wouldn't really be violating the spec, I
suspect.)

Is there a reason this wouldn't work?

Hans


> -----Original Message-----
> From: Alexandre Oliva [mailto:aoliva@redhat.com] 
> Sent: Friday, January 26, 2007 12:11 AM
> To: Boehm, Hans
> Cc: Andrew Haley; green@redhat.com; gcc-patches@gcc.gnu.org; 
> java-patches@gcc.gnu.org
> Subject: Re: Get libffi closures to cope with SELinux execmem/execmod
> 
> On Jan 25, 2007, "Boehm, Hans" <hans.boehm@hp.com> wrote:
> 
> > Normally, the GC's intended usage model is that if I have 
> an object P 
> > whose finalizer accesses other referenced objects R, then I 
> register P 
> > with something like the "normal" finalization mark 
> procedure, so that 
> > the referenced objects R don't get finalized until P is 
> done with them.
> 
> Well, Java isn't like that, as you know.
> 
> > I think you are proposing to somehow have the objects R 
> know that they 
> > might be referenced by another finalizable object, and 
> hence postpone 
> > their own finalization by a cycle.  That seems 
> fundamentally backwards 
> > and brittle.  This just isn't R's business.
> 
> I think it actually is, to some extent, because we're trying 
> to fit this R in a world in which the normal finalization 
> mark procedure is not the rule, but the exception, and this 
> requires a stronger primitive to cancel out its negative 
> effects.  See below.
> 
> > Can't we just register the referring object (P, the class 
> in the real 
> > case, I think) correctly?
> 
> No.  Consider that:
> 
> - object O1 is an instance of class P, and that R is a 
> resource that must be disposed of when P is garbage-collected.
> 
> - both O1 and P have finalizers, and, because of the Java 
> finalization model, they can both run at the same time (O1 
> must not use normal semantics to keep P's finalizer from 
> running in parallel because this would violate Java 
> semantics, and it would have to apply to all Java objects, so 
> it's a non-starter)
> 
> - O1's finalizer flips a coin to decide whether or not to 
> create a new instance of P, say O2, which thus carries the 
> same finalizer code
> 
> 
> Now, let's look at the alternatives to try to model the 
> requirement that R must be disposed of no earlier than when P 
> is disposed of.
> 
> a) dispose of R in P's finalizer
> 
> O1 and P are found to be unreachable.  Their finalizers run.  
> O2 is created and makes P reachable again, but its finalizer 
> has already run and disposed of R.  Requirement not satisfied.
> 
> b) control R by a separate object that P points to, and 
> dispose of R in R's finalizer
> 
> b.1) P has Java finalization semantics
> 
> R's finalizer may run at the time O1 and P become 
> unreachable, so it breaks just like a.
> 
> b.2) P has normal finalization semantics
> 
> O1, P and R are found to be unreachable.  R is reachable from 
> P, so it's not finalized, but O1 and P are.  O2 is created 
> and makes P and R reachable again, but P no longer has a 
> finalizer.  O2, P and R are found to be unreachable, but 
> since P no longer has a normal finalizer, R is not marked, so 
> finalizers for O2 and R are run.  O3 is created and makes P 
> reachable again, but R has already been disposed of.
> Requirement not satisfied.
> 
> c) introduce additional levels of indirection between P and R
> 
> Just extend the b.2 scenario with additional instances of O, 
> each one taking down one level of finalizers, and it 
> eventually fails in just the same way.
> 
> d) assign new semantics to P such that it can keep R from 
> being finalized before P is collected
> 
> This would amount to registering some kind of special 
> do-nothing auto-reinstalled finalizer in P, that would 
> implement the semantics of marking objects reachable from it 
> for later disposal, but that wouldn't stop P from being 
> collected if it's not brought back to life by other finalizers.
> 
> d.1) check that P is still unreachable after running finalizers
> 
> Well, that might work, but it would amount to running markers 
> after finalizers to check that P didn't become reachable.  
> Either a full mark pass, or keeping track of all changes made 
> by finalizers and checking that they haven't added new 
> references to P.  Sounds like a non-starter to me.
> 
> d.2) check that P is not reachable from other finalizable objects
> 
> If nobody else can reach P, not even finalizable objects, 
> then no new references to it can be created, so it is safe to 
> dispose of it instead of marking it as reachable and running 
> its pseudo-finalizer one more time.
> 
> This is almost equivalent to d.1 in the sense that we use in 
> d.2 the mark pass of the next round as the post-finalization 
> mark pass of a mark/finalize-and-mark/sweep cycle in d.1.  
> The sweep ends up delayed by one round, which is probably acceptable.
> 
> e) assign new semantics to R that keeps it from being 
> finalized before P is collected
> 
> Apply the same logic of d.2, but without creating a 
> do-nothing pseudo-finalizer that needlessly delays the 
> collection of P to keep R alive long enough.  Apply the 
> special semantics to R, that holds the critical resource that 
> needs special treatment, rather than to the object that 
> happens to point to it, or to multiple such objects that 
> might dynamically point to it, directly or indirectly.
> 
> 
> Of course, once we have this new e) semantics, we could apply 
> it to P directly, but this would break the Java garbage 
> collection model when P is a class, so we're better off 
> splitting out the special semantics into a separate object 
> that can't possibly take part in cycles.
> 
> The beauty of this e) semantics is that, even in a world in 
> which the Java finalization semantics is present and widely 
> used, it enables an object to restore the semantics that it 
> can only be brought back to reachable state by its own 
> finalizer, because it knows no other finalizable objects hold 
> references to it.  This semantics is safe because then the 
> finalizer can install a new finalizer, if needed to handle 
> another round of finalization.  This semantics is essential 
> for finalizers that dispose of resources outside the control 
> of the garbage collector, and that must not be disposed of 
> while the object is still reachable.
> 
> 
> So I counter your claim that this isn't R's business.  It is 
> R's business to keep a resource available while R is still 
> reachable, and to dispose of it when R becomes no longer 
> reachable.  No other object can possibly take this 
> responsibility, unless they all do, propagating the "normal" 
> semantics back to all finalizable objects that directly or 
> indirectly point to R.
> 
> In fact, this was the semantics I first understood when you 
> wrote there was a risk that my first patch wouldn't work: per 
> normal semantics, an object can rely on not having its 
> finalizer if anyone else still points at it.  Java semantics 
> breaks this property, and we just can't restore it properly 
> from the points-to side with existing primitives, as 
> demonstrated in b.2 and c, so we can fix it either in all 
> points-to objects, or in the pointed-to object.
> 
> Since it's the pointed-to object that wants the normal 
> finalization semantics, it seems just natural to provide it 
> with means to do so, instead of engineering something that 
> would require unnatural smarts elsewhere.
> 
> It just so happens that the normal semantics falls apart in 
> the frontier between the Java semantics and the normal 
> semantics, and e) introduces a way to restore the normal 
> semantics when it's needed.
> 
> Arguably, e) could be *the* normal semantics when 
> GC_java_finalization is enabled, since I can hardly see that 
> it makes much sense otherwise.
> If the hole point of the normal semantics is to keep things 
> safe, I don't see that it makes sense to depend on the 
> semantics of objects that point to you to ensure the safety 
> you rely on or not.
> 
> But maybe there are cases in which this current relaxed 
> normal semantics make sense, even in the presence of Java 
> objects.  So I thought I wouldn't impose the burden of this 
> stricter normal semantics on them all.
> 
> 
> Does this make sense to you?
> 
> -- 
> Alexandre Oliva         http://www.lsd.ic.unicamp.br/~oliva/
> FSF Latin America Board Member         http://www.fsfla.org/
> Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
> Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}
> 



More information about the Gcc-patches mailing list