Skip to content
This repository was archived by the owner on Mar 22, 2023. It is now read-only.

Commit c22738c

Browse files
authored
Merge pull request #1201 from lukaszstolarczuk/expand-containers-docs
docs: update and extend containers information
2 parents f45d359 + b421088 commit c22738c

File tree

15 files changed

+222
-115
lines changed

15 files changed

+222
-115
lines changed

CONTRIBUTING.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ make cppformat
3636
## Doxygen documentation
3737

3838
* Each new feature should be documented using doxygen comment blocks.
39-
* Each non-member function should be added to corresponding class documentation by `@relates` tag
39+
* Each non-member function should be added to corresponding class documentation by `@relates` tag.
4040
* Each new API should be assigned to group by `@ingroup` tag. See [groups definitions file](doc/groups_definitions.dox) in the repository for details.
4141

4242
# Submitting Pull Requests
@@ -95,25 +95,29 @@ Author: Random J Developer <[email protected]>
9595
When developing a persistent container make sure you follow the rules and steps described here.
9696

9797
## Steps
98-
1. Create container implementation
99-
2. Create doc_snippet/example
100-
3. Add TEST_CONTAINER CMake option for enabling/disabling container's testing.
101-
4. Add tests (+ if possible, enable libcxx tests in tests/external/libcxx)
10298

103-
## Requirements:
99+
1. Create container implementation.
100+
2. Create doc_snippet(s) and example(s).
101+
3. Add `TEST_<CONTAINER>` CMake option for enabling/disabling container's testing.
102+
4. Add tests (including, if possible, adding new/enabling existing libcxx tests in `tests/external/libcxx`).
103+
104+
## Requirements
105+
104106
* Constructors should check whether the container is being constructed on pmem and within a transaction.
105107
If not, appropriate exception should be thrown.
106108
* If any operation has to or is not allowed to be called inside of a transaction there should be a proper check for that.
107-
* Operations which modify the entire container (like operator=, clear(), etc.) should be transactional.
108-
* If any operation inside destructor can fail there should be a separate method like free_data/destroy.
109-
* Each complex container should have a mechanism for checking layout compatibility. One way is to store size of key and value on pmem and compare it with sizeof(value_type) after pool reopen.
109+
* Operations which modify the entire container (like `operator=`, `clear()`, etc.) should be transactional.
110+
* If any operation inside destructor can fail there should be a separate method like `free_data` or `destroy`.
111+
* Each complex container should have a mechanism for checking layout compatibility.
112+
One way is to store size of key and value on pmem and compare it with `sizeof(value_type)` after pool reopen.
110113
* Padding should always be explicit.
111-
* If any object from standard library is being stored on pmem its size should be verified by static_assert.
114+
* If any object from the standard library is being stored on pmem, its size should be verified by `static_assert`.
112115
* Public methods should be documented using doxygen comments.
113116

114-
## Optional features:
115-
* for_each_ptr method for defragmentation purposes.
116-
* Containers with lower memory overhead (like vector) can implement some heuristic for verifying layout (like comparing pmemobj_alloc_usable_size with expected capacity).
117+
## Optional features
118+
119+
* `for_each_ptr` method for defragmentation purposes.
120+
* Containers with lower memory overhead (like vector) can implement some heuristic for verifying layout (like comparing `pmemobj_alloc_usable_size` with expected capacity).
117121

118122
# Configuring GitHub fork
119123

doc/groups_definitions.dox

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,74 @@
11
# SPDX-License-Identifier: BSD-3-Clause
22
# Copyright 2021, Intel Corporation
33

