RESOLVED FIXED 131461
GenericHashTraits::peek() is producing copies of passed-in temporary values
https://bugs.webkit.org/show_bug.cgi?id=131461
Summary GenericHashTraits::peek() is producing copies of passed-in temporary values
Zan Dobersek
Reported 2014-04-09 15:23:21 PDT
Use an rvalue reference overload of GenericHashTraits::peek() for temporary values
Attachments
Patch (1.51 KB, patch)
2014-04-09 15:29 PDT, Zan Dobersek
no flags
Patch (1.44 KB, patch)
2014-04-20 10:55 PDT, Zan Dobersek
no flags
Patch (6.41 KB, patch)
2014-04-30 12:57 PDT, Zan Dobersek
no flags
Patch (6.41 KB, patch)
2014-08-23 01:02 PDT, Zan Dobersek
no flags
Zan Dobersek
Comment 1 2014-04-09 15:29:08 PDT
Benjamin Poulain
Comment 2 2014-04-12 13:51:28 PDT
I don't get why this is a good idea. The getters should return a reference or a copy. By returning a rvalue reference, the call site could modify the value in the hash table.
Zan Dobersek
Comment 3 2014-04-20 10:49:13 PDT
(In reply to comment #2) > I don't get why this is a good idea. The getters should return a reference or a copy. By returning a rvalue reference, the call site could modify the value in the hash table. The getters return an object of the PeekType type, which is never a reference. GenericHashTraits::peek() should simply pass through the value it receives through the parameter, much like std::forward<>(). Returning an rvalue reference here isn't a problem since it either gets RVO-ed or at worst produces a move constructor in the getter. I'll propose making GenericHashTraits::peek() a template that would deal with universal references, like std::forward<>().
Zan Dobersek
Comment 4 2014-04-20 10:55:19 PDT
Darin Adler
Comment 5 2014-04-20 20:38:00 PDT
Comment on attachment 229769 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=229769&action=review > Source/WTF/wtf/HashTraits.h:70 > + template<typename U> > + static U&& peek(U&& value) { return std::forward<U>(value); } I’d suggest doing this all on one line.
Benjamin Poulain
Comment 6 2014-04-20 21:20:10 PDT
(In reply to comment #3) > (In reply to comment #2) > > I don't get why this is a good idea. The getters should return a reference or a copy. By returning a rvalue reference, the call site could modify the value in the hash table. > > The getters return an object of the PeekType type, which is never a reference. GenericHashTraits::peek() should simply pass through the value it receives through the parameter, much like std::forward<>(). Returning an rvalue reference here isn't a problem since it either gets RVO-ed or at worst produces a move constructor in the getter. I still don't get how that would be correct. If the compiler generates a move constructor in the getter, the real value can be invalidated. That would not only break the hash map, but create security vulnerabilities.
Anders Carlsson
Comment 7 2014-04-21 11:10:45 PDT
(In reply to comment #6) > (In reply to comment #3) > > (In reply to comment #2) > > > I don't get why this is a good idea. The getters should return a reference or a copy. By returning a rvalue reference, the call site could modify the value in the hash table. > > > > The getters return an object of the PeekType type, which is never a reference. GenericHashTraits::peek() should simply pass through the value it receives through the parameter, much like std::forward<>(). Returning an rvalue reference here isn't a problem since it either gets RVO-ed or at worst produces a move constructor in the getter. > > I still don't get how that would be correct. > If the compiler generates a move constructor in the getter, the real value can be invalidated. That would not only break the hash map, but create security vulnerabilities. I don't think peek ever causes the move constructor to be invoked.
Benjamin Poulain
Comment 8 2014-04-21 12:59:30 PDT
(In reply to comment #7) > (In reply to comment #6) > > (In reply to comment #3) > > > (In reply to comment #2) > > > > I don't get why this is a good idea. The getters should return a reference or a copy. By returning a rvalue reference, the call site could modify the value in the hash table. > > > > > > The getters return an object of the PeekType type, which is never a reference. GenericHashTraits::peek() should simply pass through the value it receives through the parameter, much like std::forward<>(). Returning an rvalue reference here isn't a problem since it either gets RVO-ed or at worst produces a move constructor in the getter. > > > > I still don't get how that would be correct. > > If the compiler generates a move constructor in the getter, the real value can be invalidated. That would not only break the hash map, but create security vulnerabilities. > > I don't think peek ever causes the move constructor to be invoked. There is probably something I misunderstand about rvalue references. My understanding is rvalue can be consumed through move semantic, or collapsed to a reference. In this case, if they were to be used with move, that would be semantically incorrect. I don't understand when this change would ever be correct. What am I missing?
Benjamin Poulain
Comment 9 2014-04-21 18:08:59 PDT
Comment on attachment 229769 [details] Patch I discussed this with Darin a bit. This should have an API test that shows we avoid an extra copy with this patch.
Darin Adler
Comment 10 2014-04-21 18:43:42 PDT
Ben suspects, and I think I agree, that there are no extra temporary copies caused by the existing code. We should only fix this problem if it is, indeed, a problem. I suggest adding API tests that demonstrate the copying.
Zan Dobersek
Comment 11 2014-04-30 12:48:37 PDT
To recap, the extra copy I'm trying remove here is the one performed when passing MappedTraits::emptyValue() through MappedTraits::peek() when there's no entry for the specific key. Existing entries have their value copied when it's being returned, which is correct. The newly-constructed empty values that are being returned should simply be passed through MappedTraits::peek() and RVO-ed, without copies.
Zan Dobersek
Comment 12 2014-04-30 12:57:44 PDT
Darin Adler
Comment 13 2014-08-19 08:45:28 PDT
Comment on attachment 230513 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=230513&action=review I think this patch is fine, but the test doesn’t yet compile on Mac. > Tools/TestWebKitAPI/Tests/WTF/HashMap.cpp:162 > + EXPECT_EQ(CopyMoveCounter::constructionCount, 0); > + EXPECT_EQ(CopyMoveCounter::copyCount, 1); > + EXPECT_EQ(CopyMoveCounter::moveCount, 0); Two problems here. First, the expected value is supposed to come first, and the thing we are testing is supposed to come second. Second, the type needs to match, so these need to be 0U and 1U rather than 0 and 1.
Zan Dobersek
Comment 14 2014-08-23 01:02:30 PDT
Created attachment 237029 [details] Patch Addresses Mac build failures, EXPECT_EQ() argument orders.
WebKit Commit Bot
Comment 15 2014-08-24 13:14:47 PDT
Comment on attachment 237029 [details] Patch Clearing flags on attachment: 237029 Committed r172895: <http://trac.webkit.org/changeset/172895>
WebKit Commit Bot
Comment 16 2014-08-24 13:14:51 PDT
All reviewed patches have been landed. Closing bug.
Note You need to log in before you can comment on or make changes to this bug.