iodine 0.2.17 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +36 -3
- data/bin/config.ru +23 -2
- data/bin/http-hello +1 -1
- data/bin/ws-shootout +5 -0
- data/ext/iodine/defer.c +468 -0
- data/ext/iodine/defer.h +105 -0
- data/ext/iodine/evio.c +263 -0
- data/ext/iodine/evio.h +133 -0
- data/ext/iodine/extconf.rb +2 -1
- data/ext/iodine/facil.c +958 -0
- data/ext/iodine/facil.h +423 -0
- data/ext/iodine/http.c +90 -0
- data/ext/iodine/http.h +50 -12
- data/ext/iodine/http1.c +200 -267
- data/ext/iodine/http1.h +17 -26
- data/ext/iodine/http1_request.c +81 -0
- data/ext/iodine/http1_request.h +58 -0
- data/ext/iodine/http1_response.c +403 -0
- data/ext/iodine/http1_response.h +90 -0
- data/ext/iodine/http1_simple_parser.c +124 -108
- data/ext/iodine/http1_simple_parser.h +8 -3
- data/ext/iodine/http_request.c +104 -0
- data/ext/iodine/http_request.h +58 -102
- data/ext/iodine/http_response.c +212 -208
- data/ext/iodine/http_response.h +89 -252
- data/ext/iodine/iodine_core.c +57 -46
- data/ext/iodine/iodine_core.h +3 -1
- data/ext/iodine/iodine_http.c +105 -81
- data/ext/iodine/iodine_websocket.c +17 -13
- data/ext/iodine/iodine_websocket.h +1 -0
- data/ext/iodine/rb-call.c +9 -7
- data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
- data/ext/iodine/rb-rack-io.c +12 -6
- data/ext/iodine/rb-rack-io.h +1 -1
- data/ext/iodine/rb-registry.c +5 -2
- data/ext/iodine/sock.c +1159 -0
- data/ext/iodine/{libsock.h → sock.h} +138 -142
- data/ext/iodine/spnlock.inc +77 -0
- data/ext/iodine/websockets.c +101 -112
- data/ext/iodine/websockets.h +38 -19
- data/iodine.gemspec +3 -3
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +6 -6
- metadata +23 -19
- data/ext/iodine/http_response_http1.h +0 -382
- data/ext/iodine/libasync.c +0 -570
- data/ext/iodine/libasync.h +0 -122
- data/ext/iodine/libreact.c +0 -350
- data/ext/iodine/libreact.h +0 -244
- data/ext/iodine/libserver.c +0 -957
- data/ext/iodine/libserver.h +0 -481
- data/ext/iodine/libsock.c +0 -1025
- data/ext/iodine/spnlock.h +0 -243
@@ -1,22 +1,23 @@
|
|
1
|
+
#ifndef H_LIBSOCK_H
|
2
|
+
#define H_LIBSOCK_H
|
1
3
|
/*
|
2
4
|
Copyright: Boaz Segev, 2016-2017
|
3
5
|
License: MIT
|
4
6
|
|
5
7
|
Feel free to copy, use and enjoy according to the license provided.
|
6
8
|
*/
|
7
|
-
#ifndef LIB_SOCK
|
8
|
-
#define LIB_SOCK "0.2.2"
|
9
9
|
#define LIB_SOCK_VERSION_MAJOR 0
|
10
|
-
#define LIB_SOCK_VERSION_MINOR
|
11
|
-
#define LIB_SOCK_VERSION_PATCH
|
10
|
+
#define LIB_SOCK_VERSION_MINOR 3
|
11
|
+
#define LIB_SOCK_VERSION_PATCH 0
|
12
12
|
|
13
13
|
/** \file
|
14
|
-
The
|
14
|
+
The `sock.h` is a non-blocking socket helper library, using a user level buffer,
|
15
15
|
non-blocking sockets and some helper functions.
|
16
16
|
|
17
|
-
This library is great when using it alongside `
|
17
|
+
This library is great when using it alongside `evio.h`.
|
18
18
|
|
19
|
-
The library is designed to be thread safe, but not fork safe
|
19
|
+
The library is designed to be thread safe, but not fork safe (mostly since
|
20
|
+
sockets, except listenning sockets, shouldn't be shared among processes).
|
20
21
|
*/
|
21
22
|
|
22
23
|
#include <stdint.h>
|
@@ -39,7 +40,7 @@ This information is also useful when implementing read / write hooks.
|
|
39
40
|
(1024 * 16) /* Use 32 Kb. With sendfile, 16 Kb appears to work better. */
|
40
41
|
#endif
|
41
42
|
#ifndef BUFFER_FILE_READ_SIZE
|
42
|
-
#define BUFFER_FILE_READ_SIZE
|
43
|
+
#define BUFFER_FILE_READ_SIZE 4096UL
|
43
44
|
#endif
|
44
45
|
#ifndef BUFFER_PACKET_POOL
|
45
46
|
#define BUFFER_PACKET_POOL 1024
|
@@ -48,28 +49,8 @@ This information is also useful when implementing read / write hooks.
|
|
48
49
|
/* *****************************************************************************
|
49
50
|
A simple, predictable UUID for file-descriptors, for collision prevention
|
50
51
|
*/
|
51
|
-
|
52
|
-
#
|
53
|
-
#define FD_UUID_TYPE_DEFINED
|
54
|
-
/** fduuid_u is used to identify a specific connection, helping to manage file
|
55
|
-
* descriptor collisions (when a new connection receives an old connection's
|
56
|
-
* file descriptor), especially when the `on_close` event is fired after an
|
57
|
-
* `accept` was called and the old file descriptor was already recycled.
|
58
|
-
*
|
59
|
-
* This requires that sizeof(int) < sizeof(uintptr_t) or sizeof(int)*8 >= 32
|
60
|
-
*/
|
61
|
-
typedef union {
|
62
|
-
intptr_t uuid;
|
63
|
-
struct {
|
64
|
-
int fd : (sizeof(int) < sizeof(intptr_t) ? (sizeof(int) * 8) : 24);
|
65
|
-
unsigned counter : (sizeof(int) < sizeof(intptr_t)
|
66
|
-
? ((sizeof(intptr_t) - sizeof(int)) * 8)
|
67
|
-
: ((sizeof(intptr_t) * 8) - 24));
|
68
|
-
} data;
|
69
|
-
} fduuid_u;
|
70
|
-
|
71
|
-
#define FDUUID_FAIL(uuid) (uuid == -1)
|
72
|
-
#define sock_uuid2fd(uuid) ((fduuid_u *)(&uuid))->data.fd
|
52
|
+
#ifndef sock_uuid2fd
|
53
|
+
#define sock_uuid2fd(uuid) ((intptr_t)((uintptr_t)uuid >> 8))
|
73
54
|
#endif
|
74
55
|
|
75
56
|
/* *****************************************************************************
|
@@ -150,8 +131,8 @@ client data for "broadcasting" or when an old client task is preparing a
|
|
150
131
|
response in the background while a disconnection and a new connection occur on
|
151
132
|
the same `fd`).
|
152
133
|
|
153
|
-
When using `
|
154
|
-
listen for events.
|
134
|
+
When using `evio`, remember to call `int evio_add(sock_fd2uuid(uuid),
|
135
|
+
(void*)uuid);` to listen for events.
|
155
136
|
|
156
137
|
NOTICE:
|
157
138
|
|
@@ -159,7 +140,7 @@ This function is non-blocking, meaning that the connection probably wasn't
|
|
159
140
|
established by the time the function returns (this prevents the function from
|
160
141
|
hanging while waiting for a network timeout).
|
161
142
|
|
162
|
-
Use select, poll, `
|
143
|
+
Use select, poll, `evio` or other solutions to review the connection state
|
163
144
|
before attempting to write to the socket.
|
164
145
|
*/
|
165
146
|
intptr_t sock_connect(char *address, char *port);
|
@@ -169,11 +150,11 @@ intptr_t sock_connect(char *address, char *port);
|
|
169
150
|
as open and available for `sock_API` calls, returning a valid UUID.
|
170
151
|
|
171
152
|
This will reinitialize the data (user buffer etc') for the file descriptor
|
172
|
-
provided, calling the `
|
153
|
+
provided, calling the `sock_on_close` callback if the `fd` was previously
|
173
154
|
marked as used.
|
174
155
|
|
175
|
-
When using `
|
176
|
-
listen for events.
|
156
|
+
When using `evio`, remember to call `int evio_add(sock_fd2uuid(uuid),
|
157
|
+
(void*)uuid);` to listen for events.
|
177
158
|
|
178
159
|
Returns -1 on error. Returns a valid socket (non-random) UUID.
|
179
160
|
|
@@ -192,6 +173,20 @@ Returns 0 if not.
|
|
192
173
|
*/
|
193
174
|
int sock_isvalid(intptr_t uuid);
|
194
175
|
|
176
|
+
/** The return type for the `sock_peer_addr` function. */
|
177
|
+
typedef struct {
|
178
|
+
uint32_t addrlen;
|
179
|
+
struct sockaddr *addr;
|
180
|
+
} sock_peer_addr_s;
|
181
|
+
/** Returns the information available about the socket's peer address.
|
182
|
+
*
|
183
|
+
* If no information is available, the struct will be initialized with zero
|
184
|
+
* (`addr == NULL`).
|
185
|
+
* The information is only available when the socket was accepted using
|
186
|
+
* `sock_accept` or opened using `sock_connect`.
|
187
|
+
*/
|
188
|
+
sock_peer_addr_s sock_peer_addr(intptr_t uuid);
|
189
|
+
|
195
190
|
/**
|
196
191
|
`sock_fd2uuid` takes an existing file decriptor `fd` and returns it's active
|
197
192
|
`uuid`.
|
@@ -208,12 +203,33 @@ Returns -1 on error. Returns a valid socket (non-random) UUID.
|
|
208
203
|
intptr_t sock_fd2uuid(int fd);
|
209
204
|
|
210
205
|
/**
|
211
|
-
|
212
|
-
|
213
|
-
|
206
|
+
OVERRIDABLE:
|
207
|
+
|
208
|
+
"Touches" a socket connection.
|
209
|
+
|
210
|
+
This is a place holder for an optional callback for systems that apply timeout
|
211
|
+
reviews.
|
212
|
+
|
213
|
+
`sock` supplies a default implementation (that does nothing) is cases where a
|
214
|
+
callback wasn't defined.
|
214
215
|
*/
|
215
216
|
void sock_touch(intptr_t uuid);
|
216
217
|
|
218
|
+
/**
|
219
|
+
OVERRIDABLE:.
|
220
|
+
|
221
|
+
This is a place holder for an optional callback for when the socket is closed
|
222
|
+
locally.
|
223
|
+
|
224
|
+
Notice that calling `sock_close()` won't close the socket before all the data in
|
225
|
+
the buffer is sent. This function will be called only one the connection is
|
226
|
+
actually closed.
|
227
|
+
|
228
|
+
`sock` supplies a default implementation (that does nothing) is cases where a
|
229
|
+
callback wasn't defined.
|
230
|
+
*/
|
231
|
+
void sock_on_close(intptr_t uuid);
|
232
|
+
|
217
233
|
/**
|
218
234
|
`sock_read` attempts to read up to count bytes from the socket into the buffer
|
219
235
|
starting at buf.
|
@@ -224,8 +240,10 @@ and they aim at making far simpler sense.
|
|
224
240
|
`sock_read` returns the number of bytes read (0 is a valid return value which
|
225
241
|
simply means that no bytes were read from the buffer).
|
226
242
|
|
227
|
-
On a connection error (NOT EAGAIN or EWOULDBLOCK)
|
228
|
-
`sock_read` returns -1.
|
243
|
+
On a connection error (NOT EAGAIN or EWOULDBLOCK), signal interrupt, or when the
|
244
|
+
connection was closed, `sock_read` returns -1.
|
245
|
+
|
246
|
+
The value 0 is the valid value indicating no data was read.
|
229
247
|
|
230
248
|
Data might be available in the kernel's buffer while it is not available to be
|
231
249
|
read using `sock_read` (i.e., when using a transport layer, such as TLS).
|
@@ -233,32 +251,54 @@ read using `sock_read` (i.e., when using a transport layer, such as TLS).
|
|
233
251
|
ssize_t sock_read(intptr_t uuid, void *buf, size_t count);
|
234
252
|
|
235
253
|
typedef struct {
|
236
|
-
/** The
|
237
|
-
intptr_t
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
254
|
+
/** The fsocket uuid for sending data. */
|
255
|
+
intptr_t uuid;
|
256
|
+
union {
|
257
|
+
/** The in-memory data to be sent. */
|
258
|
+
const void *buffer;
|
259
|
+
/** The data to be sent, if this is a file. */
|
260
|
+
const intptr_t data_fd;
|
261
|
+
};
|
262
|
+
union {
|
263
|
+
/** This deallocation callback will be called when the packet is finished
|
264
|
+
* with the buffer if the `move` flags is set.
|
265
|
+
* If no deallocation callback is `free` will be used.
|
266
|
+
*/
|
267
|
+
void (*dealloc)(void *buffer);
|
268
|
+
/** This is an alternative deallocation callback accessor (same memory space
|
269
|
+
* as `dealloc`) for conveniently setting the file `close` callback.
|
270
|
+
*/
|
271
|
+
void (*close)(int fd);
|
272
|
+
};
|
273
|
+
/** The length (size) of the buffer, or the amount of data to be sent from the
|
274
|
+
* file descriptor.
|
275
|
+
*/
|
242
276
|
size_t length;
|
243
|
-
/** Starting point offset
|
244
|
-
* (see `sock_write_info_s.is_fd`). */
|
277
|
+
/** Starting point offset from the buffer or file descriptor's beginning. */
|
245
278
|
off_t offset;
|
246
|
-
/**
|
247
|
-
*
|
248
|
-
*
|
279
|
+
/** When sending data from the memory, using `move` will prevent copy and
|
280
|
+
* memory allocations, moving the memory ownership to the `sock_write2`
|
281
|
+
* function.
|
282
|
+
*/
|
249
283
|
unsigned move : 1;
|
250
284
|
/** The packet will be sent as soon as possible. */
|
251
285
|
unsigned urgent : 1;
|
252
|
-
/** The buffer contains the value of a file descriptor int - casting, not
|
253
|
-
* pointing, i.e.: `.buffer = (void*)fd;`
|
286
|
+
/** The buffer contains the value of a file descriptor (`int`) - casting, not
|
287
|
+
* pointing, i.e.: `.buffer = (void*)fd;`
|
288
|
+
*/
|
254
289
|
unsigned is_fd : 1;
|
255
290
|
/** for internal use */
|
256
291
|
unsigned rsv : 1;
|
257
292
|
} sock_write_info_s;
|
293
|
+
|
294
|
+
void SOCK_DEALLOC_NOOP(void *arg);
|
295
|
+
#define SOCK_DEALLOC_NOOP SOCK_DEALLOC_NOOP
|
296
|
+
|
258
297
|
/**
|
259
298
|
`sock_write2_fn` is the actual function behind the macro `sock_write2`.
|
260
299
|
*/
|
261
300
|
ssize_t sock_write2_fn(sock_write_info_s options);
|
301
|
+
|
262
302
|
/**
|
263
303
|
`sock_write2` is similar to `sock_write`, except special properties can be set.
|
264
304
|
|
@@ -279,8 +319,8 @@ transferred to the socket's user level buffer.
|
|
279
319
|
**Note** this is actually a specific case of `sock_write2` and this macro
|
280
320
|
actually calls `sock_write2`.
|
281
321
|
*/
|
282
|
-
#define sock_write(
|
283
|
-
sock_write2(.
|
322
|
+
#define sock_write(sock_uuid, buf, count) \
|
323
|
+
sock_write2(.uuid = (sock_uuid), .buffer = (buf), .length = (count))
|
284
324
|
|
285
325
|
/**
|
286
326
|
Sends data from a file as if it were a single atomic packet (sends up to
|
@@ -297,10 +337,10 @@ the maximum amount of data to be sent.
|
|
297
337
|
|
298
338
|
Returns -1 and closes the file on error. Returns 0 on success.
|
299
339
|
*/
|
300
|
-
UNUSED_FUNC static inline ssize_t
|
301
|
-
|
302
|
-
return sock_write2(.
|
303
|
-
.length = length, .is_fd = 1, .offset = offset);
|
340
|
+
UNUSED_FUNC static inline ssize_t
|
341
|
+
sock_sendfile(intptr_t uuid, intptr_t source_fd, off_t offset, size_t length) {
|
342
|
+
return sock_write2(.uuid = uuid, .buffer = (void *)(source_fd),
|
343
|
+
.length = length, .is_fd = 1, .offset = offset, .move = 1);
|
304
344
|
}
|
305
345
|
|
306
346
|
/**
|
@@ -310,9 +350,6 @@ the data was sent).
|
|
310
350
|
|
311
351
|
Return value: 0 will be returned on success and -1 will be returned on an error
|
312
352
|
or when the connection is closed.
|
313
|
-
|
314
|
-
**Please Note**: when using `libreact`, the `sock_flush` will be called
|
315
|
-
automatically when the socket is ready.
|
316
353
|
*/
|
317
354
|
ssize_t sock_flush(intptr_t uuid);
|
318
355
|
/**
|
@@ -340,99 +377,59 @@ void sock_force_close(intptr_t uuid);
|
|
340
377
|
|
341
378
|
/* *****************************************************************************
|
342
379
|
Direct user level buffer API.
|
380
|
+
|
381
|
+
The following API allows data to be written directly to the packet, minimizing
|
382
|
+
memory copy operations.
|
343
383
|
*/
|
344
384
|
|
345
385
|
/**
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
void *buffer;
|
358
|
-
/** Metadata about the packet. */
|
359
|
-
struct {
|
360
|
-
/** allows the linking of a number of packets together. */
|
361
|
-
struct sock_packet_s *next;
|
362
|
-
/** Starting point offset, when the buffer is a file (see
|
363
|
-
* `sock_packet_s.metadata.is_fd`). */
|
364
|
-
off_t offset;
|
365
|
-
/** This deallocation callback will be called when the packet is finished
|
366
|
-
* with the buffer.
|
367
|
-
* The deallocation callback will only be called for buffers marked as
|
368
|
-
* `external`.
|
369
|
-
* If no deallocation callback is specified,`free` will be called as a
|
370
|
-
* default deallocation method.
|
371
|
-
*/
|
372
|
-
void (*dealloc)(void *buffer);
|
373
|
-
/** sets whether a packet can be inserted before this packet without
|
374
|
-
* interrupting the communication flow. */
|
375
|
-
unsigned can_interrupt : 1;
|
376
|
-
/** sets whether a packet's buffer contains a file descriptor - casting, not
|
377
|
-
* pointing, i.e.: `packet->buffer = (void*)fd;` */
|
378
|
-
unsigned is_fd : 1;
|
379
|
-
/** Keeps the `FILE *` or fd open - avoids automatically closing the file.
|
380
|
-
*/
|
381
|
-
unsigned keep_open : 1;
|
382
|
-
/** sets whether a packet's buffer is pre-allocated (references the
|
383
|
-
* `internal_memory`) or whether the data is allocated using `malloc` and
|
384
|
-
* should be freed. */
|
385
|
-
unsigned external : 1;
|
386
|
-
/** sets whether this packet (or packet chain) should be inserted in before
|
387
|
-
* the first `can_interrupt` packet, or at the end of the queu. */
|
388
|
-
unsigned urgent : 1;
|
389
|
-
/** Reserved for internal use - (memory shifting flag)*/
|
390
|
-
unsigned internal_flag : 1;
|
391
|
-
/** Reserved for future use. */
|
392
|
-
unsigned rsrv : 2;
|
393
|
-
/**/
|
394
|
-
} metadata;
|
395
|
-
} sock_packet_s;
|
386
|
+
The buffer of a user-land sock-packet.
|
387
|
+
|
388
|
+
Remember to set the correct `len` value, so the full amount of the data is sent.
|
389
|
+
|
390
|
+
See `sock_buffer_checkout`, `sock_buffer_send` and `sock_buffer_free` for more
|
391
|
+
information.
|
392
|
+
*/
|
393
|
+
typedef struct sock_buffer_s {
|
394
|
+
size_t len;
|
395
|
+
uint8_t buf[BUFFER_PACKET_SIZE];
|
396
|
+
} sock_buffer_s;
|
396
397
|
|
397
398
|
/**
|
398
|
-
Checks out a `
|
399
|
-
|
400
|
-
|
401
|
-
time and remember to free or send the
|
399
|
+
Checks out a `sock_buffer_s` from the buffer pool.
|
400
|
+
|
401
|
+
This function will hang until a buffer becomes available, so never check out
|
402
|
+
more then a single buffer at a time and remember to free or send the buffer
|
403
|
+
using the `sock_buffer_*` functions.
|
402
404
|
|
403
405
|
Every checked out buffer packet comes with an attached buffer of
|
404
|
-
BUFFER_PACKET_SIZE bytes. This buffer is accessible using the `
|
405
|
-
pointer
|
406
|
+
BUFFER_PACKET_SIZE bytes. This buffer is accessible using the `->buf`
|
407
|
+
pointer.
|
406
408
|
|
407
|
-
This attached buffer is
|
408
|
-
|
409
|
+
This attached buffer's memory is automatically manages as long as the
|
410
|
+
`sock_buffer_send` or `sock_buffer_free` functions are called.
|
409
411
|
*/
|
410
|
-
|
412
|
+
sock_buffer_s *sock_buffer_checkout(void);
|
411
413
|
/**
|
412
414
|
Attaches a packet to a socket's output buffer and calls `sock_flush` for the
|
413
415
|
socket.
|
414
416
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
Returns -1 on error. Returns 0 on success.
|
417
|
+
Returns -1 on error. Returns 0 on success. The `buffer` memory is always
|
418
|
+
automatically managed.
|
419
419
|
*/
|
420
|
-
ssize_t
|
420
|
+
ssize_t sock_buffer_send(intptr_t uuid, sock_buffer_s *buffer);
|
421
421
|
|
422
422
|
/**
|
423
423
|
Returns TRUE (non 0) if there is data waiting to be written to the socket in the
|
424
424
|
user-land buffer.
|
425
425
|
*/
|
426
|
-
int
|
426
|
+
int sock_has_pending(intptr_t uuid);
|
427
427
|
|
428
428
|
/**
|
429
|
-
Use `
|
430
|
-
`
|
431
|
-
|
432
|
-
NEVER use `free`, for any packet checked out using the pool management function
|
433
|
-
`sock_checkout_packet`.
|
429
|
+
Use `sock_buffer_free` to free unused buffers that were checked-out using
|
430
|
+
`sock_buffer_checkout`.
|
434
431
|
*/
|
435
|
-
void
|
432
|
+
void sock_buffer_free(sock_buffer_s *buffer);
|
436
433
|
|
437
434
|
/* *****************************************************************************
|
438
435
|
TLC - Transport Layer Callback.
|
@@ -446,10 +443,10 @@ replace the default system calls to `recv` and `write`. */
|
|
446
443
|
typedef struct sock_rw_hook_s {
|
447
444
|
/** Implement reading from a file descriptor. Should behave like the file
|
448
445
|
* system `read` call, including the setup or errno to EAGAIN / EWOULDBLOCK.*/
|
449
|
-
ssize_t (*read)(intptr_t
|
446
|
+
ssize_t (*read)(intptr_t uuid, void *buf, size_t count);
|
450
447
|
/** Implement writing to a file descriptor. Should behave like the file system
|
451
448
|
* `write` call.*/
|
452
|
-
ssize_t (*write)(intptr_t
|
449
|
+
ssize_t (*write)(intptr_t uuid, const void *buf, size_t count);
|
453
450
|
/** When implemented, this function will be called to flush any data remaining
|
454
451
|
* in the internal buffer.
|
455
452
|
* The function should return the number of bytes remaining in the internal
|
@@ -459,13 +456,12 @@ typedef struct sock_rw_hook_s {
|
|
459
456
|
* writing operation returns -1 with EWOULDBLOCK or all the data was written.
|
460
457
|
*/
|
461
458
|
ssize_t (*flush)(intptr_t fduuid);
|
462
|
-
/** The `
|
463
|
-
*
|
464
|
-
* management.
|
459
|
+
/** The `on_close` callback is called when the socket is closed, allowing for
|
460
|
+
* dynamic sock_rw_hook_s memory management.
|
465
461
|
*
|
466
|
-
* The `
|
462
|
+
* The `on_close` callback should manage is own thread safety mechanism, if
|
467
463
|
* required. */
|
468
|
-
void (*
|
464
|
+
void (*on_close)(intptr_t fduuid, struct sock_rw_hook_s *rw_hook);
|
469
465
|
} sock_rw_hook_s;
|
470
466
|
|
471
467
|
/* *****************************************************************************
|
@@ -492,4 +488,4 @@ C++ extern
|
|
492
488
|
}
|
493
489
|
#endif
|
494
490
|
|
495
|
-
#endif /*
|
491
|
+
#endif /* H_LIBSOCK_H */
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#ifndef H_SPNLOCK_H
|
2
|
+
#define H_SPNLOCK_H
|
3
|
+
/* *****************************************************************************
|
4
|
+
spinlock / sync for tasks
|
5
|
+
***************************************************************************** */
|
6
|
+
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
7
|
+
#ifndef _GNU_SOURCE
|
8
|
+
#define _GNU_SOURCE
|
9
|
+
#endif
|
10
|
+
#include <time.h>
|
11
|
+
#endif /* __unix__ */
|
12
|
+
#include <stdlib.h>
|
13
|
+
|
14
|
+
/** Sets the value for the thread throttling feature, where available. */
|
15
|
+
// static size_t SPN_LOCK_THROTTLE = 8388608UL;
|
16
|
+
|
17
|
+
/* manage the way threads "wait" for the lock to release */
|
18
|
+
#if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
|
19
|
+
/* nanosleep seems to be the most effective and efficient reschedule */
|
20
|
+
#define reschedule_thread() \
|
21
|
+
{ \
|
22
|
+
static const struct timespec tm = {.tv_nsec = 1}; \
|
23
|
+
nanosleep(&tm, NULL); \
|
24
|
+
}
|
25
|
+
#define throttle_thread(micosec) \
|
26
|
+
{ \
|
27
|
+
const struct timespec tm = {.tv_nsec = (micosec)}; \
|
28
|
+
nanosleep(&tm, NULL); \
|
29
|
+
}
|
30
|
+
#else /* no effective rescheduling, just spin... */
|
31
|
+
#define reschedule_thread()
|
32
|
+
#define throttle_thread(micosec)
|
33
|
+
#endif
|
34
|
+
|
35
|
+
/** locks use a single byte */
|
36
|
+
typedef volatile unsigned char spn_lock_i;
|
37
|
+
/** The initail value of an unlocked spinlock. */
|
38
|
+
#define SPN_LOCK_INIT 0
|
39
|
+
|
40
|
+
/* Select the correct compiler builtin method. */
|
41
|
+
#if defined(__has_builtin)
|
42
|
+
#if __has_builtin(__sync_swap)
|
43
|
+
#define SPN_LOCK_BUILTIN(...) __sync_swap(__VA_ARGS__)
|
44
|
+
#elif __has_builtin(__sync_fetch_and_or)
|
45
|
+
#define SPN_LOCK_BUILTIN(...) __sync_fetch_and_or(__VA_ARGS__)
|
46
|
+
#else
|
47
|
+
#error Required builtin "__sync_swap" or "__sync_fetch_and_or" missing from compiler.
|
48
|
+
#endif /* defined(__has_builtin) */
|
49
|
+
#elif __GNUC__ > 3
|
50
|
+
#define SPN_LOCK_BUILTIN(...) __sync_fetch_and_or(__VA_ARGS__)
|
51
|
+
#else
|
52
|
+
#error Required builtin "__sync_swap" or "__sync_fetch_and_or" not found.
|
53
|
+
#endif
|
54
|
+
|
55
|
+
/** returns 1 and 0 if the lock was successfully aquired (TRUE == FAIL). */
|
56
|
+
static inline int spn_trylock(spn_lock_i *lock) {
|
57
|
+
return SPN_LOCK_BUILTIN(lock, 1);
|
58
|
+
}
|
59
|
+
|
60
|
+
/** Releases a lock. */
|
61
|
+
static inline __attribute__((unused)) void spn_unlock(spn_lock_i *lock) {
|
62
|
+
__asm__ volatile("" ::: "memory");
|
63
|
+
*lock = 0;
|
64
|
+
}
|
65
|
+
/** returns a lock's state (non 0 == Busy). */
|
66
|
+
static inline __attribute__((unused)) int spn_is_locked(spn_lock_i *lock) {
|
67
|
+
__asm__ volatile("" ::: "memory");
|
68
|
+
return *lock;
|
69
|
+
}
|
70
|
+
/** Busy waits for the lock. */
|
71
|
+
static inline __attribute__((unused)) void spn_lock(spn_lock_i *lock) {
|
72
|
+
while (spn_trylock(lock)) {
|
73
|
+
reschedule_thread();
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
#endif /* H_SPNLOCK_H */
|