4-
/** @defgroup containers Containers*/
5-
/** @defgroup experimental_containers Experimental Containers */
4+
/** @defgroup containers Containers
5+
* Custom (but STL-like) containers for persistent memory.
6+
*
7+
* Persistent containers are implemented-from-scratch with optimized on-media layouts
8+
* and algorithms to fully exploit the potential and features of persistent memory.
9+
* Besides specific internal implementation details, libpmemobj-cpp persistent memory
10+
* containers have (where possible) the well-known STL-like interface and they work
11+
* with STL algorithms. Containers' methods guarantee atomicity, consistency and
12+
* durability. Note that every single modifier method (any one modifying the state of
13+
* the container, like resize or push_back) opens transaction internally and guarantees
14+
* full exception safety (modifications will be either committed or rolled-back
15+
* if an exception was thrown, or crash happened - for more info see @ref transactions).
16+
* There is no need for using transaction when calling modifier methods whatsoever.
17+
*
18+
* There is a separate Feature describing and listing all
19+
* experimental containers, see @ref experimental_containers.
20+
*
21+
* # Rationale for implementing pmem-aware containers
22+
*
23+
* The C++ standard library containers collection is something that persistent
24+
* memory programmers may want to use. Containers manage the lifetime of held
25+
* objects through allocation/creation and deallocation/destruction with the use of
26+
* allocators. Implementing custom persistent allocator for C++ STL (Standard
27+
* Template Library) containers has two main downsides:
28+
*
29+
* Implementation details:
30+
* - STL containers do not use algorithms optimal from persistent memory programming point of view.
31+
* - Persistent memory containers should have durability and consistency properties,
32+
* while not every STL method guarantees strong exception safety.
33+
* - Persistent memory containers should be designed with an awareness of
34+
* fragmentation limitations.
35+
*
36+
* Memory layout:
37+
* - The STL does not guarantee that the container layout will remain unchanged in new library versions.
38+
*
39+
* Due to these obstacles, the libpmemobj-cpp contains the set of custom,
40+
* implemented-from-scratch containers with optimized on-media layouts and
41+
* algorithms to fully exploit the potential and features of persistent memory.
42+
* These methods guarantee atomicity, consistency and durability. Besides specific
43+
* internal implementation details, libpmemobj-cpp persistent memory containers
44+
* have the well-known STL-like interface and they work with STL algorithms.
45+
*
46+
* # Additional resources
47+
* - [Blog post on pmem.io about libpmemobj-cpp persistent containers](https://pmem.io/2018/11/20/cpp-persistent-containers.html)
48+
* - [A blog post about array container](https://pmem.io/2018/11/02/cpp-array.html)
49+
* - [Very first description of (then yet experimental) vector container](https://pmem.io/2019/02/20/cpp-vector.html)
50+
*
51+
* For curious readers - back then in 2017 - there was an approach to re-use and modify the
52+
* STL containers to be pmem-aware. Story of implementing this experiment can be found in
53+
* [the blog post on pmem.io](https://pmem.io/2017/07/10/cpp-containers.html).
54+
* The repo with that code is now dead, but left for the reference and historical perspective.
55+
*/
56+
57+
/** @defgroup experimental_containers Experimental Containers
58+
* PMem containers under development and/or in testing.
59+
*
60+
* It's very important to underline:
61+
* @note **Experimental API may change (with no backward compatibility)
62+
* and it should not be used in a production environment**
63+
*
64+
* A container is considered **experimental** because:
65+
* - it may not be a 100% functionally ready,
66+
* - tests are not finished and there might be some gaps/corner cases to be discovered and fixed,
67+
* - we may still tweak the API (if we find it's not well defined),
68+
* - its layout may change.
69+
*
70+
* For general containers' description please see @ref containers.
71+
*/
672

