Compare commits

...

36 Commits

Author SHA1 Message Date
Christian Mazakas 085414479a Avoid shadowing wanrings from DateTime 2023-06-22 11:32:45 -07:00
Christian Mazakas 515412ce0a Fix header includes for boost::shared_lock 2023-06-22 11:32:34 -07:00
Christian Mazakas 6395ce0486 Add explicit tests for rw_spinlock 2023-06-22 09:44:54 -07:00
Peter Dimov 7d39b68e46 Fix -Wsign-conversion issue 2023-06-20 21:36:25 +03:00
Peter Dimov 79a59bce72 Change the backoff algorithm of rw_spinlock 2023-06-20 21:36:25 +03:00
Peter Dimov 717048e868 Split Appveyor jobs to avoid timeouts 2023-06-20 17:49:15 +03:00
joaquintides de8c2a5ad9 Merge pull request #193 from boostorg/feature/proxy-erase
Feature/proxy erase
2023-06-20 11:49:27 +02:00
joaquintides 678f8802f4 [skip ci] updated boost::unordered_[multi]set benchmarks 2023-06-20 09:53:25 +02:00
Christian Mazakas 704b3c7a4b Update docs to describe the returned proxy type as "convertible to iterator" 2023-06-19 14:33:10 -07:00
Christian Mazakas 671b68f4d1 Restart drone CI 2023-06-19 10:45:37 -07:00
Christian Mazakas b2a4b22c89 Remove erroneous asserts from the test suite
These asserts were erroneously being removed in release builds of the tests which is never the intention
2023-06-19 09:21:48 -07:00
joaquintides 2eebe157fd refactored proxy_erase to reduce template instantiations and strengthen misuse avoidance 2023-06-19 08:21:13 -07:00
joaquintides 2b6cfe4f3c documented proxy erase(iterator) 2023-06-19 08:21:13 -07:00
Christian Mazakas 2f7bba0c21 Implement proxy erase(iterator) 2023-06-19 08:21:13 -07:00
joaquintides d65d9a65b7 Merge pull request #188 from boostorg/feature/cfoa
Feature/cfoa
2023-06-17 17:25:56 +02:00
joaquintides 67913ed99a re-enabled CI on feature/* branches 2023-06-17 17:21:09 +02:00
joaquintides 0440cd3183 widened previous workaround 2023-06-17 09:37:17 +02:00
Christian Mazakas 3a3148cc16 Add workaround for gcc-5.4 deficiencies in copy elision and new() construction 2023-06-16 14:16:26 -07:00
Christian Mazakas f51070ab21 Update free function erase_if() to support non-copyable predicates 2023-06-16 14:16:26 -07:00
Christian Mazakas ac5a99da31 Add max_size() impl 2023-06-15 22:15:57 -07:00
Christian Mazakas 6fed6ea5ad Remove extraneous fwd.hpp include from public facing headers 2023-06-15 22:15:57 -07:00
Christian Mazakas c51c06492a Update iterator range insert to use emplace() vs insert() to avoid copies in certain cases 2023-06-15 22:15:57 -07:00
Christian Mazakas f604efe866 Add missing const reference qualifiers to allocator_type constructor parameters 2023-06-15 22:15:57 -07:00
Christian Mazakas e8ec858313 Refactor type policies used by containers into their own dedicated headers 2023-06-15 22:15:57 -07:00
Christian Mazakas bc8996559b Remove unnecessary backticks from static_assert() messages 2023-06-15 22:15:57 -07:00
Christian Mazakas 449e1a30d3 Remove outdated reference to proposed synopsis 2023-06-15 22:15:57 -07:00
joaquintides c2484f05f0 avoided old GCC/Clang hiccup with {} for aggregate copy construction 2023-06-09 21:31:30 +02:00
joaquintides f7e2132bdb Merge branch 'feature/cfoa' of https://github.com/boostorg/unordered into feature/cfoa 2023-06-09 21:20:36 +02:00
joaquintides 07a8b2b14d stylistic 2023-06-09 08:55:25 -07:00
joaquintides 2eac49f0ed added copy ctor for cache_aligned_size_control 2023-06-09 08:55:25 -07:00
joaquintides 6a8d7857e2 fixed unqualified swap call 2023-06-09 08:55:25 -07:00
joaquintides ef34ec5e87 added missing #include 2023-06-09 08:55:25 -07:00
joaquintides 518a5d2e1e refactored padding into a clean design 2023-06-09 08:55:25 -07:00
joaquintides 3ea8a768ca added padding between and around ml and size_ 2023-06-09 08:55:25 -07:00
joaquintides 4b877d363a updated concurrent_flat_map benchmarks 2023-06-09 08:55:25 -07:00
Christian Mazakas 2395db7bb9 Cleanup docs 2023-06-09 08:55:25 -07:00
92 changed files with 1191 additions and 637 deletions
+84 -19
View File
@@ -40,87 +40,152 @@ environment:
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
B2_TOOLSET: msvc-12.0,msvc-14.0
- FLAVOR: Visual Studio 2017
- FLAVOR: Visual Studio 2017, C++14
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
B2_CXXSTD: 14,17
B2_CXXSTD: 14
B2_TOOLSET: msvc-14.1
- FLAVOR: Visual Studio 2017
- FLAVOR: Visual Studio 2017, C++17
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
B2_CXXSTD: 17
B2_TOOLSET: msvc-14.1
- FLAVOR: Visual Studio 2017, C++latest
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
B2_CXXSTD: latest
B2_TOOLSET: msvc-14.1
- FLAVOR: cygwin (32-bit)
- FLAVOR: cygwin (32-bit, C++03,11)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 03,11,14,1z
B2_CXXSTD: 03,11
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit)
- FLAVOR: cygwin (32-bit, C++14,1z)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 14,1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, C++03,11)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03,11,14,1z
B2_CXXSTD: 03,11
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, latest)
- FLAVOR: cygwin (64-bit, C++14,1z)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 14,1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, latest, C++03)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: cygwin (64-bit, latest)
- FLAVOR: cygwin (64-bit, latest, C++11)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 11
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: cygwin (64-bit, latest)
- FLAVOR: cygwin (64-bit, latest, C++14)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 14
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: cygwin (64-bit, latest)
- FLAVOR: cygwin (64-bit, latest, C++1z)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 1z
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
B2_VARIANT: release
- FLAVOR: mingw-w64, 32 bit
- FLAVOR: mingw-w64, 32 bit, C++03
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 03,11,14
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 32 bit
- FLAVOR: mingw-w64, 32 bit, C++11
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 17,2a
B2_CXXSTD: 11
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 64 bit
- FLAVOR: mingw-w64, 32 bit, C++14
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 14
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 32 bit, C++17
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 17
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 32 bit, C++2a
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 2a
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 64 bit, C++03
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 03,11,14
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit
- FLAVOR: mingw-w64, 64 bit, C++11
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 17,2a
B2_CXXSTD: 11
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit, C++14
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 14
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit, C++17
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 17
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit, C++2a
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 2a
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
+1 -1
View File
@@ -6,7 +6,7 @@ local library = "unordered";
local triggers =
{
branch: [ "master", "develop", "bugfix/*", "fix/*", "pr/*" ]
branch: [ "master", "develop", "feature/*", "bugfix/*", "fix/*", "pr/*" ]
};
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
+1
View File
@@ -15,6 +15,7 @@ on:
- master
- develop
- bugfix/**
- feature/**
- fix/**
- pr/**
Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 37 KiB

+3 -3
View File
@@ -16,7 +16,7 @@ The successful lookup benchmarks are done by looking up all `n` values, in their
The unsuccessful lookup benchmarks use `n` randomly generated integers but using a different seed value.
=== GCC 11 + libstdc++-v3, x64
=== GCC 12 + libstdc++-v3, x64
==== Insertion
@@ -102,7 +102,7 @@ max load factor 5
|===
=== Clang 12 + libc++, x64
=== Clang 15 + libc++, x64
==== Insertion
@@ -190,7 +190,7 @@ max load factor 5
|===
=== Visual Studio 2019 + Dinkumware, x64
=== Visual Studio 2022 + Dinkumware, x64
==== Insertion
+4
View File
@@ -10,6 +10,10 @@
* Added `boost::concurrent_flat_map`, a fast, thread-safe hashmap based on open addressing.
* Sped up iteration of open-addressing containers.
* In open-addressing containers, `erase(iterator)`, which previously returned nothing, now
returns a proxy object convertible to an iterator to the next element.
This enables the typical `it = c.erase(it)` idiom without incurring any performance penalty
when the returned proxy is not used.
== Release 1.82.0 - Major update
+3 -1
View File
@@ -135,7 +135,9 @@ The main differences with C++ unordered associative containers are:
* In general:
** `begin()` is not constant-time.
** `erase(iterator)` returns `void` instead of an iterator to the following element.
** `erase(iterator)` does not return an iterator to the following element, but
a proxy object that converts to that iterator if requested; this avoids
a potentially costly iterator increment operation when not needed.
** There is no API for bucket handling (except `bucket_count`).
** The maximum load factor of the container is managed internally and can't be set by the user. The maximum load,
exposed through the public function `max_load`, may decrease on erasure under high-load conditions.
+1 -1
View File
@@ -132,7 +132,7 @@ by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
in higher parallelization. Consult the xref:#concurrent_flat_map[reference]
for a complete list of available operations.
== Whole-table Visitation
== Whole-Table Visitation
In the absence of iterators, `boost::concurrent_flat_map` provides `visit_all`
as an alternative way to process all the elements in the map:
+2 -2
View File
@@ -34,7 +34,7 @@ boost::unordered_set +
boost::unordered_map +
boost::unordered_multiset +
boost::unordered_multimap
^| N/A
^|
^.^h|*Open addressing*
^m| boost::unordered_node_set +
@@ -85,7 +85,7 @@ namespace boost {
}
----
To store an object in an unordered associative container requires both a
Storing an object in an unordered associative container requires both a
key equality function and a hash function. The default function objects in
the standard containers support a few basic types including integer types,
floating point types, pointer types, and the standard strings. Since
+11 -8
View File
@@ -17,7 +17,6 @@ a number of aspects from that of `boost::unordered_flat_map`/`std::unordered_fla
- `value_type` must be move-constructible.
- Pointer stability is not kept under rehashing.
- `begin()` is not constant-time.
- `erase(iterator)` returns `void`.
- There is no API for bucket handling (except `bucket_count`) or node extraction/insertion.
- The maximum load factor of the container is managed internally and can't be set by the user.
@@ -155,9 +154,9 @@ namespace boost {
template<class K, class M>
iterator xref:#unordered_flat_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj);
void xref:#unordered_flat_map_erase_by_position[erase](iterator position);
void xref:#unordered_flat_map_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_flat_map_erase_by_key[erase](const key_type& k);
_convertible-to-iterator_ xref:#unordered_flat_map_erase_by_position[erase](iterator position);
_convertible-to-iterator_ xref:#unordered_flat_map_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_flat_map_erase_by_key[erase](const key_type& k);
template<class K> size_type xref:#unordered_flat_map_erase_by_key[erase](K&& k);
iterator xref:#unordered_flat_map_erase_range[erase](const_iterator first, const_iterator last);
void xref:#unordered_flat_map_swap[swap](unordered_flat_map& other)
@@ -1035,15 +1034,19 @@ The `template<class K, class M>` only participates in overload resolution if `Ha
==== Erase by Position
```c++
void erase(iterator position);
void erase(const_iterator position);
```
[source,c++,subs=+quotes]
----
_convertible-to-iterator_ erase(iterator position);
_convertible-to-iterator_ erase(const_iterator position);
----
Erase the element pointed to by `position`.
[horizontal]
Returns:;; An opaque object implicitly convertible to the `iterator` or `const_iterator`
immediately following `position` prior to the erasure.
Throws:;; Nothing.
Notes:;; The opaque object returned must only be discarded or immediately converted to `iterator` or `const_iterator`.
---
+11 -8
View File
@@ -17,7 +17,6 @@ a number of aspects from that of `boost::unordered_flat_set`/`std::unordered_fla
- `value_type` must be move-constructible.
- Pointer stability is not kept under rehashing.
- `begin()` is not constant-time.
- `erase(iterator)` returns `void`.
- There is no API for bucket handling (except `bucket_count`) or node extraction/insertion.
- The maximum load factor of the container is managed internally and can't be set by the user.
@@ -123,9 +122,9 @@ namespace boost {
template<class InputIterator> void xref:#unordered_flat_set_insert_iterator_range[insert](InputIterator first, InputIterator last);
void xref:#unordered_flat_set_insert_initializer_list[insert](std::initializer_list<value_type>);
void xref:#unordered_flat_set_erase_by_position[erase](iterator position);
void xref:#unordered_flat_set_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_flat_set_erase_by_key[erase](const key_type& k);
_convertible-to-iterator_ xref:#unordered_flat_set_erase_by_position[erase](iterator position);
_convertible-to-iterator_ xref:#unordered_flat_set_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_flat_set_erase_by_key[erase](const key_type& k);
template<class K> size_type xref:#unordered_flat_set_erase_by_key[erase](K&& k);
iterator xref:#unordered_flat_set_erase_range[erase](const_iterator first, const_iterator last);
void xref:#unordered_flat_set_swap[swap](unordered_flat_set& other)
@@ -846,15 +845,19 @@ Notes:;; Can invalidate iterators, pointers and references, but only if the inse
==== Erase by Position
```c++
void erase(iterator position);
void erase(const_iterator position);
```
[source,c++,subs=+quotes]
----
_convertible-to-iterator_ erase(iterator position);
_convertible-to-iterator_ erase(const_iterator position);
----
Erase the element pointed to by `position`.
[horizontal]
Returns:;; An opaque object implicitly convertible to the `iterator` or `const_iterator`
immediately following `position` prior to the erasure.
Throws:;; Nothing.
Notes:;; The opaque object returned must only be discarded or immediately converted to `iterator` or `const_iterator`.
---
+11 -8
View File
@@ -13,7 +13,6 @@ As a result of its using open addressing, the interface of `boost::unordered_nod
a number of aspects from that of `boost::unordered_map`/`std::unordered_map`:
- `begin()` is not constant-time.
- `erase(iterator)` returns `void`.
- There is no API for bucket handling (except `bucket_count`).
- The maximum load factor of the container is managed internally and can't be set by the user.
@@ -156,9 +155,9 @@ namespace boost {
template<class K, class M>
iterator xref:#unordered_node_map_insert_or_assign_with_hint[insert_or_assign](const_iterator hint, K&& k, M&& obj);
void xref:#unordered_node_map_erase_by_position[erase](iterator position);
void xref:#unordered_node_map_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_node_map_erase_by_key[erase](const key_type& k);
_convertible-to-iterator_ xref:#unordered_node_map_erase_by_position[erase](iterator position);
_convertible-to-iterator_ xref:#unordered_node_map_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_node_map_erase_by_key[erase](const key_type& k);
template<class K> size_type xref:#unordered_node_map_erase_by_key[erase](K&& k);
iterator xref:#unordered_node_map_erase_range[erase](const_iterator first, const_iterator last);
void xref:#unordered_node_map_swap[swap](unordered_node_map& other)
@@ -1105,15 +1104,19 @@ The `template<class K, class M>` only participates in overload resolution if `Ha
==== Erase by Position
```c++
void erase(iterator position);
void erase(const_iterator position);
```
[source,c++,subs=+quotes]
----
_convertible-to-iterator_ erase(iterator position);
_convertible-to-iterator_ erase(const_iterator position);
----
Erase the element pointed to by `position`.
[horizontal]
Returns:;; An opaque object implicitly convertible to the `iterator` or `const_iterator`
immediately following `position` prior to the erasure.
Throws:;; Nothing.
Notes:;; The opaque object returned must only be discarded or immediately converted to `iterator` or `const_iterator`.
---
+11 -8
View File
@@ -13,7 +13,6 @@ As a result of its using open addressing, the interface of `boost::unordered_nod
a number of aspects from that of `boost::unordered_set`/`std::unordered_set`:
- `begin()` is not constant-time.
- `erase(iterator)` returns `void`.
- There is no API for bucket handling (except `bucket_count`).
- The maximum load factor of the container is managed internally and can't be set by the user.
@@ -124,9 +123,9 @@ namespace boost {
insert_return_type xref:#unordered_node_set_insert_node[insert](node_type&& nh);
iterator xref:#unordered_node_set_insert_node_with_hint[insert](const_iterator hint, node_type&& nh);
void xref:#unordered_node_set_erase_by_position[erase](iterator position);
void xref:#unordered_node_set_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_node_set_erase_by_key[erase](const key_type& k);
_convertible-to-iterator_ xref:#unordered_node_set_erase_by_position[erase](iterator position);
_convertible-to-iterator_ xref:#unordered_node_set_erase_by_position[erase](const_iterator position);
size_type xref:#unordered_node_set_erase_by_key[erase](const key_type& k);
template<class K> size_type xref:#unordered_node_set_erase_by_key[erase](K&& k);
iterator xref:#unordered_node_set_erase_range[erase](const_iterator first, const_iterator last);
void xref:#unordered_node_set_swap[swap](unordered_node_set& other)
@@ -919,15 +918,19 @@ Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` a
==== Erase by Position
```c++
void erase(iterator position);
void erase(const_iterator position);
```
[source,c++,subs=+quotes]
----
_convertible-to-iterator_ erase(iterator position);
_convertible-to-iterator_ erase(const_iterator position);
----
Erase the element pointed to by `position`.
[horizontal]
Returns:;; An opaque object implicitly convertible to the `iterator` or `const_iterator`
immediately following `position` prior to the erasure.
Throws:;; Nothing.
Notes:;; The opaque object returned must only be discarded or immediately converted to `iterator` or `const_iterator`.
---
+19 -70
View File
@@ -8,15 +8,12 @@
* See https://www.boost.org/libs/unordered for library home page.
*/
/* Reference:
* https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_HPP
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <boost/unordered/detail/foa/concurrent_table.hpp>
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/container_hash/hash.hpp>
@@ -31,12 +28,12 @@
#define BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F) \
static_assert(boost::unordered::detail::is_invocable<F, value_type&>::value, \
"The provided Callable must be invocable with `value_type&`");
"The provided Callable must be invocable with value_type&");
#define BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F) \
static_assert( \
boost::unordered::detail::is_invocable<F, value_type const&>::value, \
"The provided Callable must be invocable with `value_type const&`");
"The provided Callable must be invocable with value_type const&");
#if BOOST_CXX_VERSION >= 202002L
@@ -71,7 +68,6 @@
namespace boost {
namespace unordered {
namespace detail {
template <class F, class... Args>
struct is_invocable
: std::is_constructible<std::function<void(Args...)>,
@@ -79,60 +75,6 @@ namespace boost {
{
};
template <class Key, class T> struct concurrent_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class T, class Hash, class Pred, class Allocator>
@@ -143,7 +85,7 @@ namespace boost {
class Allocator2>
friend class concurrent_flat_map;
using type_policy = detail::concurrent_map_types<Key, T>;
using type_policy = detail::foa::flat_map_types<Key, T>;
detail::foa::concurrent_table<type_policy, Hash, Pred, Allocator> table_;
@@ -151,6 +93,10 @@ namespace boost {
bool friend operator==(concurrent_flat_map<K, V, H, KE, A> const& lhs,
concurrent_flat_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Predicate>
friend typename concurrent_flat_map<K, V, H, KE, A>::size_type erase_if(
concurrent_flat_map<K, V, H, KE, A>& set, Predicate pred);
public:
using key_type = Key;
using mapped_type = T;
@@ -202,22 +148,24 @@ namespace boost {
}
template <class InputIterator>
concurrent_flat_map(InputIterator f, InputIterator l, allocator_type a)
concurrent_flat_map(
InputIterator f, InputIterator l, allocator_type const& a)
: concurrent_flat_map(f, l, 0, hasher(), key_equal(), a)
{
}
explicit concurrent_flat_map(allocator_type a)
explicit concurrent_flat_map(allocator_type const& a)
: table_(detail::foa::default_bucket_count, hasher(), key_equal(), a)
{
}
concurrent_flat_map(concurrent_flat_map const& rhs, allocator_type a)
concurrent_flat_map(
concurrent_flat_map const& rhs, allocator_type const& a)
: table_(rhs.table_, a)
{
}
concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type a)
concurrent_flat_map(concurrent_flat_map&& rhs, allocator_type const& a)
: table_(std::move(rhs.table_), a)
{
}
@@ -302,6 +250,7 @@ namespace boost {
///
size_type size() const noexcept { return table_.size(); }
size_type max_size() const noexcept { return table_.max_size(); }
BOOST_ATTRIBUTE_NODISCARD bool empty() const noexcept
{
@@ -425,7 +374,7 @@ namespace boost {
void insert(InputIterator begin, InputIterator end)
{
for (auto pos = begin; pos != end; ++pos) {
table_.insert(*pos);
table_.emplace(*pos);
}
}
@@ -477,7 +426,7 @@ namespace boost {
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F)
for (; first != last; ++first) {
table_.insert_or_visit(*first, f);
table_.emplace_or_visit(*first, f);
}
}
@@ -508,7 +457,7 @@ namespace boost {
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
for (; first != last; ++first) {
table_.insert_or_cvisit(*first, f);
table_.emplace_or_cvisit(*first, f);
}
}
@@ -759,7 +708,7 @@ namespace boost {
typename concurrent_flat_map<K, T, H, P, A>::size_type erase_if(
concurrent_flat_map<K, T, H, P, A>& c, Predicate pred)
{
return c.erase_if(pred);
return c.table_.erase_if(pred);
}
#if BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES
@@ -8,10 +8,6 @@
* See https://www.boost.org/libs/unordered for library home page.
*/
/* Reference:
* https://github.com/joaquintides/concurrent_hashmap_api#proposed-synopsis
*/
#ifndef BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP
#define BOOST_UNORDERED_CONCURRENT_FLAT_MAP_FWD_HPP
@@ -18,6 +18,7 @@
#include <boost/core/no_exceptions_support.hpp>
#include <boost/cstdint.hpp>
#include <boost/mp11/tuple.hpp>
#include <boost/static_assert.hpp>
#include <boost/unordered/detail/foa/core.hpp>
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
#include <boost/unordered/detail/foa/tuple_rotate_right.hpp>
@@ -64,6 +65,8 @@ using is_execution_policy=std::false_type;
namespace foa{
static constexpr std::size_t cacheline_size=64;
template<typename T,std::size_t N>
class cache_aligned_array
{
@@ -76,20 +79,20 @@ public:
T& operator[](std::size_t pos)noexcept{return *data(pos);}
private:
static constexpr std::size_t cacheline=64;
static constexpr std::size_t element_offset=
(sizeof(T)+cacheline-1)/cacheline*cacheline;
(sizeof(T)+cacheline_size-1)/cacheline_size*cacheline_size;
BOOST_STATIC_ASSERT(alignof(T)<=cacheline);
BOOST_STATIC_ASSERT(alignof(T)<=cacheline_size);
T* data(std::size_t pos)noexcept
{
return reinterpret_cast<T*>(
(reinterpret_cast<uintptr_t>(&buf)+cacheline-1)/cacheline*cacheline+
pos*element_offset);
(reinterpret_cast<uintptr_t>(&buf)+cacheline_size-1)/
cacheline_size*cacheline_size
+pos*element_offset);
}
unsigned char buf[element_offset*N+cacheline-1];
unsigned char buf[element_offset*N+cacheline_size-1];
};
template<typename Mutex,std::size_t N>
@@ -297,6 +300,40 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
group_access *group_accesses;
};
struct atomic_size_control
{
static constexpr auto atomic_size_t_size=sizeof(std::atomic<std::size_t>);
BOOST_STATIC_ASSERT(atomic_size_t_size<cacheline_size);
atomic_size_control(std::size_t ml_,std::size_t size_):
pad0_{},ml{ml_},pad1_{},size{size_}{}
atomic_size_control(atomic_size_control& x):
pad0_{},ml{x.ml.load()},pad1_{},size{x.size.load()}{}
/* padding to avoid false sharing internally and with sorrounding data */
unsigned char pad0_[cacheline_size-atomic_size_t_size];
std::atomic<std::size_t> ml;
unsigned char pad1_[cacheline_size-atomic_size_t_size];
std::atomic<std::size_t> size;
};
/* std::swap can't be used on non-assignable atomics */
inline void
swap_atomic_size_t(std::atomic<std::size_t>& x,std::atomic<std::size_t>& y)
{
std::size_t tmp=x;
x=static_cast<std::size_t>(y);
y=tmp;
}
inline void swap(atomic_size_control& x,atomic_size_control& y)
{
swap_atomic_size_t(x.ml,y.ml);
swap_atomic_size_t(x.size,y.size);
}
/* foa::concurrent_table serves as the foundation for end-user concurrent
* hash containers. The TypePolicy parameter can specify flat/node-based
* map-like and set-like containers, though currently we're only providing
@@ -357,7 +394,7 @@ struct concurrent_table_arrays:table_arrays<Value,Group,SizePolicy>
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using concurrent_table_core_impl=table_core<
TypePolicy,group15<atomic_integral>,concurrent_table_arrays,
std::atomic<std::size_t>,Hash,Pred,Allocator>;
atomic_size_control,Hash,Pred,Allocator>;
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
@@ -988,8 +1025,8 @@ private:
std::size_t unprotected_size()const
{
std::size_t m=this->ml;
std::size_t s=this->size_;
std::size_t m=this->size_ctrl.ml;
std::size_t s=this->size_ctrl.size;
return s<=m?s:m;
}
@@ -1105,7 +1142,7 @@ private:
if(this->find(k,pos0,hash))return false;
if(BOOST_LIKELY(this->size_<this->ml)){
if(BOOST_LIKELY(this->size_ctrl.size<this->size_ctrl.ml)){
this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...);
}
else{
@@ -1118,15 +1155,15 @@ private:
{
reserve_size(concurrent_table& x_):x{x_}
{
size_=++x.size_;
size_=++x.size_ctrl.size;
}
~reserve_size()
{
if(!commit_)--x.size_;
if(!commit_)--x.size_ctrl.size;
}
bool succeeded()const{return size_<=x.ml;}
bool succeeded()const{return size_<=x.size_ctrl.ml;}
void commit(){commit_=true;}
@@ -1200,7 +1237,9 @@ private:
void rehash_if_full()
{
auto lck=exclusive_access();
if(this->size_==this->ml)this->unchecked_rehash_for_growth();
if(this->size_ctrl.size==this->size_ctrl.ml){
this->unchecked_rehash_for_growth();
}
}
template<typename GroupAccessMode,typename F>
+24 -35
View File
@@ -1234,7 +1234,7 @@ alloc_make_insert_type(const Allocator& al,Args&&... args)
template<
typename TypePolicy,typename Group,template<typename...> class Arrays,
typename SizeImpl,typename Hash,typename Pred,typename Allocator
typename SizeControl,typename Hash,typename Pred,typename Allocator
>
class
@@ -1258,7 +1258,7 @@ public:
using alloc_traits=boost::allocator_traits<Allocator>;
using element_type=typename type_policy::element_type;
using arrays_type=Arrays<element_type,group_type,size_policy>;
using size_impl_type=SizeImpl;
using size_ctrl_type=SizeControl;
using key_type=typename type_policy::key_type;
using init_type=typename type_policy::init_type;
@@ -1279,7 +1279,7 @@ public:
const Pred& pred_=Pred(),const Allocator& al_=Allocator()):
hash_base{empty_init,h_},pred_base{empty_init,pred_},
allocator_base{empty_init,al_},arrays(new_arrays(n)),
ml{initial_max_load()},size_{0}
size_ctrl{initial_max_load(),0}
{}
table_core(const table_core& x):
@@ -1293,11 +1293,11 @@ public:
hash_base{empty_init,std::move(x.h())},
pred_base{empty_init,std::move(x.pred())},
allocator_base{empty_init,std::move(x.al())},
arrays(x.arrays),ml{std::size_t(x.ml)},size_{std::size_t(x.size_)}
arrays(x.arrays),size_ctrl(x.size_ctrl)
{
x.arrays=x.new_arrays(0);
x.ml=x.initial_max_load();
x.size_=0;
x.size_ctrl.ml=x.initial_max_load();
x.size_ctrl.size=0;
}
table_core(const table_core& x,const Allocator& al_):
@@ -1310,9 +1310,9 @@ public:
table_core{std::move(x.h()),std::move(x.pred()),al_}
{
if(al()==x.al()){
std::swap(arrays,x.arrays);
swap_size_impl(size_,x.size_);
swap_size_impl(ml,x.ml);
using std::swap;
swap(arrays,x.arrays);
swap(size_ctrl,x.size_ctrl);
}
else{
reserve(x.size());
@@ -1410,8 +1410,7 @@ public:
reserve(0);
move_assign_if<pocma>(al(),x.al());
swap(arrays,x.arrays);
swap_size_impl(ml,x.ml);
swap_size_impl(size_,x.size_);
swap(size_ctrl,x.size_ctrl);
}
else{
/* noshrink: favor memory reuse over tightness */
@@ -1437,7 +1436,7 @@ public:
allocator_type get_allocator()const noexcept{return al();}
bool empty()const noexcept{return size()==0;}
std::size_t size()const noexcept{return size_;}
std::size_t size()const noexcept{return size_ctrl.size;}
std::size_t max_size()const noexcept{return SIZE_MAX;}
BOOST_FORCEINLINE
@@ -1522,8 +1521,7 @@ public:
swap(h(),x.h());
swap(pred(),x.pred());
swap(arrays,x.arrays);
swap_size_impl(ml,x.ml);
swap_size_impl(size_,x.size_);
swap(size_ctrl,x.size_ctrl);
}
void clear()noexcept
@@ -1541,8 +1539,8 @@ public:
pg->initialize();
}
arrays.groups[arrays.groups_size_mask].set_sentinel();
ml=initial_max_load();
size_=0;
size_ctrl.ml=initial_max_load();
size_ctrl.size=0;
}
}
@@ -1562,7 +1560,7 @@ public:
float max_load_factor()const noexcept{return mlf;}
std::size_t max_load()const noexcept{return ml;}
std::size_t max_load()const noexcept{return size_ctrl.ml;}
void rehash(std::size_t n)
{
@@ -1678,7 +1676,7 @@ public:
{
auto res=nosize_unchecked_emplace_at(
arrays,pos0,hash,std::forward<Args>(args)...);
++size_;
++size_ctrl.size;
return res;
}
@@ -1708,7 +1706,7 @@ public:
/* new_arrays_ lifetime taken care of by unchecked_rehash */
unchecked_rehash(new_arrays_);
++size_;
++size_ctrl.size;
return it;
}
@@ -1725,7 +1723,7 @@ public:
auto new_arrays_=new_arrays(n);
delete_arrays(arrays);
arrays=new_arrays_;
ml=initial_max_load();
size_ctrl.ml=initial_max_load();
}
}
}
@@ -1786,8 +1784,7 @@ public:
}
arrays_type arrays;
size_impl_type ml;
size_impl_type size_;
size_ctrl_type size_ctrl;
private:
template<
@@ -1806,7 +1803,7 @@ private:
hash_base{empty_init,std::move(h_)},
pred_base{empty_init,std::move(pred_)},
allocator_base{empty_init,al_},arrays(new_arrays(0)),
ml{initial_max_load()},size_{0}
size_ctrl{initial_max_load(),0}
{}
arrays_type new_arrays(std::size_t n)
@@ -1876,7 +1873,7 @@ private:
if(arrays.elements){
copy_elements_array_from(x);
copy_groups_array_from(x);
size_=std::size_t(x.size_);
size_ctrl.size=std::size_t(x.size_ctrl.size);
}
}
@@ -1961,23 +1958,15 @@ private:
}
}
static inline void swap_size_impl(size_impl_type& x,size_impl_type& y)
{
/* std::swap can't be used on non-assignable atomics */
std::size_t tmp=x;
x=static_cast<std::size_t>(y);
y=tmp;
}
void recover_slot(unsigned char* pc)
{
/* If this slot potentially caused overflow, we decrease the maximum load so
* that average probe length won't increase unboundedly in repeated
* insert/erase cycles (drift).
*/
ml-=group_type::maybe_caused_overflow(pc);
size_ctrl.ml-=group_type::maybe_caused_overflow(pc);
group_type::reset(pc);
--size_;
--size_ctrl.size;
}
void recover_slot(group_type* pg,std::size_t pos)
@@ -2043,7 +2032,7 @@ private:
}
delete_arrays(arrays);
arrays=new_arrays_;
ml=initial_max_load();
size_ctrl.ml=initial_max_load();
}
template<typename Value>
@@ -0,0 +1,73 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
#include <boost/core/allocator_access.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T> struct flat_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_MAP_TYPES_HPP
@@ -0,0 +1,44 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
#include <boost/core/allocator_access.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key> struct flat_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type = value_type;
static Key& value_from(element_type& x) { return x; }
static element_type&& move(element_type& x) { return std::move(x); }
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_FLAT_SET_TYPES_HPP
@@ -0,0 +1,131 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
#include <boost/core/allocator_access.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key, class T> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using value_type = std::pair<Key const, T>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using element_type = foa::element_type<value_type>;
static value_type& value_from(element_type const& x)
{
return *(x.p);
}
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static raw_key_type const& extract(element_type const& kv)
{
return kv.p->first;
}
static element_type&& move(element_type& x) { return std::move(x); }
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(value_type& x)
{
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A>
static void construct(A&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A>
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A>
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
destroy(al, p->p);
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_MAP_TYPES_HPP
@@ -0,0 +1,94 @@
// Copyright (C) 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#define BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
#include <boost/core/allocator_access.hpp>
#include <boost/core/pointer_traits.hpp>
namespace boost {
namespace unordered {
namespace detail {
namespace foa {
template <class Key> struct node_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type = foa::element_type<value_type>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
static element_type&& move(element_type& x) { return std::move(x); }
static value_type&& move(value_type& x) { return std::move(x); }
template <class A>
static void construct(
A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <typename Allocator>
static void construct(
Allocator&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*p->p),
1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A>
static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
}
}
};
} // namespace foa
} // namespace detail
} // namespace unordered
} // namespace boost
#endif // BOOST_UNORDERED_DETAIL_FOA_NODE_SET_TYPES_HPP
@@ -30,8 +30,44 @@ private:
private:
// number of times to spin before sleeping
static constexpr int spin_count = 24576;
// Effects: Provides a hint to the implementation that the current thread
// has been unable to make progress for k+1 iterations.
static void yield( unsigned k ) noexcept
{
unsigned const sleep_every = 1024; // see below
k %= sleep_every;
if( k < 5 )
{
// Intel recommendation from the Optimization Reference Manual
// Exponentially increase number of PAUSE instructions each
// iteration until reaching a maximum which is approximately
// one timeslice long (2^4 == 16 in our case)
unsigned const pause_count = 1u << k;
for( unsigned i = 0; i < pause_count; ++i )
{
boost::core::sp_thread_pause();
}
}
else if( k < sleep_every - 1 )
{
// Once the maximum number of PAUSE instructions is reached,
// we switch to yielding the timeslice immediately
boost::core::sp_thread_yield();
}
else
{
// After `sleep_every` iterations of no progress, we sleep,
// to avoid a deadlock if a lower priority thread has the lock
boost::core::sp_thread_sleep();
}
}
public:
@@ -51,22 +87,17 @@ public:
void lock_shared() noexcept
{
for( ;; )
for( unsigned k = 0; ; ++k )
{
for( int k = 0; k < spin_count; ++k )
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st < reader_lock_count_mask )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st < reader_lock_count_mask )
{
std::uint32_t newst = st + 1;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
boost::core::sp_thread_pause();
std::uint32_t newst = st + 1;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
boost::core::sp_thread_sleep();
yield( k );
}
}
@@ -102,73 +133,34 @@ public:
void lock() noexcept
{
for( ;; )
for( unsigned k = 0; ; ++k )
{
for( int k = 0; k < spin_count; ++k )
std::uint32_t st = state_.load( std::memory_order_relaxed );
if( st & locked_exclusive_mask )
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
// locked exclusive, spin
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// not locked exclusive, not locked shared, try to lock
if( st & locked_exclusive_mask )
{
// locked exclusive, spin
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// not locked exclusive, not locked shared, try to lock
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( st & writer_pending_mask )
{
// writer pending bit already set, nothing to do
}
else
{
// locked shared, set writer pending bit
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( st & writer_pending_mask )
{
// writer pending bit already set, nothing to do
}
else
{
// locked shared, set writer pending bit
std::uint32_t newst = st | writer_pending_mask;
state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed );
}
boost::core::sp_thread_pause();
std::uint32_t newst = st | writer_pending_mask;
state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed );
}
// clear writer pending bit before going to sleep
{
std::uint32_t st = state_.load( std::memory_order_relaxed );
for( ;; )
{
if( st & locked_exclusive_mask )
{
// locked exclusive, nothing to do
break;
}
else if( ( st & reader_lock_count_mask ) == 0 )
{
// lock free, try to take it
std::uint32_t newst = locked_exclusive_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_acquire, std::memory_order_relaxed ) ) return;
}
else if( ( st & writer_pending_mask ) == 0 )
{
// writer pending bit already clear, nothing to do
break;
}
else
{
// clear writer pending bit
std::uint32_t newst = st & ~writer_pending_mask;
if( state_.compare_exchange_weak( st, newst, std::memory_order_relaxed, std::memory_order_relaxed ) ) break;
}
}
}
boost::core::sp_thread_sleep();
yield( k );
}
}
+53 -4
View File
@@ -46,6 +46,12 @@ struct plain_integral
Integral n;
};
struct plain_size_control
{
std::size_t ml;
std::size_t size;
};
template<typename,typename,typename,typename>
class table;
@@ -117,6 +123,7 @@ public:
private:
template<typename,typename,bool> friend class table_iterator;
template<typename> friend class table_erase_return_type;
template<typename,typename,typename,typename> friend class table;
table_iterator(Group* pg,std::size_t n,const table_element_type* p_):
@@ -192,6 +199,45 @@ private:
table_element_type *p=nullptr;
};
/* Returned by table::erase([const_]iterator) to avoid iterator increment
* if discarded.
*/
template<typename Iterator>
class table_erase_return_type;
template<typename TypePolicy,typename Group,bool Const>
class table_erase_return_type<table_iterator<TypePolicy,Group,Const>>
{
using iterator=table_iterator<TypePolicy,Group,Const>;
using const_iterator=table_iterator<TypePolicy,Group,true>;
public:
/* can't delete it because VS in pre-C++17 mode needs to see it for RVO */
table_erase_return_type(const table_erase_return_type&);
operator iterator()const noexcept
{
auto it=pos;
it.increment(); /* valid even if *it was erased */
return iterator(const_iterator_cast_tag{},it);
}
template<
bool dependent_value=false,
typename std::enable_if<!Const||dependent_value>::type* =nullptr
>
operator const_iterator()const noexcept{return this->operator iterator();}
private:
template<typename,typename,typename,typename> friend class table;
table_erase_return_type(const_iterator pos_):pos{pos_}{}
table_erase_return_type& operator=(const table_erase_return_type&)=delete;
const_iterator pos;
};
/* foa::table interface departs in a number of ways from that of C++ unordered
* associative containers because it's not for end-user consumption
* (boost::unordered_(flat|node)_(map|set) wrappers complete it as
@@ -221,7 +267,7 @@ private:
template <typename TypePolicy,typename Hash,typename Pred,typename Allocator>
using table_core_impl=
table_core<TypePolicy,group15<plain_integral>,table_arrays,
std::size_t,Hash,Pred,Allocator>;
plain_size_control,Hash,Pred,Allocator>;
#include <boost/unordered/detail/foa/ignore_wshadow.hpp>
@@ -265,6 +311,7 @@ public:
has_mutable_iterator,
table_iterator<type_policy,group_type,false>,
const_iterator>::type;
using erase_return_type=table_erase_return_type<iterator>;
table(
std::size_t n=default_bucket_count,const Hash& h_=Hash(),
@@ -347,12 +394,14 @@ public:
typename std::enable_if<
has_mutable_iterator||dependent_value>::type* =nullptr
>
void erase(iterator pos)noexcept{return erase(const_iterator(pos));}
erase_return_type erase(iterator pos)noexcept
{return erase(const_iterator(pos));}
BOOST_FORCEINLINE
void erase(const_iterator pos)noexcept
erase_return_type erase(const_iterator pos)noexcept
{
super::erase(pos.pc,pos.p);
return {pos};
}
template<typename Key>
@@ -475,7 +524,7 @@ private:
if(loc){
return {make_iterator(loc),false};
}
if(BOOST_LIKELY(this->size_<this->ml)){
if(BOOST_LIKELY(this->size_ctrl.size<this->size_ctrl.ml)){
return {
make_iterator(
this->unchecked_emplace_at(pos0,hash,std::forward<Args>(args)...)),
+12 -62
View File
@@ -10,6 +10,7 @@
#pragma once
#endif
#include <boost/unordered/detail/foa/flat_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_map_fwd.hpp>
@@ -32,67 +33,10 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class Key, class T> struct flat_map_types
{
using key_type = Key;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using value_type = std::pair<Key const, T>;
using element_type = value_type;
static value_type& value_from(element_type& x) { return x; }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(element_type& x)
{
// TODO: we probably need to launder here
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_flat_map
{
using map_types = detail::flat_map_types<Key, T>;
using map_types = detail::foa::flat_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -101,8 +45,7 @@ namespace boost {
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(
unordered_flat_map<K, V, H, KE, A> const& lhs,
bool friend operator==(unordered_flat_map<K, V, H, KE, A> const& lhs,
unordered_flat_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Pred>
@@ -438,11 +381,18 @@ namespace boost {
.first;
}
BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); }
BOOST_FORCEINLINE void erase(const_iterator pos)
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
iterator pos)
{
return table_.erase(pos);
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
@@ -12,7 +12,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
+6 -32
View File
@@ -10,6 +10,7 @@
#pragma once
#endif
#include <boost/unordered/detail/foa/flat_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_flat_set_fwd.hpp>
@@ -30,38 +31,10 @@ namespace boost {
#pragma warning(disable : 4714) /* marked as __forceinline not inlined */
#endif
namespace detail {
template <class Key> struct flat_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type = value_type;
static Key& value_from(element_type& x) { return x; }
static element_type&& move(element_type& x) { return std::move(x); }
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
};
} // namespace detail
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_flat_set
{
using set_types = detail::flat_set_types<Key>;
using set_types = detail::foa::flat_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -70,8 +43,7 @@ namespace boost {
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(
unordered_flat_set<K, H, KE, A> const& lhs,
bool friend operator==(unordered_flat_set<K, H, KE, A> const& lhs,
unordered_flat_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Pred>
@@ -310,10 +282,12 @@ namespace boost {
return table_.emplace(std::forward<Args>(args)...).first;
}
BOOST_FORCEINLINE void erase(const_iterator pos)
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
@@ -12,7 +12,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
@@ -13,7 +13,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
+12 -111
View File
@@ -12,6 +12,7 @@
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/node_map_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_map_fwd.hpp>
@@ -35,112 +36,6 @@ namespace boost {
#endif
namespace detail {
template <class Key, class T> struct node_map_types
{
using key_type = Key;
using mapped_type = T;
using raw_key_type = typename std::remove_const<Key>::type;
using raw_mapped_type = typename std::remove_const<T>::type;
using init_type = std::pair<raw_key_type, raw_mapped_type>;
using value_type = std::pair<Key const, T>;
using moved_type = std::pair<raw_key_type&&, raw_mapped_type&&>;
using element_type=foa::element_type<value_type>;
static value_type& value_from(element_type const& x) { return *(x.p); }
template <class K, class V>
static raw_key_type const& extract(std::pair<K, V> const& kv)
{
return kv.first;
}
static raw_key_type const& extract(element_type const& kv)
{
return kv.p->first;
}
static element_type&& move(element_type& x) { return std::move(x); }
static moved_type move(init_type& x)
{
return {std::move(x.first), std::move(x.second)};
}
static moved_type move(value_type& x)
{
return {std::move(const_cast<raw_key_type&>(x.first)),
std::move(const_cast<raw_mapped_type&>(x.second))};
}
template <class A>
static void construct(A&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A>
static void construct(A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <class A, class... Args>
static void construct(A& al, init_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, init_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
using pointer_type = typename boost::allocator_pointer<A>::type;
using pointer_traits = boost::pointer_traits<pointer_type>;
destroy(al, p->p);
boost::allocator_deallocate(
al, pointer_traits::pointer_to(*(p->p)), 1);
}
}
};
template <class TypePolicy, class Allocator>
struct node_map_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
@@ -179,7 +74,7 @@ namespace boost {
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class unordered_node_map
{
using map_types = detail::node_map_types<Key, T>;
using map_types = detail::foa::node_map_types<Key, T>;
using table_type = detail::foa::table<map_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -188,8 +83,7 @@ namespace boost {
table_type table_;
template <class K, class V, class H, class KE, class A>
bool friend operator==(
unordered_node_map<K, V, H, KE, A> const& lhs,
bool friend operator==(unordered_node_map<K, V, H, KE, A> const& lhs,
unordered_node_map<K, V, H, KE, A> const& rhs);
template <class K, class V, class H, class KE, class A, class Pred>
@@ -564,11 +458,18 @@ namespace boost {
.first;
}
BOOST_FORCEINLINE void erase(iterator pos) { table_.erase(pos); }
BOOST_FORCEINLINE void erase(const_iterator pos)
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
iterator pos)
{
return table_.erase(pos);
}
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
@@ -12,7 +12,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
+6 -75
View File
@@ -12,6 +12,7 @@
#include <boost/unordered/detail/foa/element_type.hpp>
#include <boost/unordered/detail/foa/node_handle.hpp>
#include <boost/unordered/detail/foa/node_set_types.hpp>
#include <boost/unordered/detail/foa/table.hpp>
#include <boost/unordered/detail/type_traits.hpp>
#include <boost/unordered/unordered_node_set_fwd.hpp>
@@ -34,77 +35,6 @@ namespace boost {
#endif
namespace detail {
template <class Key> struct node_set_types
{
using key_type = Key;
using init_type = Key;
using value_type = Key;
static Key const& extract(value_type const& key) { return key; }
using element_type=foa::element_type<value_type>;
static value_type& value_from(element_type const& x) { return *x.p; }
static Key const& extract(element_type const& k) { return *k.p; }
static element_type&& move(element_type& x) { return std::move(x); }
static value_type&& move(value_type& x) { return std::move(x); }
template <class A>
static void construct(A& al, element_type* p, element_type const& copy)
{
construct(al, p, *copy.p);
}
template <typename Allocator>
static void construct(
Allocator&, element_type* p, element_type&& x) noexcept
{
p->p = x.p;
x.p = nullptr;
}
template <class A, class... Args>
static void construct(A& al, value_type* p, Args&&... args)
{
boost::allocator_construct(al, p, std::forward<Args>(args)...);
}
template <class A, class... Args>
static void construct(A& al, element_type* p, Args&&... args)
{
p->p = boost::to_address(boost::allocator_allocate(al, 1));
BOOST_TRY
{
boost::allocator_construct(al, p->p, std::forward<Args>(args)...);
}
BOOST_CATCH(...)
{
boost::allocator_deallocate(al,
boost::pointer_traits<
typename boost::allocator_pointer<A>::type>::pointer_to(*p->p),
1);
BOOST_RETHROW
}
BOOST_CATCH_END
}
template <class A> static void destroy(A& al, value_type* p) noexcept
{
boost::allocator_destroy(al, p);
}
template <class A> static void destroy(A& al, element_type* p) noexcept
{
if (p->p) {
destroy(al, p->p);
boost::allocator_deallocate(al,
boost::pointer_traits<typename boost::allocator_pointer<
A>::type>::pointer_to(*(p->p)),
1);
}
}
};
template <class TypePolicy, class Allocator>
struct node_set_handle
: public detail::foa::node_handle_base<TypePolicy, Allocator>
@@ -135,7 +65,7 @@ namespace boost {
template <class Key, class Hash, class KeyEqual, class Allocator>
class unordered_node_set
{
using set_types = detail::node_set_types<Key>;
using set_types = detail::foa::node_set_types<Key>;
using table_type = detail::foa::table<set_types, Hash, KeyEqual,
typename boost::allocator_rebind<Allocator,
@@ -144,8 +74,7 @@ namespace boost {
table_type table_;
template <class K, class H, class KE, class A>
bool friend operator==(
unordered_node_set<K, H, KE, A> const& lhs,
bool friend operator==(unordered_node_set<K, H, KE, A> const& lhs,
unordered_node_set<K, H, KE, A> const& rhs);
template <class K, class H, class KE, class A, class Pred>
@@ -423,10 +352,12 @@ namespace boost {
return table_.emplace(std::forward<Args>(args)...).first;
}
BOOST_FORCEINLINE void erase(const_iterator pos)
BOOST_FORCEINLINE typename table_type::erase_return_type erase(
const_iterator pos)
{
return table_.erase(pos);
}
iterator erase(const_iterator first, const_iterator last)
{
while (first != last) {
@@ -12,7 +12,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
@@ -13,7 +13,6 @@
#endif
#include <boost/functional/hash_fwd.hpp>
#include <boost/unordered/detail/fwd.hpp>
#include <functional>
#include <memory>
+1
View File
@@ -140,6 +140,7 @@ foa_tests(SOURCES exception/merge_exception_tests.cpp)
# CFOA tests
cfoa_tests(SOURCES cfoa/rw_spinlock_tests.cpp)
cfoa_tests(SOURCES cfoa/latch_tests.cpp)
cfoa_tests(SOURCES cfoa/insert_tests.cpp)
cfoa_tests(SOURCES cfoa/erase_tests.cpp)
+1
View File
@@ -177,6 +177,7 @@ alias foa_tests :
local CFOA_TESTS =
latch_tests
rw_spinlock_tests
insert_tests
erase_tests
try_emplace_tests
+67 -29
View File
@@ -157,7 +157,10 @@ namespace {
thread_runner(values, [&x, &values](boost::span<map_value_type> s) {
(void)s;
map_type y(values.begin(), values.end(), values.size());
map_type y(values.size());
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(x.empty());
BOOST_TEST(!y.empty());
@@ -183,8 +186,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_cc = +raii::copy_constructor;
@@ -218,8 +223,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_size = x.size();
auto const old_cc = +raii::copy_constructor;
@@ -227,7 +234,10 @@ namespace {
thread_runner(values, [&x, &values](boost::span<map_value_type> s) {
(void)s;
map_type y(values.begin(), values.end(), values.size());
map_type y(values.size());
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(!x.empty());
BOOST_TEST(!y.empty());
@@ -251,8 +261,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_cc = +raii::copy_constructor;
@@ -288,8 +300,11 @@ namespace {
raii::reset_counts();
pocca_map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), pocca_allocator_type(3));
pocca_map_type x(
values.size(), hasher(1), key_equal(2), pocca_allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_size = x.size();
auto const old_cc = +raii::copy_constructor;
@@ -297,7 +312,10 @@ namespace {
thread_runner(values, [&x, &values](boost::span<map_value_type> s) {
(void)s;
pocca_map_type y(values.begin(), values.end(), values.size());
pocca_map_type y(values.size());
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(!x.empty());
BOOST_TEST(!y.empty());
@@ -410,8 +428,10 @@ namespace {
values, [&x, &values, &num_transfers](boost::span<map_value_type> s) {
(void)s;
map_type y(values.begin(), values.end(), values.size(), hasher(2),
key_equal(1), allocator_type(3));
map_type y(values.size(), hasher(2), key_equal(1), allocator_type(3));
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(x.empty());
BOOST_TEST(!y.empty());
@@ -447,8 +467,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
@@ -496,8 +518,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_size = x.size();
auto const old_cc = +raii::copy_constructor;
@@ -509,8 +533,10 @@ namespace {
boost::span<map_value_type> s) {
(void)s;
map_type y(values.begin(), values.end(), values.size(), hasher(2),
key_equal(1), allocator_type(3));
map_type y(values.size(), hasher(2), key_equal(1), allocator_type(3));
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(!y.empty());
BOOST_TEST(x.get_allocator() == y.get_allocator());
@@ -549,8 +575,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_size = x.size();
auto const old_cc = +raii::copy_constructor;
@@ -562,8 +590,10 @@ namespace {
boost::span<map_value_type> s) {
(void)s;
map_type y(values.begin(), values.end(), values.size(), hasher(2),
key_equal(1), allocator_type(13));
map_type y(values.size(), hasher(2), key_equal(1), allocator_type(13));
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(
!boost::allocator_is_always_equal<allocator_type>::type::value);
@@ -608,8 +638,11 @@ namespace {
{
raii::reset_counts();
pocma_map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), pocma_allocator_type(3));
pocma_map_type x(
values.size(), hasher(1), key_equal(2), pocma_allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_size = x.size();
auto const old_cc = +raii::copy_constructor;
@@ -621,8 +654,11 @@ namespace {
boost::span<map_value_type> s) {
(void)s;
pocma_map_type y(values.begin(), values.end(), values.size(), hasher(2),
key_equal(1), pocma_allocator_type(13));
pocma_map_type y(
values.size(), hasher(2), key_equal(1), pocma_allocator_type(13));
for (auto const& v : values) {
y.insert(v);
}
BOOST_TEST(!y.empty());
BOOST_TEST(x.get_allocator() != y.get_allocator());
@@ -661,8 +697,10 @@ namespace {
{
raii::reset_counts();
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
map_type x(values.size(), hasher(1), key_equal(2), allocator_type(3));
for (auto const& v : values) {
x.insert(v);
}
auto const old_cc = +raii::copy_constructor;
auto const old_mc = +raii::move_constructor;
+3 -2
View File
@@ -31,8 +31,8 @@ namespace {
map_type x(values.begin(), values.end(), values.size(), hasher(1),
key_equal(2), allocator_type(3));
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::destructor, 0u);
auto const old_size = x.size();
auto const old_d = +raii::destructor;
thread_runner(values, [&x](boost::span<map_value_type> s) {
(void)s;
@@ -40,6 +40,7 @@ namespace {
});
BOOST_TEST(x.empty());
BOOST_TEST_EQ(raii::destructor, old_d + 2 * old_size);
check_raii_counts();
}
+13 -4
View File
@@ -605,6 +605,7 @@ namespace {
}
UNORDERED_AUTO_TEST (initializer_list_with_all_params) {
// hard-code 11 unique values
std::initializer_list<map_value_type> ilist{
map_value_type{raii{0}, raii{0}},
map_value_type{raii{1}, raii{1}},
@@ -640,7 +641,9 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal(2));
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * 11u);
}
check_raii_counts();
@@ -654,7 +657,9 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * 11u);
}
check_raii_counts();
@@ -668,7 +673,9 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * 11u);
}
check_raii_counts();
@@ -682,7 +689,9 @@ namespace {
BOOST_TEST_EQ(x.key_eq(), key_equal());
BOOST_TEST(x.get_allocator() == allocator_type(3));
BOOST_TEST_EQ(raii::move_constructor, 0u);
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * ilist.size());
BOOST_TEST_EQ(raii::move_constructor, 2 * 11u);
}
check_raii_counts();
}
+3 -1
View File
@@ -227,7 +227,9 @@ namespace {
{
X x(values.size());
x.insert(values.begin(), values.end());
for (auto const& v : values) {
x.insert(v);
}
BOOST_TEST_EQ(x.size(), reference_map.size());
BOOST_TEST_EQ(raii::destructor, 0u);
+9
View File
@@ -5,6 +5,7 @@
#include "helpers.hpp"
#include <boost/config/workaround.hpp>
#include <boost/unordered/concurrent_flat_map_fwd.hpp>
#include <limits>
test::seed_t initialize_seed{32304628};
@@ -58,4 +59,12 @@ UNORDERED_AUTO_TEST (fwd_unequal_call) {
BOOST_TEST_NOT(unequal_call(x1, x2));
}
// this isn't the best place for this test but it's better than introducing a
// new file
UNORDERED_AUTO_TEST (max_size) {
map_type x1;
BOOST_TEST_EQ(
x1.max_size(), std::numeric_limits<typename map_type::size_type>::max());
}
RUN_TESTS()
+67 -20
View File
@@ -8,6 +8,14 @@
#include <boost/core/ignore_unused.hpp>
struct raii_convertible
{
int x, y;
raii_convertible(int x_, int y_) : x{x_}, y{y_} {}
operator std::pair<raii const, raii>() { return {x, y}; }
};
namespace {
test::seed_t initialize_seed(78937);
@@ -95,9 +103,24 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
thread_runner(
values, [&x](boost::span<T> s) { x.insert(s.begin(), s.end()); });
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
thread_runner(values2, [&x](boost::span<raii_convertible> s) {
x.insert(s.begin(), s.end());
});
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// some versions of old gcc have trouble eliding copies here
// https://godbolt.org/z/Ebo6TbvaG
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
@@ -361,19 +384,31 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
x.insert_or_cvisit(
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_cvisit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_cvisit;
@@ -382,19 +417,31 @@ namespace {
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& p : values) {
values2.push_back(raii_convertible(p.first.x_, p.second.x_));
}
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values, [&x, &num_invokes](boost::span<T> s) {
x.insert_or_visit(
s.begin(), s.end(), [&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * values2.size());
#if BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_or_visit;
+4 -1
View File
@@ -63,7 +63,10 @@ namespace {
using map2_type = boost::unordered::concurrent_flat_map<raii, raii,
std::hash<raii>, std::equal_to<raii>, allocator_type>;
map2_type y(s.begin(), s.end(), s.size(), allocator_type(3));
map2_type y(s.size(), allocator_type(3));
for (auto const& v : s) {
y.insert(v);
}
expected_copies += 2 * y.size();
BOOST_TEST(x.get_allocator() == y.get_allocator());
+279
View File
@@ -0,0 +1,279 @@
// Copyright 2023 Peter Dimov
// Copyright 2023 Christian Mazakas
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include "helpers.hpp"
#include <boost/unordered/detail/foa/rw_spinlock.hpp>
#ifdef BOOST_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#ifdef BOOST_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#endif
#include <boost/thread/locks.hpp>
#include <boost/thread/lock_types.hpp>
#ifdef BOOST_GCC
#pragma GCC diagnostic pop
#endif
#ifdef BOOST_CLANG
#pragma GCC diagnostic pop
#endif
#include <mutex>
#include <shared_mutex>
#include <thread>
using boost::unordered::detail::foa::rw_spinlock;
static int lcount = 0;
UNORDERED_AUTO_TEST (rw_spinlock_test) {
rw_spinlock sp, sp2;
sp.lock();
sp2.lock();
sp.unlock();
sp2.unlock();
{
std::lock_guard<rw_spinlock> lock(sp);
std::lock_guard<rw_spinlock> lock2(sp2);
}
}
UNORDERED_AUTO_TEST (rw_spinlock_test2) {
rw_spinlock sp, sp2;
BOOST_TEST(sp.try_lock());
BOOST_TEST(!sp.try_lock());
BOOST_TEST(sp2.try_lock());
BOOST_TEST(!sp.try_lock());
BOOST_TEST(!sp2.try_lock());
sp.unlock();
sp2.unlock();
sp.lock();
BOOST_TEST(!sp.try_lock());
sp2.lock();
BOOST_TEST(!sp.try_lock());
BOOST_TEST(!sp2.try_lock());
sp.unlock();
sp2.unlock();
{
std::lock_guard<rw_spinlock> lock(sp);
BOOST_TEST(!sp.try_lock());
std::lock_guard<rw_spinlock> lock2(sp2);
BOOST_TEST(!sp.try_lock());
BOOST_TEST(!sp2.try_lock());
}
}
void f(rw_spinlock& sp, int n)
{
for (int i = 0; i < n; ++i) {
std::lock_guard<rw_spinlock> lock(sp);
++lcount;
}
}
UNORDERED_AUTO_TEST (rw_spinlock_test3) {
lcount = 0;
rw_spinlock sp;
int const N = 1000000; // iterations
int const M = 8; // threads
std::thread th[M];
for (int i = 0; i < M; ++i) {
th[i] = std::thread([=, &sp] { f(sp, N); });
}
for (int i = 0; i < M; ++i) {
th[i].join();
}
BOOST_TEST_EQ(lcount, N * M);
}
UNORDERED_AUTO_TEST (rw_spinlock_test4) {
rw_spinlock sp, sp2;
sp.lock();
sp2.lock_shared();
sp2.lock_shared();
sp.unlock();
sp2.unlock_shared();
sp2.unlock_shared();
{
std::lock_guard<rw_spinlock> lock(sp);
boost::shared_lock<rw_spinlock> lock2(sp2);
boost::shared_lock<rw_spinlock> lock3(sp2);
}
}
UNORDERED_AUTO_TEST (rw_spinlock_test5) {
rw_spinlock sp;
{
BOOST_TEST(sp.try_lock_shared());
BOOST_TEST(sp.try_lock_shared());
sp.unlock_shared();
sp.unlock_shared();
}
{
BOOST_TEST(sp.try_lock());
BOOST_TEST(!sp.try_lock_shared());
sp.unlock();
}
{
std::lock_guard<rw_spinlock> lock(sp);
BOOST_TEST(!sp.try_lock_shared());
}
{
boost::shared_lock<rw_spinlock> lock(sp);
BOOST_TEST(!sp.try_lock());
BOOST_TEST(sp.try_lock_shared());
sp.unlock_shared();
}
}
UNORDERED_AUTO_TEST (rw_spinlock_test6) {
lcount = 0;
rw_spinlock sp;
int const N = 1000000; // total iterations
int const M = 8; // threads
std::thread th[M];
for (int i = 0; i < M; ++i) {
int n = N;
th[i] = std::thread([n, &sp] {
for (;;) {
{
boost::shared_lock<rw_spinlock> lock(sp);
if (lcount >= n)
break;
}
{
std::lock_guard<rw_spinlock> lock(sp);
if (lcount >= n)
break;
++lcount;
}
}
});
}
for (int i = 0; i < M; ++i) {
th[i].join();
}
BOOST_TEST_EQ(lcount, N);
}
UNORDERED_AUTO_TEST (rw_spinlock_test7) {
rw_spinlock sp;
int const N = 1000000; // total iterations
int const M = 8; // threads
std::thread th[M];
for (int i = 0; i < M; ++i) {
int n = N;
th[i] = std::thread([=, &sp] {
for (;;) {
int oldc;
{
boost::shared_lock<rw_spinlock> lock(sp);
if (lcount >= n)
break;
oldc = lcount;
}
{
std::lock_guard<rw_spinlock> lock(sp);
if (lcount == oldc)
++lcount;
}
}
});
}
for (int i = 0; i < M; ++i) {
th[i].join();
}
BOOST_TEST_EQ(lcount, N);
}
UNORDERED_AUTO_TEST (rw_spinlock_test8) {
lcount = 0;
rw_spinlock sp;
int const N = 1000; // total iterations
int const M = 4; // threads
std::thread th[M];
for (int i = 0; i < M; ++i) {
int k = i;
int m = M;
int n = N;
th[i] = std::thread([k, m, n, &sp] {
for (int j = 0; j < n; ++j) {
int oldc;
for (;;) {
{
boost::shared_lock<rw_spinlock> lock(sp);
oldc = lcount;
}
if (oldc % m == k)
break;
}
{
std::lock_guard<rw_spinlock> lock(sp);
if (lcount == oldc)
++lcount;
}
}
});
}
for (int i = 0; i < M; ++i) {
th[i].join();
}
BOOST_TEST_EQ(lcount, N * M);
}
RUN_TESTS()
+1
View File
@@ -14,6 +14,7 @@
#include <boost/unordered/unordered_node_map.hpp>
#include <boost/unordered/unordered_node_set.hpp>
#include <boost/unordered/detail/implementation.hpp>
#include <boost/unordered/detail/fwd.hpp>
#else
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
+2 -2
View File
@@ -844,7 +844,7 @@ void unordered_copyable_test(X& x, Key& k, T& t, Hash& hf, Pred& eq)
a10.insert(t);
q = a10.cbegin();
#ifdef BOOST_UNORDERED_FOA_TESTS
BOOST_STATIC_ASSERT(std::is_same<void, decltype(a10.erase(q))>::value);
test::check_return_type<iterator>::convertible(a10.erase(q));
#else
test::check_return_type<iterator>::equals(a10.erase(q));
#endif
@@ -937,7 +937,7 @@ void unordered_movable_test(X& x, Key& k, T& /* t */, Hash& hf, Pred& eq)
a10.insert(boost::move(v5));
q = a10.cbegin();
#ifdef BOOST_UNORDERED_FOA_TESTS
BOOST_STATIC_ASSERT(std::is_same<void, decltype(a10.erase(q))>::value);
test::check_return_type<iterator>::convertible(a10.erase(q));
#else
test::check_return_type<iterator>::equals(a10.erase(q));
#endif
-11
View File
@@ -23,9 +23,7 @@ namespace erase_tests {
template <class Container>
void erase_tests1(Container*, test::random_generator generator)
{
#ifndef BOOST_UNORDERED_FOA_TESTS
typedef typename Container::iterator iterator;
#endif
typedef typename Container::const_iterator c_iterator;
BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Erase by key.\n";
@@ -59,12 +57,8 @@ namespace erase_tests {
while (size > 0 && !x.empty()) {
typename Container::key_type key = test::get_key<Container>(*x.begin());
std::size_t count = x.count(key);
#ifdef BOOST_UNORDERED_FOA_TESTS
x.erase(x.begin());
#else
iterator pos = x.erase(x.begin());
BOOST_TEST(pos == x.begin());
#endif
--size;
BOOST_TEST(x.count(key) == count - 1);
BOOST_TEST(x.size() == size);
@@ -95,15 +89,10 @@ namespace erase_tests {
typename Container::key_type key = test::get_key<Container>(*pos);
std::size_t count = x.count(key);
BOOST_TEST(count > 0);
#ifdef BOOST_UNORDERED_FOA_TESTS
x.erase(pos);
--size;
#else
BOOST_TEST(next == x.erase(pos));
--size;
if (size > 0)
BOOST_TEST(index == 0 ? next == x.begin() : next == test::next(prev));
#endif
BOOST_TEST(x.count(key) == count - 1);
if (x.count(key) != count - 1) {
BOOST_LIGHTWEIGHT_TEST_OSTREAM << count << " => " << x.count(key)
+2 -2
View File
@@ -213,8 +213,8 @@ UNORDERED_AUTO_TEST (allocator_check) {
typedef boost::allocator_rebind<A<int>, float>::type alloc_rebound;
alloc_rebound b;
A<int> a(b);
BOOST_ASSERT(alloc_rebound(a) == b);
BOOST_ASSERT(A<int>(b) == a);
BOOST_TEST(alloc_rebound(a) == b);
BOOST_TEST(A<int>(b) == a);
}
#ifdef BOOST_UNORDERED_FOA_TESTS
+2 -18
View File
@@ -1065,11 +1065,7 @@ typedef boost::unordered_map<int, int, transparent_hasher,
// test that in the presence of the member function template `erase()`, we still
// invoke the correct iterator overloads when the type is implicitly convertible
//
#ifdef BOOST_UNORDERED_FOA_TESTS
void
#else
transparent_unordered_map::iterator
#endif
map_erase_overload_compile_test()
{
convertible_to_iterator<transparent_unordered_map> c;
@@ -1079,11 +1075,7 @@ map_erase_overload_compile_test()
return map.erase(c);
}
#ifdef BOOST_UNORDERED_FOA_TESTS
void
#else
transparent_unordered_map::const_iterator
#endif
map_erase_const_overload_compile_test()
{
convertible_to_const_iterator<transparent_unordered_map> c;
@@ -1226,11 +1218,7 @@ typedef boost::unordered_multiset<int, transparent_hasher,
transparent_unordered_multiset;
#endif
#ifdef BOOST_UNORDERED_FOA_TESTS
void
#else
transparent_unordered_set::iterator
#endif
set_erase_overload_compile_test()
{
convertible_to_iterator<transparent_unordered_set> c;
@@ -1240,11 +1228,7 @@ set_erase_overload_compile_test()
return set.erase(c);
}
#ifdef BOOST_UNORDERED_FOA_TESTS
void
#else
transparent_unordered_set::const_iterator
#endif
set_erase_const_overload_compile_test()
{
convertible_to_const_iterator<transparent_unordered_set> c;
@@ -1657,7 +1641,7 @@ template <class UnorderedMap> void test_map_transparent_subscript(UnorderedMap*)
int key_count = key::count_;
map[0] = 7331;
BOOST_ASSERT(BOOST_TEST_EQ(key::count_, key_count));
BOOST_TEST_EQ(key::count_, key_count);
map[4] = 7331;
BOOST_TEST_EQ(key::count_, key_count + 1);
@@ -1680,7 +1664,7 @@ void test_map_non_transparent_subscript(UnorderedMap*)
int key_count = key::count_;
map[0] = 7331;
BOOST_ASSERT(BOOST_TEST_EQ(key::count_, key_count + 1));
BOOST_TEST_EQ(key::count_, key_count + 1);
key_count = key::count_;
map[4] = 7331;