1() PMDK Programmer's Manual ()
2
3
4
6 pmemobj_tx_stage(),
7
8 pmemobj_tx_begin(), pmemobj_tx_lock(), pmemobj_tx_xlock(), pmemo‐
9 bj_tx_abort(), pmemobj_tx_commit(), pmemobj_tx_end(), pmemobj_tx_er‐
10 rno(), pmemobj_tx_process(),
11
12 TX_BEGIN_PARAM(), TX_BEGIN_CB(), TX_BEGIN(), TX_ONABORT, TX_ONCOMMIT,
13 TX_FINALLY, TX_END,
14
15 pmemobj_tx_log_append_buffer(), pmemobj_tx_xlog_append_buffer(), pmemo‐
16 bj_tx_log_auto_alloc(), pmemobj_tx_log_snapshots_max_size(), pmemo‐
17 bj_tx_log_intents_max_size(),
18
19 pmemobj_tx_set_user_data(), pmemobj_tx_get_user_data(),
20
21 pmemobj_tx_set_failure_behavior(), pmemobj_tx_get_failure_behavior() -
22 transactional object manipulation
23
25 #include <libpmemobj.h>
26
27 enum pobj_tx_stage pmemobj_tx_stage(void);
28
29 int pmemobj_tx_begin(PMEMobjpool *pop, jmp_buf *env, enum pobj_tx_param, ...);
30 int pmemobj_tx_lock(enum tx_lock lock_type, void *lockp);
31 int pmemobj_tx_xlock(enum tx_lock lock_type, void *lockp, uint64_t flags);
32 void pmemobj_tx_abort(int errnum);
33 void pmemobj_tx_commit(void);
34 int pmemobj_tx_end(void);
35 int pmemobj_tx_errno(void);
36 void pmemobj_tx_process(void);
37
38 TX_BEGIN_PARAM(PMEMobjpool *pop, ...)
39 TX_BEGIN_CB(PMEMobjpool *pop, cb, arg, ...)
40 TX_BEGIN(PMEMobjpool *pop)
41 TX_ONABORT
42 TX_ONCOMMIT
43 TX_FINALLY
44 TX_END
45
46 int pmemobj_tx_log_append_buffer(enum pobj_log_type type, void *addr, size_t size);
47 int pmemobj_tx_xlog_append_buffer(enum pobj_log_type type, void *addr, size_t size, uint64_t flags);
48 int pmemobj_tx_log_auto_alloc(enum pobj_log_type type, int on_off);
49 size_t pmemobj_tx_log_snapshots_max_size(size_t *sizes, size_t nsizes);
50 size_t pmemobj_tx_log_intents_max_size(size_t nintents);
51
52 void pmemobj_tx_set_user_data(void *data);
53 void *pmemobj_tx_get_user_data(void);
54
55 void pmemobj_tx_set_failure_behavior(enum pobj_tx_failure_behavior behavior);
56 enum pobj_tx_failure_behavior pmemobj_tx_get_failure_behavior(void);
57
59 The non-transactional functions and macros described in pmemobj_al‐
60 loc(3), pmemobj_list_insert(3) and POBJ_LIST_HEAD(3) only guarantee the
61 atomicity of a single operation on an object. In case of more complex
62 changes involving multiple operations on an object, or allocation and
63 modification of multiple objects, data consistency and fail-safety may
64 be provided only by using atomic transactions.
65
66 A transaction is defined as series of operations on persistent memory
67 objects that either all occur, or nothing occurs. In particular, if
68 the execution of a transaction is interrupted by a power failure or a
69 system crash, it is guaranteed that after system restart, all the
70 changes made as a part of the uncompleted transaction will be rolled
71 back, restoring the consistent state of the memory pool from the moment
72 when the transaction was started.
73
74 Note that transactions do not provide atomicity with respect to other
75 threads. All the modifications performed within the transactions are
76 immediately visible to other threads. Therefore it is the responsibil‐
77 ity of the application to implement a proper thread synchronization
78 mechanism.
79
80 Each thread may have only one transaction open at a time, but that
81 transaction may be nested. Nested transactions are flattened. Commit‐
82 ting the nested transaction does not commit the outer transaction; how‐
83 ever, errors in the nested transaction are propagated up to the outer‐
84 most level, resulting in the interruption of the entire transaction.
85
86 Each transaction is visible only for the thread that started it. No
87 other threads can add operations, commit or abort the transaction ini‐
88 tiated by another thread. Multiple threads may have transactions open
89 on a given memory pool at the same time.
90
91 Please see the CAVEATS section below for known limitations of the
92 transactional API.
93
94 The pmemobj_tx_stage() function returns the current transaction stage
95 for a thread. Stages are changed only by the pmemobj_tx_*() functions.
96 Transaction stages are defined as follows:
97
98 • TX_STAGE_NONE - no open transaction in this thread
99
100 • TX_STAGE_WORK - transaction in progress
101
102 • TX_STAGE_ONCOMMIT - successfully committed
103
104 • TX_STAGE_ONABORT - starting the transaction failed or transaction
105 aborted
106
107 • TX_STAGE_FINALLY - ready for clean up
108
109 The pmemobj_tx_begin() function starts a new transaction in the current
110 thread. If called within an open transaction, it starts a nested
111 transaction. The caller may use the env argument to provide a pointer
112 to a calling environment to be restored in case of transaction abort.
113 This information must be provided by the caller using the setjmp(3)
114 macro.
115
116 A new transaction may be started only if the current stage is
117 TX_STAGE_NONE or TX_STAGE_WORK. If successful, the transaction stage
118 changes to TX_STAGE_WORK. Otherwise, the stage is changed to
119 TX_STAGE_ONABORT.
120
121 Optionally, a list of parameters for the transaction may be provided.
122 Each parameter consists of a type followed by a type-specific number of
123 values. Currently there are 4 types:
124
125 • TX_PARAM_NONE, used as a termination marker. No following value.
126
127 • TX_PARAM_MUTEX, followed by one value, a pmem-resident PMEMmutex
128
129 • TX_PARAM_RWLOCK, followed by one value, a pmem-resident PMEMrwlock
130
131 • TX_PARAM_CB, followed by two values: a callback function of type
132 pmemobj_tx_callback, and a void pointer
133
134 Using TX_PARAM_MUTEX or TX_PARAM_RWLOCK causes the specified lock to be
135 acquired at the beginning of the transaction. TX_PARAM_RWLOCK acquires
136 the lock for writing. It is guaranteed that pmemobj_tx_begin() will
137 acquire all locks prior to successful completion, and they will be held
138 by the current thread until the outermost transaction is finished.
139 Locks are taken in order from left to right. To avoid deadlocks, the
140 user is responsible for proper lock ordering.
141
142 TX_PARAM_CB registers the specified callback function to be executed at
143 each transaction stage. For TX_STAGE_WORK, the callback is executed
144 prior to commit. For all other stages, the callback is executed as the
145 first operation after a stage change. It will also be called after
146 each transaction; in this case the stage parameter will be set to
147 TX_STAGE_NONE. pmemobj_tx_callback must be compatible with:
148
149 void func(PMEMobjpool *pop, enum pobj_tx_stage stage, void *arg)
150
151 pop is a pool identifier used in pmemobj_tx_begin(), stage is a current
152 transaction stage and arg is the second parameter of TX_PARAM_CB.
153 Without considering transaction nesting, this mechanism can be consid‐
154 ered an alternative method for executing code between stages (instead
155 of TX_ONCOMMIT, TX_ONABORT, etc). However, there are 2 significant
156 differences when nested transactions are used:
157
158 • The registered function is executed only in the outermost transac‐
159 tion, even if registered in an inner transaction.
160
161 • There can be only one callback in the entire transaction, that is,
162 the callback cannot be changed in an inner transaction.
163
164 Note that TX_PARAM_CB does not replace the TX_ONCOMMIT, TX_ONABORT,
165 etc. macros. They can be used together: the callback will be executed
166 before a TX_ONCOMMIT, TX_ONABORT, etc. section.
167
168 TX_PARAM_CB can be used when the code dealing with transaction stage
169 changes is shared between multiple users or when it must be executed
170 only in the outer transaction. For example it can be very useful when
171 the application must synchronize persistent and transient state.
172
173 The pmemobj_tx_lock() function acquires the lock lockp of type
174 lock_type and adds it to the current transaction. lock_type may be
175 TX_LOCK_MUTEX or TX_LOCK_RWLOCK; lockp must be of type PMEMmutex or
176 PMEMrwlock, respectively. If lock_type is TX_LOCK_RWLOCK the lock is
177 acquired for writing. If the lock is not successfully acquired, the
178 function returns an error number. This function must be called during
179 TX_STAGE_WORK.
180
181 The pmemobj_tx_xlock() function behaves exactly the same as pmemo‐
182 bj_tx_lock() when flags equals POBJ_XLOCK_NO_ABORT. When flags equals
183 0 and if the lock is not successfully acquired,the transaction is
184 aborted. flags is a bitmask of the following values:
185
186 • POBJ_XLOCK_NO_ABORT - if the function does not end successfully, do
187 not abort the transaction.
188
189 pmemobj_tx_abort() aborts the current transaction and causes a transi‐
190 tion to TX_STAGE_ONABORT. If errnum is equal to 0, the transaction er‐
191 ror code is set to ECANCELED; otherwise, it is set to errnum. This
192 function must be called during TX_STAGE_WORK.
193
194 The pmemobj_tx_commit() function commits the current open transaction
195 and causes a transition to TX_STAGE_ONCOMMIT. If called in the context
196 of the outermost transaction, all the changes may be considered as
197 durably written upon successful completion. This function must be
198 called during TX_STAGE_WORK.
199
200 The pmemobj_tx_end() function performs a cleanup of the current trans‐
201 action. If called in the context of the outermost transaction, it re‐
202 leases all the locks acquired by pmemobj_tx_begin() for outer and nest‐
203 ed transactions. If called in the context of a nested transaction, it
204 returns to the context of the outer transaction in TX_STAGE_WORK, with‐
205 out releasing any locks. The pmemobj_tx_end() function can be called
206 during TX_STAGE_NONE if transitioned to this stage using pmemo‐
207 bj_tx_process(). If not already in TX_STAGE_NONE, it causes the tran‐
208 sition to TX_STAGE_NONE. pmemobj_tx_end must always be called for each
209 pmemobj_tx_begin(), even if starting the transaction failed. This
210 function must not be called during TX_STAGE_WORK.
211
212 The pmemobj_tx_errno() function returns the error code of the last
213 transaction.
214
215 The pmemobj_tx_process() function performs the actions associated with
216 the current stage of the transaction, and makes the transition to the
217 next stage. It must be called in a transaction. The current stage
218 must always be obtained by a call to pmemobj_tx_stage(). pmemo‐
219 bj_tx_process() performs the following transitions in the transaction
220 stage flow:
221
222 • TX_STAGE_WORK -> TX_STAGE_ONCOMMIT
223
224 • TX_STAGE_ONABORT -> TX_STAGE_FINALLY
225
226 • TX_STAGE_ONCOMMIT -> TX_STAGE_FINALLY
227
228 • TX_STAGE_FINALLY -> TX_STAGE_NONE
229
230 • TX_STAGE_NONE -> TX_STAGE_NONE
231
232 pmemobj_tx_process() must not be called after calling pmemobj_tx_end()
233 for the outermost transaction.
234
235 In addition to the above API, libpmemobj(7) offers a more intuitive
236 method of building transactions using the set of macros described be‐
237 low. When using these macros, the complete transaction flow looks like
238 this:
239
240 TX_BEGIN(Pop) {
241 /* the actual transaction code goes here... */
242 } TX_ONCOMMIT {
243 /*
244 * optional - executed only if the above block
245 * successfully completes
246 */
247 } TX_ONABORT {
248 /*
249 * optional - executed only if starting the transaction fails,
250 * or if transaction is aborted by an error or a call to
251 * pmemobj_tx_abort()
252 */
253 } TX_FINALLY {
254 /*
255 * optional - if exists, it is executed after
256 * TX_ONCOMMIT or TX_ONABORT block
257 */
258 } TX_END /* mandatory */
259
260 TX_BEGIN_PARAM(PMEMobjpool *pop, ...)
261 TX_BEGIN_CB(PMEMobjpool *pop, cb, arg, ...)
262 TX_BEGIN(PMEMobjpool *pop)
263
264 The TX_BEGIN_PARAM(), TX_BEGIN_CB() and TX_BEGIN() macros start a new
265 transaction in the same way as pmemobj_tx_begin(), except that instead
266 of the environment buffer provided by a caller, they set up the local
267 jmp_buf buffer and use it to catch the transaction abort. The TX_BE‐
268 GIN() macro starts a transaction without any options. TX_BEGIN_PARAM
269 may be used when there is a need to acquire locks prior to starting a
270 transaction (such as for a multi-threaded program) or set up a transac‐
271 tion stage callback. TX_BEGIN_CB is just a wrapper around TX_BE‐
272 GIN_PARAM that validates the callback signature. (For compatibility
273 there is also a TX_BEGIN_LOCK macro, which is an alias for TX_BE‐
274 GIN_PARAM). Each of these macros must be followed by a block of code
275 with all the operations that are to be performed atomically.
276
277 The TX_ONABORT macro starts a block of code that will be executed only
278 if starting the transaction fails due to an error in pmemobj_tx_be‐
279 gin(), or if the transaction is aborted. This block is optional, but
280 in practice it should not be omitted. If it is desirable to crash the
281 application when a transaction aborts and there is no TX_ONABORT sec‐
282 tion, the application can define the POBJ_TX_CRASH_ON_NO_ONABORT macro
283 before inclusion of <libpmemobj.h>. This provides a default TX_ONABORT
284 section which just calls abort(3).
285
286 The TX_ONCOMMIT macro starts a block of code that will be executed only
287 if the transaction is successfully committed, which means that the exe‐
288 cution of code in the TX_BEGIN() block has not been interrupted by an
289 error or by a call to pmemobj_tx_abort(). This block is optional.
290
291 The TX_FINALLY macro starts a block of code that will be executed re‐
292 gardless of whether the transaction is committed or aborted. This
293 block is optional.
294
295 The TX_END macro cleans up and closes the transaction started by the
296 TX_BEGIN() / TX_BEGIN_PARAM() / TX_BEGIN_CB() macros. It is mandatory
297 to terminate each transaction with this macro. If the transaction was
298 aborted, errno is set appropriately.
299
300 TRANSACTION LOG TUNING
301 From libpmemobj implementation perspective there are two types of oper‐
302 ations in a transaction:
303
304 • snapshots, where action must be persisted immediately,
305
306 • intents, where action can be persisted at the transaction commit
307 phase
308
309 pmemobj_tx_add_range(3) and all its variants belong to the snapshots
310 group.
311
312 pmemobj_tx_alloc(3) (with its variants), pmemobj_tx_free(3), pmemo‐
313 bj_tx_realloc(3) (with its variants) and pmemobj_tx_publish(3) belong
314 to the intents group. Even though pmemobj_tx_alloc() allocates memory
315 immediately, it modifies only the runtime state and postpones persis‐
316 tent memory modifications to the commit phase. pmemobj_tx_free(3) can‐
317 not free the object immediately, because of possible transaction roll‐
318 back, so it postpones both the action and persistent memory modifica‐
319 tions to the commit phase. pmemobj_tx_realloc(3) is just a combination
320 of those two. pmemobj_tx_publish(3) postpones reservations and de‐
321 ferred frees to the commit phase.
322
323 Those two types of operations (snapshots and intents) require that
324 libpmemobj builds a persistent log of operations. Intent log (also
325 known as a “redo log”) is applied on commit and snapshot log (also
326 known as an “undo log”) is applied on abort.
327
328 When libpmemobj transaction starts, it’s not possible to predict how
329 much persistent memory space will be needed for those logs. This means
330 that libpmemobj must internally allocate this space whenever it’s need‐
331 ed. This has two downsides:
332
333 • when transaction snapshots a lot of memory or does a lot of alloca‐
334 tions, libpmemobj may need to do many internal allocations, which
335 must be freed when transaction ends, adding time overhead when big
336 transactions are frequent,
337
338 • transactions can start to fail due to not enough space for logs -
339 this can be especially problematic for transactions that want to de‐
340 allocate objects, as those might also fail
341
342 To solve both of these problems libpmemobj exposes the following func‐
343 tions:
344
345 • pmemobj_tx_log_append_buffer(),
346
347 • pmemobj_tx_xlog_append_buffer(),
348
349 • pmemobj_tx_log_auto_alloc()
350
351 pmemobj_tx_log_append_buffer() appends a given range of memory [addr,
352 addr + size) to the log type of the current transaction. type can be
353 one of the two values (with meanings described above):
354
355 • TX_LOG_TYPE_SNAPSHOT,
356
357 • TX_LOG_TYPE_INTENT
358
359 The range of memory must belong to the same pool the transaction is on
360 and must not be used by more than one thread at the same time. The
361 latter condition can be verified with tx.debug.verify_user_buffers ctl
362 (see pmemobj_ctl_get(3)).
363
364 The pmemobj_tx_xlog_append_buffer() function behaves exactly the same
365 as pmemobj_tx_log_append_buffer() when flags equals zero. flags is a
366 bitmask of the following values:
367
368 • POBJ_XLOG_APPEND_BUFFER_NO_ABORT - if the function does not end suc‐
369 cessfully, do not abort the transaction.
370
371 pmemobj_tx_log_snapshots_max_size calculates the maximum size of a buf‐
372 fer which will be able to hold nsizes snapshots, each of size sizes[i].
373 Application should not expect this function to return the same value
374 between restarts. In future versions of libpmemobj this function can
375 return smaller (because of better accuracy or space optimizations) or
376 higher (because of higher alignment required for better performance)
377 value. This function is independent of transaction stage and can be
378 called both inside and outside of transaction. If the returned value S
379 is greater than PMEMOBJ_MAX_ALLOC_SIZE, the buffer should be split into
380 N chunks of size PMEMOBJ_MAX_ALLOC_SIZE, where N is equal to (S / PMEM‐
381 OBJ_MAX_ALLOC_SIZE) (rounded down) and the last chunk of size (S - (N *
382 PMEMOBJ_MAX_ALLOC_SIZE)).
383
384 pmemobj_tx_log_intents_max_size calculates the maximum size of a buffer
385 which will be able to hold nintents intents. Just like with pmemo‐
386 bj_tx_log_snapshots_max_size, application should not expect this func‐
387 tion to return the same value between restarts, for the same reasons.
388 This function is independent of transaction stage and can be called
389 both inside and outside of transaction.
390
391 pmemobj_tx_log_auto_alloc() disables (on_off set to 0) or enables
392 (on_off set to 1) automatic allocation of internal logs of given type.
393 It can be used to verify that the buffer set with pmemobj_tx_log_ap‐
394 pend_buffer() is big enough to hold the log, without reaching out-of-
395 space scenario.
396
397 The pmemobj_tx_set_user_data() function associates custom volatile
398 state, represented by pointer data, with the current transaction. This
399 state can later be retrieved using pmemobj_tx_get_user_data() function.
400 If pmemobj_tx_set_user_data() was not called for a current transaction,
401 pmemobj_tx_get_user_data() will return NULL. These functions must be
402 called during TX_STAGE_WORK or TX_STAGE_ONABORT or TX_STAGE_ONCOMMIT or
403 TX_STAGE_FINALLY.
404
405 pmemobj_tx_set_failure_behavior() specifies what should happen in case
406 of an error within the transaction. It only affects functions which
407 take a NO_ABORT flag. If pmemobj_tx_set_failure_behavior() is called
408 with POBJ_TX_FAILURE_RETURN a NO_ABORT flag is implicitly passed to all
409 functions which accept this flag. If called with POBJ_TX_FAILURE_ABORT
410 then all functions abort the transaction (unless NO_ABORT flag is
411 passed explicitly). This setting is inherited by inner transactions.
412 It does not affect any of the outer transactions. Aborting on failure
413 is the default behavior. pmemobj_tx_get_failure_behavior() returns
414 failure behavior for the current transaction. Both pmemo‐
415 bj_tx_set_failure_behavior() and pmemobj_tx_get_failure_behavior() must
416 be called during TX_STAGE_WORK.
417
419 The pmemobj_tx_stage() function returns the stage of the current trans‐
420 action stage for a thread.
421
422 On success, pmemobj_tx_begin() returns 0. Otherwise, an error number
423 is returned.
424
425 The pmemobj_tx_begin() and pmemobj_tx_lock() functions return zero if
426 lockp is successfully added to the transaction. Otherwise, an error
427 number is returned.
428
429 The pmemobj_tx_xlock() function return zero if lockp is successfully
430 added to the transaction. Otherwise, the error number is returned, er‐
431 rno is set and when flags do not contain POBJ_XLOCK_NO_ABORT, the
432 transaction is aborted.
433
434 The pmemobj_tx_abort() and pmemobj_tx_commit() functions return no val‐
435 ue.
436
437 The pmemobj_tx_end() function returns 0 if the transaction was success‐
438 ful. Otherwise it returns the error code set by pmemobj_tx_abort().
439 Note that pmemobj_tx_abort() can be called internally by the library.
440
441 The pmemobj_tx_errno() function returns the error code of the last
442 transaction.
443
444 The pmemobj_tx_process() function returns no value.
445
446 On success, pmemobj_tx_log_append_buffer() returns 0. Otherwise, the
447 stage is changed to TX_STAGE_ONABORT, errno is set appropriately and
448 transaction is aborted.
449
450 On success, pmemobj_tx_xlog_append_buffer() returns 0. Otherwise, the
451 error number is returned, errno is set and when flags do not contain
452 POBJ_XLOG_NO_ABORT, the transaction is aborted.
453
454 On success, pmemobj_tx_log_auto_alloc() returns 0. Otherwise, the
455 transaction is aborted and an error number is returned.
456
457 On success, pmemobj_tx_log_snapshots_max_size() returns size of the
458 buffer. On failure it returns SIZE_MAX and sets errno appropriately.
459
460 On success, pmemobj_tx_log_intents_max_size() returns size of the buf‐
461 fer. On failure it returns SIZE_MAX and sets errno appropriately.
462
464 Transaction flow control is governed by the setjmp(3) and longjmp(3)
465 macros, and they are used in both the macro and function flavors of the
466 API. The transaction will longjmp on transaction abort. This has one
467 major drawback, which is described in the ISO C standard subsection
468 7.13.2.1. It says that the values of objects of automatic storage du‐
469 ration that are local to the function containing the setjmp invocation
470 that do not have volatile-qualified type and have been changed between
471 the setjmp invocation and longjmp call are indeterminate.
472
473 The following example illustrates the issue described above.
474
475 int *bad_example_1 = (int *)0xBAADF00D;
476 int *bad_example_2 = (int *)0xBAADF00D;
477 int *bad_example_3 = (int *)0xBAADF00D;
478 int * volatile good_example = (int *)0xBAADF00D;
479
480 TX_BEGIN(pop) {
481 bad_example_1 = malloc(sizeof(int));
482 bad_example_2 = malloc(sizeof(int));
483 bad_example_3 = malloc(sizeof(int));
484 good_example = malloc(sizeof(int));
485
486 /* manual or library abort called here */
487 pmemobj_tx_abort(EINVAL);
488 } TX_ONCOMMIT {
489 /*
490 * This section is longjmp-safe
491 */
492 } TX_ONABORT {
493 /*
494 * This section is not longjmp-safe
495 */
496 free(good_example); /* OK */
497 free(bad_example_1); /* undefined behavior */
498 } TX_FINALLY {
499 /*
500 * This section is not longjmp-safe on transaction abort only
501 */
502 free(bad_example_2); /* undefined behavior */
503 } TX_END
504
505 free(bad_example_3); /* undefined behavior */
506
507 Objects which are not volatile-qualified, are of automatic storage du‐
508 ration and have been changed between the invocations of setjmp(3) and
509 longjmp(3) (that also means within the work section of the transaction
510 after TX_BEGIN()) should not be used after a transaction abort, or
511 should be used with utmost care. This also includes code after the
512 TX_END macro.
513
514 libpmemobj(7) is not cancellation-safe. The pool will never be cor‐
515 rupted because of a canceled thread, but other threads may stall wait‐
516 ing on locks taken by that thread. If the application wants to use
517 pthread_cancel(3), it must disable cancellation before calling any
518 libpmemobj(7) APIs (see pthread_setcancelstate(3) with PTHREAD_CAN‐
519 CEL_DISABLE), and re-enable it afterwards. Deferring cancellation
520 (pthread_setcanceltype(3) with PTHREAD_CANCEL_DEFERRED) is not safe
521 enough, because libpmemobj(7) internally may call functions that are
522 specified as cancellation points in POSIX.
523
524 libpmemobj(7) relies on the library destructor being called from the
525 main thread. For this reason, all functions that might trigger de‐
526 struction (e.g. dlclose(3)) should be called in the main thread. Oth‐
527 erwise some of the resources associated with that thread might not be
528 cleaned up properly.
529
531 dlclose(3), longjmp(3), pmemobj_tx_add_range(3), pmemobj_tx_alloc(3),
532 pthread_setcancelstate(3), pthread_setcanceltype(3), setjmp(3), libp‐
533 memobj(7) and <https://pmem.io>
534
535
536
537PMDK - 2023-06-05 ()