-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GC sensing via virtual-object method identity #4892
Comments
For #2069 auxdata, since the object graph we're building is immutable and behavior-free, we can enumerate all the individual (now, would this help if they put the sub- To manage something like this with a virtual object, where new sub-objects could be created by behavior at any time, we'd probably need some sort of membrane to spot them all. Double ugh. Oh, hey, the same issue probably exists with the |
I have an idea, but it's too hazy to write down yet. We should talk. |
Where is that? |
Any method on a Presence Hm, and another concern is whether |
Yes, it is. And it does cause this problem. This proto does have a |
For both Presences and Representatives, let's use the term "instance" to talk about the JS We need to prevent userspace from distinguishing Instance 1 from Instance 2, otherwise userspace can use this to sense when GC happens, and we've decided we don't want to allow that. The tools that userspace has to work with are PresencesHere's what the situation looks like for Presences: The red rounded rectangles are unique The prototype that let remotePresence;
const p = new HandledPromise((_res, _rej, resolveWithPresence) => {
// Use Remotable rather than Far to make a remote from a presence
remotePresence = Remotable(
iface,
undefined,
resolveWithPresence(fulfilledHandler),
); The Presence object doesn't cause a problem: userspace can't use The prototype is a problem, because they could hold I think we can fix this by adding an extra WeakMap and doing |
I see 2 kind of objects tied to presences / representatives:
For the former we can add those objects to a Set held as value in a WeakMap keyed by the presences/representatives. The problem is mainly figuring out what is unique and stopping at what is shared. The latter should be prevented by making sure representatives are a pure function of the virtual object's state. |
Would it work for |
Representatives / FacetsHere's the situation for virtual objects (which either produce a single Representative, or a cohort of "Facets" which are Representatives that share a In this case, each method (the We use a few WeakMaps to ensure that any facet of the cohort will keep the cohort alive, and since the cohort is a record of all facets, the reverse is also true. We also ensure that anyone keeping Each Facet has a vref, so the The methods are known to us during deserialization: we could conceivably walk the facet's properties and establish a WeakMap link from each to the parent Facet object, causing them to share a lifetime. The big sticking point is the "other objects created during The best idea I've had so far (and it's a horrible one) is to impose a Membrane around the facets, and use it to establish a WeakMap link from every object the facet ever returns, to the facet itself. This might provide the GC-insensitivity that we need, but might keep a bunch of other objects alive for longer than they need to be (which might be mitigated by the fact that we expect Representatives to be fairly short-lived, since they're supposed to be virtual anyways). I've wondered if there's some way to constrain the definition of a Representative that could prevent the creation of (or reference to) additional objects by the behavior. Like, if the behavior was defined by a batch of functions which all take a That would be quite distant from the objects-as-closures pattern, but it would prevent userspace from getting control during deserialization, and thus give us complete control over the unique objects associated with each instance. Then we can WeakMap-link them together and force their lifetimes to be the same. |
Yeah, although then we need an in-RAM I've seen Presences with toStrings like |
Yeah... but. That'd lose some of the expressiveness of our current |
I'm confused because I thought I raised this issue months ago about makeKind enabling the representatives to close over state we couldn't control, creating exactly this kind of non-determinism and GC sensitivity issues. At that time I didn't understand fully how our distributed object system worked, so I probably had a difficult time conveying that concern. I thought that the virtual object as classes that @erights is working on was the way forward to solve this issue, exactly by doing what you suggest, which is making the declaration of the methods of the facets unique per kind, then either binding them to the state and record when creating the representatives, or passing that state as first arguments. In either case, that would nullify both concerns of closed over state per instance, and methods as unique related but gc independent objects. |
Scratch that, it isn't a complete fix. We're learning more about the set of constraints we need to meet:
We can force multiple objects to share a lifetime by establishing a full cycle of references among them. This happens naturally for some directions (Representatives hold strong refs to their method properties, the cohort record holds strong refs to the Represenatives), and we can use an internal We once considered having each act of deserialization return a distinct Representative, which would make the comparison (and that of all its components) Userspace can also perform comparison between instances/components with We identified this problem for Presences and Representatives a while ago, and attempted to mitigate it by replacing the userspace-visible However, this doesn't prevent WeakSet-predicate -based distinguishing between the extra components of the Presences/Representatives. For example, userspace could extract the unique prototype object from a Presence, and compare it later: const ws = new WeakSet();
function one(pres) {
ws.add(pres);
ws.add(Object.getPrototypeOf(pres));
}
// time passes, GC happens or does not
// caller sends the same vref
function two(pres) {
ws.has(pres); // always true, because WeakSet is vref-aware
if (ws.has(Object.getPrototypeOf(pres)) { // varies
console.log(`GC did not happen`);
} else {
console.log(`GC *did* happen`);
}
} To mitigate this, I think we need one of the following:
The former might be possible. The latter would be pretty awkward to explain to userspace, "sorry, we broke WeakMap a little bit because mumble mumble security". |
@FUDCo came up with #4905 (comment) to deal with this in a third way: hold those other components strongly iff they're used as a key in a |
Here's a picture of the protective mechanisms we'll have: If userspace defines any methods at all, those methods will be bound (using some If userspace does not define any methods, the first The things we need to add / make sure are present:
Userspace might provide Functions in We should include a comment on the code that creates the context argument that every object we add to it must be marked as |
@FUDCo implemented his suggestion by adding We also add the prototype of Kind handles (both durable and virtual). Since we changed Kind definition (#4905) to provide a bunch of functions, we no longer invoke user code when rebuilding a Representative, which removes some of the original concerns. I don't think we can close this ticket yet. I need to do a thorough scan, but we're at least not yet adding the prototype of plain Presences. I'm guessing that everywhere in liveslots that does a |
@erights I think we still need to do an audit for this. I'm looking at agoric-sdk/packages/SwingSet/src/liveslots/liveslots.js Lines 384 to 388 in 97f8d1d
where we use Also, I wasn't involved in the Far Classes transformation. I suspect they provide fewer troublesome objects than the original design, but we should walk through and make sure that any per-instance objects are properly marked |
Btw, it occurs to me that in #5000 where I talk about GC sensing through userland WeakMap based on return override + private field stamping, the solution I propose relies on the ability to explicitly create all objects that should be prevented from such trick. The problem is that this wouldn't be compatible with any functions, as you can mint those through |
Having recently overhauled the VOM LRU cache and |
While reviewing #4618, I think I spotted a problem with our plan to prevent userspace from sensing GC. Imagine a stripped-down Purse kind:
Now imagine that userspace does:
If GC does not happen, and the p1kit cohort is still in memory, the
virtualCollection.get()
will call the actualizer (so it can't sense GC by counting invocations) and throw away its value, and p2kit will be the same object as p1kit (although userspace threw away p1kit so it can't sense this directly). In this case, rx2 will be the same as rx1.If GC did happen, we'll still have the deposit method, but the purse (and indeed the whole cohort) will be gone. So the
vc.get()
will use the actualizer value, which will have a newdeposit
method, which will not be the same as rx1. This would mean userspace can sense GC when it's supposed to be oblivious to it.And, now that I write it, we don't need multiple facets at all: the same problem would occur with just
purse
.We established a weakmap from each Representative to the cohort, so that userspace holding on to e.g.
p1kit.purse
would be sufficient to keep the wholep1kit
cohort in memory, preventing them from using this to sense GC. But that doesn't help for interior values.I'm not seeing an obvious way to fix this. Any new object created by actualize might be comparable, and I don't think we have a way for
makeKind
to find them all (and use them as WeakMap keys to keep the cohort alive).This feels a bit like the "every Presence is unique and userspace shouldn't ever compare them" world we were in a year ago, which we escaped by preventing userspace from using distinct Presence
Object
s as WeakMap keys (theinescapableGlobals.WeakMap = VrefAwareWeakMap
trick). But those Presences didn't have internal structure that could be used for identity comparison. And, now that I think about it, #2069 auxdata might open up the same sort of problem on Presences.cc @erights @FUDCo
The text was updated successfully, but these errors were encountered: