forked from 51Degrees/common-cxx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pool.h
269 lines (251 loc) · 9.37 KB
/
pool.h
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
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2019 51 Degrees Mobile Experts Limited, 5 Charlotte Close,
* Caversham, Reading, Berkshire, United Kingdom RG4 7BY.
*
* This Original Work is licensed under the European Union Public Licence (EUPL)
* v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */
#ifndef FIFTYONE_DEGREES_POOL_H_INCLUDED
#define FIFTYONE_DEGREES_POOL_H_INCLUDED
/**
* @ingroup FiftyOneDegreesCommon
* @defgroup FiftyOneDegreesPool Pool
*
* Pool of handles to allow safe access to multiple threads.
*
* ## Introduction
*
* A Pool structure defines a pool of handles to a resource, and allows a safe
* way to access them in a multi-threaded environment.
*
* ## Creation
*
* The #fiftyoneDegreesPoolInit method is used to initialise a pointer to
* a #fiftyoneDegreesPool. A concurrency value is provided to indicate the
* maximum number of threads that will be in operation. If this value is lower
* than the actual number of threads the stack can be exhausted and a null
* pointer is returned instead of a valid item. The concurrency value must
* always be the same or greater than the number of threads. When compiled
* in single threaded operation a pool is not strictly required and the
* implementation maintains a simple stack for consistency of interface and to
* minimise divergent code.
*
* ## Get & Release
*
* Handles are retrieved from the pool via the #fiftyoneDegreesPoolItemGet
* method. The handle **MUST** be returned with the
* #fiftyoneDegreesPoolItemRelease method when it is finished with. The
* handle will always be open and ready for read only operation. If too many
* threads are accessing the pool simultaneously, meaning a handle cannot be
* secured, then a NULL pointer is returned.
*
* ## Free
*
* The items are closed when the pool is released via the
* #fiftyoneDegreesPoolFree method. Any memory allocated by the implementation
* for the stack is freed.
*
* ## Usage Example
*
* ```
* FIFTYONE_DEGREES_EXCEPTION_CREATE;
* void *state;
* fiftyoneDegreesPoolResourceCreate resourceCreate;
* fiftyoneDegreesPoolResourceFree resourceFree;
* fiftyoneDegreesPool pool;
*
* // Initialise a pool of resources and check that it was successful
* if (fiftyoneDegreesPoolInit(
* &pool,
* 1,
* state,
* resourceCreate,
* resourceFree,
* exception) != NULL) {
*
* // Get a resource from the pool
* fiftyoneDegreesPoolItem *item = fiftyoneDegreesPoolItemGet(
* &pool,
* exception);
*
* // Check that there was a resource available
* if (item != NULL) {
*
* // Do something with the resource without being interrupted by other
* // threads
* // ...
*
* // Release the resource back to the pool
* fiftyoneDegreesPoolItemRelease(item);
* }
*
* // Free the pool of resources
* fiftyoneDegreesPoolFree(&pool);
* }
* ```
*
* ## Design
*
* To improve performance in multi-threaded operation a non locking stack is
* used where a Compare and Swap (CAS) atomic operation is used to pop and push
* handles on and off the stack. The design was adapted from the following
* article (http://nullprogram.com/blog/2014/09/02/) which explains some of the
* challenges involved including the ABA problem
* (https://en.wikipedia.org/wiki/ABA_problem). It is for this reason the head
* structure is implemented as a union between the values and the exchange
* integer. Pointers are not used as the address space for the stack is
* continuous and always very small compared to the total addressable memory
* space.
*
* @{
*/
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#ifdef _MSC_VER
#include <windows.h>
#endif
#include <assert.h>
#include <limits.h>
#include "data.h"
#include "exceptions.h"
#include "memory.h"
#ifndef FIFTYONE_DEGREES_NO_THREADING
#include "threading.h"
#endif
#ifdef __cplusplus
#define EXTERNAL extern "C"
#else
#define EXTERNAL
#endif
/** @cond FORWARD_DECLARATIONS */
typedef struct fiftyone_degrees_pool_item_t fiftyoneDegreesPoolItem;
typedef struct fiftyone_degrees_pool_t fiftyoneDegreesPool;
/** @endcond */
/**
* Used to create a new resource for use in the pool.
* @param pool to create the resource for
* @param state pointer to data used by the method
* @param exception pointer to an exception data structure to be used if an
* exception occurs. See exceptions.h
* @return pointer to the new resource
*/
typedef void*(*fiftyoneDegreesPoolResourceCreate)(
fiftyoneDegreesPool *pool,
void *state,
fiftyoneDegreesException *exception);
/**
* Used to determine the additional size beyond the pointer used for each
* resource added to the pool.
* @param state pointer to a state containing the resource and any additional
* data needed for the calculation
* @return addition size to allocate per resource
*/
typedef size_t(*fiftyoneDegreesPoolResourceSize)(void *state);
/**
* Frees a resource previously created with #fiftyoneDegreesPoolResourceCreate.
* @param pool containing the resource
* @param resource to free
*/
typedef void(*fiftyoneDegreesPoolResourceFree)(
fiftyoneDegreesPool *pool,
void *resource);
/**
* Pool item node in the stack of items.
*/
typedef struct fiftyone_degrees_pool_item_t {
void *resource; /**< Pointer to the resource in the pool */
uint16_t next; /**< The next item in the stack */
fiftyoneDegreesPool *pool; /**< Reader the handle belongs to */
} fiftyoneDegreesPoolItem;
/**
* The head of the stack used for pop and push operations.
*/
typedef union fiftyone_degrees_pool_head_t {
volatile long exchange; /**< Number used in the compare exchange operation */
struct {
uint16_t index; /**< Index of the item in the linked list */
uint16_t aba; /**< ABA value used to ensure proper operation */
} values; /**< Value index with its ABA value */
} fiftyoneDegreesPoolHead;
/**
* Pool of resources stored as items in a stack.
*/
typedef struct fiftyone_degrees_pool_t {
fiftyoneDegreesPoolItem *stack; /**< Pointer to the memory used by the
stack */
fiftyoneDegreesPoolHead head; /**< Head of the stack */
uint16_t count; /**< Number of resource items that stack can hold */
fiftyoneDegreesPoolResourceFree resourceFree; /**< Frees a resource */
} fiftyoneDegreesPool;
/**
* Initialises a pool data structure to support the number of concurrent
* requests that can be made to the pool for resources that can be reused.
* The resourceCreate method is used to create a new resource for use in the
* pool which will be freed during when the release method is called on the
* pool using the resourceFree method.
* @param pool data structure to be initialised.
* @param concurrency the number of resources the pool should contain.
* @param state passed to the create resource method.
* @param resourceCreate method used to create the resource to be added to
* items in the pool.
* @param resourceFree method used to free a resource from the pool when the
* pool is freed.
* @param exception pointer to an exception data structure to be used if an
* exception occurs. See exceptions.h.
* @return a pointer to the pool if successful, otherwise NULL.
*/
EXTERNAL fiftyoneDegreesPool* fiftyoneDegreesPoolInit(
fiftyoneDegreesPool *pool,
uint16_t concurrency,
void *state,
fiftyoneDegreesPoolResourceCreate resourceCreate,
fiftyoneDegreesPoolResourceFree resourceFree,
fiftyoneDegreesException *exception);
/**
* Gets the next free item from the pool for exclusive use by the caller. Every
* item returned must be released when the caller has finished with it using
* the #fiftyoneDegreesPoolItemRelease method.
* @param pool to return items from.
* @param exception pointer to an exception data structure to be used if an
* exception occurs. See exceptions.h.
* @return the next free item, or NULL if no items are available.
*/
EXTERNAL fiftyoneDegreesPoolItem* fiftyoneDegreesPoolItemGet(
fiftyoneDegreesPool *pool,
fiftyoneDegreesException *exception);
/**
* Releases the item back to the pool it belongs ready to be reused by another
* operation.
* @param item to be released back to the pool
*/
EXTERNAL void fiftyoneDegreesPoolItemRelease(fiftyoneDegreesPoolItem* item);
/**
* Releases the items used by the pool freeing the resources used by each
* item by calling the resourceFree method provided when the pool was created.
* @param pool to be freed
*/
EXTERNAL void fiftyoneDegreesPoolFree(fiftyoneDegreesPool* pool);
/**
* Resets the pool without releasing any resources.
* @param pool to be reset
*/
EXTERNAL void fiftyoneDegreesPoolReset(fiftyoneDegreesPool *pool);
/**
* @}
*/
#endif