773
/** @defgroup transactions Transactions
874
* This section introduces transaction feature in libpmemobj-cpp.

include/libpmemobj++/README.md

Lines changed: 14 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -105,39 +105,17 @@ Be aware that the old behavior can lead to segfaults in some cases
105105

106106
# Persistent containers
107107

108-
The C++ standard library containers collection is something that persistent
109-
memory programmers may want to use. Containers manage the lifetime of held
110-
objects through allocation/creation and deallocation/destruction with the use of
111-
allocators. Implementing custom persistent allocator for C++ STL (Standard
112-
Template Library) containers has two main downsides:
113-
114-
Implementation details:
115-
- STL containers do not use algorithms optimal from persistent memory programming point of view.
116-
- Persistent memory containers should have durability and consistency properties,
117-
while not every STL method guarantees strong exception safety.
118-
- Persistent memory containers should be designed with an awareness of
119-
fragmentation limitations.
120-
121-
Memory layout:
122-
- The STL does not guarantee that the container layout will remain unchanged in new library versions.
123-
124-
Due to these obstacles, the libpmemobj-cpp contains the set of custom,
125-
implemented-from-scratch containers with optimized on-media layouts and
126-
algorithms to fully exploit the potential and features of persistent memory.
127-
These methods guarantee atomicity, consistency and durability. Besides specific
128-
internal implementation details, libpmemobj-cpp persistent memory containers
129-
have the well-known STL-like interface and they work with STL algorithms.
130-
131-
## Available containers
132-
133-
* array with STL-like interface - [pmem::obj::array](@ref pmem::obj::array)
134-
* string with STL-like interface - [pmem::obj::string](@ref pmem::obj::basic_string)
135-
* vector with STL-like interface - [pmem::obj::vector](@ref pmem::obj::vector)
136-
* segment_vector with std::vector-like interface (no STL counterpart) -
137-
[pmem::obj::segment_vector](@ref pmem::obj::segment_vector)
138-
* concurrent_hash_map (no STL counterpart) -
139-
[pmem::obj::concurrent_hash_map](@ref pmem::obj::concurrent_hash_map)
140-
* radix_tree (partially compatible with std::map) -
141-
[pmem::obj::experimental::radix_tree](@ref pmem::obj::experimental::radix_tree)
142-
* concurrent_map (partially compatible with std::map) -
143-
[pmem::obj::experimental::concurrent_map](@ref pmem::obj::experimental::concurrent_map)
108+
Persistent containers are implemented-from-scratch with optimized on-media layouts
109+
and algorithms to fully exploit the potential and features of persistent memory.
110+
Methods implemented within containers guarantee atomicity, consistency and durability.
111+
Besides specific internal implementation details, libpmemobj-cpp persistent memory containers
112+
have the well-known STL-like interfaces and they work with STL algorithms.
113+
114+
For more information and a complete list of available containers
115+
(including experimental containers and specializations) see @ref containers.
116+
117+
<!--
118+
If you're reading this in a markdown readme file you can found
119+
Containers Feature description in our Doxygen documentation online:
120+
https://pmem.io/libpmemobj-cpp/master/doxygen/group__containers.html
121+
-->

include/libpmemobj++/container/array.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace obj
2929
{
3030

3131
/**
32-
* pmem::obj::array - persistent container with std::array compatible interface.
32+
* Persistent container with std::array compatible interface.
3333
*
3434
* pmem::obj::array can only be stored on pmem. Creating array on
3535
* stack will result with "pool_error" exception.
@@ -132,7 +132,7 @@ struct array {
132132
* Copy assignment operator - perform assignment from other
133133
* pmem::obj::array.
134134
*
135-
* This function creates a transaction internally.
135+
* This function internally creates a transaction.
136136
*
137137
* @throw transaction_error when adding the object to the
138138
* transaction failed.
@@ -163,7 +163,7 @@ struct array {
163163
* Move assignment operator - perform move assignment from other
164164
* pmem::obj::array.
165165
*
166-
* This function creates a transaction internally.
166+
* This function internally creates a transaction.
167167
*
168168
* @throw transaction_error when adding the object to the
169169
* transaction failed.

include/libpmemobj++/container/basic_string.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,13 @@ namespace obj
3434
{
3535

3636
/**
37-
* pmem::obj::string - persistent container with std::basic_string compatible
37+
* Persistent string container with std::basic_string compatible
3838
* interface.
3939
*
40-
* The implementation is still missing some methods.
40+
* The implementation is still missing some methods, but it doesn't
41+
* impact regular use of this container.
4142
*
42-
* Simple example of pmem::obj::string usage
43+
* Simple example of pmem::obj::string usage:
4344
* @snippet string/string.cpp string_example
4445
* @ingroup containers
4546
*/

include/libpmemobj++/container/concurrent_hash_map.hpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ class scoped_lock_traits<ScopedLockType, false> {
222222
return false;
223223
}
224224
};
225-
}
225+
} /* namespace concurrent_hash_map_internal */
226226

227227
template <typename Key, typename T, typename Hash = std::hash<Key>,
228228
typename KeyEqual = std::equal_to<Key>,
@@ -1375,7 +1375,7 @@ class hash_map_base {
13751375
}; /* End of class hash_map_base */
13761376

