xref: /netbsd-src/external/bsd/openldap/dist/servers/lloadd/epoch.h (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1*549b59edSchristos /*	$NetBSD: epoch.h,v 1.2 2021/08/14 16:14:58 christos Exp $	*/
2e670fd5cSchristos 
3e670fd5cSchristos /* epoch.h - epoch based memory reclamation */
4e670fd5cSchristos /* $OpenLDAP$ */
5e670fd5cSchristos /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6e670fd5cSchristos  *
7e670fd5cSchristos  * Copyright 2018-2021 The OpenLDAP Foundation.
8e670fd5cSchristos  * All rights reserved.
9e670fd5cSchristos  *
10e670fd5cSchristos  * Redistribution and use in source and binary forms, with or without
11e670fd5cSchristos  * modification, are permitted only as authorized by the OpenLDAP
12e670fd5cSchristos  * Public License.
13e670fd5cSchristos  *
14e670fd5cSchristos  * A copy of this license is available in the file LICENSE in the
15e670fd5cSchristos  * top-level directory of the distribution or, alternatively, at
16e670fd5cSchristos  * <http://www.OpenLDAP.org/license.html>.
17e670fd5cSchristos  */
18e670fd5cSchristos 
19e670fd5cSchristos #ifndef __LLOAD_EPOCH_H
20e670fd5cSchristos #define __LLOAD_EPOCH_H
21e670fd5cSchristos 
22e670fd5cSchristos /** @file epoch.h
23e670fd5cSchristos  *
24e670fd5cSchristos  * Implementation of epoch based memory reclamation, in principle
25e670fd5cSchristos  * similar to the algorithm presented in
26e670fd5cSchristos  * https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
27e670fd5cSchristos  */
28e670fd5cSchristos 
29e670fd5cSchristos typedef uintptr_t epoch_t;
30e670fd5cSchristos 
31e670fd5cSchristos /** @brief A callback function used to free object and associated data */
32e670fd5cSchristos typedef void (dispose_cb)( void *object );
33e670fd5cSchristos 
34e670fd5cSchristos /** @brief Initiate global state */
35e670fd5cSchristos void epoch_init( void );
36e670fd5cSchristos 
37e670fd5cSchristos /** @brief Finalise global state and free any objects still pending */
38e670fd5cSchristos void epoch_shutdown( void );
39e670fd5cSchristos 
40e670fd5cSchristos /** @brief Register thread as active
41e670fd5cSchristos  *
42e670fd5cSchristos  * In order to safely access managed objects, a thread should call
43e670fd5cSchristos  * this function or make sure no other thread is running (e.g. config
44e670fd5cSchristos  * pause, late shutdown). After calling this, it is guaranteed that no
45e670fd5cSchristos  * reachable objects will be freed before all threads have called
46e670fd5cSchristos  * `epoch_leave( current_epoch + 1 )` so it is essential that there
47e670fd5cSchristos  * is an upper limit to the amount of time between #epoch_join and
48e670fd5cSchristos  * corresponding #epoch_leave or the number of unfreed objects might
49e670fd5cSchristos  * grow without bounds.
50e670fd5cSchristos  *
51e670fd5cSchristos  * To simplify locking, memory is only freed when the current epoch
52e670fd5cSchristos  * is advanced rather than on leaving it.
53e670fd5cSchristos  *
54e670fd5cSchristos  * Can be safely called multiple times by the same thread as long as
55e670fd5cSchristos  * a matching #epoch_leave() call is made eventually.
56e670fd5cSchristos  *
57e670fd5cSchristos  * @return The observed epoch, to be passed to #epoch_leave()
58e670fd5cSchristos  */
59e670fd5cSchristos epoch_t epoch_join( void );
60e670fd5cSchristos 
61e670fd5cSchristos /** @brief Register thread as inactive
62e670fd5cSchristos  *
63e670fd5cSchristos  * A thread should call this after they are finished with work
64e670fd5cSchristos  * performed since matching call to #epoch_join(). It is not safe
65e670fd5cSchristos  * to keep a local reference to managed objects after this call
66e670fd5cSchristos  * unless other precautions have been made to prevent it being
67e670fd5cSchristos  * released.
68e670fd5cSchristos  *
69e670fd5cSchristos  * @param[in] epoch Epoch identifier returned by a previous call to
70e670fd5cSchristos  * #epoch_join().
71e670fd5cSchristos  */
72e670fd5cSchristos void epoch_leave( epoch_t epoch );
73e670fd5cSchristos 
74e670fd5cSchristos /** @brief Return an unreachable object to be freed
75e670fd5cSchristos  *
76e670fd5cSchristos  * The object should already be unreachable at the point of call and
77e670fd5cSchristos  * cb will be invoked when no other thread that could have seen it
78e670fd5cSchristos  * is active any more. This happens when we have advanced by two
79e670fd5cSchristos  * epochs.
80e670fd5cSchristos  *
81e670fd5cSchristos  * @param[in] ptr Object to be released/freed
82e670fd5cSchristos  * @param[in] cb Callback to invoke when safe to do so
83e670fd5cSchristos  */
84e670fd5cSchristos void epoch_append( void *ptr, dispose_cb *cb );
85e670fd5cSchristos 
86e670fd5cSchristos /**
87e670fd5cSchristos  * \defgroup Reference counting helpers
88e670fd5cSchristos  */
89e670fd5cSchristos /**@{*/
90e670fd5cSchristos 
91e670fd5cSchristos /** @brief Acquire a reference if possible
92e670fd5cSchristos  *
93e670fd5cSchristos  * Atomically, check reference count is non-zero and increment if so.
94e670fd5cSchristos  * Returns old reference count.
95e670fd5cSchristos  *
96e670fd5cSchristos  * @param[in] refp Pointer to a reference counter
97e670fd5cSchristos  * @return 0 if reference was already zero, non-zero if reference
98e670fd5cSchristos  * count was successfully incremented
99e670fd5cSchristos  */
100e670fd5cSchristos int acquire_ref( uintptr_t *refp );
101e670fd5cSchristos 
102e670fd5cSchristos /** @brief Check reference count and try to decrement
103e670fd5cSchristos  *
104e670fd5cSchristos  * Atomically, decrement reference count if non-zero and register
105e670fd5cSchristos  * object if decremented to zero. Returning previous reference count.
106e670fd5cSchristos  *
107e670fd5cSchristos  * @param[in] refp Pointer to a reference counter
108e670fd5cSchristos  * @param[in] object The managed object
109e670fd5cSchristos  * @param[in] cb Callback to invoke when safe to do so
110e670fd5cSchristos  * @return 0 if reference was already zero, non-zero if reference
111e670fd5cSchristos  * count was non-zero at the time of call
112e670fd5cSchristos  */
113e670fd5cSchristos int try_release_ref( uintptr_t *refp, void *object, dispose_cb *cb );
114e670fd5cSchristos 
115e670fd5cSchristos /** @brief Read reference count
116e670fd5cSchristos  *
117e670fd5cSchristos  * @param[in] object Pointer to the managed object
118e670fd5cSchristos  * @param[in] ref_field Member where reference count is stored in
119e670fd5cSchristos  * the object
120e670fd5cSchristos  * @return Current value of reference counter
121e670fd5cSchristos  */
122e670fd5cSchristos #define IS_ALIVE( object, ref_field ) \
123e670fd5cSchristos     __atomic_load_n( &(object)->ref_field, __ATOMIC_ACQUIRE )
124e670fd5cSchristos 
125e670fd5cSchristos /** @brief Release reference
126e670fd5cSchristos  *
127e670fd5cSchristos  * A cheaper alternative to #try_release_ref(), safe only when we know
128e670fd5cSchristos  * reference count was already non-zero.
129e670fd5cSchristos  *
130e670fd5cSchristos  * @param[in] object The managed object
131e670fd5cSchristos  * @param[in] ref_field Member where reference count is stored in
132e670fd5cSchristos  * the object
133e670fd5cSchristos  * @param[in] cb Callback to invoke when safe to do so
134e670fd5cSchristos  */
135e670fd5cSchristos #define RELEASE_REF( object, ref_field, cb ) \
136e670fd5cSchristos     do { \
137e670fd5cSchristos         assert( IS_ALIVE( (object), ref_field ) ); \
138e670fd5cSchristos         if ( !__atomic_sub_fetch( \
139e670fd5cSchristos                      &(object)->ref_field, 1, __ATOMIC_ACQ_REL ) ) { \
140e670fd5cSchristos             epoch_append( object, (dispose_cb *)cb ); \
141e670fd5cSchristos         } \
142e670fd5cSchristos     } while (0)
143e670fd5cSchristos 
144e670fd5cSchristos /**@}*/
145e670fd5cSchristos 
146e670fd5cSchristos #endif /* __LLOAD_EPOCH_H */
147