I support the allocation-scheme approach. And I like the type-safe wrapper approach, at least in theory. We can actually do both, and they would complement each other: when you go from int->wrapper, it's a natural place to enforce the allocation scheme.For the wrapper types to be worth it, we must come up with something simple enough to be easily understood, but comprehensive enough to express all the weird things that happen with routing_ids and process_ids in the code today.
Some open questions I have:- Do we use different wrapper types on the browser/ and renderer/ side (e.g. keyed to RenderFrame vs. RenderFrameHost)? What about in common/?
- Will it be possible to use wrapper types in IPC messages?
The ctor of a IPC::Message classes is the place where we most commonly use routing ids, so it would be natural to add safety there. Can we do this without touching every IPC message declaration? How does it work?
- If we use wrapper types in IPC messages, what about messages that need to be handled by more than one type of object? How much IPC magic will we need to add for that?- Will we be able to define a FrameOrProxyRouteID wrapper type, that can hold a value of more than one flavor, for cases like this? If so, is it still just an int wrapper, or is it some kind of tagged union?- For classes like MessageRouter, that accept all kinds of routing ids, can we meaningfully switch to type-safe wrappers?
- Will we support wrappers for different child_process_id types as well (renderer, extension, nacl, utility), or is that a non-goal?
- Do we expose a routing pair <process_id, route_id> pattern as well, or let people keep rolling their own as needed?
Also, I would suggest not working in base/ to begin with; we'll have more flexibility if we incubate the idea in content/, and hoist it out for general use once it's proven itself there. We don't have a solid case for base/ inclusion yet.
On Fri, Dec 11, 2015 at 1:15 PM, Nick Carter <nca...@google.com> wrote:I support the allocation-scheme approach. And I like the type-safe wrapper approach, at least in theory. We can actually do both, and they would complement each other: when you go from int->wrapper, it's a natural place to enforce the allocation scheme.For the wrapper types to be worth it, we must come up with something simple enough to be easily understood, but comprehensive enough to express all the weird things that happen with routing_ids and process_ids in the code today.I am not sure I agree. It would be great if we had some way of expressing relationships between different id types (i.e. render_frame_proxy_routing_id VS render_frame_routing_id VS render_view_routing_id), but I believe that a simple, separate id type that has not relationship to other id types is beneficial on its own.
Some open questions I have:- Do we use different wrapper types on the browser/ and renderer/ side (e.g. keyed to RenderFrame vs. RenderFrameHost)? What about in common/?- Will it be possible to use wrapper types in IPC messages?Yes - it would be possible to use the wrapper type in *payload* of IPC messages - see https://codereview.chromium.org/1496103002/patch/1/10032.
The ctor of a IPC::Message classes is the place where we most commonly use routing ids, so it would be natural to add safety there. Can we do this without touching every IPC message declaration? How does it work?That is a good question. OTOH, does not knowing the answer for routing ids mean that we can't have base::IdType-based SavePackageId?
- If we use wrapper types in IPC messages, what about messages that need to be handled by more than one type of object? How much IPC magic will we need to add for that?- Will we be able to define a FrameOrProxyRouteID wrapper type, that can hold a value of more than one flavor, for cases like this? If so, is it still just an int wrapper, or is it some kind of tagged union?- For classes like MessageRouter, that accept all kinds of routing ids, can we meaningfully switch to type-safe wrappers?- Will we support wrappers for different child_process_id types as well (renderer, extension, nacl, utility), or is that a non-goal?I think that for cases like above we can only rely on runtime checks to see if a generic routing_id is of a specific/narrower flavor (i.e. what you are asking for is like a dynamic_cast<...> from a base class to a specific class and this cannot be statically verified).
- Do we expose a routing pair <process_id, route_id> pattern as well, or let people keep rolling their own as needed?Maybe. Does that decision depend on whether process_id is a raw int vs a separate id type?
Also, I would suggest not working in base/ to begin with; we'll have more flexibility if we incubate the idea in content/, and hoist it out for general use once it's proven itself there. We don't have a solid case for base/ inclusion yet.To support pickling into / out-of IPC message payload (required by cc::SurfaceId), we can do the following if base::IdType is in base: https://codereview.chromium.org/1496103002/patch/1/10032
We wouldn't be able to do this if base::IdType were in //content (because of no //ipc -> //content DEPS allowed).
On Fri, Dec 11, 2015 at 3:41 PM, Łukasz Anforowicz <luk...@google.com> wrote:On Fri, Dec 11, 2015 at 1:15 PM, Nick Carter <nca...@google.com> wrote:I support the allocation-scheme approach. And I like the type-safe wrapper approach, at least in theory. We can actually do both, and they would complement each other: when you go from int->wrapper, it's a natural place to enforce the allocation scheme.For the wrapper types to be worth it, we must come up with something simple enough to be easily understood, but comprehensive enough to express all the weird things that happen with routing_ids and process_ids in the code today.I am not sure I agree. It would be great if we had some way of expressing relationships between different id types (i.e. render_frame_proxy_routing_id VS render_frame_routing_id VS render_view_routing_id), but I believe that a simple, separate id type that has not relationship to other id types is beneficial on its own.
Some open questions I have:- Do we use different wrapper types on the browser/ and renderer/ side (e.g. keyed to RenderFrame vs. RenderFrameHost)? What about in common/?- Will it be possible to use wrapper types in IPC messages?Yes - it would be possible to use the wrapper type in *payload* of IPC messages - see https://codereview.chromium.org/1496103002/patch/1/10032.The ctor of a IPC::Message classes is the place where we most commonly use routing ids, so it would be natural to add safety there. Can we do this without touching every IPC message declaration? How does it work?That is a good question. OTOH, does not knowing the answer for routing ids mean that we can't have base::IdType-based SavePackageId?I see your point, but this only belongs in base if it's generally useful. I think routing ids are a good litmus test for that. If it doesn't work for routing_ids, I'm not necessary opposed to it: I just care a lot less about it.
- If we use wrapper types in IPC messages, what about messages that need to be handled by more than one type of object? How much IPC magic will we need to add for that?- Will we be able to define a FrameOrProxyRouteID wrapper type, that can hold a value of more than one flavor, for cases like this? If so, is it still just an int wrapper, or is it some kind of tagged union?- For classes like MessageRouter, that accept all kinds of routing ids, can we meaningfully switch to type-safe wrappers?- Will we support wrappers for different child_process_id types as well (renderer, extension, nacl, utility), or is that a non-goal?I think that for cases like above we can only rely on runtime checks to see if a generic routing_id is of a specific/narrower flavor (i.e. what you are asking for is like a dynamic_cast<...> from a base class to a specific class and this cannot be statically verified).Are you suggesting we would leave them as ints, then?- Do we expose a routing pair <process_id, route_id> pattern as well, or let people keep rolling their own as needed?Maybe. Does that decision depend on whether process_id is a raw int vs a separate id type?There's not a hard dependency, but if we're touching all routing ids anyway, it makes sense to consider unifying this too. I was only asking whether we should.Also, I would suggest not working in base/ to begin with; we'll have more flexibility if we incubate the idea in content/, and hoist it out for general use once it's proven itself there. We don't have a solid case for base/ inclusion yet.To support pickling into / out-of IPC message payload (required by cc::SurfaceId), we can do the following if base::IdType is in base: https://codereview.chromium.org/1496103002/patch/1/10032How does pickling work if TypeBeingIdentified only exists on one side of the IPC pipe (e.g. it's a browser-only object like RenderFrameHost)?
We wouldn't be able to do this if base::IdType were in //content (because of no //ipc -> //content DEPS allowed).I missed why we are even targeting cc::SurfaceId initially? It already has some level of type safety.
Moreover, you can define ParamTraits anywhere, not just in //ipc. E.g. content/common/content_param_traits.h
The ctor of a IPC::Message classes is the place where we most commonly use routing ids, so it would be natural to add safety there. Can we do this without touching every IPC message declaration? How does it work?That is a good question. OTOH, does not knowing the answer for routing ids mean that we can't have base::IdType-based SavePackageId?I see your point, but this only belongs in base if it's generally useful. I think routing ids are a good litmus test for that. If it doesn't work for routing_ids, I'm not necessary opposed to it: I just care a lot less about it.I'm a huge supporter of TypeId.I think the solution allows for incremental use of type-safe ids. I.e. they can be wrapped/unwrapped as needed on API boundaries.I think it's tremendously useful for routing ids - especially for routing ids actually - we can adopt them in many places without necessarily changing APIs (at the cost of locally wrapping/unwrapping), and progressively adding type safety in APIs, removing the wrapping/unwrapping churn. I.e. no need to boil the ocean :)
How does pickling work if TypeBeingIdentified only exists on one side of the IPC pipe (e.g. it's a browser-only object like RenderFrameHost)?TypeBeingIdentified is just a type discriminator - it's not used for anything, it doesn't even need to be complete - just forward declared. We could just define an empty RenderFrameIdTypeDiscriminator struct in content/common if using RenderFrame or RenderFrameHost is somehow controversial.
Moreover, you can define ParamTraits anywhere, not just in //ipc. E.g. content/common/content_param_traits.hIf IdType is defined in a place visible by ipc (i.e. pretty much base/ or ipc/), then a single template ParamTraits definition is needed for all instances of IdType.I guess we can either put IdType in base, or copy-pasta all over the place.
I'm not sure why there is this much opposition. Type safety proved useful for SurfaceId. There's evidence that routing ids would benefit from it as well. The proposed solution is incremental, has no run-time cost. In the end game, the complexity cost is virtually nil except at the id generation locus. Take a look at the SavePackageId CL for an excellent example - it doesn't change any code except using the type-safe type everywhere instead of the raw int.
On Mon, Dec 14, 2015 at 12:18 AM, Antoine Labour <pi...@chromium.org> wrote:The ctor of a IPC::Message classes is the place where we most commonly use routing ids, so it would be natural to add safety there. Can we do this without touching every IPC message declaration? How does it work?That is a good question. OTOH, does not knowing the answer for routing ids mean that we can't have base::IdType-based SavePackageId?I see your point, but this only belongs in base if it's generally useful. I think routing ids are a good litmus test for that. If it doesn't work for routing_ids, I'm not necessary opposed to it: I just care a lot less about it.I'm a huge supporter of TypeId.I think the solution allows for incremental use of type-safe ids. I.e. they can be wrapped/unwrapped as needed on API boundaries.I think it's tremendously useful for routing ids - especially for routing ids actually - we can adopt them in many places without necessarily changing APIs (at the cost of locally wrapping/unwrapping), and progressively adding type safety in APIs, removing the wrapping/unwrapping churn. I.e. no need to boil the ocean :)I, too, am strongly in favor of some kind of IdType for routing ids -- and I'm sorry if it came across differently! My questions were meant as questions, not arguments against. The currently proposed IdType doesn't seem to work for routing IDs end-to-end, so I'm trying to understand which shortcomings are inherent, and which are ones that we'll be able to iteratively improve.For example, creating a FrameOrProxyRoutingId or an generic-flavor RoutingId wrapper seems totally tractable, though I don't quite see how to use the proposed template for that. Making IPC message ctors accept IdType instead of int seems implausibly tough. One big gotcha is any case like FrameMsg_UpdateOpener, which is sent to both frames and proxies, so you'd either need the message ctor to accept a FrameOrProxyRoutingId argument, or duplicate the message definition.It's starting to look as if "int routing_id" is too baked into IPC::Message to be worth changing, unless somebody has a clever way to do it.
I've discussed this with Nick on Tuesday and:
- For now I'll try to repackage things into a CL that 1) adds id_type.h to //content rather than //base and 2) uses it to implement SavePackageId and SaveItemId. See: crrev.com/1529363006.
- Pros
- This seems less controversial than //base changes and consequently has a higher chance of landing.
- This will allow further experimentation with other id-types under //content
- This takes care of SavePackageId and SaveItemId which for me were the main motivation for going in this direction.
- Cons
- For now SurfaceId CL will be abandoned / SurfaceId will remain a separate implementation. There is a small risk that content::IdType<...> will evolve in a way that makes it difficult to adopt it in the future by SurfaceId
- Neutral
- There are some things in content::IdType<...> that are not needed by SavePackageId and/or SaveItemId (i.e. GetUnsafeValue, some map kinds), but 1) AFAIK unused template parts should be compiled out and 2) the code has reasonable unit tests coverage so shouldn't rot.
- For some scenarios, a hierarchy of id types might be desirable - for example: FrameRoutingId --is-a--> FrameOrFrameProxyRoutingId --is-a--> AnyRoutingId.
- I did some experimentation on Wednesday and classes derived from IdType<...> are possible, but are rather clunky around std::hash<T> (requiring each and every derived class to duplicate std::hash definition / partial specialization). I am guessing that we would hit similar clunkiness around ipc::ParamTraits<T>. OTOH, I did add some extra derived-class tests to id_type_unittest.cc.
- I think that even without a hierarchy, a single RoutingId type would still provide extra value (by making it distinct at compile-time from SavePackageId, SaveItemId and ProcessId [this is not just hypothetical :-) - all 4 kinds are used as parameters of SaveFileManager::OnSaveURL])
- In some scenarios it might be difficult to migrate to IdType-based approach in one shot. In such scenarios it might be desirable to temporarily allow implicit coercions (i.e. if GetRoutingId returns a strongly-typed IdType, then it might be nice if code passing the return value to Message constructors doesn't have to change and explicitly call GetUnsafeValue). OTOH, in scenarios where full adoption of IdType-based approach is possible (i.e. SavePackageId), introducing implicit coercions is undesirable. Therefore it seems to me that the decision to allow implicit coercions (both ways) should be left to individual id types and not included in the generic IdType<...> template. I understand that this is far from ideal :-/ - it means either deriving those relaxed-case-id-types from IdType<...> template (but then see std::hash trouble in the previous bullet) or defining such relaxed-case-id-types from scratch, by copy&pasting IdType<...> code.
On Fri, Dec 18, 2015 at 7:17 AM, Łukasz <luk...@chromium.org> wrote:I've discussed this with Nick on Tuesday and:
- For now I'll try to repackage things into a CL that 1) adds id_type.h to //content rather than //base and 2) uses it to implement SavePackageId and SaveItemId. See: crrev.com/1529363006.
- Pros
- This seems less controversial than //base changes and consequently has a higher chance of landing.
Ugh, that's unfortunate. Besides cc, here's another example of a fix for a bug that would not have existed if we had an IdType: https://codereview.chromium.org/1525273003 . IdType would have been needed in gpu/
content, cc, gpu... Heck, if we want to make a RoutingId type being understood natively by IPCs, it needs to be visible in ipc/ as well.There's a fundamental reason why this is useful in Chrome in general - we need different subsystems to refer to the same objects. Typically one uses pointers for that. However, when things like security boundaries, address spaces, VMs or even sometimes threads are involved, pointers aren't safe, and that's when we typically use ids + (hash)maps instead. This is pervasive in Chrome. Pointers express both identity and (static) type, so it is useful to add static typing to any id that effectively replaces a pointer.IMO that clearly meets the bar for base/.