From bee4ed2e5fb0d18e40daa038ec7a00f8d51fded1 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 21 Jul 2023 18:16:28 +0200 Subject: [PATCH 1/5] implemented [c]visit_(until|while) --- .../boost/unordered/concurrent_flat_map.hpp | 100 ++++++++++ .../unordered/detail/foa/concurrent_table.hpp | 171 ++++++++++++++++-- 2 files changed, 258 insertions(+), 13 deletions(-) diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index 181c8987..10712c9e 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -355,6 +355,106 @@ namespace boost { } #endif + template bool visit_until(F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + return table_.visit_until(f); + } + + template bool visit_until(F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + return table_.visit_until(f); + } + + template bool cvisit_until(F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + return table_.cvisit_until(f); + } + +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + template + typename std::enable_if::value, + bool>::type + visit_until(ExecPolicy&& p, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.visit_until(p, f); + } + + template + typename std::enable_if::value, + bool>::type + visit_until(ExecPolicy&& p, F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.visit_until(p, f); + } + + template + typename std::enable_if::value, + bool>::type + cvisit_until(ExecPolicy&& p, F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.cvisit_until(p, f); + } +#endif + + template bool visit_while(F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + return table_.visit_while(f); + } + + template bool visit_while(F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + return table_.visit_while(f); + } + + template bool cvisit_while(F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + return table_.cvisit_while(f); + } + +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + template + typename std::enable_if::value, + bool>::type + visit_while(ExecPolicy&& p, F f) + { + BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.visit_while(p, f); + } + + template + typename std::enable_if::value, + bool>::type + visit_while(ExecPolicy&& p, F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.visit_while(p, f); + } + + template + typename std::enable_if::value, + bool>::type + cvisit_while(ExecPolicy&& p, F f) const + { + BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) + BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) + return table_.cvisit_while(p, f); + } +#endif + /// Modifiers /// diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 0f1f1145..5a28f782 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -539,6 +539,86 @@ public: } #endif + template bool visit_until(F&& f) + { + return !visit_while([&](value_type& x){return !f(x);}); + } + + template bool visit_until(F&& f)const + { + return !visit_while([&](const value_type& x){return !f(x);}); + } + + template bool cvisit_until(F&& f)const + { + return visit_while(std::forward(f)); + } + +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + template + bool visit_until(ExecutionPolicy&& policy,F&& f) + { + return !visit_while( + std::forward(policy), + [&](value_type& x){return !f(x);}); + } + + template + bool visit_until(ExecutionPolicy&& policy,F&& f)const + { + return !visit_while( + std::forward(policy), + [&](const value_type& x){return !f(x);}); + } + + template + bool cvisit_until(ExecutionPolicy&& policy,F&& f)const + { + return visit_until( + std::forward(policy),std::forward(f)); + } +#endif + + template bool visit_while(F&& f) + { + return visit_while_impl(group_exclusive{},std::forward(f)); + } + + template bool visit_while(F&& f)const + { + return visit_while_impl(group_shared{},std::forward(f)); + } + + template bool cvisit_while(F&& f)const + { + return visit_while(std::forward(f)); + } + +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + template + bool visit_while(ExecutionPolicy&& policy,F&& f) + { + return visit_while_impl( + group_exclusive{}, + std::forward(policy),std::forward(f)); + } + + template + bool visit_while(ExecutionPolicy&& policy,F&& f)const + { + return visit_while_impl( + group_shared{}, + std::forward(policy),std::forward(f)); + } + + template + bool cvisit_while(ExecutionPolicy&& policy,F&& f)const + { + return visit_while( + std::forward(policy),std::forward(f)); + } +#endif + bool empty()const noexcept{return size()==0;} std::size_t size()const noexcept @@ -970,6 +1050,29 @@ private: } #endif + template + bool visit_while_impl(GroupAccessMode access_mode,F&& f)const + { + auto lck=shared_access(); + return for_all_elements_while(access_mode,[&](element_type* p){ + return f(cast_for(access_mode,type_policy::value_from(*p))); + }); + } + +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + template + bool visit_while_impl( + GroupAccessMode access_mode,ExecutionPolicy&& policy,F&& f)const + { + auto lck=shared_access(); + return for_all_elements_while( + access_mode,std::forward(policy), + [&](element_type* p){ + return f(cast_for(access_mode,type_policy::value_from(*p))); + }); + } +#endif + template BOOST_FORCEINLINE std::size_t unprotected_visit( GroupAccessMode access_mode, @@ -1253,19 +1356,38 @@ private: template auto for_all_elements(GroupAccessMode access_mode,F f)const ->decltype(f(nullptr,0,nullptr),void()) + { + for_all_elements_while( + access_mode,[&](group_type* pg,unsigned int n,element_type* p) + {f(pg,n,p);return true;}); + } + + template + auto for_all_elements_while(GroupAccessMode access_mode,F f)const + ->decltype(f(nullptr),bool()) + { + return for_all_elements_while( + access_mode,[&](group_type*,unsigned int,element_type* p){return f(p);}); + } + + template + auto for_all_elements_while(GroupAccessMode access_mode,F f)const + ->decltype(f(nullptr,0,nullptr),bool()) { auto p=this->arrays.elements; - if(!p)return; - for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1; - pg!=last;++pg,p+=N){ - auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups)); - auto mask=this->match_really_occupied(pg,last); - while(mask){ - auto n=unchecked_countr_zero(mask); - f(pg,n,p+n); - mask&=mask-1; + if(p){ + for(auto pg=this->arrays.groups,last=pg+this->arrays.groups_size_mask+1; + pg!=last;++pg,p+=N){ + auto lck=access(access_mode,(std::size_t)(pg-this->arrays.groups)); + auto mask=this->match_really_occupied(pg,last); + while(mask){ + auto n=unchecked_countr_zero(mask); + if(!f(pg,n,p+n))return false; + mask&=mask-1; + } } } + return true; } #if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) @@ -1289,10 +1411,10 @@ private: last=first+this->arrays.groups_size_mask+1; std::for_each(std::forward(policy),first,last, [&,this](group_type& g){ - std::size_t pos=static_cast(&g-first); - auto p=this->arrays.elements+pos*N; - auto lck=access(access_mode,pos); - auto mask=this->match_really_occupied(&g,last); + auto pos=static_cast(&g-first); + auto p=this->arrays.elements+pos*N; + auto lck=access(access_mode,pos); + auto mask=this->match_really_occupied(&g,last); while(mask){ auto n=unchecked_countr_zero(mask); f(&g,n,p+n); @@ -1301,6 +1423,29 @@ private: } ); } + + template + bool for_all_elements_while( + GroupAccessMode access_mode,ExecutionPolicy&& policy,F f)const + { + if(!this->arrays.elements)return true; + auto first=this->arrays.groups, + last=first+this->arrays.groups_size_mask+1; + return std::all_of(std::forward(policy),first,last, + [&,this](group_type& g){ + auto pos=static_cast(&g-first); + auto p=this->arrays.elements+pos*N; + auto lck=access(access_mode,pos); + auto mask=this->match_really_occupied(&g,last); + while(mask){ + auto n=unchecked_countr_zero(mask); + if(!f(p+n))return false; + mask&=mask-1; + } + return true; + } + ); + } #endif static std::atomic thread_counter; From 5a4d93a72d44cdafb1eb4de63d2963ecc1b31eb2 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Fri, 21 Jul 2023 19:39:43 +0200 Subject: [PATCH 2/5] documented [c]visit_(until|while) --- doc/unordered/changes.adoc | 5 ++ doc/unordered/concurrent.adoc | 33 +++++++- doc/unordered/concurrent_flat_map.adoc | 108 +++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 3 deletions(-) diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 5679a9ea..6cf4c767 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -6,6 +6,11 @@ :github-pr-url: https://github.com/boostorg/unordered/pull :cpp: C++ +== Release 1.84.0 + +* Added `[c]visit_until` and `[c]visit_while` operations to `boost::concurrent_map`, +with serial and parallel variants. + == Release 1.83.0 - Major update * Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing. diff --git a/doc/unordered/concurrent.adoc b/doc/unordered/concurrent.adoc index 3410570a..1d66a53c 100644 --- a/doc/unordered/concurrent.adoc +++ b/doc/unordered/concurrent.adoc @@ -154,7 +154,34 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel }); ---- -There is another whole-table visitation operation, `erase_if`: +Traversal can be interrupted midway: + +[source,c++] +---- +// finds the key to a given (unique value) + +int key = 0; +int value = ...; +bool found = m.visit_until([&](const auto& x) { + if(x.second == value) { + key = x.first; + return true; // finish + } + else { + return false; // keep on visiting + } +}); + +if(found) { ... } + +// check if all values are != 0 + +bool all_ok = m.visit_while([&](const auto& x) { + return x.second != 0; +}); +---- + +There is one last whole-table visitation operation, `erase_if`: [source,c++] ---- @@ -163,8 +190,8 @@ m.erase_if([](auto& x) { }); ---- -`erase_if` can also be parallelized. Note that, in order to increase efficiency, -these operations do not block the table during execution: this implies that elements +`visit_until`, `visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency, +whole-table visitation operations do not block the table during execution: this implies that elements may be inserted, modified or erased by other threads during visitation. It is advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map` at any point in your program. diff --git a/doc/unordered/concurrent_flat_map.adoc b/doc/unordered/concurrent_flat_map.adoc index fcdc8662..dd9199b7 100644 --- a/doc/unordered/concurrent_flat_map.adoc +++ b/doc/unordered/concurrent_flat_map.adoc @@ -114,6 +114,26 @@ namespace boost { template void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; + template bool xref:#concurrent_flat_map_cvisit_until[visit_until](F f); + template bool xref:#concurrent_flat_map_cvisit_until[visit_until](F f) const; + template bool xref:#concurrent_flat_map_cvisit_until[cvisit_until](F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_until[visit_until](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_flat_map_parallel_cvisit_until[visit_until](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_until[cvisit_until](ExecutionPolicy&& policy, F f) const; + + template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f); + template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const; + template bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f); + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const; + template + bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const; + // capacity ++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept; size_type xref:#concurrent_flat_map_size[size]() const noexcept; @@ -720,6 +740,94 @@ Unsequenced execution policies are not allowed. --- +==== [c]visit_until + +```c++ +template bool visit_until(F f); +template bool visit_until(F f) const; +template bool cvisit_until(F f) const; +``` + +Successively invokes `f` with references to each of the elements in the table until `f` returns `true` +or all the elements are visited. +Such references to the elements are const iff `*this` is const. + +[horizontal] +Returns:;; `true` iff `f` ever returns `true`. + +--- + +==== Parallel [c]visit_until + +```c++ +template bool visit_until(ExecutionPolicy&& policy, F f); +template bool visit_until(ExecutionPolicy&& policy, F f) const; +template bool cvisit_until(ExecutionPolicy&& policy, F f) const; +``` + +Invokes `f` with references to each of the elements in the table until `f` returns `true` +or all the elements are visited. +Such references to the elements are const iff `*this` is const. +Execution is parallelized according to the semantics of the execution policy specified. + +[horizontal] +Returns:;; `true` iff `f` ever returns `true`. +Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`. +Notes:;; Only available in compilers supporting C++17 parallel algorithms. + ++ +These overloads only participate in overload resolution if `std::is_execution_policy_v>` is `true`. + ++ +Unsequenced execution policies are not allowed. + ++ +Parallelization implies that execution does not necessary finish as soon as `f` returns `true`, and as a result +`f` may be invoked with further elements for which the return value is also `true`. + +--- + +==== [c]visit_while + +```c++ +template bool visit_while(F f); +template bool visit_while(F f) const; +template bool cvisit_while(F f) const; +``` + +Successively invokes `f` with references to each of the elements in the table until `f` returns `false` +or all the elements are visited. +Such references to the elements are const iff `*this` is const. + +[horizontal] +Returns:;; `false` iff `f` ever returns `false`. + +--- + +==== Parallel [c]visit_while + +```c++ +template bool visit_while(ExecutionPolicy&& policy, F f); +template bool visit_while(ExecutionPolicy&& policy, F f) const; +template bool cvisit_while(ExecutionPolicy&& policy, F f) const; +``` + +Invokes `f` with references to each of the elements in the table until `f` returns `false` +or all the elements are visited. +Such references to the elements are const iff `*this` is const. +Execution is parallelized according to the semantics of the execution policy specified. + +[horizontal] +Returns:;; `false` iff `f` ever returns `false`. +Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`. +Notes:;; Only available in compilers supporting C++17 parallel algorithms. + ++ +These overloads only participate in overload resolution if `std::is_execution_policy_v>` is `true`. + ++ +Unsequenced execution policies are not allowed. + ++ +Parallelization implies that execution does not necessary finish as soon as `f` returns `false`, and as a result +`f` may be invoked with further elements for which the return value is also `false`. + +--- + === Size and Capacity ==== empty From 470abf41d80586893ff03719b2788cb5b914c85d Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 24 Jul 2023 20:13:18 +0200 Subject: [PATCH 3/5] dropped [c]visit_until --- doc/unordered/changes.adoc | 2 +- doc/unordered/concurrent.adoc | 14 ++--- doc/unordered/concurrent_flat_map.adoc | 54 ------------------- .../boost/unordered/concurrent_flat_map.hpp | 50 ----------------- .../unordered/detail/foa/concurrent_table.hpp | 40 -------------- 5 files changed, 5 insertions(+), 155 deletions(-) diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 6cf4c767..757a7000 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -8,7 +8,7 @@ == Release 1.84.0 -* Added `[c]visit_until` and `[c]visit_while` operations to `boost::concurrent_map`, +* Added `[c]visit_while` operations to `boost::concurrent_map`, with serial and parallel variants. == Release 1.83.0 - Major update diff --git a/doc/unordered/concurrent.adoc b/doc/unordered/concurrent.adoc index 1d66a53c..aed9c466 100644 --- a/doc/unordered/concurrent.adoc +++ b/doc/unordered/concurrent.adoc @@ -162,23 +162,17 @@ Traversal can be interrupted midway: int key = 0; int value = ...; -bool found = m.visit_until([&](const auto& x) { +bool found = !m.visit_while([&](const auto& x) { if(x.second == value) { key = x.first; - return true; // finish + return false; // finish } else { - return false; // keep on visiting + return true; // keep on visiting } }); if(found) { ... } - -// check if all values are != 0 - -bool all_ok = m.visit_while([&](const auto& x) { - return x.second != 0; -}); ---- There is one last whole-table visitation operation, `erase_if`: @@ -190,7 +184,7 @@ m.erase_if([](auto& x) { }); ---- -`visit_until`, `visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency, +`visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency, whole-table visitation operations do not block the table during execution: this implies that elements may be inserted, modified or erased by other threads during visitation. It is advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map` diff --git a/doc/unordered/concurrent_flat_map.adoc b/doc/unordered/concurrent_flat_map.adoc index dd9199b7..8038f210 100644 --- a/doc/unordered/concurrent_flat_map.adoc +++ b/doc/unordered/concurrent_flat_map.adoc @@ -114,16 +114,6 @@ namespace boost { template void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const; - template bool xref:#concurrent_flat_map_cvisit_until[visit_until](F f); - template bool xref:#concurrent_flat_map_cvisit_until[visit_until](F f) const; - template bool xref:#concurrent_flat_map_cvisit_until[cvisit_until](F f) const; - template - bool xref:#concurrent_flat_map_parallel_cvisit_until[visit_until](ExecutionPolicy&& policy, F f); - template - bool xref:#concurrent_flat_map_parallel_cvisit_until[visit_until](ExecutionPolicy&& policy, F f) const; - template - bool xref:#concurrent_flat_map_parallel_cvisit_until[cvisit_until](ExecutionPolicy&& policy, F f) const; - template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f); template bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const; template bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const; @@ -740,50 +730,6 @@ Unsequenced execution policies are not allowed. --- -==== [c]visit_until - -```c++ -template bool visit_until(F f); -template bool visit_until(F f) const; -template bool cvisit_until(F f) const; -``` - -Successively invokes `f` with references to each of the elements in the table until `f` returns `true` -or all the elements are visited. -Such references to the elements are const iff `*this` is const. - -[horizontal] -Returns:;; `true` iff `f` ever returns `true`. - ---- - -==== Parallel [c]visit_until - -```c++ -template bool visit_until(ExecutionPolicy&& policy, F f); -template bool visit_until(ExecutionPolicy&& policy, F f) const; -template bool cvisit_until(ExecutionPolicy&& policy, F f) const; -``` - -Invokes `f` with references to each of the elements in the table until `f` returns `true` -or all the elements are visited. -Such references to the elements are const iff `*this` is const. -Execution is parallelized according to the semantics of the execution policy specified. - -[horizontal] -Returns:;; `true` iff `f` ever returns `true`. -Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`. -Notes:;; Only available in compilers supporting C++17 parallel algorithms. + -+ -These overloads only participate in overload resolution if `std::is_execution_policy_v>` is `true`. + -+ -Unsequenced execution policies are not allowed. + -+ -Parallelization implies that execution does not necessary finish as soon as `f` returns `true`, and as a result -`f` may be invoked with further elements for which the return value is also `true`. - ---- - ==== [c]visit_while ```c++ diff --git a/include/boost/unordered/concurrent_flat_map.hpp b/include/boost/unordered/concurrent_flat_map.hpp index 10712c9e..9364d349 100644 --- a/include/boost/unordered/concurrent_flat_map.hpp +++ b/include/boost/unordered/concurrent_flat_map.hpp @@ -355,56 +355,6 @@ namespace boost { } #endif - template bool visit_until(F f) - { - BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) - return table_.visit_until(f); - } - - template bool visit_until(F f) const - { - BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) - return table_.visit_until(f); - } - - template bool cvisit_until(F f) const - { - BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) - return table_.cvisit_until(f); - } - -#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) - template - typename std::enable_if::value, - bool>::type - visit_until(ExecPolicy&& p, F f) - { - BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) - BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) - return table_.visit_until(p, f); - } - - template - typename std::enable_if::value, - bool>::type - visit_until(ExecPolicy&& p, F f) const - { - BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) - BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) - return table_.visit_until(p, f); - } - - template - typename std::enable_if::value, - bool>::type - cvisit_until(ExecPolicy&& p, F f) const - { - BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) - BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy) - return table_.cvisit_until(p, f); - } -#endif - template bool visit_while(F f) { BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index 5a28f782..51615446 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -539,46 +539,6 @@ public: } #endif - template bool visit_until(F&& f) - { - return !visit_while([&](value_type& x){return !f(x);}); - } - - template bool visit_until(F&& f)const - { - return !visit_while([&](const value_type& x){return !f(x);}); - } - - template bool cvisit_until(F&& f)const - { - return visit_while(std::forward(f)); - } - -#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) - template - bool visit_until(ExecutionPolicy&& policy,F&& f) - { - return !visit_while( - std::forward(policy), - [&](value_type& x){return !f(x);}); - } - - template - bool visit_until(ExecutionPolicy&& policy,F&& f)const - { - return !visit_while( - std::forward(policy), - [&](const value_type& x){return !f(x);}); - } - - template - bool cvisit_until(ExecutionPolicy&& policy,F&& f)const - { - return visit_until( - std::forward(policy),std::forward(f)); - } -#endif - template bool visit_while(F&& f) { return visit_while_impl(group_exclusive{},std::forward(f)); From 9c476ef72a9f9d2d2df361f1f0edfa506afe9595 Mon Sep 17 00:00:00 2001 From: joaquintides Date: Mon, 24 Jul 2023 20:19:09 +0200 Subject: [PATCH 4/5] typo --- doc/unordered/concurrent.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/unordered/concurrent.adoc b/doc/unordered/concurrent.adoc index aed9c466..7ed1be44 100644 --- a/doc/unordered/concurrent.adoc +++ b/doc/unordered/concurrent.adoc @@ -158,7 +158,7 @@ Traversal can be interrupted midway: [source,c++] ---- -// finds the key to a given (unique value) +// finds the key to a given (unique) value int key = 0; int value = ...; From a22c133c3d8347547c5c98feaf16ac53be756e1a Mon Sep 17 00:00:00 2001 From: Christian Mazakas Date: Wed, 26 Jul 2023 10:14:11 -0700 Subject: [PATCH 5/5] Add tests for visit_while --- test/cfoa/visit_tests.cpp | 216 +++++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 1 deletion(-) diff --git a/test/cfoa/visit_tests.cpp b/test/cfoa/visit_tests.cpp index fff8f69d..267457df 100644 --- a/test/cfoa/visit_tests.cpp +++ b/test/cfoa/visit_tests.cpp @@ -349,6 +349,106 @@ namespace { } visit_all; + struct visit_while_type + { + template + void operator()(std::vector& values, X& x, M const& reference_map) + { + using value_type = typename X::value_type; + + auto mut_truthy_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return true; + }; + }; + + auto const_truthy_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return true; + }; + }; + + auto mut_falsey_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + ++num_visits; + return (kv.second.x_ % 100) == 0; + }; + }; + + auto const_falsey_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + ++num_visits; + return (kv.second.x_ % 100) == 0; + }; + }; + + { + thread_runner(values, [&x, &mut_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST(x.visit_while(mut_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &const_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + auto const& y = x; + BOOST_TEST(y.visit_while(const_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &const_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST(x.cvisit_while(const_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &mut_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST_NOT(x.visit_while(mut_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } + + { + thread_runner(values, [&x, &const_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + auto const& y = x; + BOOST_TEST_NOT(y.visit_while(const_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } + + { + thread_runner(values, [&x, &const_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST_NOT(x.cvisit_while(const_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } + } + } visit_while; + struct exec_policy_visit_all_type { template @@ -407,6 +507,120 @@ namespace { } } exec_policy_visit_all; + struct exec_policy_visit_while_type + { + template + void operator()(std::vector& values, X& x, M const& reference_map) + { +#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS) + using value_type = typename X::value_type; + + auto mut_truthy_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return true; + }; + }; + + auto const_truthy_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return true; + }; + }; + + auto mut_falsey_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return (kv.second.x_ % 100) == 0; + }; + }; + + auto const_falsey_visitor = [&reference_map]( + std::atomic& num_visits) { + return [&reference_map, &num_visits](value_type const& kv) { + BOOST_TEST(reference_map.contains(kv.first)); + BOOST_TEST_EQ(kv.second, reference_map.find(kv.first)->second); + ++num_visits; + return (kv.second.x_ % 100) == 0; + }; + }; + + { + thread_runner(values, [&x, &mut_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST( + x.visit_while(std::execution::par, mut_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &const_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + auto const& y = x; + BOOST_TEST(y.visit_while( + std::execution::par, const_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &const_truthy_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST(x.cvisit_while( + std::execution::par, const_truthy_visitor(num_visits))); + BOOST_TEST_EQ(x.size(), num_visits); + }); + } + + { + thread_runner(values, [&x, &mut_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST_NOT( + x.visit_while(std::execution::par, mut_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } + + { + thread_runner(values, [&x, &const_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + auto const& y = x; + BOOST_TEST_NOT(y.visit_while( + std::execution::par, const_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } + + { + thread_runner(values, [&x, &const_falsey_visitor](boost::span) { + std::atomic num_visits{0}; + BOOST_TEST_NOT(x.cvisit_while( + std::execution::par, const_falsey_visitor(num_visits))); + BOOST_TEST_LT(num_visits, x.size()); + BOOST_TEST_GT(num_visits, 0u); + }); + } +#else + (void)values; + (void)x; + (void)reference_map; +#endif + } + } exec_policy_visit_while; + template void visit(X*, G gen, F visitor, test::random_generator rg) { @@ -570,7 +784,7 @@ UNORDERED_TEST( visit, ((map)) ((value_type_generator)(init_type_generator)) - ((lvalue_visitor)(visit_all)(exec_policy_visit_all)) + ((lvalue_visitor)(visit_all)(visit_while)(exec_policy_visit_all)(exec_policy_visit_while)) ((default_generator)(sequential)(limited_range))) UNORDERED_TEST(