iodine 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/README.md +5 -1
  4. data/ext/iodine/base64.c +3 -3
  5. data/ext/iodine/base64.h +3 -3
  6. data/ext/iodine/bscrypt-common.h +5 -5
  7. data/ext/iodine/bscrypt.h +4 -4
  8. data/ext/iodine/hex.c +11 -10
  9. data/ext/iodine/hex.h +3 -3
  10. data/ext/iodine/http.c +34 -27
  11. data/ext/iodine/http.h +15 -8
  12. data/ext/iodine/http1.c +63 -60
  13. data/ext/iodine/http1.h +10 -5
  14. data/ext/iodine/http1_simple_parser.c +6 -0
  15. data/ext/iodine/http1_simple_parser.h +10 -6
  16. data/ext/iodine/http_request.h +28 -22
  17. data/ext/iodine/http_response.c +22 -7
  18. data/ext/iodine/http_response.h +6 -0
  19. data/ext/iodine/http_response_http1.h +9 -1
  20. data/ext/iodine/iodine_core.c +6 -1
  21. data/ext/iodine/iodine_core.h +13 -6
  22. data/ext/iodine/iodine_http.c +6 -0
  23. data/ext/iodine/iodine_http.h +6 -0
  24. data/ext/iodine/iodine_websocket.c +23 -4
  25. data/ext/iodine/iodine_websocket.h +2 -2
  26. data/ext/iodine/libasync.c +5 -3
  27. data/ext/iodine/libasync.h +8 -8
  28. data/ext/iodine/libreact.c +8 -8
  29. data/ext/iodine/libreact.h +5 -5
  30. data/ext/iodine/libserver.c +2 -2
  31. data/ext/iodine/libserver.h +2 -2
  32. data/ext/iodine/libsock.c +2 -2
  33. data/ext/iodine/libsock.h +3 -3
  34. data/ext/iodine/mempool.h +826 -0
  35. data/ext/iodine/misc.c +14 -14
  36. data/ext/iodine/misc.h +3 -3
  37. data/ext/iodine/random.c +6 -6
  38. data/ext/iodine/random.h +3 -3
  39. data/ext/iodine/rb-call.c +6 -0
  40. data/ext/iodine/rb-call.h +2 -2
  41. data/ext/iodine/rb-libasync.h +5 -3
  42. data/ext/iodine/rb-rack-io.c +15 -3
  43. data/ext/iodine/rb-rack-io.h +5 -2
  44. data/ext/iodine/rb-registry.c +6 -0
  45. data/ext/iodine/rb-registry.h +2 -2
  46. data/ext/iodine/sha1.c +13 -11
  47. data/ext/iodine/sha1.h +3 -3
  48. data/ext/iodine/sha2.c +8 -6
  49. data/ext/iodine/sha2.h +3 -3
  50. data/ext/iodine/siphash.c +9 -2
  51. data/ext/iodine/siphash.h +8 -1
  52. data/ext/iodine/spnlock.h +9 -3
  53. data/ext/iodine/websockets.c +62 -38
  54. data/ext/iodine/websockets.h +6 -0
  55. data/ext/iodine/xor-crypt.c +3 -3
  56. data/ext/iodine/xor-crypt.h +3 -3
  57. data/lib/iodine/version.rb +1 -1
  58. metadata +3 -4
  59. data/ext/iodine/rb-call.c_old +0 -127
  60. data/ext/iodine/rb-registry_old.c_old +0 -213
@@ -1,10 +1,12 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
2
+ copyright: Boaz segev, 2016-2017
3
3
  license: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
+ // clang-format off
7
8
  #include "rb-libasync.h"
9
+ // clang-format on
8
10
  #ifndef _GNU_SOURCE
9
11
  #define _GNU_SOURCE
10
12
  #endif
@@ -43,7 +45,7 @@ Performance options.
43
45
 
44
46
  /* Sentinal thread to respawn crashed threads - limited crash resistance. */
45
47
  #ifndef ASYNC_USE_SENTINEL
46
- #define ASYNC_USE_SENTINEL 1
48
+ #define ASYNC_USE_SENTINEL 0
47
49
  #endif
48
50
 
49
51
  /* *****************************************************************************
@@ -241,7 +243,7 @@ static inline void perform_tasks(void) {
241
243
  }
242
244
 
243
245
  /******************************************************************************
244
- Pasuing and resuming threads
246
+ Pausing and resuming threads
245
247
  */
246
248
 
