This repository was archived by the owner on Mar 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 76
/
Copy pathgroups_definitions.dox
276 lines (268 loc) · 15.4 KB
/
groups_definitions.dox
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# SPDX-License-Identifier: BSD-3-Clause
# Copyright 2021-2022, Intel Corporation
/** @defgroup containers Containers
* Custom (but STL-like) containers for persistent memory.
*
* Persistent containers are implemented-from-scratch with optimized on-media layouts
* and algorithms to fully exploit the potential and features of persistent memory.
* Besides specific internal implementation details, libpmemobj-cpp persistent memory
* containers have (where possible) the well-known STL-like interface and they work
* with STL algorithms. Containers' methods guarantee atomicity, consistency and
* durability. Note that every single modifier method (any one modifying the state of
* the container, like resize or push_back) opens transaction internally and guarantees
* full exception safety (modifications will be either committed or rolled-back
* if an exception was thrown, or crash happened - for more info see @ref transactions).
* There is no need for using transaction when calling modifier methods whatsoever.
*
* There is a separate Feature describing and listing all
* experimental containers, see @ref experimental_containers.
*
* ## Rationale for implementing pmem-aware containers
*
* The C++ standard library containers collection is something that persistent
* memory programmers may want to use. Containers manage the lifetime of held
* objects through allocation/creation and deallocation/destruction with the use of
* allocators. Implementing custom persistent allocator for C++ STL (Standard
* Template Library) containers has two main downsides:
*
* Implementation details:
* - STL containers do not use algorithms optimal from persistent memory programming point of view.
* - Persistent memory containers should have durability and consistency properties,
* while not every STL method guarantees strong exception safety.
* - Persistent memory containers should be designed with an awareness of
* fragmentation limitations.
*
* Memory layout:
* - The STL does not guarantee that the container layout will remain unchanged in new library versions.
*
* Due to these obstacles, the libpmemobj-cpp contains the set of custom,
* implemented-from-scratch containers with optimized on-media layouts and
* algorithms to fully exploit the potential and features of persistent memory.
* These methods guarantee atomicity, consistency and durability. Besides specific
* internal implementation details, libpmemobj-cpp persistent memory containers
* have the well-known STL-like interface and they work with STL algorithms.
*
* ## Additional resources
* - [Blog post on pmem.io about libpmemobj-cpp persistent containers](https://pmem.io/2018/11/20/cpp-persistent-containers.html)
* - [A blog post about array container](https://pmem.io/2018/11/02/cpp-array.html)
* - [Very first description of (then yet experimental) vector container](https://pmem.io/2019/02/20/cpp-vector.html)
*
* For curious readers - back then in 2017 - there was an approach to re-use and modify the
* STL containers to be pmem-aware. Story of implementing this experiment can be found in
* [the blog post on pmem.io](https://pmem.io/2017/07/10/cpp-containers.html).
* The repo with that code is now dead, but left for the reference and historical perspective.
*/
/** @defgroup experimental_containers Experimental Containers
* PMem containers under development and/or in testing.
*
* It's very important to underline:
* @note **Experimental API may change (with no backward compatibility)
* and it should not be used in a production environment**
*
* A container is considered **experimental** because:
* - it may not be a 100% functionally ready,
* - tests are not finished and there might be some gaps/corner cases to be discovered and fixed,
* - we may still tweak the API (if we find it's not well defined),
* - its layout may change.
*
* For general containers' description please see @ref containers.
*/
/** @defgroup transactions Transactions
* Transactional approach to store data on pmem.
*
* ## General info about transactions
*
* The heart of the libpmemobj are transactions. A transaction is defined as series of operations on
* **persistent memory objects** that either all occur, or nothing occurs. In particular, if the execution
* of a transaction is interrupted by a power failure or a system crash, it is guaranteed that after
* system restart, all the changes made as a part of the uncompleted transaction will be rolled back,
* restoring the consistent state of the memory pool from the moment when the transaction was started.
*
* @note Any operations not using libpmemobj-cpp API (like `int x = 5` or `malloc()`) used within
* transaction will not be a part ot the transaction and won't be rolled back on failure! To properly
* store variables on pmem use @ref primitives , to allocate data on pmem see @ref allocation functions.
*
* In C++ bindings (*this library*) transactions were designed (in comparison to C API)
* to be as developer-friendly as possible. Even though libpmemobj++ are the bindings you should not
* mix these two APIs - using libpmemobj (C API) in C++ application will not work!
*
* Let's take a look at the following snippet:
*
* @snippet transaction/transaction.cpp general_tx_example
*
* Code above is an example how automatic transaction can look like.
* After object creation there are a few statements executed within a transaction.
* Transaction will be committed during *tx* object's destruction at the end of the scope.
*
* It's worth noticing that @ref pmem::obj::flat_transaction is recommended to use over @ref pmem::obj::basic_transaction.
* An extra explanation is provided inline an example in @ref pmem::obj::flat_transaction description.
*
* Mentioned above transactions are handled through two internal classes:
*
* - **manual** transactions has to be committed explicitly, otherwise it will abort.
* All operations between creating and destroying the transaction
* object are treated as performed in a transaction block and
* can be rolled back.
* The best way to use manual transactions is by
* @ref pmem::obj::transaction::run, which is used in example above.
*
* - **automatic** transactions are only available in C++17. All operations
* between creating and destroying the transaction object are treated as
* performed in a transaction block and can be rolled back.
* If you have a C++17 compliant compiler, the automatic transaction will
* commit and abort automatically depending on the context of object's destruction.
*
* In both approaches one of the parameters is the `locks`. They are held for the entire duration
* of the transaction and they are released at the end of the scope - so within the `catch` block,
* they are already unlocked.
*
* If you want to read more and see example usages of both, you have to see
* flat or basic transaction documentation, because each implementation may differ.
*
* ## Lifecycle and stages:
*
* When you are using transaction API a transaction can be in one of the following states:
* - *TX_STAGE_NONE* - no open transaction in this thread
* - *TX_STAGE_WORK* - the transaction in progress
* - *TX_STAGE_ONCOMMIT* - the transaction is successfully committed
* - *TX_STAGE_FINALLY* - ready for clean up
* - *TX_STAGE_ONABORT* - starting the transaction failed or transaction aborted
*
* Moving from one stage to another is possible under some conditions, but
* in libpmemobj-cpp it's transparent for user, so please focus on relationships between stages.
* Look at the diagram below:
*
* 
*
* To be more familiar with functions used in diagram read e.g. **pmemobj_tx_begin**(3) manpage
* (C API for [libpmemobj](https://pmem.io/pmdk/libpmemobj/), link below in *Additional resources*).
*
* If you need to read general information about transaction move to the *Additional resources* section.
*
* ## Example of flat_transaction
* For comparison with the previous snippet, here is a code snippet of
* @ref pmem::obj::flat_transaction which is listed below with basic explanation inline.
* @snippet transaction/transaction.cpp tx_nested_struct_example
* If you read the inline comments you should be able to notice the difference
* between @ref pmem::obj::flat_transaction and @ref pmem::obj::basic_transaction.
* For more examples please look at the
* [examples directory](https://github.com/pmem/libpmemobj-cpp/tree/master/examples) in libpmemobj-cpp repository.
*
* ## Additional resources
* - [pmemobj_tx_begin(3) manpage with transaction description (C API)](https://pmem.io/pmdk/manpages/linux/master/libpmemobj/pmemobj_tx_begin.3)
* - [blog post about transactions](https://pmem.io/2016/05/25/cpp-07.html)
* - [blog post about transactional allocations](https://pmem.io/2016/05/19/cpp-06.html)
*/
/** @defgroup allocation Allocation
* Functions and classes to support allocations on pmem
*
* Libpmemobj-cpp introduced special functions for allocating objects and arrays
* of objects on persistent memory, with ease. For all `make_persistent` specializations
* there are also `delete_persistent` counterparts. The latter are used to free memory
* of previously allocated objects or arrays, and calling their destructors. Each
* specialization comes in two versions - atomic and transactional. First one is
* distinguished with `_atomic` suffix (e.g. `make_persistent_atomic()`) and should not
* be called within an active transaction - it may lead to undefined behavior in case
* of transaction's rollback. On the other hand, transactional functions will throw
* an exception if called outside of an active transaction.
*
* For more flexibly approach we introduced pmem::obj::allocator class, which implements
* the concept of C++ Allocator. Allocation and deallocation using this class can only
* happen within an active transactions.
*
* The *typical use case* of data allocation on pmem would be:
* @snippet make_persistent/make_persistent.cpp make_example
*
* For allocator usage see pmem::obj::experimental::inline_string example:
* @snippet inline_string/inline_string.cpp inline_string_example
*/
/** @defgroup data_view Data View
* Various views of persistent objects
*
* Classes listed on this page provide (often simplified) views of persistent data.
* They are delivered to ease the usage or for better performance. Please,
* take a look at description of each class to get the details of their behavior.
*/
/** @defgroup synchronization Synchronization Primitives
* Persistent memory resident implementation of synchronization primitives.
*
* In concurrent programming, we often require mechanisms for synchronizing access to shared resources.
* Typically to solve such issues we use synchronization primitives like mutexes and condition variables.
* As persistent memory offers bigger capacity than DRAM it may be useful to store synchronization primitives
* on it. Unfortunately such approach may cause performance degradation due to frequent writes to a memory
* with relatively higher latency (it's because taking a lock or signaling a conditional variable often
* requires additional writes). Few extra words how locks can be used in libpmemobj-cpp can be found in
* @ref transactions.
*
* It's worth noticing that pmem locks are automatically released on recovery or when crash happened.
*
* ## Additional resources
* - [Libpmemobj-cpp - lessons learned](https://pmem.io/blog/2021/09/libpmemobj-cpp-lessons-learned)
* In this blog post we explain, i.a., why keeping locks on pmem is not a good idea.
*/
/** @defgroup primitives Primitives
* Basic classes that provide PMEM-aware pointers and pool handlers.
*
* ## Pointers
* There are few types to handle data on PMEM.
* - @ref pmem::obj::persistent_ptr<T> - implements a smart pointer.
* It encapsulates the [PMEMoid](https://pmem.io/2015/06/11/type-safety-macros.html) fat
* pointer and provides member access, dereference and array access operators.
* - @ref pmem::obj::experimental::self_relative_ptr<T> - implements a smart ptr.
* It encapsulates the self-offsetted pointer and provides member access, dereference and array access operators.
* self_relative_ptr in comparison to persistent_ptr is:
* - smaller in size (8B vs 16B),
* - can be used with atomic operations,
* - dereferenced faster (it's important, e.g., in loops),
* - allows vectorization,
* - if stored in a persistent memory pool, it can only points to elements within the same pool.
* - @ref pmem::obj::p<T> - template class that can be used for all variables (except persistent pointers),
* which are used in @ref transactions.
* This class is not designed to be used with compound types. For that see the @ref pmem::obj::persistent_ptr.
* - @ref pmem::obj::experimental::v<T> - property-like template class that has to be used for
* all volatile variables that reside on persistent memory. This class ensures that the enclosed
* type is always properly initialized by always calling the class default constructor
* exactly once per instance of the application. This class has 8 bytes of storage overhead.
*
* ## Pool handles
* Pool class provides basic operations on pmemobj [pools](https://pmem.io/2016/05/10/cpp-05.html).
* C++ API for pools should not be mixed with C API. For example explicitly calling `pmemobj_set_user_data(pop)`
* on pool which is handled by C++ pool object is undefined behaviour.
*
* There are few pool handlers:
* - @ref pmem::obj::pool<T> - the template parameter defines the type of the root object within the pool.
* This pool class inherits also some methods from the base class: @ref pmem::obj::pool_base.
* Example: @snippet pool/pool.cpp pool_example
* - @ref pmem::obj::pool_base<T> - non template, basic version of pool,
* useful when specifying root type is not desirable. The typical usage
* example would be:
* @snippet pool/pool.cpp pool_base_example
*/
/** @defgroup exceptions Exceptions
* Possible exceptions that could be thrown by the libpmemobj++.
*
* In runtime, some operations may fail, then all you need is to catch the exception.
* Every pmem exception has `std::runtime_error` in its inheritance tree, which means
* that all exceptions can be caught using just this type. Each exception contains
* proper message with an error description.
*
* Look at the list on this page to explore all exceptions with their descriptions.
*
* Transaction handles uncaught exceptions thrown inside its scope, then aborts
* and rethrow the previous exception. That way you never loose the original
* exception and at the same time, the transaction state is handled
* properly by the library.
*
* Let's consider following example:
* @snippet examples/mpsc_queue/mpsc_queue.cpp mpsc_main
*
* There are plenty of try-catch blocks placed to handle possible errors that can occur in some
* conditions. E.g. @ref pmem::obj::pool<T>::open can lead to @ref pmem::pool_error.
* The next exception, **std::exception**, is placed to handle possible errors during allocation,
* coming from @ref pmem::obj::make_persistent. Worth being careful using any new function
* because some exceptions are not obvious, e.g., pmem::obj::pool<T>::close
* at the end of the code, which may throw **std::logic_error**.
*
* You should check every function you will use in the context of possible
* exceptions and then handle them to avoid a crash.
*/