|
1 | 1 | # SPDX-License-Identifier: BSD-3-Clause |
2 | | -# Copyright 2021, Intel Corporation |
| 2 | +# Copyright 2021-2022, Intel Corporation |
3 | 3 |
|
4 | 4 | /** @defgroup containers Containers |
5 | 5 | * Custom (but STL-like) containers for persistent memory. |
|
18 | 18 | * There is a separate Feature describing and listing all |
19 | 19 | * experimental containers, see @ref experimental_containers. |
20 | 20 | * |
21 | | - * # Rationale for implementing pmem-aware containers |
| 21 | + * ## Rationale for implementing pmem-aware containers |
22 | 22 | * |
23 | 23 | * The C++ standard library containers collection is something that persistent |
24 | 24 | * memory programmers may want to use. Containers manage the lifetime of held |
|
43 | 43 | * internal implementation details, libpmemobj-cpp persistent memory containers |
44 | 44 | * have the well-known STL-like interface and they work with STL algorithms. |
45 | 45 | * |
46 | | - * # Additional resources |
| 46 | + * ## Additional resources |
47 | 47 | * - [Blog post on pmem.io about libpmemobj-cpp persistent containers](https://pmem.io/2018/11/20/cpp-persistent-containers.html) |
48 | 48 | * - [A blog post about array container](https://pmem.io/2018/11/02/cpp-array.html) |
49 | 49 | * - [Very first description of (then yet experimental) vector container](https://pmem.io/2019/02/20/cpp-vector.html) |
|
73 | 73 | /** @defgroup transactions Transactions |
74 | 74 | * Transactional approach to store data on pmem. |
75 | 75 | * |
76 | | - * # General info about transactions |
| 76 | + * ## General info about transactions |
77 | 77 | * |
78 | 78 | * The heart of the libpmemobj are transactions. A transaction is defined as series of operations on |
79 | 79 | * **persistent memory objects** that either all occur, or nothing occurs. In particular, if the execution |
|
106 | 106 | * All operations between creating and destroying the transaction |
107 | 107 | * object are treated as performed in a transaction block and |
108 | 108 | * can be rolled back. |
109 | | - * The locks are held for the entire duration of the transaction. They |
110 | | - * are released at the end of the scope, so within the `catch` block, |
111 | | - * they are already unlocked. |
112 | 109 | * The best way to use manual transactions is by |
113 | 110 | * @ref pmem::obj::transaction::run, which is used in example above. |
114 | 111 | * |
|
117 | 114 | * performed in a transaction block and can be rolled back. |
118 | 115 | * If you have a C++17 compliant compiler, the automatic transaction will |
119 | 116 | * commit and abort automatically depending on the context of object's destruction. |
120 | | - * The locks are held for the entire duration of the transaction. They |
121 | | - * are released at the end of the scope, so within the `catch` block, |
| 117 | + * |
| 118 | + * In both approaches one of the parameters is the `locks`. They are held for the entire duration |
| 119 | + * of the transaction and they are released at the end of the scope - so within the `catch` block, |
122 | 120 | * they are already unlocked. |
123 | 121 | * |
124 | 122 | * If you want to read more and see example usages of both, you have to see |
125 | 123 | * flat or basic transaction documentation, because each implementation may differ. |
126 | 124 | * |
127 | | - * # Lifecycle and stages: |
| 125 | + * ## Lifecycle and stages: |
128 | 126 | * |
129 | 127 | * When you are using transaction API a transaction can be in one of the following states: |
130 | 128 | * - *TX_STAGE_NONE* - no open transaction in this thread |
|
137 | 135 | * in libpmemobj-cpp it's transparent for user, so please focus on relationships between stages. |
138 | 136 | * Look at the diagram below: |
139 | 137 | * |
140 | | - *  |
| 138 | + *  |
141 | 139 | * |
142 | 140 | * To be more familiar with functions used in diagram read e.g. **pmemobj_tx_begin**(3) manpage |
143 | 141 | * (C API for [libpmemobj](https://pmem.io/pmdk/libpmemobj/), link below in *Additional resources*). |
144 | 142 | * |
145 | 143 | * If you need to read general information about transaction move to the *Additional resources* section. |
146 | 144 | * |
147 | | - * # Example of flat_transaction |
| 145 | + * ## Example of flat_transaction |
148 | 146 | * For comparison with the previous snippet, here is a code snippet of |
149 | 147 | * @ref pmem::obj::flat_transaction which is listed below with basic explanation inline. |
150 | 148 | * @snippet transaction/transaction.cpp tx_nested_struct_example |
|
153 | 151 | * For more examples please look at the |
154 | 152 | * [examples directory](https://github.com/pmem/libpmemobj-cpp/tree/master/examples) in libpmemobj-cpp repository. |
155 | 153 | * |
156 | | - * # Additional resources |
| 154 | + * ## Additional resources |
157 | 155 | * - [pmemobj_tx_begin(3) manpage with transaction description (C API)](https://pmem.io/pmdk/manpages/linux/master/libpmemobj/pmemobj_tx_begin.3) |
158 | 156 | * - [blog post about transactions](https://pmem.io/2016/05/25/cpp-07.html) |
159 | 157 | * - [blog post about transactional allocations](https://pmem.io/2016/05/19/cpp-06.html) |
|
191 | 189 | * take a look at description of each class to get the details of their behavior. |
192 | 190 | */ |
193 | 191 |
|
194 | | -/** @defgroup synchronization Synchronization Primitives*/ |
| 192 | +/** @defgroup synchronization Synchronization Primitives |
| 193 | + * Persistent memory resident implementation of synchronization primitives. |
| 194 | + * |
| 195 | + * In concurrent programming, we often require mechanisms for synchronizing access to shared resources. |
| 196 | + * Typically to solve such issues we use synchronization primitives like mutexes and condition variables. |
| 197 | + * As persistent memory offers bigger capacity than DRAM it may be useful to store synchronization primitives |
| 198 | + * on it. Unfortunately such approach may cause performance degradation due to frequent writes to a memory |
| 199 | + * with relatively higher latency (it's because taking a lock or signaling a conditional variable often |
| 200 | + * requires additional writes). Few extra words how locks can be used in libpmemobj-cpp can be found in |
| 201 | + * @ref transactions. |
| 202 | + * |
| 203 | + * It's worth noticing that pmem locks are automatically released on recovery or when crash happened. |
| 204 | + * |
| 205 | + * ## Additional resources |
| 206 | + * - [Libpmemobj-cpp - lessons learned](https://pmem.io/blog/2021/09/libpmemobj-cpp-lessons-learned) |
| 207 | + * In this blog post we explain, i.a., why keeping locks on pmem is not a good idea. |
| 208 | + */ |
| 209 | + |
195 | 210 | /** @defgroup primitives Primitives |
196 | | - * Basic classes that provides PMEM-aware pointers and pool handlers. |
| 211 | + * Basic classes that provide PMEM-aware pointers and pool handlers. |
197 | 212 | * |
198 | 213 | * ## Pointers |
199 | 214 | * There are few types to handle data on PMEM. |
200 | 215 | * - @ref pmem::obj::persistent_ptr<T> - implements a smart pointer. |
201 | 216 | * It encapsulates the [PMEMoid](https://pmem.io/2015/06/11/type-safety-macros.html) fat |
202 | 217 | * pointer and provides member access, dereference and array access operators. |
203 | 218 | * - @ref pmem::obj::experimental::self_relative_ptr<T> - implements a smart ptr. |
204 | | - * It encapsulates the self offsetted pointer and provides member access, dereference and array access operators. |
| 219 | + * It encapsulates the self-offsetted pointer and provides member access, dereference and array access operators. |
205 | 220 | * self_relative_ptr in comparison to persistent_ptr is: |
206 | | - * - smaller size (8B vs 16B) |
207 | | - * - can be used with atomic operations |
208 | | - * - faster dereference (important in loop), also allows vectorization |
209 | | - * - if stored in a persistent memory pool, it can only points to elements within the same pool |
| 221 | + * - smaller in size (8B vs 16B), |
| 222 | + * - can be used with atomic operations, |
| 223 | + * - dereferenced faster (it's important, e.g., in loops), |
| 224 | + * - allows vectorization, |
| 225 | + * - if stored in a persistent memory pool, it can only points to elements within the same pool. |
210 | 226 | * - @ref pmem::obj::p<T> - template class that can be used for all variables (except persistent pointers), |
211 | 227 | * which are used in @ref transactions. |
212 | 228 | * This class is not designed to be used with compound types. For that see the @ref pmem::obj::persistent_ptr. |
|
216 | 232 | * exactly once per instance of the application. This class has 8 bytes of storage overhead. |
217 | 233 | * |
218 | 234 | * ## Pool handles |
219 | | - * Pool class provides basic operations on pmemobj [pools](https://pmem.io/2016/05/10/cpp-05.html). C++ API for pools should |
220 | | - * not be mixed with C API. For example explicitly calling pmemobj_set_user_data(pop) |
| 235 | + * Pool class provides basic operations on pmemobj [pools](https://pmem.io/2016/05/10/cpp-05.html). |
| 236 | + * C++ API for pools should not be mixed with C API. For example explicitly calling `pmemobj_set_user_data(pop)` |
221 | 237 | * on pool which is handled by C++ pool object is undefined behaviour. |
222 | 238 | * |
223 | 239 | * There are few pool handlers: |
|
234 | 250 | * Possible exceptions that could be thrown by the libpmemobj++. |
235 | 251 | * |
236 | 252 | * In runtime, some operations may fail, then all you need is to catch the exception. |
237 | | - * Every pmem exception has **std::runtime_error** in its inheritance tree |
238 | | - * and contains proper message with an error description. |
239 | | - * All exceptions can be caught using just **std::runtime_error**. |
| 253 | + * Every pmem exception has `std::runtime_error` in its inheritance tree, which means |
| 254 | + * that all exceptions can be caught using just this type. Each exception contains |
| 255 | + * proper message with an error description. |
| 256 | + * |
240 | 257 | * Look at the list on this page to explore all exceptions with their descriptions. |
241 | 258 | * |
242 | 259 | * Transaction handles uncaught exceptions thrown inside its scope, then aborts |
|
247 | 264 | * Let's consider following example: |
248 | 265 | * @snippet examples/mpsc_queue/mpsc_queue.cpp mpsc_main |
249 | 266 | * |
250 | | - * There are plenty of try-catch blocks placed to handle possible errors that can happen in some conditions. |
251 | | - * E.g. @ref pmem::obj::pool<T>::open can lead to @ref pmem::pool_error. |
252 | | - * Next exception, **std::exception**, is placed to handle possible errors during allocation, |
253 | | - * coming from @ref pmem::obj::make_persistent. Worth being careful using every new function |
254 | | - * because some of exceptions are not obvious, e.g., pmem::obj::pool<T>::close |
255 | | - * at the end of the code which may throw **std::logic_error**. |
| 267 | + * There are plenty of try-catch blocks placed to handle possible errors that can occur in some |
| 268 | + * conditions. E.g. @ref pmem::obj::pool<T>::open can lead to @ref pmem::pool_error. |
| 269 | + * The next exception, **std::exception**, is placed to handle possible errors during allocation, |
| 270 | + * coming from @ref pmem::obj::make_persistent. Worth being careful using any new function |
| 271 | + * because some exceptions are not obvious, e.g., pmem::obj::pool<T>::close |
| 272 | + * at the end of the code, which may throw **std::logic_error**. |
256 | 273 | * |
257 | | - * You should check every function you will use in context of possible |
258 | | - * exceptions and then handle them to avoid crash. |
259 | | - **/ |
| 274 | + * You should check every function you will use in the context of possible |
| 275 | + * exceptions and then handle them to avoid a crash. |
| 276 | + */ |
0 commit comments