13771377
/**
1378-
* Meets requirements of a forward iterator for STL
1378+
* Meets requirements of a forward iterator for STL.
13791379
* Value is either the T or const T type of the container.
13801380
* @ingroup containers
13811381
*/
@@ -1572,7 +1572,9 @@ operator!=(const hash_map_iterator<Container, M> &i,
15721572
/** @endcond */
15731573

15741574
/**
1575-
* Persistent memory aware implementation of Intel TBB concurrent_hash_map.
1575+
* Persistent memory aware implementation of Intel TBB
1576+
* [concurrent_hash_map](https://spec.oneapi.io/versions/0.5.0/oneTBB/containers/concurrent_hash_map_cls.html)
1577+
*
15761578
* The implementation is based on a concurrent hash table algorithm
15771579
* (https://arxiv.org/ftp/arxiv/papers/1509/1509.02235.pdf) where elements
15781580
* are assigned to buckets based on a hash code calculated from a key.
@@ -1581,6 +1583,9 @@ operator!=(const hash_map_iterator<Container, M> &i,
15811583
* of an array of buckets, and each bucket consists of a list of nodes and a
15821584
* read-write lock to control concurrent access by multiple threads.
15831585
*
1586+
* There's no STL counterpart for this container (only the TBB's
1587+
* implementation).
1588+
*
15841589
* Each time, the pool with concurrent_hash_map is being opened, the
15851590
* concurrent_hash_map requires runtime_initialize() to be called (in order to
15861591
* recalculate mask and restore the size).
@@ -1609,10 +1614,9 @@ operator!=(const hash_map_iterator<Container, M> &i,
16091614
* improve performance if MutexType supports efficient upgrading and
16101615
* downgrading operations.
16111616
*
1612-
* Testing note:
1613-
* In some case, helgrind and drd might report lock ordering errors for
1614-
* concurrent_hash_map. This might happen when calling find, insert or erase
1615-
* while already holding an accessor to some element.
1617+
* @note In some cases, when testing with Valgrind, helgrind and drd might
1618+
* report lock ordering errors. This might happen when calling find, insert or
1619+
* erase while already holding an accessor to some element.
16161620
*
16171621
* The typical usage example would be:
16181622
* @snippet concurrent_hash_map/concurrent_hash_map.cpp concurrent_hash_map_ex

include/libpmemobj++/container/segment_vector.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,8 @@ using exponential_size_vector_policy =
484484
SegmentType>;
485485

486486
/**
487-
* A persistent version of segment vector implementation.
487+
* Persistent version of segment vector with std::vector compatible
488+
* interface.
488489
*
489490
* Segment table is a data type with a vector-like interface. Differences
490491
* are: it does not do reallocations and iterators are not invalidated

include/libpmemobj++/container/string.hpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: BSD-3-Clause
2-
/* Copyright 2019, Intel Corporation */
2+
/* Copyright 2019-2021, Intel Corporation */
33

44
/**
55
* @file
@@ -17,9 +17,28 @@ namespace pmem
1717
namespace obj
1818
{
1919

20+
/**
21+
* The most typical string usage - the char specialization.
22+
* @ingroup containers
23+
*/
2024
using string = basic_string<char>;
25+
26+
/**
27+
* The wide char specialization.
28+
* @ingroup containers
29+
*/
2130
using wstring = basic_string<wchar_t>;
31+
32+
/**
33+
* The char16 specialization.
34+
* @ingroup containers
35+
*/
2236
using u16string = basic_string<char16_t>;
37+
38+
/**
39+
* The char32 specialization.
40+
* @ingroup containers
41+
*/
2342
using u32string = basic_string<char32_t>;
2443

2544
} /* namespace obj */

include/libpmemobj++/container/vector.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ namespace obj
3434
{
3535

3636
/**
37-
* pmem::obj::vector - persistent container with std::vector compatible
38-
* interface.
37+
* Persistent container with std::vector compatible interface.
3938
* @ingroup containers
4039
*/
4140
template <typename T>

include/libpmemobj++/detail/ebr.hpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
/**
3131
* @file
32-
* C++ EBR API.
32+
* C++ Epoch-based reclamation API.
3333
*/
3434

3535
#ifndef LIBPMEMOBJ_EBR_HPP
@@ -51,13 +51,7 @@ namespace detail
5151
{
5252

5353
/**
54-
* Epoch-based reclamation (EBR). Reference:
55-
*
56-
* K. Fraser, Practical lock-freedom,
57-
* Technical Report UCAM-CL-TR-579, February 2004
58-
* https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
59-
*
60-
* Summary:
54+
* Epoch-based reclamation (EBR).
6155
*
6256
* Any workers (threads or processes) actively referencing (accessing)
6357
* the globally visible objects must do that in the critical path covered
@@ -66,7 +60,10 @@ namespace detail
6660
* each epoch). Objects in the current global epoch can be staged for
6761
* reclamation (garbage collection). Then, the objects in the target epoch can
6862
* be reclaimed after two successful increments of the global epoch. Only three
69-
* epochs are needed (e, e-1 and e-2), therefore we use clock arithmetics.
63+
* epochs are needed (e, e-1 and e-2), therefore we use clock arithmetic.
64+
*
65+
* \see [K. Fraser, Practical lock-freedom, Technical Report UCAM-CL-TR-579,
66+
* February 2004](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf)
7067
*/
7168
class ebr {
7269
using atomic = std::atomic<size_t>;

0 commit comments

Comments
 (0)