Can and when "mojo::OutgoingInvitation::Send" and "mojo::IncomingInvitation::Accept" block?

61 views
Skip to first unread message

Honglin Yu

unread,
Apr 26, 2021, 8:16:46 AM4/26/21
to chromium-mojo
Hi Chromium-Mojo,

I am trying to use Mojo for IPC between two non-chromium processes. I am using the fd (extracted from channel) passed by command line arguments to bootstrap the connection. But I am worrying whether the processes can be blocked by "mojo::OutgoingInvitation::Send" and "mojo::IncomingInvitation::Accept" due to some errors. For example,

1. What if when "mojo::OutgoingInvitation::Send" is run, the target process has already crashed (i.e. the target process specified by the pid does not exist).
2. What if when "mojo::IncomingInvitation::Accept" is run, the process sending the invitation has already crashed/disappeared.
3. What would happen if the "Send" or "Accept" have been delayed (the processes do not exit)? How would these affect the other process (will the other process be blocked)? If can be blocked, can we set some "timeout" on this?

Thanks!

Best,
Honglin

Ken Rockot

unread,
Apr 26, 2021, 12:43:06 PM4/26/21
to Honglin Yu, chromium-mojo
On Mon, Apr 26, 2021 at 5:16 AM 'Honglin Yu' via chromium-mojo <chromi...@chromium.org> wrote:
Hi Chromium-Mojo,

I am trying to use Mojo for IPC between two non-chromium processes. I am using the fd (extracted from channel) passed by command line arguments to bootstrap the connection. But I am worrying whether the processes can be blocked by "mojo::OutgoingInvitation::Send" and "mojo::IncomingInvitation::Accept" due to some errors. For example,

1. What if when "mojo::OutgoingInvitation::Send" is run, the target process has already crashed (i.e. the target process specified by the pid does not exist).

Send never blocks. If the target process crashes or fails to start properly, the pipe you got from AttachMessagePipe on the OutgoingInvitation will eventually see a disconnection event.

2. What if when "mojo::IncomingInvitation::Accept" is run, the process sending the invitation has already crashed/disappeared.

Accept blocks until a basic handshake completes, but if the inviting process is already gone it will detect this and return early. In any case you'll still get an IncomingInvitation object from which you can extract a message pipe, and the message pipe will eventually see a disconnection event as above.

3. What would happen if the "Send" or "Accept" have been delayed (the processes do not exit)? How would these affect the other process (will the other process be blocked)? If can be blocked, can we set some "timeout" on this?

Accept will block if the Send is delayed. There's no API support to set a timeout on this operation.

You can use OutgoingInvitation::SendAsync and IncomingInvitation::AcceptAsync to avoid any blocking operations, but they aren't supported in all platform/sandbox configurations.


Thanks!

Best,
Honglin

--
You received this message because you are subscribed to the Google Groups "chromium-mojo" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-moj...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-mojo/CAFstQzOFx8%2B0qD%2BU1mPfN_8VSsvUWpSue8u5D056dGz12_tvKA%40mail.gmail.com.

Honglin Yu

unread,
Apr 26, 2021, 4:53:02 PM4/26/21
to Ken Rockot, chromium-mojo
Thanks, Ken, this is *really* helpful! (Maybe we should add them to the comments of these functions)
 
1. What if when "mojo::OutgoingInvitation::Send" is run, the target process has already crashed (i.e. the target process specified by the pid does not exist).

Send never blocks. If the target process crashes or fails to start properly, the pipe you got from AttachMessagePipe on the OutgoingInvitation will eventually see a disconnection event.
This is great news! 

2. What if when "mojo::IncomingInvitation::Accept" is run, the process sending the invitation has already crashed/disappeared.

Accept blocks until a basic handshake completes, but if the inviting process is already gone it will detect this and return early. In any case you'll still get an IncomingInvitation object from which you can extract a message pipe, and the message pipe will eventually see a disconnection event as above.
Great news again!
 

3. What would happen if the "Send" or "Accept" have been delayed (the processes do not exit)? How would these affect the other process (will the other process be blocked)? If can be blocked, can we set some "timeout" on this?

Accept will block if the Send is delayed. There's no API support to set a timeout on this operation.

You can use OutgoingInvitation::SendAsync and IncomingInvitation::AcceptAsync to avoid any blocking operations, but they aren't supported in all platform/sandbox configurations.
It should be acceptable for us that "Accept" is blocking (and this case should be very unlikely because in the other process, the "Send" will be run immediately after the creating the new process). We may explore SendAsync and AcceptAsync in the future.

Thanks again!

Best,
Honglin

Honglin Yu

unread,
Jun 20, 2021, 7:56:45 PM6/20/21
to Ken Rockot, chromium-mojo
Hi Ken, may I ask a few follow-up questions as below?

1. What if when "mojo::OutgoingInvitation::Send" is run, the target process has already crashed (i.e. the target process specified by the pid does not exist).

Send never blocks. If the target process crashes or fails to start properly, the pipe you got from AttachMessagePipe on the OutgoingInvitation will eventually see a disconnection event.
 
