iodine 0.4.19 → 0.5.0

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -0,0 +1,257 @@
1
+ #ifndef H_FIO_SIMPLE_LIST_H
2
+ /*
3
+ Copyright: Boaz Segev, 2017-2018
4
+ License: MIT
5
+ */
6
+
7
+ /* *****************************************************************************
8
+ Simple Linked List - Used for existing objects (not embeddable).
9
+ ***************************************************************************** */
10
+
11
+ /**
12
+ This header includes all the internal rescources / data and types required to
13
+ create object types.
14
+ */
15
+ #define H_FIO_SIMPLE_LIST_H
16
+
17
+ #ifndef _GNU_SOURCE
18
+ #define _GNU_SOURCE
19
+ #endif
20
+
21
+ #ifndef FIO_FUNC
22
+ #define FIO_FUNC static __attribute__((unused))
23
+ #endif
24
+
25
+ #include <errno.h>
26
+ #include <stdio.h>
27
+ #include <stdlib.h>
28
+
29
+ /* *****************************************************************************
30
+ Data Structure and Initialization.
31
+ ***************************************************************************** */
32
+
33
+ /** an embeded linked list. */
34
+ typedef struct fio_ls_embd_s {
35
+ struct fio_ls_embd_s *prev;
36
+ struct fio_ls_embd_s *next;
37
+ } fio_ls_embd_s;
38
+
39
+ /** an independent linked list. */
40
+ typedef struct fio_ls_s {
41
+ struct fio_ls_s *prev;
42
+ struct fio_ls_s *next;
43
+ const void *obj;
44
+ } fio_ls_s;
45
+
46
+ #define FIO_LS_INIT(name) \
47
+ { .next = &(name), .prev = &(name) }
48
+
49
+ /* *****************************************************************************
50
+ Independent List API
51
+ ***************************************************************************** */
52
+
53
+ /** Adds an object to the list's head. */
54
+ FIO_FUNC inline void fio_ls_push(fio_ls_s *pos, const void *obj);
55
+
56
+ /** Adds an object to the list's tail. */
57
+ FIO_FUNC inline void fio_ls_unshift(fio_ls_s *pos, const void *obj);
58
+
59
+ /** Removes an object from the list's head. */
60
+ FIO_FUNC inline void *fio_ls_pop(fio_ls_s *list);
61
+
62
+ /** Removes an object from the list's tail. */
63
+ FIO_FUNC inline void *fio_ls_shift(fio_ls_s *list);
64
+
65
+ /** Removes a node from the list, returning the contained object. */
66
+ FIO_FUNC inline void *fio_ls_remove(fio_ls_s *node);
67
+
68
+ /** Tests if the list is empty. */
69
+ FIO_FUNC inline int fio_ls_is_empty(fio_ls_s *list);
70
+
71
+ /** Tests if the list is NOT empty (contains any nodes). */
72
+ FIO_FUNC inline int fio_ls_any(fio_ls_s *list);
73
+
74
+ /**
75
+ * Iterates through the list using a `for` loop.
76
+ *
77
+ * Access the data with `pos->obj` (`pos` can be named however you pleas..
78
+ */
79
+ #define FIO_LS_FOR(list, pos)
80
+
81
+ /* *****************************************************************************
82
+ Embedded List API
83
+ ***************************************************************************** */
84
+
85
+ /** Adds a node to the list's head. */
86
+ FIO_FUNC inline void fio_ls_embd_push(fio_ls_embd_s *dest, fio_ls_embd_s *node);
87
+
88
+ /** Adds a node to the list's tail. */
89
+ FIO_FUNC inline void fio_ls_embd_unshift(fio_ls_embd_s *dest,
90
+ fio_ls_embd_s *node);
91
+
92
+ /** Removes a node from the list's head. */
93
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_pop(fio_ls_embd_s *list);
94
+
95
+ /** Removes a node from the list's tail. */
96
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_shift(fio_ls_embd_s *list);
97
+
98
+ /** Removes a node from the containing node. */
99
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_remove(fio_ls_embd_s *node);
100
+
101
+ /** Tests if the list is empty. */
102
+ FIO_FUNC inline int fio_ls_embd_is_empty(fio_ls_embd_s *list);
103
+
104
+ /** Tests if the list is NOT empty (contains any nodes). */
105
+ FIO_FUNC inline int fio_ls_embd_any(fio_ls_embd_s *list);
106
+
107
+ /**
108
+ * Iterates through the list using a `for` loop.
109
+ *
110
+ * Access the data with `pos->obj` (`pos` can be named however you pleas..
111
+ */
112
+ #define FIO_LS_EMBD_FOR(list, node)
113
+
114
+ /**
115
+ * Takes a list pointer `plist` and returns a pointer to it's container.
116
+ *
117
+ * This uses pointer offset calculations and can be used to calculate any
118
+ * struct's pointer (not just list containers) as an offset from a pointer of
119
+ * one of it's members.
120
+ *
121
+ * Very useful.
122
+ */
123
+ #define FIO_LS_EMBD_OBJ(type, member, plist) \
124
+ ((type *)((uintptr_t)(plist) - (uintptr_t)(&(((type *)0)->member))))
125
+
126
+ /* *****************************************************************************
127
+ Independent Implementation
128
+ ***************************************************************************** */
129
+
130
+ /** Adds an object to the list's head. */
131
+ FIO_FUNC inline void fio_ls_push(fio_ls_s *pos, const void *obj) {
132
+ /* prepare item */
133
+ fio_ls_s *item = (fio_ls_s *)malloc(sizeof(*item));
134
+ if (!item) {
135
+ perror("ERROR: simple list couldn't allocate memory");
136
+ exit(errno);
137
+ }
138
+ *item = (fio_ls_s){.prev = pos, .next = pos->next, .obj = obj};
139
+ /* inject item */
140
+ pos->next->prev = item;
141
+ pos->next = item;
142
+ }
143
+
144
+ /** Adds an object to the list's tail. */
145
+ FIO_FUNC inline void fio_ls_unshift(fio_ls_s *pos, const void *obj) {
146
+ pos = pos->prev;
147
+ fio_ls_push(pos, obj);
148
+ }
149
+
150
+ /** Removes an object from the list's head. */
151
+ FIO_FUNC inline void *fio_ls_pop(fio_ls_s *list) {
152
+ if (list->next == list)
153
+ return NULL;
154
+ fio_ls_s *item = list->next;
155
+ const void *ret = item->obj;
156
+ list->next = item->next;
157
+ list->next->prev = list;
158
+ free(item);
159
+ return (void *)ret;
160
+ }
161
+
162
+ /** Removes an object from the list's tail. */
163
+ FIO_FUNC inline void *fio_ls_shift(fio_ls_s *list) {
164
+ if (list->prev == list)
165
+ return NULL;
166
+ fio_ls_s *item = list->prev;
167
+ const void *ret = item->obj;
168
+ list->prev = item->prev;
169
+ list->prev->next = list;
170
+ free(item);
171
+ return (void *)ret;
172
+ }
173
+
174
+ /** Removes an object from the containing node. */
175
+ FIO_FUNC inline void *fio_ls_remove(fio_ls_s *node) {
176
+ const void *ret = node->obj;
177
+ node->next->prev = node->prev;
178
+ node->prev->next = node->next;
179
+ free(node);
180
+ return (void *)ret;
181
+ }
182
+
183
+ /** Tests if the list is empty. */
184
+ FIO_FUNC inline int fio_ls_is_empty(fio_ls_s *list) {
185
+ return list->next == list;
186
+ }
187
+
188
+ /** Tests if the list is NOT empty (contains any nodes). */
189
+ FIO_FUNC inline int fio_ls_any(fio_ls_s *list) { return list->next != list; }
190
+
191
+ #undef FIO_LS_FOR
192
+ #define FIO_LS_FOR(list, pos) \
193
+ for (fio_ls_s *pos = (list)->next; pos != (list); pos = pos->next)
194
+
195
+ /* *****************************************************************************
196
+ Embeded List Implementation
197
+ ***************************************************************************** */
198
+
199
+ /** Adds a node to the list's head. */
200
+ FIO_FUNC inline void fio_ls_embd_push(fio_ls_embd_s *dest,
201
+ fio_ls_embd_s *node) {
202
+ node->next = dest->next;
203
+ node->prev = dest->next->prev;
204
+ dest->next->prev = node;
205
+ dest->next = node;
206
+ }
207
+
208
+ /** Adds a node to the list's tail. */
209
+ FIO_FUNC inline void fio_ls_embd_unshift(fio_ls_embd_s *dest,
210
+ fio_ls_embd_s *node) {
211
+ fio_ls_embd_push(dest->prev, node);
212
+ }
213
+
214
+ /** Removes a node from the list's head. */
215
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_pop(fio_ls_embd_s *list) {
216
+ if (list->next == list)
217
+ return NULL;
218
+ fio_ls_embd_s *item = list->next;
219
+ list->next = item->next;
220
+ list->next->prev = list;
221
+ return item;
222
+ }
223
+
224
+ /** Removes a node from the list's tail. */
225
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_shift(fio_ls_embd_s *list) {
226
+ if (list->prev == list)
227
+ return NULL;
228
+ fio_ls_embd_s *item = list->prev;
229
+ list->prev = item->prev;
230
+ list->prev->next = list;
231
+ return item;
232
+ }
233
+
234
+ /** Removes a node from the containing node. */
235
+ FIO_FUNC inline fio_ls_embd_s *fio_ls_embd_remove(fio_ls_embd_s *node) {
236
+ node->next->prev = node->prev;
237
+ node->prev->next = node->next;
238
+ return node;
239
+ }
240
+
241
+ /** Tests if the list is empty. */
242
+ FIO_FUNC inline int fio_ls_embd_is_empty(fio_ls_embd_s *list) {
243
+ return list->next == list;
244
+ }
245
+
246
+ /** Tests if the list is NOT empty (contains any nodes). */
247
+ FIO_FUNC inline int fio_ls_embd_any(fio_ls_embd_s *list) {
248
+ return list->next != list;
249
+ }
250
+
251
+ #undef FIO_LS_EMBD_FOR
252
+ #define FIO_LS_EMBD_FOR(list, node) \
253
+ for (fio_ls_embd_s *node = (list)->next; node != (list); node = node->next)
254
+
255
+ #undef FIO_FUNC
256
+
257
+ #endif
@@ -0,0 +1,672 @@
1
+ /*
2
+ Copyright: Boaz Segev, 2018
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #include "spnlock.inc"
9
+ #include <errno.h>
10
+ #include <stdint.h>
11
+ #include <stdio.h>
12
+ #include <string.h>
13
+ #include <sys/mman.h>
14
+ #include <unistd.h>
15
+
16
+ /* *****************************************************************************
17
+ If FIO_FORCE_MALLOC is set, use glibc / library malloc
18
+ ***************************************************************************** */
19
+ #if FIO_FORCE_MALLOC
20
+
21
+ void *fio_malloc(size_t size) { return malloc(size); }
22
+
23
+ void *fio_calloc(size_t size, size_t count) { return calloc(size, count); }
24
+
25
+ void fio_free(void *ptr) { free(ptr); }
26
+
27
+ void *fio_realloc(void *ptr, size_t new_size) { return realloc(ptr, new_size); }
28
+ void *fio_realloc2(void *ptr, size_t new_size, size_t valid_len) {
29
+ return realloc(ptr, new_size);
30
+ (void)valid_len;
31
+ }
32
+
33
+ void fio_malloc_after_fork(void) {}
34
+
35
+ /* *****************************************************************************
36
+ facil.io malloc implementation
37
+ ***************************************************************************** */
38
+ #else
39
+
40
+ #include "fio_mem.h"
41
+
42
+ #if !defined(__clang__) && !defined(__GNUC__)
43
+ #define __thread _Thread_value
44
+ #endif
45
+
46
+ #undef malloc
47
+ #undef calloc
48
+ #undef free
49
+ #undef realloc
50
+
51
+ /* *****************************************************************************
52
+ Memory Copying by 16 byte units
53
+ ***************************************************************************** */
54
+
55
+ #if __SIZEOF_INT128__ == 9 /* a 128bit type exists... but tests favor 64bit */
56
+ static inline void fio_memcpy(__uint128_t *__restrict dest,
57
+ __uint128_t *__restrict src, size_t units) {
58
+ #elif SIZE_MAX == 0xFFFFFFFFFFFFFFFF /* 64 bit size_t */
59
+ static inline void fio_memcpy(size_t *__restrict dest, size_t *__restrict src,
60
+ size_t units) {
61
+ units = units << 1;
62
+ #elif SIZE_MAX == 0xFFFFFFFF /* 32 bit size_t */
63
+ static inline void fio_memcpy(size_t *__restrict dest, size_t *__restrict src,
64
+ size_t units) {
65
+ units = units << 2;
66
+ #else /* unknow... assume 16 bit? */
67
+ static inline void fio_memcpy(uint16_t *__restrict dest,
68
+ uint16_t *__restrict src, size_t units) {
69
+ units = units << 3;
70
+ #endif
71
+ while (units >= 16) { /* unroll loop */
72
+ dest[0] = src[0];
73
+ dest[1] = src[1];
74
+ dest[2] = src[2];
75
+ dest[3] = src[3];
76
+ dest[4] = src[4];
77
+ dest[5] = src[5];
78
+ dest[6] = src[6];
79
+ dest[7] = src[7];
80
+ dest[8] = src[8];
81
+ dest[9] = src[9];
82
+ dest[10] = src[10];
83
+ dest[11] = src[11];
84
+ dest[12] = src[12];
85
+ dest[13] = src[13];
86
+ dest[14] = src[14];
87
+ dest[15] = src[15];
88
+ dest += 16;
89
+ src += 16;
90
+ units -= 16;
91
+ }
92
+ switch (units) {
93
+ case 15:
94
+ *(dest++) = *(src++); /* fallthrough */
95
+ case 14:
96
+ *(dest++) = *(src++); /* fallthrough */
97
+ case 13:
98
+ *(dest++) = *(src++); /* fallthrough */
99
+ case 12:
100
+ *(dest++) = *(src++); /* fallthrough */
101
+ case 11:
102
+ *(dest++) = *(src++); /* fallthrough */
103
+ case 10:
104
+ *(dest++) = *(src++); /* fallthrough */
105
+ case 9:
106
+ *(dest++) = *(src++); /* fallthrough */
107
+ case 8:
108
+ *(dest++) = *(src++); /* fallthrough */
109
+ case 7:
110
+ *(dest++) = *(src++); /* fallthrough */
111
+ case 6:
112
+ *(dest++) = *(src++); /* fallthrough */
113
+ case 5:
114
+ *(dest++) = *(src++); /* fallthrough */
115
+ case 4:
116
+ *(dest++) = *(src++); /* fallthrough */
117
+ case 3:
118
+ *(dest++) = *(src++); /* fallthrough */
119
+ case 2:
120
+ *(dest++) = *(src++); /* fallthrough */
121
+ case 1:
122
+ *(dest++) = *(src++);
123
+ }
124
+ }
125
+
126
+ /* *****************************************************************************
127
+ System Memory wrappers
128
+ ***************************************************************************** */
129
+
130
+ /*
131
+ * allocates memory using `mmap`, but enforces block size alignment.
132
+ * requires page aligned `len`.
133
+ *
134
+ * `align_shift` is used to move the memory page alignment to allow for a single
135
+ * page allocation header. align_shift MUST be either 0 (normal) or 1 (single
136
+ * page header). Other values might cause errors.
137
+ */
138
+ static inline void *sys_alloc(size_t len, uint8_t is_indi) {
139
+ void *result;
140
+ static void *next_alloc = NULL;
141
+ /* hope for the best? */
142
+ #ifdef MAP_ALIGNED
143
+ result = mmap(
144
+ next_alloc, len, PROT_READ | PROT_WRITE | PROT_EXEC,
145
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_ALIGNED(FIO_MEMORY_BLOCK_SIZE), -1, 0);
146
+ #else
147
+ result = mmap(next_alloc, len, PROT_READ | PROT_WRITE | PROT_EXEC,
148
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
149
+ #endif
150
+ if (result == MAP_FAILED)
151
+ return NULL;
152
+ if (((uintptr_t)result & FIO_MEMORY_BLOCK_MASK)) {
153
+ munmap(result, len);
154
+ result = mmap(NULL, len + FIO_MEMORY_BLOCK_SIZE,
155
+ PROT_READ | PROT_WRITE | PROT_EXEC,
156
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
157
+ if (result == MAP_FAILED)
158
+ return NULL;
159
+ const uintptr_t offset =
160
+ (FIO_MEMORY_BLOCK_SIZE - ((uintptr_t)result & FIO_MEMORY_BLOCK_MASK));
161
+ if (offset) {
162
+ munmap(result, offset);
163
+ result = (void *)((uintptr_t)result + offset);
164
+ }
165
+ munmap((void *)((uintptr_t)result + len), FIO_MEMORY_BLOCK_SIZE - offset);
166
+ }
167
+ next_alloc =
168
+ (void *)((uintptr_t)result + FIO_MEMORY_BLOCK_SIZE +
169
+ (is_indi * ((uintptr_t)1 << 30))); /* add 1TB for realloc */
170
+ return result;
171
+ }
172
+
173
+ /* frees memory using `munmap`. requires exact, page aligned, `len` */
174
+ static inline void sys_free(void *mem, size_t len) { munmap(mem, len); }
175
+
176
+ static void *sys_realloc(void *mem, size_t prev_len, size_t new_len) {
177
+ if (new_len > prev_len) {
178
+ #if defined(__linux__) && defined(MREMAP_MAYMOVE)
179
+ void *result = mremap(mem, prev_len, new_len, MREMAP_MAYMOVE);
180
+ if (result == MAP_FAILED)
181
+ return NULL;
182
+ #else
183
+ void *result = mmap((void *)((uintptr_t)mem + prev_len), new_len - prev_len,
184
+ PROT_READ | PROT_WRITE | PROT_EXEC,
185
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
186
+ if (result == (void *)((uintptr_t)mem + prev_len)) {
187
+ result = mem;
188
+ } else {
189
+ /* copy and free */
190
+ munmap(result, new_len - prev_len); /* free the failed attempt */
191
+ result = sys_alloc(new_len, 1); /* allocate new memory */
192
+ if (!result)
193
+ return NULL;
194
+ fio_memcpy(result, mem, prev_len >> 4); /* copy data */
195
+ // memcpy(result, mem, prev_len);
196
+ munmap(mem, prev_len); /* free original memory */
197
+ }
198
+ #endif
199
+ return result;
200
+ }
201
+ if (new_len + 4096 < prev_len) /* more than a single dangling page */
202
+ munmap((void *)((uintptr_t)mem + new_len), prev_len - new_len);
203
+ return mem;
204
+ }
205
+
206
+ /** Rounds up any size to the nearest page alignment (assumes 4096 bytes per
207
+ * page) */
208
+ static inline size_t sys_round_size(size_t size) {
209
+ return (size & (~4095)) + (4096 * (!!(size & 4095)));
210
+ }
211
+
212
+ /* *****************************************************************************
213
+ Data Types
214
+ ***************************************************************************** */
215
+
216
+ /* The basic block header. Starts a 64Kib memory block */
217
+ typedef struct block_s {
218
+ uint16_t ref; /* reference count (per memory page) */
219
+ uint16_t pos; /* position into the block */
220
+ uint16_t max; /* available memory count */
221
+ uint16_t pad; /* memory padding */
222
+ } block_s;
223
+
224
+ /* a per-CPU core "arena" for memory allocations */
225
+ typedef struct {
226
+ block_s *block;
227
+ spn_lock_i lock;
228
+ } arena_s;
229
+
230
+ /* The memory allocators persistent state */
231
+ static struct {
232
+ size_t active_size; /* active array size */
233
+ block_s *available; /* free list for memory blocks */
234
+ intptr_t count; /* free list counter */
235
+ size_t cores; /* the number of detected CPU cores*/
236
+ spn_lock_i lock; /* a global lock */
237
+ } memory = {
238
+ .cores = 1, .lock = SPN_LOCK_INIT,
239
+ };
240
+
241
+ /* The per-CPU arena array. */
242
+ static arena_s *arenas;
243
+
244
+ /* *****************************************************************************
245
+ Per-CPU Arena management
246
+ ***************************************************************************** */
247
+
248
+ /* returned a locked arena. Attempts the preffered arena first. */
249
+ static inline arena_s *arena_lock(arena_s *preffered) {
250
+ if (!preffered)
251
+ preffered = arenas;
252
+ if (!spn_trylock(&preffered->lock))
253
+ return preffered;
254
+ do {
255
+ arena_s *arena = preffered;
256
+ for (size_t i = (size_t)(arena - arenas); i < memory.cores; ++i) {
257
+ if ((preffered == arenas || arena != preffered) &&
258
+ !spn_trylock(&arena->lock))
259
+ return arena;
260
+ ++arena;
261
+ }
262
+ if (preffered == arenas)
263
+ reschedule_thread();
264
+ preffered = arenas;
265
+ } while (1);
266
+ }
267
+
268
+ static __thread arena_s *arena_last_used;
269
+
270
+ static void arena_enter(void) { arena_last_used = arena_lock(arena_last_used); }
271
+
272
+ static inline void arena_exit(void) { spn_unlock(&arena_last_used->lock); }
273
+
274
+ /** Clears any memory locks, in case of a system call to `fork`. */
275
+ void fio_malloc_after_fork(void) {
276
+ arena_last_used = NULL;
277
+ if (!arenas) {
278
+ return;
279
+ }
280
+ memory.lock = SPN_LOCK_INIT;
281
+ for (size_t i = 0; i < memory.cores; ++i) {
282
+ arenas[i].lock = SPN_LOCK_INIT;
283
+ }
284
+ }
285
+
286
+ /* *****************************************************************************
287
+ Block management
288
+ ***************************************************************************** */
289
+
290
+ // static inline block_s **block_find(void *mem_) {
291
+ // const uintptr_t mem = (uintptr_t)mem_;
292
+ // block_s *blk = memory.active;
293
+ // }
294
+
295
+ /* intializes the block header for an available block of memory. */
296
+ static inline block_s *block_init(void *blk_) {
297
+ block_s *blk = blk_;
298
+ *blk = (block_s){
299
+ .ref = 1,
300
+ .pos = (2 + (sizeof(block_s) >> 4)),
301
+ .max = (FIO_MEMORY_BLOCK_SLICES - 1) -
302
+ (sizeof(block_s) >> 4), /* count available units of 16 bytes */
303
+ };
304
+ return blk;
305
+ }
306
+
307
+ /* intializes the block header for an available block of memory. */
308
+ static inline void block_free(block_s *blk) {
309
+ if (spn_sub(&blk->ref, 1))
310
+ return;
311
+
312
+ if (spn_add(&memory.count, 1) >
313
+ (intptr_t)(FIO_MEM_MAX_BLOCKS_PER_CORE * memory.cores)) {
314
+ /* TODO: return memory to the system */
315
+ spn_sub(&memory.count, 1);
316
+ sys_free(blk, FIO_MEMORY_BLOCK_SIZE);
317
+ return;
318
+ }
319
+ memset(blk, 0, FIO_MEMORY_BLOCK_SIZE);
320
+ spn_lock(&memory.lock);
321
+ *(block_s **)blk = memory.available;
322
+ memory.available = (block_s *)blk;
323
+ spn_unlock(&memory.lock);
324
+ }
325
+
326
+ /* intializes the block header for an available block of memory. */
327
+ static inline block_s *block_new(void) {
328
+ block_s *blk = NULL;
329
+
330
+ if (memory.available) {
331
+ spn_lock(&memory.lock);
332
+ blk = (block_s *)memory.available;
333
+ if (blk) {
334
+ memory.available = ((block_s **)blk)[0];
335
+ }
336
+ spn_unlock(&memory.lock);
337
+ }
338
+ if (blk) {
339
+ spn_sub(&memory.count, 1);
340
+ ((block_s **)blk)[0] = NULL;
341
+ ((block_s **)blk)[1] = NULL;
342
+ return block_init(blk);
343
+ }
344
+ /* TODO: collect memory from the system */
345
+ blk = sys_alloc(FIO_MEMORY_BLOCK_SIZE, 0);
346
+ if (!blk)
347
+ return NULL;
348
+ return block_init(blk);
349
+ ;
350
+ }
351
+
352
+ static inline void *block_slice(uint16_t units) {
353
+ block_s *blk = arena_last_used->block;
354
+ if (!blk) {
355
+ /* arena is empty */
356
+ blk = block_new();
357
+ arena_last_used->block = blk;
358
+ } else if (blk->pos + units > blk->max) {
359
+ /* not enough memory in the block - rotate */
360
+ block_free(blk);
361
+ blk = block_new();
362
+ arena_last_used->block = blk;
363
+ }
364
+ if (!blk) {
365
+ /* no system memory available? */
366
+ return NULL;
367
+ }
368
+ /* slice block starting at blk->pos and increase reference count */
369
+ const void *mem = (void *)((uintptr_t)blk + ((uintptr_t)blk->pos << 4));
370
+ spn_add(&blk->ref, 1);
371
+ blk->pos += units;
372
+ if (blk->pos >= blk->max) {
373
+ /* it's true that a 16 bytes slice remains, but statistically... */
374
+ /* ... the block was fully utilized, clear arena */
375
+ block_free(blk);
376
+ arena_last_used->block = NULL;
377
+ }
378
+ return (void *)mem;
379
+ }
380
+
381
+ static inline void block_slice_free(void *mem) {
382
+ /* locate block boundary */
383
+ block_s *blk = (block_s *)((uintptr_t)mem & (~FIO_MEMORY_BLOCK_MASK));
384
+ block_free(blk);
385
+ }
386
+
387
+ /* *****************************************************************************
388
+ Non-Block allocations (direct from the system)
389
+ ***************************************************************************** */
390
+
391
+ static inline void *big_alloc(size_t size) {
392
+ size = sys_round_size(size + 16);
393
+ size_t *mem = sys_alloc(size, 1);
394
+ *mem = size;
395
+ return (void *)(((uintptr_t)mem) + 16);
396
+ }
397
+
398
+ static inline void big_free(void *ptr) {
399
+ size_t *mem = (void *)(((uintptr_t)ptr) - 16);
400
+ sys_free(mem, *mem);
401
+ }
402
+
403
+ static inline void *big_realloc(void *ptr, size_t new_size) {
404
+ size_t *mem = (void *)(((uintptr_t)ptr) - 16);
405
+ new_size = sys_round_size(new_size + 16);
406
+ mem = sys_realloc(mem, *mem, new_size);
407
+ if (!mem)
408
+ return NULL;
409
+ *mem = new_size;
410
+ return (void *)(((uintptr_t)mem) + 16);
411
+ }
412
+
413
+ /* *****************************************************************************
414
+ Library Initialization (initialize arenas and allocate a block for each CPU)
415
+ ***************************************************************************** */
416
+
417
+ static void __attribute__((constructor)) fio_mem_init(void) {
418
+ if (arenas)
419
+ return;
420
+
421
+ #ifdef _SC_NPROCESSORS_ONLN
422
+ ssize_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
423
+ #else
424
+ #warning Dynamic CPU core count is unavailable - assuming 8 cores for memory allocation pools.
425
+ ssize_t cpu_count = 8; /* fallback */
426
+ #endif
427
+ memory.cores = cpu_count;
428
+ memory.count = 0 - (intptr_t)cpu_count;
429
+ arenas = big_alloc(sizeof(*arenas) * cpu_count);
430
+ if (!arenas) {
431
+ perror("FATAL ERROR: Couldn't initialize memory allocator");
432
+ exit(errno);
433
+ }
434
+ size_t pre_pool = cpu_count > 32 ? 32 : cpu_count;
435
+ for (size_t i = 0; i < pre_pool; ++i) {
436
+ void *block = sys_alloc(FIO_MEMORY_BLOCK_SIZE, 0);
437
+ if (block) {
438
+ block_init(block);
439
+ block_free(block);
440
+ }
441
+ }
442
+ }
443
+
444
+ static void __attribute__((destructor)) fio_mem_destroy(void) {
445
+ if (!arenas)
446
+ return;
447
+
448
+ arena_s *arena = arenas;
449
+ for (size_t i = 0; i < memory.cores; ++i) {
450
+ if (arena->block)
451
+ block_free(arena->block);
452
+ arena->block = NULL;
453
+ ++arena;
454
+ }
455
+ while (memory.available) {
456
+ block_s *b = memory.available;
457
+ memory.available = *(block_s **)b;
458
+ sys_free(b, FIO_MEMORY_BLOCK_SIZE);
459
+ }
460
+ big_free(arenas);
461
+ arenas = NULL;
462
+ }
463
+
464
+ /* *****************************************************************************
465
+ Memory allocation / deacclocation API
466
+ ***************************************************************************** */
467
+
468
+ void *fio_malloc(size_t size) {
469
+ if (!size)
470
+ return NULL;
471
+ if (size >= FIO_MEMORY_BLOCK_ALLOC_LIMIT) {
472
+ /* system allocation - must be block aligned */
473
+ return big_alloc(size);
474
+ }
475
+ /* ceiling for 16 byte alignement, translated to 16 byte units */
476
+ size = (size >> 4) + (!!(size & 15));
477
+ arena_enter();
478
+ void *mem = block_slice(size);
479
+ arena_exit();
480
+ return mem;
481
+ }
482
+
483
+ void *fio_calloc(size_t size, size_t count) {
484
+ return fio_malloc(size * count); // memory is pre-initialized by mmap or pool.
485
+ }
486
+
487
+ void fio_free(void *ptr) {
488
+ if (!ptr)
489
+ return;
490
+ if (((uintptr_t)ptr & FIO_MEMORY_BLOCK_MASK) == 16) {
491
+ /* big allocation - direct from the system */
492
+ big_free(ptr);
493
+ return;
494
+ }
495
+ /* allocated within block */
496
+ block_slice_free(ptr);
497
+ }
498
+
499
+ /**
500
+ * Re-allocates memory. An attept to avoid copying the data is made only for
501
+ * memory allocations larger than 64Kb.
502
+ *
503
+ * This variation is slightly faster as it might copy less data
504
+ */
505
+ void *fio_realloc2(void *ptr, size_t new_size, size_t copy_length) {
506
+ if (!ptr)
507
+ return fio_malloc(new_size);
508
+ if (((uintptr_t)ptr & FIO_MEMORY_BLOCK_MASK) == 16) {
509
+ /* big reallocation - direct from the system */
510
+ return big_realloc(ptr, new_size);
511
+ }
512
+ /* allocated within block - don't even try to expand the allocation */
513
+ /* ceiling for 16 byte alignement, translated to 16 byte units */
514
+ void *new_mem = fio_malloc(new_size);
515
+ if (!new_mem)
516
+ return NULL;
517
+ new_size = ((new_size >> 4) + (!!(new_size & 15)));
518
+ copy_length = ((copy_length >> 4) + (!!(copy_length & 15)));
519
+ // memcpy(new_mem, ptr, (copy_length > new_size ? new_size : copy_length) <<
520
+ // 4);
521
+ fio_memcpy(new_mem, ptr, (copy_length > new_size ? new_size : copy_length));
522
+ block_slice_free(ptr);
523
+ return new_mem;
524
+ }
525
+
526
+ void *fio_realloc(void *ptr, size_t new_size) {
527
+ const size_t max_old =
528
+ FIO_MEMORY_BLOCK_SIZE - ((uintptr_t)ptr & FIO_MEMORY_BLOCK_MASK);
529
+ return fio_realloc2(ptr, new_size, max_old);
530
+ }
531
+
532
+ /* *****************************************************************************
533
+ FIO_OVERRIDE_MALLOC - override glibc / library malloc
534
+ ***************************************************************************** */
535
+ #if FIO_OVERRIDE_MALLOC
536
+ void *malloc(size_t size) { return fio_malloc(size); }
537
+ void *calloc(size_t size, size_t count) { return fio_calloc(size, count); }
538
+ void free(void *ptr) { fio_free(ptr); }
539
+ void *realloc(void *ptr, size_t new_size) { return fio_realloc(ptr, new_size); }
540
+ #endif
541
+
542
+ #endif
543
+
544
+ /* *****************************************************************************
545
+ Some Tests
546
+ ***************************************************************************** */
547
+
548
+ #if DEBUG && !FIO_FORCE_MALLOC
549
+
550
+ void fio_malloc_test(void) {
551
+ #define TEST_ASSERT(cond, ...) \
552
+ if (!(cond)) { \
553
+ fprintf(stderr, "* " __VA_ARGS__); \
554
+ fprintf(stderr, "\nTesting failed.\n"); \
555
+ exit(-1); \
556
+ }
557
+
558
+ fprintf(stderr, "=== Testing facil.io memory allocator's system calls\n");
559
+ char *mem = sys_alloc(FIO_MEMORY_BLOCK_SIZE, 0);
560
+ TEST_ASSERT(mem, "sys_alloc failed to allocate memory!\n");
561
+ TEST_ASSERT(!((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK),
562
+ "Memory allocation not aligned to FIO_MEMORY_BLOCK_SIZE!");
563
+ mem[0] = 'a';
564
+ mem[FIO_MEMORY_BLOCK_SIZE - 1] = 'z';
565
+ fprintf(stderr, "* Testing reallocation from %p\n", (void *)mem);
566
+ char *mem2 =
567
+ sys_realloc(mem, FIO_MEMORY_BLOCK_SIZE, FIO_MEMORY_BLOCK_SIZE * 2);
568
+ if (mem == mem2)
569
+ fprintf(stderr, "* Performed system realloc without copy :-)\n");
570
+ TEST_ASSERT(mem2[0] = 'a' && mem2[FIO_MEMORY_BLOCK_SIZE - 1] == 'z',
571
+ "Reaclloc data was lost!");
572
+ sys_free(mem2, FIO_MEMORY_BLOCK_SIZE * 2);
573
+ fprintf(stderr, "=== Testing facil.io memory allocator's internal data.\n");
574
+ TEST_ASSERT(arenas, "Missing arena data - library not initialized!");
575
+ TEST_ASSERT(fio_malloc(0) == NULL, "fio_malloc 0 bytes should be NULL!\n");
576
+ fio_free(NULL); /* fio_free(NULL) shouldn't crash... */
577
+ mem = fio_malloc(1);
578
+ TEST_ASSERT(mem, "fio_malloc failed to allocate memory!\n");
579
+ TEST_ASSERT(!((uintptr_t)mem & 15), "fio_malloc memory not aligned!\n");
580
+ TEST_ASSERT(((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) != 16,
581
+ "small fio_malloc memory indicates system allocation!\n");
582
+ mem[0] = 'a';
583
+ TEST_ASSERT(mem[0] == 'a', "allocate memory wasn't written to!\n");
584
+ mem = fio_realloc(mem, 1);
585
+ TEST_ASSERT(mem[0] == 'a', "fio_realloc memory wasn't copied!\n");
586
+ TEST_ASSERT(arena_last_used, "arena_last_used wasn't initialized!\n");
587
+ block_s *b = arena_last_used->block;
588
+ size_t count = 2;
589
+ intptr_t old_memory_pool_count = memory.count;
590
+ do {
591
+ TEST_ASSERT(mem, "fio_malloc failed to allocate memory!\n");
592
+ TEST_ASSERT(!((uintptr_t)mem & 15),
593
+ "fio_malloc memory not aligned at allocation #%zu!\n", count);
594
+ TEST_ASSERT((((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) != 16),
595
+ "fio_malloc memory indicates system allocation!\n");
596
+ #if __x86_64__
597
+ fio_memcpy((size_t *)mem, (size_t *)"0123456789abcdefg", 1);
598
+ #else
599
+ mem[0] = 'a';
600
+ #endif
601
+ fio_free(mem); /* make sure we hold on to the block, so it rotates */
602
+ mem = fio_malloc(1);
603
+ ++count;
604
+ } while (arena_last_used->block == b);
605
+ {
606
+ fprintf(
607
+ stderr,
608
+ "* Performed %zu allocations out of expected %zu allocations per "
609
+ "block.\n",
610
+ count,
611
+ (size_t)((FIO_MEMORY_BLOCK_SLICES - 2) - (sizeof(block_s) >> 4) - 1));
612
+ TEST_ASSERT(memory.available,
613
+ "memory pool empty (memory block wasn't freed)!\n");
614
+ TEST_ASSERT(old_memory_pool_count == memory.count,
615
+ "memory.count == %ld (memory block not counted)!\n",
616
+ (long)old_memory_pool_count);
617
+ fio_free(mem);
618
+ }
619
+ /* rotate block again */
620
+ b = arena_last_used->block;
621
+ mem = fio_realloc(mem, 1);
622
+ do {
623
+ mem2 = mem;
624
+ mem = fio_malloc(1);
625
+ fio_free(mem2); /* make sure we hold on to the block, so it rotates */
626
+ TEST_ASSERT(mem, "fio_malloc failed to allocate memory!\n");
627
+ TEST_ASSERT(!((uintptr_t)mem & 15),
628
+ "fio_malloc memory not aligned at allocation #%zu!\n", count);
629
+ TEST_ASSERT((((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) != 16),
630
+ "fio_malloc memory indicates system allocation!\n");
631
+ #if __x86_64__
632
+ fio_memcpy((size_t *)mem, (size_t *)"0123456789abcdefg", 1);
633
+ #else
634
+ mem[0] = 'a';
635
+ #endif
636
+ ++count;
637
+ } while (arena_last_used->block == b);
638
+
639
+ mem = fio_calloc(FIO_MEMORY_BLOCK_ALLOC_LIMIT - 64, 1);
640
+ TEST_ASSERT(mem,
641
+ "failed to allocate FIO_MEMORY_BLOCK_ALLOC_LIMIT - 64 bytes!\n");
642
+ TEST_ASSERT(((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) != 16,
643
+ "fio_calloc (under limit) memory alignment error!\n");
644
+ mem2 = fio_malloc(1);
645
+ TEST_ASSERT(mem2, "fio_malloc(1) failed to allocate memory!\n");
646
+ mem2[0] = 'a';
647
+
648
+ for (uintptr_t i = 0; i < (FIO_MEMORY_BLOCK_ALLOC_LIMIT - 64); ++i) {
649
+ TEST_ASSERT(mem[i] == 0,
650
+ "calloc returned memory that wasn't initialized?!\n");
651
+ }
652
+ fio_free(mem);
653
+
654
+ mem = fio_malloc(FIO_MEMORY_BLOCK_SIZE);
655
+ TEST_ASSERT(mem, "fio_malloc failed to FIO_MEMORY_BLOCK_SIZE bytes!\n");
656
+ TEST_ASSERT(((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) == 16,
657
+ "fio_malloc (big) memory isn't aligned!\n");
658
+ mem = fio_realloc(mem, FIO_MEMORY_BLOCK_SIZE * 2);
659
+ TEST_ASSERT(mem,
660
+ "fio_realloc (big) failed on FIO_MEMORY_BLOCK_SIZE X2 bytes!\n");
661
+ fio_free(mem);
662
+ TEST_ASSERT(((uintptr_t)mem & FIO_MEMORY_BLOCK_MASK) == 16,
663
+ "fio_realloc (big) memory isn't aligned!\n");
664
+
665
+ fprintf(stderr, "* passed.\n");
666
+ }
667
+
668
+ #else
669
+
670
+ void fio_malloc_test(void) {}
671
+
672
+ #endif