|
1 # Memory Pool |
|
2 |
|
3 A memory pool is providing an allocator implementation that automatically deallocates the memory upon its destruction. |
|
4 It also allows you to register destructor functions for the allocated memory, which are automatically called before |
|
5 the memory is deallocated. |
|
6 |
|
7 Additionally, you may also register _independent_ destructor functions. |
|
8 This can be useful, for example, when some library allocates memory that you wish to destroy when the memory pool gets destroyed. |
|
9 |
|
10 A memory pool can be used with all UCX features that support the use of an [allocator](allocator.h.md). |
|
11 For example, the UCX [string](string.h.md) functions provide several variants suffixed with `_a` for that purpose. |
|
12 |
|
13 ## Overview |
|
14 |
|
15 ```C |
|
16 #include <cx/mempool.h> |
|
17 |
|
18 CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func fnc); |
|
19 |
|
20 CxMempool *cxMempoolCreateSimple(size_t capacity); |
|
21 |
|
22 void cxMempoolFree(CxMempool *pool); |
|
23 |
|
24 void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc); |
|
25 |
|
26 void cxMempoolRemoveDestructor(void *memory); |
|
27 |
|
28 int cxMempoolRegister(CxMempool *pool, void *memory, |
|
29 cx_destructor_func fnc); |
|
30 ``` |
|
31 |
|
32 ## Description |
|
33 |
|
34 A memory pool is created with the `cxMempoolCreate()` function with a default `capacity` |
|
35 and an optional default destructor function `fnc`. |
|
36 If specified, the default destructor function is registered for all freshly allocated memory within the pool, |
|
37 as if `cxMempoolSetDestructor()` was called immediately after allocation. |
|
38 When you set `fnc` is to `NULL` during pool creation, or use `cxMempoolCreateSimple`, no default destructor is registered. |
|
39 |
|
40 After creating a memory pool `CxMempool *mpool`, you can access the provided allocator via `mpool->allocator`. |
|
41 |
|
42 The functions `cxMempoolSetDestructor()` and `cxMempoolRemoveDestructor()` can be used to assign a specific destructor |
|
43 function to an allocated object or remove any assigned destructor function, respectively. |
|
44 The `memory` pointer points to the allocated object, which must have been allocated by any `CxMempool`'s provided allocator. |
|
45 |
|
46 The `cxMempoolRegister()` function allocates a new wrapper object for `memory` with `pool`'s allocator that |
|
47 will call the specified destructor function when destroyed. |
|
48 Usually this function returns zero except for platforms where memory allocations are likely to fail, |
|
49 in which case a non-zero value is returned. |
|
50 |
|
51 ## Order of Destruction |
|
52 |
|
53 When you call `cxMempoolFree()` the following actions are performed: |
|
54 |
|
55 1. In any order, for each object in the pool |
|
56 1. the destructor function assigned to that object is called |
|
57 2. the object's memory is deallocated |
|
58 2. The pool memory is deallocated |
|
59 3. The pool structure is deallocated |
|
60 |
|
61 ## Example |
|
62 |
|
63 The following code illustrates how the contents of a CSV file are read into pooled memory. |
|
64 ```C |
|
65 #include <stdio.h> |
|
66 #include <cx/mempool.h> |
|
67 #include <cx/linked_list.h> |
|
68 #include <cx/string.h> |
|
69 #include <cx/buffer.h> |
|
70 #include <cx/utils.h> |
|
71 |
|
72 typedef struct { |
|
73 cxstring column_a; |
|
74 cxstring column_b; |
|
75 cxstring column_c; |
|
76 } CSVData; |
|
77 |
|
78 int main(void) { |
|
79 // create a simple pool for various different objects |
|
80 CxMempool* pool = cxMempoolCreateSimple(128); |
|
81 |
|
82 FILE *f = fopen("test.csv", "r"); |
|
83 if (f == NULL) { |
|
84 perror("Cannot open file"); |
|
85 return 1; |
|
86 } |
|
87 // close the file automatically at pool destruction |
|
88 cxMempoolRegister(pool, f, (cx_destructor_func) fclose); |
|
89 |
|
90 // create a buffer using the memory pool for destruction |
|
91 CxBuffer *content = cxBufferCreate( |
|
92 NULL, 256, pool->allocator, CX_BUFFER_AUTO_EXTEND |
|
93 ); |
|
94 |
|
95 // read the file into the buffer and turn it into a string |
|
96 cx_stream_copy( |
|
97 f, content, (cx_read_func) fread, cxBufferWriteFunc |
|
98 ); |
|
99 fclose(f); |
|
100 cxstring contentstr = cx_strn(content->space, content->size); |
|
101 |
|
102 // split the string into lines |
|
103 // use the memory pool to allocate the target array |
|
104 cxstring* lines; |
|
105 size_t lc = cx_strsplit_a( |
|
106 pool->allocator, contentstr, cx_str("\n"), SIZE_MAX, &lines |
|
107 ); |
|
108 |
|
109 // skip the header and parse the remaining data into a linked list |
|
110 // the nodes of the list shall also be allocated by the pool |
|
111 CxList* datalist = cxLinkedListCreate( |
|
112 pool->allocator, NULL, sizeof(CSVData) |
|
113 ); |
|
114 for (size_t i = 1 ; i < lc ; i++) { |
|
115 if (lines[i].length == 0) continue; |
|
116 cxstring fields[3]; |
|
117 size_t fc = cx_strsplit(lines[i], cx_str(";"), 3, fields); |
|
118 if (fc != 3) { |
|
119 fprintf(stderr, "Syntax error in line %zu.\n", i); |
|
120 cxMempoolFree(pool); |
|
121 return 1; |
|
122 } |
|
123 CSVData data; |
|
124 data.column_a = fields[0]; |
|
125 data.column_b = fields[1]; |
|
126 data.column_c = fields[2]; |
|
127 cxListAdd(datalist, &data); |
|
128 } |
|
129 |
|
130 // iterate through the list and output the data |
|
131 CxIterator iter = cxListIterator(datalist); |
|
132 cx_foreach(CSVData*, data, iter) { |
|
133 printf("Column A: %.*s | " |
|
134 "Column B: %.*s | " |
|
135 "Column C: %.*s\n", |
|
136 (int)data->column_a.length, data->column_a.ptr, |
|
137 (int)data->column_b.length, data->column_b.ptr, |
|
138 (int)data->column_c.length, data->column_c.ptr |
|
139 ); |
|
140 } |
|
141 |
|
142 // cleanup everything, no manual free() needed |
|
143 cxMempoolFree(pool); |
|
144 |
|
145 return 0; |
|
146 } |
|
147 ``` |
|
148 |
|
149 <seealso> |
|
150 <category ref="apidoc"> |
|
151 <a href="https://ucx.sourceforge.io/api/mempool_8h.html">mempool.h</a> |
|
152 </category> |
|
153 </seealso> |