Mark S. Miller (2013-11-09T03:03:16.000Z)
domenic at domenicdenicola.com (2013-11-17T17:54:18.545Z)
there's a distributed race condition that's fixed by the use of importCount. Fixing it using post-mortem finalization creates a local race condition which is fixed by this test, not if the proxy is *still* alive, but if a new proxy has been installed as its reincarnation. For background, see [Two-Vat DAGC](http://www.erights.org/elib/distrib/captp/dagc.html) and [CapTP Ops: DeliverOp](http://www.erights.org/elib/distrib/captp/DeliverOp.html). Kenton Varda is implementing [a high performance adaptation of CapTP](https://github.com/kentonv/capnproto/blob/master/c++/src/capnp/rpc.capnp) as part of [Cap'n Proto](http://kentonv.github.io/capnproto/). (Kenton did Google's protobufs 2 and open sourced them before leaving Google to do this successor.) This CapTP-in-Cap'n Proto has essentially the same distributed gc logic for coping with the distributed race conditions, but in its C++ bindings, relies on local manual collection rather than observation of local GC, so it doesn't have the same local race condition. The distributed race condition is that VatA notices (by finalization of the proxy) that it has dropped all local references to its bobProxy for VatB's Bob object. So VatA sends a GCExportOp(i, importCount) (in original CapTP terminology) to VatB. However, in the meantime, a message from VatB to VatA contains a reference to VatB's Bob. When it serializes the references to Bob, it sees that Bob is already registered in its VatB-to-VatA exports table at index i, so it serializes the reference as an ImportDesc(i). (When it sends Bob the first time it sends a NewFarDesc instead.) The GCExportOp(i, importCount) from VatA-to-VatB might cross on the wire with the ImportDesc(i) from VatB to VatA, which is the distributed race condition issue. The solution is that every time VatB sends an ImportDesc(i) to VatA, it increments exportCount[i] in its VatB to VatA export table. Everytime VatA unserializes an ImportDesc(i), it calls the Import(i) function at http://wiki.ecmascript.org/doku.php?id=strawman:weak_references#distributed_acyclic_garbage_collection, which increments importCount[i] and returns a valid proxy registered in is VatA-from-VatB imports table at i. If the existing proxy still exists, it uses that one. Otherwise it creates a new one. When VatB receiver the GCExportOp(i, importCount) from VatA, it decrements its exportCount by that amount. If its exportCount reaches 0, then it knows it can null out entry i in its exports table, enabling local collection of Bob if there are no other local uses. Otherwise, it knows that it needs to keep Bob registered as exports[i]. The local race condition is that VatA's local GC might have collected bobProxy, therefore nulling out the proxyRef, and scheduled a notification of the handler that would send the GCExportOp. However, already queued on VatA ahead of the notification of the handler is the processing of the ImportDesc from VatB, so that gets processed first, creating a successor proxy at imports[i]. Finally, when the handler does get notified, it needs to check if such a successor has already been installed and is still alive. I know this is complex, but after many years of looking at it, I don't know how to make it any simpler. Consider this a challenge ;). (OTOH, after many years of use, I've come to have high confidence in it.) The Dec Src Network Object system has similar solutions to many of these distributed race condition problems. But they had a very different model of local concurrency (shared state concurrency with Tony Hoare monitors IIRC), so they had different local race condition problems.