-
Notifications
You must be signed in to change notification settings - Fork 261
Remove throwing/originating errors in expected scenarios (Lookup/TryLookup and Cancel) #1512
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
base: master
Are you sure you want to change the base?
Conversation
…me, make shouldOrigiante a template param.
…eakig change for usages of map that are not default (i.e. custom std::less) if so, might need to split up templates into separate template, or spin out into separate pr. changed bool to template parameter in hresult_error, as the char*-to-bool decaying conversion failed the old tests. also fixed runsettings, added note in readme.
1>G:\source\repos\cppwinrt\_build\x64\Debug\winrt\Windows.Foundation.Collections.h(868,51): error C3878: syntax error: unexpected token '>' following 'simple-type-specifier' 1>(compiling source file '/Class.cpp') 1> G:\source\repos\cppwinrt\_build\x64\Debug\winrt\Windows.Foundation.Collections.h(868,51): 1> missing one of: '(' '{' ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super interesting!
…g TryLookup impls, added test to verify this.
… existing hresult_cancelled
The PR title and description are extremely misleading. It's not “Avoid throwing/originating errors in expected scenarios” because straight Lookup failure is not an expected scenario. Should be “Remove RoOriginate from Lookup failure (usually expected)”. The PR description is too focused on TryLookup, which be a red herring for people investigating a Lookup regression. Should be "We are removing RoOriginate from the internal implementation of Lookup, which benefits TryLookup performance" and then describe how you're doing it. |
|
||
// Map implementations can implement TryLookup with trylookup_from_abi_t as an optimization | ||
struct trylookup_from_abi_t {}; | ||
inline constexpr trylookup_from_abi_t trylookup_from_abi{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinced that we need this. Can we just call TryLookup if it is present and returns std::optional<T>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was added at Ryan's suggestion, to make sure that we don't inadvertently create a new compiler error for possible custom IMap implementations that have their own TryLookup implementations that is incompatible with our usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems very unlikely right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i agreed with his suggestion as since is not a "breaking" change, any code that compiled before this change, should compile after this change. So if a custom IMap has TryLookup(key, extra args) or bool TryLookup(key, outval), we won't break compilation of their code by merely updating their cppwinrt versions.
strings/base_error.h
Outdated
struct hresult_canceled : hresult_error | ||
{ | ||
hresult_canceled(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_canceled, sourceInformation) {} | ||
hresult_canceled(hresult_error::avoid_originate_t) noexcept : hresult_error(impl::error_canceled, hresult_error::avoid_originate) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we add this to all of the hresult_* types? I don't know if slim_source_location
works with this consolidation
struct hresult_blah : hresult_error
{
template<typename...Args>
hresult_blah(Args&&...args) : hresult_error(impl::error_blah, std::forward<Args>(args)...) {}
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added test, and would that be a greater change that i would want to make in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it makes sense that if there's no origination, there's no logging. so i removed the error. As far as the greater consolidation, does that make sense to make that change in this PR? would it be too big a diff for this change?
add new unittest to test nullable lookup.
|
||
// Map implementations can implement TryLookup with trylookup_from_abi_t as an optimization | ||
struct trylookup_from_abi_t {}; | ||
inline constexpr trylookup_from_abi_t trylookup_from_abi{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems very unlikely right?
…, special-cased further by checking the typename.
This pull request is stale because it has been open 10 days with no activity. Remove stale label or comment or this will be closed in 5 days. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to decipher if there is also a test case that verifies that a map with a TryLookup lacking trylookup_from_abi_t
does not have its TryLookup called.
Fixes: #1511 #1376
TL;DR:
We are removing RoOriginate from the default winrt implementations of Map/MapView::Lookup, which improves TryLookup performance in the case of a key missing from the Map/View.
We are also adding a way to elide RoOriginate from cancellation calls, for cppwinrt implementors who use cancellation in non-exceptional circumstances.
Problem Statement:
The winrt::IMapView projection exposes Lookup (which throws on not found), and a TryLookup function (that returns an optional/null) as an optimization to avoid the throw. Both of these projections call ABI::IMap/View::Lookup, which is expected to return an HRESULT, which the projection either throws, or compares to E_BOUNDS and returns nullopt/nullptr. This is an optimization to avoid the possible RPC of doing both a HasKey and a Lookup.
The issue is that the winrt producer of IMapView, does not have such an optimization. it exposes the ABI::IMap/View as
winrt::impl::map_impl
, which wraps a winrt::IMap/View implementation. The default Map/View implementation iswinrt::map_view_base
. Therefore when map_impl forwards the Lookup call towinrt::map_view_base::Lookup
, is forced to throw an hresult_error, which thewinrt::impl::map_impl
, will catch, then convert to an HRESULT. This throw/catch is pessimistic, as it's an extra throw, which is a ~6000ns perf hit, and a RoOriginate, which is a ~15000ns hit, and affects performance of Lookup for all calls where Lookup fails. Thus it causes bad performance AND a bad developer/debugging experience due to the extraneous printfs from the RoOriginate.Similarly, when an async task is cancelled, the await_adapter is expected to throw, but the origination causes that same 15000ns perf hit, and an extra debug output while debugging.
Both of these cases are extraneous use of RoOriginate. Origination is supposed to inform the developer about unexpected scenarios such as misconfigured apps, or unexpected state, possibly with a string telling the developer what went wrong and how to fix it. A missing value in a Map is not a good use of RoOriginate, as there is no helpful string to add to the fact that a map didn't have the value, and the line number isn't helpful either, as the
map_view_base
is a common implementation for any cppwinrt app that uses the default map.Solution
The solution is to remove the throw/originate in both Lookup and TryLookup. We create an escape hatch in the
map_impl
ABI producer,TryLookup
, for implementers of IMap/View, such asmap_view_base
, to use.winrt::impl::map_impl
to know aboutTryLookup
and call it if it is presentwinrt::map_view_base
trylookup_from_abi
for the cancellation optimization
cancellation_token
to choose whether to create a winrt::hresult_cancelled with OR without an originationgeneral improvements
Verified:
output of the generation:
ran build_test_all.cmd, and ran tests in Visual Studio:
===============================================================================
All tests passed (2964 assertions in 94 test cases)
===============================================================================
All tests passed (55 assertions in 17 test cases)
===============================================================================
All tests passed (9 assertions in 1 test case)
===============================================================================
All tests passed (15 assertions in 3 test cases)
===============================================================================
All tests passed (11 assertions in 2 test cases)
===============================================================================
All tests passed (4765 assertions in 393 test cases)
===============================================================================
All tests passed (3 assertions in 1 test case)
===============================================================================
All tests passed (7 assertions in 1 test case)
In a simple perf test (done in the scratch app):
exception
TryLookup fails
with unordered_map implemenation
TryLookup fails