247
249
  static inline void pause_thread() {
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -10,8 +10,8 @@ Feel free to copy, use and enjoy according to the license provided.
10
10
  #define LIB_ASYNC_VERSION_MINOR 4
11
11
  #define LIB_ASYNC_VERSION_PATCH 0
12
12
 
13
- #include <stdlib.h>
14
13
  #include <stdio.h>
14
+ #include <stdlib.h>
15
15
 
16
16
  #ifdef DEBUG
17
17
  // prints out testing and benchmarking data
@@ -101,17 +101,17 @@ Use:
101
101
  async_run(task, arg);
102
102
 
103
103
  */
104
- int async_run(void (*task)(void*), void* arg);
104
+ int async_run(void (*task)(void *), void *arg);
105
105
 
106
106
  /**
107
107
  Same as:
108
108
 
109
109
  `async_signal(); async_wait();`
110
110
  */
111
- #define async_finish() \
112
- { \
113
- async_signal(); \
114
- async_join(); \
111
+ #define async_finish() \
112
+ { \
113
+ async_signal(); \
114
+ async_join(); \
115
115
  }
116
116
 
117
117
  #endif /* LIB_ASYNC */
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -9,20 +9,20 @@ Feel free to copy, use and enjoy according to the license provided.
9
9
  #if !defined(__linux__) && !defined(__CYGWIN__)
10
10
  #include <sys/event.h>
11
11
  #else
12
- #include <sys/timerfd.h>
13
12
  #include <sys/epoll.h>
13
+ #include <sys/timerfd.h>
14
14
  #endif
15
- #include <time.h>
16
15
  #include <assert.h>
17
- #include <unistd.h>
18
- #include <fcntl.h>
19
16
  #include <errno.h>
17
+ #include <fcntl.h>
18
+ #include <netdb.h>
20
19
  #include <stdio.h>
21
20
  #include <stdlib.h>
22
21
  #include <string.h>
23
- #include <netdb.h>
24
- #include <sys/types.h>
25
22
  #include <sys/socket.h>
23
+ #include <sys/types.h>
24
+ #include <time.h>
25
+ #include <unistd.h>
26
26
 
27
27
  /* The (sadly, global) reactor fd */
28
28
  static int reactor_fd = -1;
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -22,11 +22,11 @@ Feel free to copy, use and enjoy according to the license provided.
22
22
  #endif
23
23
 
24
24
  #include <stdint.h>
25
- #include <unistd.h>
26
25
  #include <sys/time.h>
27
26
  #include <sys/types.h>
27
+ #include <unistd.h>
28
28
 
29
- #if !defined(__unix__) && !defined(__linux__) && !defined(__APPLE__) && \
29
+ #if !defined(__unix__) && !defined(__linux__) && !defined(__APPLE__) && \
30
30
  !defined(__CYGWIN__)
31
31
  #error This library currently supports only Unix based systems (i.e. Linux and BSD)
32
32
  #endif
@@ -83,8 +83,8 @@ Here are the supported events and their callbacks:
83
83
 
84
84
  Here's a quick example for an HTTP hello world (no HTTP parsing required)...:
85
85
 
86
- #include "libsock.h" // easy socket functions, also allows integration.
87
86
  #include "libreact.h" // the reactor library
87
+ #include "libsock.h" // easy socket functions, also allows integration.
88
88
 
89
89
  // a global server socket
90
90
  int srvfd = -1;
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /*
2
- copyright: Boaz segev, 2016
3
- license: MIT
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
4
 
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
@@ -20,10 +20,10 @@ The library is designed to be thread safe, but not fork safe.
20
20
  */
21
21
 
22
22
  #include <stdint.h>
23
- #include <unistd.h>
24
23
  #include <stdio.h>
25
24
  #include <stdlib.h>
26
25
  #include <sys/types.h>
26
+ #include <unistd.h>
27
27
 
28
28
  #ifndef __unused
29
29
  #define __unused __attribute__((unused))
@@ -0,0 +1,826 @@
1
+ /*
2
+ Copyright: Boaz segev, 2016-2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef MEMPOOL_H
8
+
9
+ /* *****************************************************************************
10
+ A simple `mmap` based localized memory pool (localized `malloc` alternative).
11
+
12
+ The memory pool is localized to the same object file (C file). See NOTICE.
13
+
14
+ The issue: objects that have a long life, such as Websocket / HTTP2 protocol
15
+ objects or server wide strings with reference counts, cause memory fragmentation
16
+ when allocated on the heap alongside objects that have a short life.
17
+
18
+ This is a common issue when using `malloc` in long running processes for all
19
+ allocations.
20
+
21
+ This issue effects long running processes (such as servers) while it's effect on
22
+ short lived proccesses are less accute and could often be ignored.
23
+
24
+ To circumvent this issue, a seperate memory allocation method is used for
25
+ long-lived objects.
26
+
27
+ This memory pool allocates large blocks of memory (~2Mb at a time), minimizing
28
+ small memory fragmentation by both reserving large memory blocks and seperating
29
+ memory locality between long lived objects and short lived objects.
30
+
31
+ The memory pool isn't expected to be faster than the system's `malloc`
32
+ (although, sometimes it might perform better). However, selective use of this
33
+ memory pool could improve concurrency (each pool has a seperate lock, unlike
34
+ `malloc`'s system lock') as well as help with memory fragmentation.
35
+
36
+ ================================================================================
37
+ NOTICE:
38
+
39
+ The memory pool is attached to the specific file. in which `mempool.h` is
40
+ included.
41
+
42
+ The memory shoule NEVER be freed from a different file.
43
+
44
+ However, it's easy to work around this limitation by wrapping the `mempool_`
45
+ functions using proper `create` / `destroy` functions for any objects.
46
+
47
+ ================================================================================
48
+
49
+ This file requires the "spnlock.h" library file as well. Together these files
50
+ can be used also seperately from the `facil.io` library.
51
+
52
+ */
53
+ #define MEMPOOL_H
54
+ MEMPOOL_H
55
+ #ifndef _GNU_SOURCE
56
+ #define _GNU_SOURCE
57
+ #endif
58
+
59
+ #include <errno.h>
60
+ #include <signal.h>
61
+ #include <stdint.h>
62
+ #include <stdio.h>
63
+ #include <stdlib.h>
64
+ #include <string.h>
65
+
66
+ #ifndef __unused
67
+ #define __unused __attribute__((unused))
68
+ #endif
69
+
70
+ /* *****************************************************************************
71
+ ********************************************************************************
72
+ API Declerations
73
+ ********************************************************************************
74
+ ***************************************************************************** */
75
+
76
+ /** Allocates memory from the pool. */
77
+ static __unused void *mempool_malloc(size_t size);
78
+ /**
79
+ * Frees the memory, releasing it back to the pool (or, sometimes, the system).
80
+ */
81
+ static __unused void mempool_free(void *ptr);
82
+ /**
83
+ * Behaves the same a the systems `realloc`, attempting to resize the memory
84
+ * when possible.
85
+ *
86
+ * On error returns NULL (the old pointer data remains allocated and valid)
87
+ * otherwise returns a new pointer (either equal to the old or after
88
+ * deallocating the old one).
89
+ */
90
+ static __unused void *mempool_realloc(void *ptr, size_t new_size);
91
+
92
+ #if defined(DEBUG) && DEBUG == 1
93
+ /** Tests the memory pool, both testing against issues / corruption and testing
94
+ * it's performance against the system's `malloc`.
95
+ */
96
+ static __unused void mempool_test(void);
97
+ #endif
98
+
99
+ /* *****************************************************************************
100
+ ********************************************************************************
101
+ Implementation
102
+ ********************************************************************************
103
+ ***************************************************************************** */
104
+
105
+ /* *****************************************************************************
106
+ Memory block allocation
107
+ */
108
+
109
+ #define MEMPOOL_BLOCK_SIZE (1UL << 21)
110
+ #define MEMPOOL_ORDERING_LIMIT 32
111
+ #define MEMPOOL_RETURN_MEM_TO_SYSTEM 1
112
+
113
+ /* Will we use mmap or malloc? */
114
+ // clang-format off
115
+ #ifdef __has_include
116
+ /* check for unix support */
117
+ # if __has_include(<unistd.h>) && __has_include(<sys/mman.h>)
118
+ # define HAS_UNIX_FEATURES
119
+ # endif
120
+ #endif
121
+ // clang-format on
122
+
123
+ #ifdef HAS_UNIX_FEATURES
124
+ #include <sys/mman.h>
125
+ #include <unistd.h>
126
+
127
+ /* *****************************************************************************
128
+ spnlock.h (can also be embeded instead of included)
129
+ */
130
+ #include "spnlock.h"
131
+
132
+ /* *****************************************************************************
133
+ Memory slices, tree and helpers
134
+ */
135
+
136
+ typedef struct mempool_reserved_slice_s {
137
+ struct mempool_reserved_slice_s_offset { /** offset from this slice */
138
+ uint32_t reserved1; /* used to make the offset 16 bytes long */
139
+ uint32_t ahead;
140
+ uint32_t behind;
141
+ uint32_t reserved2; /* used to make the offset 16 bytes long */
142
+ } offset;
143
+ /** Used for the free slices linked list. */
144
+ struct mempool_reserved_slice_s *next;
145
+ struct mempool_reserved_slice_s *prev;
146
+ } mempool_reserved_slice_s;
147
+
148
+ static struct {
149
+ mempool_reserved_slice_s *available;
150
+ spn_lock_i lock;
151
+ } mempool_reserved_pool = {.available = NULL, .lock = SPN_LOCK_INIT};
152
+
153
+ #define MEMPOOL_LOCK() spn_lock(&mempool_reserved_pool.lock)
154
+ #define MEMPOOL_UNLOCK() spn_unlock(&mempool_reserved_pool.lock)
155
+
156
+ #define MEMPOOL_USED_MARKER ((uint32_t)(~0UL << 21))
157
+ #define MEMPOOL_INDI_MARKER ((uint32_t)0xF7F7F7F7UL)
158
+ #define MEMPOOL_SIZE_MASK (MEMPOOL_BLOCK_SIZE - 1)
159
+
160
+ #define MEMPOOL_SLICE2PTR(slice) \
161
+ ((void *)(((uintptr_t)(slice)) + \
162
+ (sizeof(struct mempool_reserved_slice_s_offset))))
163
+ #define MEMPOOL_PTR2SLICE(ptr) \
164
+ ((mempool_reserved_slice_s *)(((uintptr_t)(ptr)) - \
165
+ (sizeof( \
166
+ struct mempool_reserved_slice_s_offset))))
167
+
168
+ /* *****************************************************************************
169
+ Memory Block Allocation / Deallocation
170
+ */
171
+
172
+ #define MEMPOOL_ALLOC_SPECIAL(target, size) \
173
+ do { \
174
+ target = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, \
175
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \
176
+ if (target == MAP_FAILED) \
177
+ target == NULL; \
178
+ } while (0);
179
+
180
+ #define MEMPOOL_DEALLOC_SPECIAL(target, size) munmap((target), (size))
181
+
182
+ #else
183
+
184
+ #define MEMPOOL_ALLOC_SPECIAL(target, size) \
185
+ do { \
186
+ target = malloc(size); \
187
+ } while (0);
188
+
189
+ #define MEMPOOL_DEALLOC_SPECIAL(target, size) free((target));
190
+
191
+ #endif
192
+
193
+ /* *****************************************************************************
194
+ Helpers: Memory block slicing and memory pool list maintanence.
195
+ */
196
+
197
+ /* *****************************************************************************
198
+ API implementation
199
+ */
200
+
201
+ static __unused void *mempool_malloc(size_t size) {
202
+ if (!size)
203
+ return NULL;
204
+ if (size & 15) {
205
+ size = (size & (~16)) + 16;
206
+ }
207
+ size += sizeof(struct mempool_reserved_slice_s_offset);
208
+
209
+ mempool_reserved_slice_s *slice = NULL;
210
+
211
+ if (size > (MEMPOOL_BLOCK_SIZE - (sizeof(mempool_reserved_slice_s) << 1)))
212
+ goto alloc_indi;
213
+ slice = mempool_reserved_pool.available;
214
+ MEMPOOL_LOCK();
215
+ while (slice && slice->offset.ahead < size)
216
+ slice = slice->next;
217
+ if (slice) {
218
+ /* remove slice from available memory list */
219
+ if (slice->next)
220
+ slice->next->prev = slice->prev;
221
+ if (slice->prev)
222
+ slice->prev->next = slice->next;
223
+ else
224
+ mempool_reserved_pool.available = slice->next;
225
+ slice->next = NULL;
226
+ slice->prev = NULL;
227
+ } else {
228
+ MEMPOOL_ALLOC_SPECIAL(slice, MEMPOOL_BLOCK_SIZE);
229
+ // fprintf(stderr, "Allocated Block at %p\n", slice);
230
+ slice->offset.behind = 0;
231
+ slice->offset.ahead =
232
+ MEMPOOL_BLOCK_SIZE - (sizeof(struct mempool_reserved_slice_s_offset));
233
+
234
+ mempool_reserved_slice_s *tmp =
235
+ (mempool_reserved_slice_s
236
+ *)(((uintptr_t)(slice)) + MEMPOOL_BLOCK_SIZE -
237
+ (sizeof(struct mempool_reserved_slice_s_offset)));
238
+ tmp->offset.ahead = 0;
239
+ tmp->offset.behind = slice->offset.ahead;
240
+ }
241
+
242
+ if (!slice) {
243
+ MEMPOOL_UNLOCK();
244
+ fprintf(stderr, "mempool: no memory\n");
245
+ return NULL;
246
+ }
247
+
248
+ if (slice->offset.ahead > (size + sizeof(mempool_reserved_slice_s))) {
249
+ /* cut the slice in two */
250
+ mempool_reserved_slice_s *tmp =
251
+ (mempool_reserved_slice_s *)(((uintptr_t)slice) + size);
252
+ tmp->offset.behind = size;
253
+ tmp->offset.ahead = slice->offset.ahead - size;
254
+ slice->offset.ahead = size;
255
+ /* inform higher neighbor about any updates */
256
+ ((mempool_reserved_slice_s *)(((uintptr_t)tmp) + tmp->offset.ahead))
257
+ ->offset.behind = tmp->offset.ahead;
258
+ /* place the new slice in the available memory list */
259
+ uint16_t limit = MEMPOOL_ORDERING_LIMIT;
260
+ tmp->next = NULL;
261
+ tmp->prev = NULL;
262
+ mempool_reserved_slice_s **pos = &mempool_reserved_pool.available;
263
+ while (limit && *pos && ((*pos)->offset.ahead < tmp->offset.ahead)) {
264
+ tmp->prev = *pos;
265
+ pos = &(*pos)->next;
266
+ --limit;
267
+ }
268
+ if (*pos) {
269
+ tmp->next = *pos;
270
+ tmp->next->prev = tmp;
271
+ *pos = tmp;
272
+ } else {
273
+ *pos = tmp;
274
+ }
275
+ }
276
+
277
+ slice->offset.ahead |= MEMPOOL_USED_MARKER;
278
+ MEMPOOL_UNLOCK();
279
+ slice->next = NULL;
280
+ slice->prev = NULL;
281
+ // mempool_reserved_slice_s *tmp =
282
+ // (void *)((uintptr_t)slice + (slice->offset.ahead &
283
+ // MEMPOOL_SIZE_MASK));
284
+ // fprintf(stderr, "Allocated %lu bytes at: %u <- %p -> %u."
285
+ // "next: %u <- %p -> %u\n ",
286
+ // size, slice->offset.behind, slice,
287
+ // (uint32_t)(slice->offset.ahead & MEMPOOL_SIZE_MASK),
288
+ // tmp->offset.behind, tmp,
289
+ // (uint32_t)(tmp->offset.ahead & MEMPOOL_SIZE_MASK));
290
+ return MEMPOOL_SLICE2PTR(slice);
291
+ alloc_indi:
292
+ MEMPOOL_ALLOC_SPECIAL(slice, size);
293
+ if (slice) {
294
+ slice->offset.ahead = size;
295
+ slice->offset.behind = MEMPOOL_INDI_MARKER;
296
+ }
297
+ return MEMPOOL_SLICE2PTR(slice);
298
+ }
299
+
300
+ /**
301
+ * Frees the memory, releasing it back to the pool (or, sometimes, the
302
+ * system).
303
+ */
304
+ static __unused void mempool_free(void *ptr) {
305
+ if (!ptr)
306
+ return;
307
+ mempool_reserved_slice_s **pos, *slice = MEMPOOL_PTR2SLICE(ptr), *tmp;
308
+
309
+ if (slice->offset.behind == MEMPOOL_INDI_MARKER)
310
+ goto alloc_indi;
311
+ if ((slice->offset.ahead & MEMPOOL_USED_MARKER) != MEMPOOL_USED_MARKER)
312
+ goto error;
313
+
314
+ MEMPOOL_LOCK();
315
+ slice->offset.ahead &= MEMPOOL_SIZE_MASK;
316
+ /* merge slice with upper boundry */
317
+ while ((tmp = (mempool_reserved_slice_s *)(((uintptr_t)slice) +
318
+ slice->offset.ahead))
319
+ ->offset.ahead &&
320
+ (tmp->offset.ahead & MEMPOOL_USED_MARKER) == 0) {
321
+ /* extract merged slice from list */
322
+ if (tmp->next)
323
+ tmp->next->prev = tmp->prev;
324
+ if (tmp->prev)
325
+ tmp->prev->next = tmp->next;
326
+ else
327
+ mempool_reserved_pool.available = tmp->next;
328
+
329
+ tmp->next = NULL;
330
+ tmp->prev = NULL;
331
+ slice->offset.ahead += tmp->offset.ahead;
332
+ }
333
+ /* merge slice with lower boundry */
334
+ while (slice->offset.behind &&
335
+ ((tmp = (mempool_reserved_slice_s *)(((uintptr_t)slice) -
336
+ slice->offset.behind))
337
+ ->offset.ahead &
338
+ MEMPOOL_USED_MARKER) == 0) {
339
+ /* extract merged slice from list */
340
+ if (tmp->next)
341
+ tmp->next->prev = tmp->prev;
342
+ if (tmp->prev)
343
+ tmp->prev->next = tmp->next;
344
+ else
345
+ mempool_reserved_pool.available = tmp->next;
346
+
347
+ tmp->next = NULL;
348
+ tmp->prev = NULL;
349
+ tmp->offset.ahead += slice->offset.ahead;
350
+
351
+ slice = tmp;
352
+ }
353
+
354
+ /* return memory to system, if the block is no longer required. */
355
+ if (MEMPOOL_RETURN_MEM_TO_SYSTEM && mempool_reserved_pool.available &&
356
+ slice->offset.behind == 0 &&
357
+ ((mempool_reserved_slice_s *)(((uintptr_t)slice) + slice->offset.ahead))
358
+ ->offset.ahead == 0) {
359
+ MEMPOOL_UNLOCK();
360
+ // fprintf(
361
+ // stderr, "DEALLOCATED BLOCK %p, size review %u == %lu %s\n", slice,
362
+ // slice->offset.ahead,
363
+ // MEMPOOL_BLOCK_SIZE - sizeof(struct mempool_reserved_slice_s_offset),
364
+ // (slice->offset.ahead ==
365
+ // MEMPOOL_BLOCK_SIZE - sizeof(struct mempool_reserved_slice_s_offset))
366
+ // ? "passed."
367
+ // : "FAILED.");
368
+ MEMPOOL_DEALLOC_SPECIAL(slice, MEMPOOL_BLOCK_SIZE);
369
+ return;
370
+ }
371
+
372
+ /* inform higher neighbor about any updates */
373
+ // fprintf(stderr, "slice: %p -> %u\n", slice, slice->offset.ahead);
374
+ ((mempool_reserved_slice_s *)(((uintptr_t)slice) + slice->offset.ahead))
375
+ ->offset.behind = slice->offset.ahead;
376
+
377
+ /* place slice in list */
378
+ uint8_t limit = MEMPOOL_ORDERING_LIMIT;
379
+ slice->next = NULL;
380
+ slice->prev = NULL;
381
+ pos = &mempool_reserved_pool.available;
382
+ while (limit && *pos && ((*pos)->offset.ahead < slice->offset.ahead)) {
383
+ slice->prev = *pos;
384
+ pos = &(*pos)->next;
385
+ --limit;
386
+ }
387
+ if (*pos) {
388
+ slice->next = *pos;
389
+ slice->next->prev = slice;
390
+ *pos = slice;
391
+ } else {
392
+ *pos = slice;
393
+ }
394
+
395
+ MEMPOOL_UNLOCK();
396
+ return;
397
+ alloc_indi:
398
+ MEMPOOL_DEALLOC_SPECIAL(slice, slice->offset.ahead);
399
+ return;
400
+ error:
401
+ MEMPOOL_UNLOCK();
402
+ if ((slice->offset.ahead & MEMPOOL_USED_MARKER) == 0)
403
+ fprintf(stderr, "mempool: memory being freed is already free.\n");
404
+ else
405
+ fprintf(stderr, "mempool: memory allocation data corrupted. possible "
406
+ "buffer overflow?\n");
407
+ errno = EFAULT;
408
+ raise(SIGSEGV); /* support longjmp rescue */
409
+ exit(EFAULT);
410
+ }
411
+ /**
412
+ * Behaves the same a the systems `realloc`, attempting to resize the memory
413
+ * when possible. On error returns NULL (the old pointer data remains allocated
414
+ * and valid) otherwise returns a new pointer (either equal to the old or after
415
+ * deallocating the old one).
416
+ */
417
+ static __unused void *mempool_realloc(void *ptr, size_t size) {
418
+ if (!size)
419
+ return NULL;
420
+ if (size & 15) {
421
+ size = (size & (~16)) + 16;
422
+ }
423
+ size += sizeof(struct mempool_reserved_slice_s_offset);
424
+
425
+ mempool_reserved_slice_s *tmp = NULL, *slice = MEMPOOL_PTR2SLICE(ptr);
426
+
427
+ if (slice->offset.behind == MEMPOOL_INDI_MARKER)
428
+ goto realloc_indi;
429
+ if ((slice->offset.ahead & MEMPOOL_USED_MARKER) != MEMPOOL_USED_MARKER)
430
+ goto error;
431
+
432
+ slice->offset.ahead &= MEMPOOL_SIZE_MASK;
433
+
434
+ MEMPOOL_LOCK();
435
+ /* merge slice with upper boundry */
436
+ while ((tmp = (mempool_reserved_slice_s *)(((uintptr_t)slice) +
437
+ slice->offset.ahead))
438
+ ->offset.ahead &&
439
+ (tmp->offset.ahead & MEMPOOL_USED_MARKER) == 0) {
440
+ /* extract merged slice from list */
441
+ if (tmp->next)
442
+ tmp->next->prev = tmp->prev;
443
+ if (tmp->prev)
444
+ tmp->prev->next = tmp->next;
445
+ else
446
+ mempool_reserved_pool.available = tmp->next;
447
+
448
+ tmp->next = NULL;
449
+ tmp->prev = NULL;
450
+ slice->offset.ahead += tmp->offset.ahead;
451
+ }
452
+
453
+ /* inform higher neighbor about any updates */
454
+ ((mempool_reserved_slice_s *)(((uintptr_t)slice) + slice->offset.ahead))
455
+ ->offset.behind = slice->offset.ahead;
456
+
457
+ if ((slice->offset.ahead) > size + sizeof(mempool_reserved_slice_s)) {
458
+ /* cut the slice in two */
459
+ tmp = (mempool_reserved_slice_s *)(((uintptr_t)slice) + size);
460
+ tmp->offset.behind = size;
461
+ tmp->offset.ahead = slice->offset.ahead - size;
462
+ slice->offset.ahead = size;
463
+ /* inform higher neighbor about any updates */
464
+ ((mempool_reserved_slice_s *)(((uintptr_t)tmp) + tmp->offset.ahead))
465
+ ->offset.behind = tmp->offset.ahead;
466
+ /* place the new slice in the available memory list */
467
+ tmp->next = NULL;
468
+ tmp->prev = NULL;
469
+ mempool_reserved_slice_s **pos = &mempool_reserved_pool.available;
470
+ uint8_t limit = MEMPOOL_ORDERING_LIMIT;
471
+ while (limit && *pos && ((*pos)->offset.ahead < tmp->offset.ahead)) {
472
+ tmp->prev = *pos;
473
+ pos = &(*pos)->next;
474
+ --limit;
475
+ }
476
+ if (*pos) {
477
+ tmp->next = *pos;
478
+ tmp->next->prev = tmp;
479
+ *pos = tmp;
480
+ } else {
481
+ *pos = tmp;
482
+ }
483
+
484
+ slice->offset.ahead |= MEMPOOL_USED_MARKER;
485
+ MEMPOOL_UNLOCK();
486
+ return ptr;
487
+ }
488
+ slice->offset.ahead |= MEMPOOL_USED_MARKER;
489
+ MEMPOOL_UNLOCK();
490
+
491
+ if ((slice->offset.ahead & MEMPOOL_SIZE_MASK) < size) {
492
+ void *new_mem =
493
+ mempool_malloc(size - sizeof(struct mempool_reserved_slice_s_offset));
494
+ if (!new_mem)
495
+ return NULL;
496
+ memcpy(new_mem, ptr, slice->offset.ahead & MEMPOOL_SIZE_MASK);
497
+ mempool_free(ptr);
498
+ ptr = new_mem;
499
+ }
500
+ return ptr;
501
+
502
+ realloc_indi:
503
+ /* indi doesn't shrink */
504
+ if (slice->offset.ahead > size)
505
+ return ptr;
506
+ /* reallocate indi */
507
+ void *new_mem =
508
+ mempool_malloc(size - sizeof(struct mempool_reserved_slice_s_offset));
509
+ if (!new_mem)
510
+ return NULL;
511
+ memcpy(new_mem, ptr, slice->offset.ahead & MEMPOOL_SIZE_MASK);
512
+ mempool_free(ptr);
513
+ return new_mem;
514
+ error:
515
+ errno = EFAULT;
516
+ raise(SIGSEGV); /* support longjmp rescue */
517
+ exit(EFAULT);
518
+ }
519
+
520
+ /* *****************************************************************************
521
+ ********************************************************************************
522
+ TESTING
523
+ ********************************************************************************
524
+ ***************************************************************************** */
525
+
526
+ #if defined(DEBUG) && DEBUG == 1
527
+
528
+ #define MEMTEST_SLICE 32
529
+
530
+ #include <time.h>
531
+ static void mempool_stats(void) {
532
+ fprintf(stderr, "* Pool object: %lu bytes\n"
533
+ "* Alignment: %lu \n"
534
+ "* Minimal Allocation Size (including header): %lu\n"
535
+ "* Minimal Allocation Space (no header): %lu\n"
536
+ "* Header size: %lu\n",
537
+ sizeof(mempool_reserved_pool),
538
+ sizeof(struct mempool_reserved_slice_s_offset),
539
+ sizeof(mempool_reserved_slice_s),
540
+ sizeof(mempool_reserved_slice_s) -
541
+ sizeof(struct mempool_reserved_slice_s_offset),
542
+ sizeof(struct mempool_reserved_slice_s_offset));
543
+ }
544
+
545
+ static void mempool_speedtest(size_t memtest_repeats, void *(*mlk)(size_t),
546
+ void (*fr)(void *),
547
+ void *(*ralc)(void *, size_t)) {
548
+ void **pntrs = mlk(memtest_repeats * sizeof(*pntrs));
549
+ clock_t start, end, mlk_time, fr_time, zr_time;
550
+ mlk_time = 0;
551
+ fr_time = 0;
552
+ zr_time = 0;
553
+ struct timespec start_test, end_test;
554
+ clock_gettime(CLOCK_MONOTONIC, &start_test);
555
+
556
+ start = clock();
557
+ for (size_t i = 0; i < memtest_repeats; i++) {
558
+ __asm__ volatile("" ::: "memory");
559
+ }
560
+ end = clock();
561
+ mlk_time = end - start;
562
+ fprintf(stderr, "* Doing nothing: %lu CPU cycles.\n", mlk_time);
563
+
564
+ start = clock();
565
+ for (size_t i = 0; i < memtest_repeats; i++) {
566
+ // fprintf(stderr, "malloc %lu\n", i);
567
+ pntrs[i] = mlk(MEMTEST_SLICE);
568
+ *((uint8_t *)pntrs[i]) = 1;
569
+ }
570
+ end = clock();
571
+ mlk_time = end - start;
572
+ fprintf(stderr,
573
+ "* Allocating %lu consecutive blocks %d each: %lu CPU cycles.\n",
574
+ memtest_repeats, MEMTEST_SLICE, mlk_time);
575
+
576
+ start = clock();
577
+ for (size_t i = 0; i < memtest_repeats; i += 2) {
578
+ fr(pntrs[i]);
579
+ }
580
+ end = clock();
581
+ fr_time = end - start;
582
+
583
+ start = clock();
584
+ for (size_t i = 0; i < memtest_repeats; i += 2) {
585
+ pntrs[i] = mlk(MEMTEST_SLICE);
586
+ }
587
+ end = clock();
588
+ mlk_time = end - start;
589
+
590
+ fprintf(stderr,
591
+ "* Freeing %lu Fragmented (single space) blocks %d each: %lu CPU "
592
+ "cycles.\n",
593
+ memtest_repeats / 2, MEMTEST_SLICE, fr_time);
594
+
595
+ fprintf(stderr, "* Allocating %lu Fragmented (single space) blocks %d "
596
+ "bytes each: %lu CPU "
597
+ "cycles.\n",
598
+ memtest_repeats / 2, MEMTEST_SLICE, mlk_time);
599
+
600
+ mlk_time = 0;
601
+ fr_time = 0;
602
+
603
+ for (size_t xtimes = 0; xtimes < 100; xtimes++) {
604
+ start = clock();
605
+ for (size_t i = 0; i < memtest_repeats; i += 7) {
606
+ fr(pntrs[i]);
607
+ }
608
+ end = clock();
609
+ fr_time += end - start;
610
+
611
+ start = clock();
612
+ for (size_t i = 0; i < memtest_repeats; i += 7) {
613
+ pntrs[i] = mlk(MEMTEST_SLICE);
614
+ }
615
+ end = clock();
616
+ mlk_time += end - start;
617
+ }
618
+
619
+ fprintf(stderr,
620
+ "* 100X Freeing %lu Fragmented (7 spaces) blocks %d each: %lu CPU "
621
+ "cycles.\n",
622
+ memtest_repeats / 7, MEMTEST_SLICE, fr_time);
623
+
624
+ fprintf(stderr, "* 100X Allocating %lu Fragmented (7 spaces) blocks %d "
625
+ "bytes each: %lu CPU "
626
+ "cycles.\n",
627
+ memtest_repeats / 7, MEMTEST_SLICE, mlk_time);
628
+
629
+ start = clock();
630
+ for (size_t i = 0; i < memtest_repeats; i++) {
631
+ memset(pntrs[i], 170, MEMTEST_SLICE);
632
+ }
633
+ end = clock();
634
+ zr_time = end - start;
635
+ fprintf(stderr, "* Set bits (0b10) for %lu consecutive blocks %dB "
636
+ "each: %lu CPU cycles.\n",
637
+ memtest_repeats, MEMTEST_SLICE, zr_time);
638
+
639
+ start = clock();
640
+ for (size_t i = 0; i < memtest_repeats; i++) {
641
+ fr(pntrs[i]);
642
+ }
643
+ end = clock();
644
+ fr_time = end - start;
645
+ fprintf(stderr, "* Freeing %lu consecutive blocks %d each: %lu CPU cycles.\n",
646
+ memtest_repeats, MEMTEST_SLICE, fr_time);
647
+
648
+ start = clock();
649
+ for (size_t i = 0; i < memtest_repeats; i++) {
650
+ pntrs[i] = mlk(MEMTEST_SLICE);
651
+ }
652
+ end = clock();
653
+ start = clock();
654
+ for (size_t i = 0; i < memtest_repeats; i += 2) {
655
+ fr(pntrs[i]);
656
+ }
657
+ end = clock();
658
+ mlk_time = end - start;
659
+ fprintf(stderr,
660
+ "* Freeing every other block %dB X %lu blocks: %lu CPU cycles.\n",
661
+ MEMTEST_SLICE, memtest_repeats >> 1, mlk_time);
662
+
663
+ start = clock();
664
+ for (size_t i = 1; i < memtest_repeats; i += 2) {
665
+ pntrs[i] = ralc(pntrs[i], MEMTEST_SLICE << 1);
666
+ if (pntrs[i] == NULL)
667
+ fprintf(stderr, "REALLOC RETURNED NULL - Memory leaked during test\n");
668
+ }
669
+ end = clock();
670
+ mlk_time = end - start;
671
+ fprintf(
672
+ stderr,
673
+ "* Reallocating every other block %dB X %lu blocks: %lu CPU cycles.\n",
674
+ MEMTEST_SLICE, memtest_repeats >> 1, mlk_time);
675
+
676
+ start = clock();
677
+ for (size_t i = 1; i < memtest_repeats; i += 2) {
678
+ fr(pntrs[i]);
679
+ }
680
+ end = clock();
681
+ mlk_time = end - start;
682
+ fprintf(stderr,
683
+ "* Freeing every other block %dB X %lu blocks: %lu CPU cycles.\n",
684
+ MEMTEST_SLICE, memtest_repeats >> 1, mlk_time);
685
+
686
+ start = clock();
687
+ for (size_t i = 0; i < memtest_repeats; i++) {
688
+ pntrs[i] = mlk(MEMTEST_SLICE);
689
+ }
690
+ end = clock();
691
+ mlk_time = end - start;
692
+ fprintf(stderr,
693
+ "* Allocating %lu consecutive blocks %d each: %lu CPU cycles.\n",
694
+ memtest_repeats, MEMTEST_SLICE, mlk_time);
695
+ start = clock();
696
+ for (size_t i = 0; i < memtest_repeats; i++) {
697
+ fr(pntrs[i]);
698
+ }
699
+ end = clock();
700
+ fr_time = end - start;
701
+ fprintf(stderr, "* Freeing %lu consecutive blocks %d each: %lu CPU cycles.\n",
702
+ memtest_repeats, MEMTEST_SLICE, fr_time);
703
+ fprintf(stderr, "* Freeing pointer array %p.\n", pntrs);
704
+ fr(pntrs);
705
+
706
+ clock_gettime(CLOCK_MONOTONIC, &end_test);
707
+ uint64_t msec_for_test =
708
+ (end_test.tv_nsec < start_test.tv_nsec)
709
+ ? ((end_test.tv_sec -= 1), (start_test.tv_nsec - end_test.tv_nsec))
710
+ : (end_test.tv_nsec - start_test.tv_nsec);
711
+ uint64_t sec_for_test = end_test.tv_sec - start_test.tv_sec;
712
+
713
+ fprintf(stderr, "Finished test in %llum, %llus %llu mili.sec.\n",
714
+ sec_for_test / 60, sec_for_test - (((sec_for_test) / 60) * 60),
715
+ msec_for_test / 1000000);
716
+ }
717
+
718
+ static __unused void mempool_test(void) {
719
+ fprintf(stderr, "*****************************\n");
720
+ fprintf(stderr, "mempool implementation details:\n");
721
+ mempool_stats();
722
+ fprintf(stderr, "*****************************\n");
723
+ fprintf(stderr, "System memory test for ~2Mb\n");
724
+ mempool_speedtest((2 << 20) / MEMTEST_SLICE, malloc, free, realloc);
725
+ fprintf(stderr, "*****************************\n");
726
+ fprintf(stderr, " mempool memory test for ~2Mb\n");
727
+ mempool_speedtest((2 << 20) / MEMTEST_SLICE, mempool_malloc, mempool_free,
728
+ mempool_realloc);
729
+ fprintf(stderr, "*****************************\n");
730
+ fprintf(stderr, "System memory test for ~4Mb\n");
731
+ mempool_speedtest((2 << 21) / MEMTEST_SLICE, malloc, free, realloc);
732
+ fprintf(stderr, "*****************************\n");
733
+ fprintf(stderr, " mempool memory test for ~4Mb\n");
734
+ mempool_speedtest((2 << 21) / MEMTEST_SLICE, mempool_malloc, mempool_free,
735
+ mempool_realloc);
736
+ fprintf(stderr, "*****************************\n");
737
+ fprintf(stderr, "System memory test for ~8Mb\n");
738
+ mempool_speedtest((2 << 22) / MEMTEST_SLICE, malloc, free, realloc);
739
+ fprintf(stderr, "*****************************\n");
740
+ fprintf(stderr, " mempool memory test for ~8Mb\n");
741
+ mempool_speedtest((2 << 22) / MEMTEST_SLICE, mempool_malloc, mempool_free,
742
+ mempool_realloc);
743
+ fprintf(stderr, "*****************************\n");
744
+ fprintf(stderr, "System memory test for ~16Mb\n");
745
+ mempool_speedtest((2 << 23) / MEMTEST_SLICE, malloc, free, realloc);
746
+ fprintf(stderr, "*****************************\n");
747
+ fprintf(stderr, " mempool memory test for ~16Mb\n");
748
+ mempool_speedtest((2 << 23) / MEMTEST_SLICE, mempool_malloc, mempool_free,
749
+ mempool_realloc);
750
+ fprintf(stderr, "*****************************\n");
751
+
752
+ fprintf(stderr, "*****************************\n");
753
+ fprintf(stderr, "Stressing the system\n");
754
+ fprintf(stderr, "*****************************\n");
755
+ size_t repeat = 1024 * 1024 * 16;
756
+ size_t unit = 16;
757
+ struct timespec start_test, end_test;
758
+ clock_t start, end;
759
+ fprintf(stderr, "Stress allocation/deallocation using "
760
+ "1:5 fragmentation of ~134Mb:\n");
761
+ while (repeat >= 1024) {
762
+ fprintf(stderr, " * %lu X %lu bytes", repeat, unit);
763
+ clock_gettime(CLOCK_MONOTONIC, &start_test);
764
+ start = clock();
765
+ void **ptrs = mempool_malloc(repeat * sizeof(void *));
766
+ for (size_t i = 0; i < repeat; i++) {
767
+ ptrs[i] = mempool_malloc(unit);
768
+ }
769
+ for (size_t i = 0; i < repeat; i += 5) {
770
+ mempool_free(ptrs[i]);
771
+ }
772
+ for (size_t i = 1; i < repeat; i += 5) {
773
+ mempool_free(ptrs[i]);
774
+ }
775
+ for (size_t i = 2; i < repeat; i += 5) {
776
+ mempool_free(ptrs[i]);
777
+ }
778
+ for (size_t i = 3; i < repeat; i += 5) {
779
+ mempool_free(ptrs[i]);
780
+ }
781
+ for (size_t i = 4; i < repeat; i += 5) {
782
+ mempool_free(ptrs[i]);
783
+ }
784
+ for (size_t i = 0; i < repeat; i++) {
785
+ ptrs[i] = mempool_malloc(unit);
786
+ }
787
+ for (size_t i = 0; i < repeat; i++) {
788
+ mempool_free(ptrs[i]);
789
+ }
790
+ mempool_free(ptrs);
791
+ end = clock();
792
+ clock_gettime(CLOCK_MONOTONIC, &end_test);
793
+ uint64_t msec_for_test =
794
+ (end_test.tv_nsec < start_test.tv_nsec)
795
+ ? ((end_test.tv_sec -= 1), (start_test.tv_nsec - end_test.tv_nsec))
796
+ : (end_test.tv_nsec - start_test.tv_nsec);
797
+ uint64_t sec_for_test = end_test.tv_sec - start_test.tv_sec;
798
+
799
+ fprintf(stderr, " %llum, %llus %llu mili.sec. ( %lu CPU)\n",
800
+ sec_for_test / 60, sec_for_test - (((sec_for_test) / 60) * 60),
801
+ msec_for_test / 1000000, end - start);
802
+
803
+ unit <<= 1;
804
+ repeat >>= 1;
805
+ }
806
+ }
807
+
808
+ #undef MEMTEST_SLICE
809
+
810
+ #endif
811
+
812
+ /* *****************************************************************************
813
+ Cleanup
814
+ */
815
+ #undef MEMPOOL_BLOCK_SIZE
816
+ #undef MEMPOOL_ALLOC_SPECIAL
817
+ #undef MEMPOOL_DEALLOC_SPECIAL
818
+ #undef MEMPOOL_SIZE_MASK
819
+ #undef MEMPOOL_USED_MARKER
820
+ #undef MEMPOOL_INDI_MARKER
821
+ #undef MEMPOOL_SLICE2PTR
822
+ #undef MEMPOOL_PTR2SLICE
823
+ #undef MEMPOOL_LOCK
824
+ #undef MEMPOOL_UNLOCK
825
+ #undef MEMPOOL_ORDERING_LIMIT
826
+ #endif