1. (Just want to confirm) Is it true even the target process has not accepted the connection?
2. What if we already used the initial remote which can be used immediately after being bound, even if the connection has not been accepted? The main concern is, the callback used to "call" the remote will be dropped --- will this cause an error (e.g. callback being destroyed without being called)?
3. Is there any method to recover the callback in the message that has been queued and not sent? This is important to use because in our case, the callback actually originates from a third process (i.e. the process holding the remote is forwarding the request/callback to the target process). We want to use the callback to notify the third process if the mojo bootstrap failed.

Thanks!

Best,
Honglin

Ken Rockot

unread,
Jun 21, 2021, 10:35:03 AM6/21/21
to Honglin Yu, chromium-mojo
On Sun, Jun 20, 2021 at 4:56 PM Honglin Yu <hong...@google.com> wrote:
Hi Ken, may I ask a few follow-up questions as below?

1. What if when "mojo::OutgoingInvitation::Send" is run, the target process has already crashed (i.e. the target process specified by the pid does not exist).

Send never blocks. If the target process crashes or fails to start properly, the pipe you got from AttachMessagePipe on the OutgoingInvitation will eventually see a disconnection event.
 
1. (Just want to confirm) Is it true even the target process has not accepted the connection?

Yes
 
2. What if we already used the initial remote which can be used immediately after being bound, even if the connection has not been accepted? The main concern is, the callback used to "call" the remote will be dropped --- will this cause an error (e.g. callback being destroyed without being called)?

As long as the receiving process dies or otherwise discards the invitation endpoint, it will appear to the sending process like any other disconnection on the Remote. So disconnect handler will be invoked, and yes any pending reply callbacks will be dropped.
 
3. Is there any method to recover the callback in the message that has been queued and not sent? This is important to use because in our case, the callback actually originates from a third process (i.e. the process holding the remote is forwarding the request/callback to the target process). We want to use the callback to notify the third process if the mojo bootstrap failed.

If you want to guard the lifetime of a callback coming from elsewhere, it's recommended that you store the callback yourself instead of forwarding it directly to another Remote. You could e.g. assign it an ID and store it in a map. When the reply from the target process comes back you can forward the args to the original callback you stored for that request. Otherwise if the target disconnects you can perform some reasonable default invocation on all pending callbacks.

Another more convenient option is to wrap each callback such that it runs on destruction if necessary. Doing arbitrary work on destruction can bring many surprises though, and combined with the inherent type-erasure of callbacks I think this is an inferior option when explicit tracking is feasible. Still, the bindings provide two helpers for this kind of thing if you really want to go that route:

Thanks!

Best,
Honglin

Honglin Yu

unread,
Jun 21, 2021, 7:03:43 PM6/21/21
to Ken Rockot, chromium-mojo
As long as the receiving process dies or otherwise discards the invitation endpoint, it will appear to the sending process like any other disconnection on the Remote. So disconnect handler will be invoked, and yes any pending reply callbacks will be dropped.
Understood now. So may I confirm that if the receiving process freezes (before or after mojo calls, e.g. invitations or normal mojo calls) but not died, the disconnection handler will NOT be called (i.e. there is no timeout mechanism)? Sorry that this question may be a bit silly --- I just want to make it really clear :)

3. Is there any method to recover the callback in the message that has been queued and not sent? This is important to use because in our case, the callback actually originates from a third process (i.e. the process holding the remote is forwarding the request/callback to the target process). We want to use the callback to notify the third process if the mojo bootstrap failed.

If you want to guard the lifetime of a callback coming from elsewhere, it's recommended that you store the callback yourself instead of forwarding it directly to another Remote. You could e.g. assign it an ID and store it in a map. When the reply from the target process comes back you can forward the args to the original callback you stored for that request. Otherwise if the target disconnects you can perform some reasonable default invocation on all pending callbacks.

Another more convenient option is to wrap each callback such that it runs on destruction if necessary. Doing arbitrary work on destruction can bring many surprises though, and combined with the inherent type-erasure of callbacks I think this is an inferior option when explicit tracking is feasible. Still, the bindings provide two helpers for this kind of thing if you really want to go that route:
Thank you! Creating another callback is a good idea. And I didn't know WrapCallbackWithDropHandler stuff. I will investigate that. Thanks again!

Ken Rockot

unread,
Jun 21, 2021, 7:11:44 PM6/21/21
to Honglin Yu, chromium-mojo
On Mon, Jun 21, 2021 at 4:03 PM Honglin Yu <hong...@google.com> wrote:
As long as the receiving process dies or otherwise discards the invitation endpoint, it will appear to the sending process like any other disconnection on the Remote. So disconnect handler will be invoked, and yes any pending reply callbacks will be dropped.
Understood now. So may I confirm that if the receiving process freezes (before or after mojo calls, e.g. invitations or normal mojo calls) but not died, the disconnection handler will NOT be called (i.e. there is no timeout mechanism)? Sorry that this question may be a bit silly --- I just want to make it really clear :)

Yes

Honglin Yu

unread,
Jun 21, 2021, 7:13:31 PM6/21/21
to Ken Rockot, chromium-mojo
cool, thanks for the super quick response again, Ken!
Reply all
Reply to author
Forward
0 new messages