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.

Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -3
  4. data/bin/config.ru +23 -2
  5. data/bin/http-hello +1 -1
  6. data/bin/ws-shootout +5 -0
  7. data/ext/iodine/defer.c +468 -0
  8. data/ext/iodine/defer.h +105 -0
  9. data/ext/iodine/evio.c +263 -0
  10. data/ext/iodine/evio.h +133 -0
  11. data/ext/iodine/extconf.rb +2 -1
  12. data/ext/iodine/facil.c +958 -0
  13. data/ext/iodine/facil.h +423 -0
  14. data/ext/iodine/http.c +90 -0
  15. data/ext/iodine/http.h +50 -12
  16. data/ext/iodine/http1.c +200 -267
  17. data/ext/iodine/http1.h +17 -26
  18. data/ext/iodine/http1_request.c +81 -0
  19. data/ext/iodine/http1_request.h +58 -0
  20. data/ext/iodine/http1_response.c +403 -0
  21. data/ext/iodine/http1_response.h +90 -0
  22. data/ext/iodine/http1_simple_parser.c +124 -108
  23. data/ext/iodine/http1_simple_parser.h +8 -3
  24. data/ext/iodine/http_request.c +104 -0
  25. data/ext/iodine/http_request.h +58 -102
  26. data/ext/iodine/http_response.c +212 -208
  27. data/ext/iodine/http_response.h +89 -252
  28. data/ext/iodine/iodine_core.c +57 -46
  29. data/ext/iodine/iodine_core.h +3 -1
  30. data/ext/iodine/iodine_http.c +105 -81
  31. data/ext/iodine/iodine_websocket.c +17 -13
  32. data/ext/iodine/iodine_websocket.h +1 -0
  33. data/ext/iodine/rb-call.c +9 -7
  34. data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
  35. data/ext/iodine/rb-rack-io.c +12 -6
  36. data/ext/iodine/rb-rack-io.h +1 -1
  37. data/ext/iodine/rb-registry.c +5 -2
  38. data/ext/iodine/sock.c +1159 -0
  39. data/ext/iodine/{libsock.h → sock.h} +138 -142
  40. data/ext/iodine/spnlock.inc +77 -0
  41. data/ext/iodine/websockets.c +101 -112
  42. data/ext/iodine/websockets.h +38 -19
  43. data/iodine.gemspec +3 -3
  44. data/lib/iodine/version.rb +1 -1
  45. data/lib/rack/handler/iodine.rb +6 -6
  46. metadata +23 -19
  47. data/ext/iodine/http_response_http1.h +0 -382
  48. data/ext/iodine/libasync.c +0 -570
  49. data/ext/iodine/libasync.h +0 -122
  50. data/ext/iodine/libreact.c +0 -350
  51. data/ext/iodine/libreact.h +0 -244
  52. data/ext/iodine/libserver.c +0 -957
  53. data/ext/iodine/libserver.h +0 -481
  54. data/ext/iodine/libsock.c +0 -1025
  55. 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 2
11
- #define LIB_SOCK_VERSION_PATCH 2
10
+ #define LIB_SOCK_VERSION_MINOR 3
11
+ #define LIB_SOCK_VERSION_PATCH 0
12
12
 
13
13
  /** \file
14
- The libsock is a non-blocking socket helper library, using a user level buffer,
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 `libreact`.
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 BUFFER_PACKET_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
- #ifndef FD_UUID_TYPE_DEFINED
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 `libreact`, remember to call `int reactor_add(intptr_t uuid);` to
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, `libreact` or other solutions to review the connection state
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 `reactor_on_close` callback if the `fd` was previously
153
+ provided, calling the `sock_on_close` callback if the `fd` was previously
173
154
  marked as used.
174
155
 
175
- When using `libreact`, remember to call `int reactor_add(intptr_t uuid);` to
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
- "Touches" a socket connection. This is a place holder for an optional callback
212
- for systems that apply timeout reviews. `libsock` supplies a default
213
- implementation (that does nothing) is cases where a callback wasn't defined.
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) or signal interrupt,
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 fd for sending data. */
237
- intptr_t fduuid;
238
- /** The data to be sent. This can be either a byte stream or a file pointer
239
- * (`FILE *`). */
240
- const void *buffer;
241
- /** The length (size) of the buffer. irrelevant for file pointers. */
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, when the buffer is a file
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
- /** The user land buffer will receive ownership of the buffer (forced as
247
- * TRUE
248
- * when `file` is set). */
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(uuid, buf, count) \
283
- sock_write2(.fduuid = (uuid), .buffer = (buf), .length = (count))
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 sock_sendfile(intptr_t uuid, int source_fd,
301
- off_t offset, size_t length) {
302
- return sock_write2(.fduuid = uuid, .buffer = (void *)((intptr_t)source_fd),
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
- Buffer packets - can be used for directly writing individual or multiple packets
347
- to the buffer instead of using the `sock_write(2)` helper functions / macros.
348
-
349
- See `sock_checkout_packet` and `sock_send_packet` for more information.
350
-
351
- Unused Packets that were checked out using the `sock_checkout_packet` function,
352
- should never be freed using `free` and should always use the `sock_free_packet`
353
- function.
354
- */
355
- typedef struct sock_packet_s {
356
- ssize_t length;
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 `sock_packet_s` from the packet pool, transfering the
399
- ownership of the memory to the calling function. The function will hang until a
400
- packet becomes available, so never check out more then a single packet at a
401
- time and remember to free or send the packet.
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 `packet->buffer`
405
- pointer (which can be safely overwritten to point to an external buffer).
406
+ BUFFER_PACKET_SIZE bytes. This buffer is accessible using the `->buf`
407
+ pointer.
406
408
 
407
- This attached buffer is safely and automatically freed or returned to the memory
408
- pool once `sock_send_packet` or `sock_free_packet` are called.
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
- sock_packet_s *sock_checkout_packet(void);
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
- The packet's memory is **always** handled by the `sock_send_packet` function
416
- (even on error).
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 sock_send_packet(intptr_t uuid, sock_packet_s *packet);
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 sock_packets_pending(intptr_t uuid);
426
+ int sock_has_pending(intptr_t uuid);
427
427
 
428
428
  /**
429
- Use `sock_free_packet` to free unused packets that were checked-out using
430
- `sock_checkout_packet`.
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 sock_free_packet(sock_packet_s *packet);
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 fduuid, void *buf, size_t count);
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 fduuid, const void *buf, size_t count);
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 `on_clear` callback is called when the socket data is cleared, ideally
463
- * when the connection is closed, allowing for dynamic sock_rw_hook_s memory
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 `on_clear` callback should manage is own thread safety mechanism, if
462
+ * The `on_close` callback should manage is own thread safety mechanism, if
467
463
  * required. */
468
- void (*on_clear)(intptr_t fduuid, struct sock_rw_hook_s *rw_hook);
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 /* LIB_SOCK */
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 */