Get libffi closures to cope with SELinux execmem/execmod
Alexandre Oliva
aoliva@redhat.com
Fri Jan 26 08:11:00 GMT 2007
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 Java-patches
mailing list