On Thu, Jul 20, 2017 at 4:02 AM, Thomas Leonard <[email protected]> wrote:
> I thought that must be the reason originally, but it seems that > takeFromOtherQuestion requires sharing even if it can only be used > once, because the struct is held by the original answer (for > pipelining) and also by the question that took it. > True, but this is a restricted case, and may still allow the implementation more freedom than general sharing would. For example, for pipelining purposes, technically the implementation only needs to keep the capabilities around, along with remembering their pointer paths. It doesn't otherwise need to remember the content of the response. > >> If you're implementing level 1 (two-party), then really the only place > >> where this applies is when you receive a capability that the receiver > hosts > >> as part of a return or resolve after you have made calls on the promised > >> capability. This implies that the RPC system needs to keep track of > which > >> parts of the answer have had calls made on them. When this occurs, the > >> receiver gives the application code an embargoed client, and then sends > a > >> Disembargo with senderLoopback set. It releases the embargo once the > same > >> disembargo ID is returned with receiverLoopback set. > > Maybe I got this bit wrong. I attached the "used" flags to the > question, but maybe I should be tagging the reference to the question > instead. Can different references to the same question need different > disembargoes? e.g. should forwarding a message mark the promised > answer as needing a disembargo or not? > Sorry, I don't understand your question here. > > Example: > > That example is straight-forward, but there are more complex cases > that are unclear to me. Here's one I'm not sure about: > > There are two vats, Client and Server, each of which starts with a > reference to the other's bootstrap service. All calls either return a > single capability (field-name `x`) or Void. > > 1. Client makes a call, q1, on the server's bootstrap object, getting > a promise a=q1.x > 2. Client makes another call, q2, on the same target, getting promise > b=q2.x. > 3. Server asks one question, q3 (c=q3.x) > 4. Client responds to q3 with a (the unresolved promised cap from its q1) > 5. Server responds to q1 with client_bs (the client's bootstrap > service, resolved) > 6. Server responds to q2 with c (q3.x, still unresolved) > 7. Client makes call m1 on b (sent to q2) > 8. Client receives response a=client_bs (no embargo needed) > 9. Client receives response b=q3.x, which is a. > This was q1.x at the time q3 returned, but client_bs now. Which > should it use? > If client_bs, it embargoes the target due to m1. > If not, b now points at the returned q1, which seems odd. 10. Client makes call m2 on b (which is then held at the embargo). > 11. Server receives response that c=q1.x (which is client_bs). > 12. Server receives m1 and forwards it to q3.x. > 13. Server sends disembargo response back to client. > 14. Client receives m1 and forwards it to q1 (the resolution it gave for > q3). > 15. Client disembargoes b and sends m2 to client_bs. > Nice example! It looks like the C++ implementation today will decide b = q1.x, and never allow it to further resolve to client_bs. This "works" but is clearly suboptimal. For a correct solution, we need to recognize that Disembargo messages can "bounce" multiple times: The disembargo sent in step 9 has a final destination of client_bs. In order to get there, it has to bounce back and forth between the client and server twice: * The client sends it towards q2.x. * The server, recognizing that it resolved q2.x to q3.x, reflects the embargo towards q3.x. * The client, recognizing that it resolved q3.x to q1.x, reflects back to the server again. * The server, recognizing that q1.x resolved to client_bs, finally reflects back to client_bs. This gives m1 enough time to arrive before the disembargo. It looks like this is not implemented correctly in C++ currently. It appears the C++ implementation ignores disembargo.messageTarget in the case that the Disembargo has type `receiverLoopback`. This is incorrect -- it needs to verify that the embargo has reached its final destination, not an intermediate promise. (However, it is "saved" by the suboptimal behavior mentioned above.) On another note, you say you found this with AFL, which is amazing. Could your fuzzing strategy be applied to the C++ implementation as well? -Kenton -- You received this message because you are subscribed to the Google Groups "Cap'n Proto" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. Visit this group at https://groups.google.com/group/capnproto.
