iodine 0.1.21 → 0.2.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
data/ext/iodine/sha2.h ADDED
@@ -0,0 +1,138 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef bscrypt_SHA2_H
9
+ #define bscrypt_SHA2_H
10
+ #include "bscrypt-common.h"
11
+ /* *****************************************************************************
12
+ C++ extern
13
+ */
14
+ #if defined(__cplusplus)
15
+ extern "C" {
16
+ #endif
17
+
18
+ /* ***************************************************************************
19
+ SHA-2 hashing
20
+ */
21
+
22
+ /**
23
+ SHA-2 function variants.
24
+
25
+ This enum states the different SHA-2 function variants. placing SHA_512 at the
26
+ beginning is meant to set this variant as the default (in case a 0 is passed).
27
+ */
28
+ typedef enum {
29
+ SHA_512 = 1,
30
+ SHA_512_256 = 3,
31
+ SHA_512_224 = 5,
32
+ SHA_384 = 7,
33
+ SHA_256 = 2,
34
+ SHA_224 = 4,
35
+ } sha2_variant;
36
+
37
+ /**
38
+ SHA-2 hashing container - you should ignore the contents of this struct.
39
+
40
+ The `sha2_s` type will contain all the SHA-2 data required to perform the
41
+ hashing, managing it's encoding. If it's stack allocated, no freeing will be
42
+ required.
43
+
44
+ Use, for example:
45
+
46
+ #include "mini-crypt.h"
47
+ sha2_s sha2;
48
+ bscrypt.sha2_init(&sha2, SHA_512);
49
+ bscrypt.sha2_write(&sha2,
50
+ "The quick brown fox jumps over the lazy dog", 43);
51
+ char *hashed_result = bscrypt.sha2_result(&sha2);
52
+
53
+ */
54
+ typedef struct {
55
+ /* notice: we're counting bits, not bytes. max length: 2^128 bits */
56
+ bits128_u length;
57
+ uint8_t buffer[128];
58
+ union {
59
+ uint32_t i32[16];
60
+ uint64_t i64[8];
61
+ uint8_t str[65]; /* added 64+1 for the NULL byte.*/
62
+ } digest;
63
+ sha2_variant type;
64
+ } sha2_s;
65
+
66
+ /**
67
+ Initialize/reset the SHA-2 object.
68
+
69
+ SHA-2 is actually a family of functions with different variants. When
70
+ initializing the SHA-2 container, you must select the variant you intend to
71
+ apply. The following are valid options (see the sha2_variant enum):
72
+
73
+ - SHA_512 (== 0)
74
+ - SHA_384
75
+ - SHA_512_224
76
+ - SHA_512_256
77
+ - SHA_256
78
+ - SHA_224
79
+
80
+ */
81
+ sha2_s bscrypt_sha2_init(sha2_variant variant);
82
+ /**
83
+ Writes data to the SHA-2 buffer.
84
+ */
85
+ void bscrypt_sha2_write(sha2_s *s, const void *data, size_t len);
86
+ /**
87
+ Finalizes the SHA-2 hash, returning the Hashed data.
88
+
89
+ `sha2_result` can be called for the same object multiple times, but the
90
+ finalization will only be performed the first time this function is called.
91
+ */
92
+ char *bscrypt_sha2_result(sha2_s *s);
93
+
94
+ /**
95
+ An SHA2 helper function that performs initialiation, writing and finalizing.
96
+ Uses the SHA2 512 variant.
97
+ */
98
+ static inline __unused char *bscrypt_sha2_512(sha2_s *s, const void *data,
99
+ size_t len) {
100
+ *s = bscrypt_sha2_init(SHA_512);
101
+ bscrypt_sha2_write(s, data, len);
102
+ return bscrypt_sha2_result(s);
103
+ }
104
+
105
+ /**
106
+ An SHA2 helper function that performs initialiation, writing and finalizing.
107
+ Uses the SHA2 256 variant.
108
+ */
109
+ static inline __unused char *bscrypt_sha2_256(sha2_s *s, const void *data,
110
+ size_t len) {
111
+ *s = bscrypt_sha2_init(SHA_256);
112
+ bscrypt_sha2_write(s, data, len);
113
+ return bscrypt_sha2_result(s);
114
+ }
115
+
116
+ /**
117
+ An SHA2 helper function that performs initialiation, writing and finalizing.
118
+ Uses the SHA2 384 variant.
119
+ */
120
+ static inline __unused char *bscrypt_sha2_384(sha2_s *s, const void *data,
121
+ size_t len) {
122
+ *s = bscrypt_sha2_init(SHA_384);
123
+ bscrypt_sha2_write(s, data, len);
124
+ return bscrypt_sha2_result(s);
125
+ }
126
+
127
+ #if defined(DEBUG) && DEBUG == 1
128
+ void bscrypt_test_sha2(void);
129
+ #endif
130
+
131
+ /* *****************************************************************************
132
+ C++ extern finish
133
+ */
134
+ #if defined(__cplusplus)
135
+ }
136
+ #endif
137
+
138
+ #endif
@@ -0,0 +1,136 @@
1
+ #include "siphash.h"
2
+ #include <stdlib.h>
3
+ #include <stdint.h>
4
+
5
+ // clang-format off
6
+ #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
7
+ # if defined(__has_include)
8
+ # if __has_include(<endian.h>)
9
+ # include <endian.h>
10
+ # elif __has_include(<sys/endian.h>)
11
+ # include <sys/endian.h>
12
+ # endif
13
+ # endif
14
+ # if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
15
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
16
+ # define __BIG_ENDIAN__
17
+ # endif
18
+ #endif
19
+
20
+ #ifndef __unused
21
+ # define __unused __attribute__((unused))
22
+ #endif
23
+ // clang-format on
24
+
25
+ /** 64Bit left rotation, inlined. */
26
+ #define _lrot64(i, bits) \
27
+ (((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
28
+
29
+ #ifdef __BIG_ENDIAN__
30
+ /* the algorithm was designed as little endian */
31
+ /** inplace byte swap 64 bit integer */
32
+ #define sip_local64(i) \
33
+ (((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) | \
34
+ (((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
35
+ (((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
36
+ (((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56)
37
+
38
+ #else
39
+ /** no need */
40
+ #define sip_local64(i) (i)
41
+ #endif
42
+
43
+ uint64_t siphash24(const void *data, size_t len, uint64_t iv_key[2]) {
44
+ /* initialize the 4 words */
45
+ uint64_t v0 = iv_key[0] ^ 0x736f6d6570736575ULL;
46
+ uint64_t v1 = iv_key[1] ^ 0x646f72616e646f6dULL;
47
+ uint64_t v2 = iv_key[0] ^ 0x6c7967656e657261ULL;
48
+ uint64_t v3 = iv_key[1] ^ 0x7465646279746573ULL;
49
+ const uint64_t *w64 = data;
50
+ uint8_t len_mod = len & 255;
51
+ union {
52
+ uint64_t i;
53
+ uint8_t str[8];
54
+ } word;
55
+
56
+ #define _bs_map_SipRound \
57
+ do { \
58
+ v2 += v3; \
59
+ v3 = _lrot64(v3, 16) ^ v2; \
60
+ v0 += v1; \
61
+ v1 = _lrot64(v1, 13) ^ v0; \
62
+ v0 = _lrot64(v0, 32); \
63
+ v2 += v1; \
64
+ v0 += v3; \
65
+ v1 = _lrot64(v1, 17) ^ v2; \
66
+ v3 = _lrot64(v3, 21) ^ v0; \
67
+ v2 = _lrot64(v2, 32); \
68
+ } while (0);
69
+
70
+ while (len >= 8) {
71
+ word.i = sip_local64(*w64);
72
+ v3 ^= word.i;
73
+ /* Sip Rounds */
74
+ _bs_map_SipRound;
75
+ _bs_map_SipRound;
76
+ v0 ^= word.i;
77
+ w64 += 1;
78
+ len -= 8;
79
+ }
80
+ word.i = 0;
81
+ uint8_t *pos = word.str;
82
+ uint8_t *w8 = (void *)w64;
83
+ switch (len) {
84
+ case 7:
85
+ pos[6] = w8[6];
86
+ case 6:
87
+ pos[5] = w8[5];
88
+ case 5:
89
+ pos[4] = w8[4];
90
+ case 4:
91
+ pos[3] = w8[3];
92
+ case 3:
93
+ pos[2] = w8[2];
94
+ case 2:
95
+ pos[1] = w8[1];
96
+ case 1:
97
+ pos[0] = w8[0];
98
+ }
99
+ word.str[7] = len_mod;
100
+ // word.i = sip_local64(word.i);
101
+
102
+ /* last round */
103
+ v3 ^= word.i;
104
+ _bs_map_SipRound;
105
+ _bs_map_SipRound;
106
+ v0 ^= word.i;
107
+ /* Finalization */
108
+ v2 ^= 0xff;
109
+ /* d iterations of SipRound */
110
+ _bs_map_SipRound;
111
+ _bs_map_SipRound;
112
+ _bs_map_SipRound;
113
+ _bs_map_SipRound;
114
+ /* XOR it all together */
115
+ v0 ^= v1 ^ v2 ^ v3;
116
+ #undef _bs_map_SipRound
117
+ return v0;
118
+ }
119
+
120
+ #undef sip_local64
121
+ #undef _lrot64
122
+
123
+ #if defined(DEBUG) && DEBUG == 1
124
+
125
+ #include <stdio.h>
126
+ void bscrypt_test_siphash(void) {
127
+ uint64_t result =
128
+ siphash24("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e",
129
+ 15, SIPHASH_DEFAULT_KEY);
130
+ fprintf(stderr, "===================================\n");
131
+ fprintf(stderr, "SipHash simple test %s\n",
132
+ (result == 0xa129ca6149be45e5ULL) ? "passed" : "FAILED");
133
+ fprintf(stderr, "===================================\n");
134
+ }
135
+
136
+ #endif
@@ -0,0 +1,15 @@
1
+ #ifndef BS_SIPHASH_H
2
+ #define BS_SIPHASH_H
3
+ #include <stdlib.h>
4
+ #include <stdint.h>
5
+
6
+ #define SIPHASH_DEFAULT_KEY \
7
+ (uint64_t[]) { 0x0706050403020100, 0x0f0e0d0c0b0a0908 }
8
+ uint64_t siphash24(const void *data, size_t len, uint64_t iv_key[2]);
9
+
10
+ #if defined(DEBUG) && DEBUG == 1
11
+ void bscrypt_test_siphash(void);
12
+
13
+ #endif
14
+
15
+ #endif
@@ -0,0 +1,235 @@
1
+ /* *****************************************************************************
2
+ A Simple busy lock implementation ... (spnlock.h)
3
+
4
+ Based on a lot of internet reading as well as comperative work (i.e the Linux
5
+ karnel's code and the more readable Apple's kernel code)
6
+
7
+ Written by Boaz Segev at 2016. Donated to the public domain for all to enjoy.
8
+ */
9
+ #ifndef _SPN_LOCK_H
10
+ #define _SPN_LOCK_H
11
+
12
+ /* allow of the unused flag */
13
+ #ifndef __unused
14
+ #define __unused __attribute__((unused))
15
+ #endif
16
+
17
+ #include <stdlib.h>
18
+ #include <stdint.h>
19
+
20
+ /*********
21
+ * manage the way threads "wait" for the lock to release
22
+ */
23
+ #if defined(__unix__) || defined(__APPLE__) || defined(__linux__)
24
+ /* nanosleep seems to be the most effective and efficient reschedule */
25
+ #include <time.h>
26
+ #define reschedule_thread() \
27
+ { \
28
+ static const struct timespec tm = {.tv_nsec = 1}; \
29
+ nanosleep(&tm, NULL); \
30
+ }
31
+
32
+ #else /* no effective rescheduling, just spin... */
33
+ #define reschedule_thread()
34
+
35
+ /* these are SUPER slow when comapred with nanosleep or CPU cycling */
36
+ // #if defined(__SSE2__) || defined(__SSE2)
37
+ // #define reschedule_thread() __asm__("pause" :::)
38
+ //
39
+ // #elif defined(__has_include) && __has_include(<pthread.h>)
40
+ // #include "pthread.h"
41
+ // #define reschedule_thread() sched_yield()
42
+ // #endif
43
+
44
+ #endif
45
+ /* end `reschedule_thread` block*/
46
+
47
+ /*********
48
+ * The spin lock core functions (spn_trylock, spn_unlock, is_spn_locked)
49
+ */
50
+
51
+ /* prefer C11 standard implementation where available (trust the system) */
52
+ #if defined(__has_include)
53
+ #if __has_include(<stdatomic.h>)
54
+ #define SPN_TMP_HAS_ATOMICS 1
55
+ #include <stdatomic.h>
56
+ typedef atomic_bool spn_lock_i;
57
+ #define SPN_LOCK_INIT ATOMIC_VAR_INIT(0)
58
+ /** returns 1 if the lock was busy (TRUE == FAIL). */
59
+ __unused static inline int spn_trylock(spn_lock_i *lock) {
60
+ __asm__ volatile("" ::: "memory");
61
+ return atomic_exchange(lock, 1);
62
+ }
63
+ /** Releases a lock. */
64
+ __unused static inline void spn_unlock(spn_lock_i *lock) {
65
+ atomic_store(lock, 0);
66
+ __asm__ volatile("" ::: "memory");
67
+ }
68
+ /** returns a lock's state (non 0 == Busy). */
69
+ __unused static inline int spn_is_locked(spn_lock_i *lock) {
70
+ return atomic_load(lock);
71
+ }
72
+ #endif
73
+ #endif
74
+
75
+ /* Chack if stdatomic was available */
76
+ #ifdef SPN_TMP_HAS_ATOMICS
77
+ #undef SPN_TMP_HAS_ATOMICS
78
+
79
+ #else
80
+ /* Test for compiler builtins */
81
+
82
+ /* use clang builtins if available - trust the compiler */
83
+ #if defined(__clang__)
84
+ #if defined(__has_builtin) && __has_builtin(__sync_swap)
85
+ /* define the type */
86
+ typedef volatile uint8_t spn_lock_i;
87
+ /** returns 1 if the lock was busy (TRUE == FAIL). */
88
+ __unused static inline int spn_trylock(spn_lock_i *lock) {
89
+ return __sync_swap(lock, 1);
90
+ }
91
+ #define SPN_TMP_HAS_BUILTIN 1
92
+ #endif
93
+ /* use gcc builtins if available - trust the compiler */
94
+ #elif defined(__GNUC__) && \
95
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))
96
+ /* define the type */
97
+ typedef volatile uint8_t spn_lock_i;
98
+ /** returns 1 if the lock was busy (TRUE == FAIL). */
99
+ __unused static inline int spn_trylock(spn_lock_i *lock) {
100
+ return __sync_fetch_and_or(lock, 1);
101
+ }
102
+ #define SPN_TMP_HAS_BUILTIN 1
103
+ #endif
104
+
105
+ /* Check if compiler builtins were available, if not, try assembly*/
106
+ #if SPN_TMP_HAS_BUILTIN
107
+ #undef SPN_TMP_HAS_BUILTIN
108
+
109
+ /* use Intel's asm if on Intel - trust Intel's documentation */
110
+ #elif defined(__amd64__) || defined(__x86_64__) || defined(__x86__) || \
111
+ defined(__i386__) || defined(__ia64__) || defined(_M_IA64) || \
112
+ defined(__itanium__) || defined(__i386__)
113
+ /* define the type */
114
+ typedef volatile uint8_t spn_lock_i;
115
+ /** returns 1 if the lock was busy (TRUE == FAIL). */
116
+ __unused static inline int spn_trylock(spn_lock_i *lock) {
117
+ spn_lock_i tmp;
118
+ __asm__ volatile("xchgb %0,%1" : "=r"(tmp), "=m"(*lock) : "0"(1) : "memory");
119
+ return tmp;
120
+ }
121
+
122
+ /* use SPARC's asm if on SPARC - trust the design */
123
+ #elif defined(__sparc__) || defined(__sparc)
124
+ /* define the type */
125
+ typedef volatile uint8_t spn_lock_i;
126
+ /** returns TRUE (non-zero) if the lock was busy (TRUE == FAIL). */
127
+ __unused static inline int spn_trylock(spn_lock_i *lock) {
128
+ spn_lock_i tmp;
129
+ __asm__ volatile("ldstub [%1], %0" : "=r"(tmp) : "r"(lock) : "memory");
130
+ return tmp; /* return 0xFF if the lock was busy, 0 if free */
131
+ }
132
+
133
+ #else
134
+ /* I don't know how to provide green thread safety on PowerPC or ARM */
135
+ #error "Couldn't implement a spinlock for this system / compiler"
136
+ #endif /* types and atomic exchange */
137
+ /** Initialization value in `free` state. */
138
+ #define SPN_LOCK_INIT 0
139
+
140
+ /** Releases a lock. */
141
+ __unused static inline void spn_unlock(spn_lock_i *lock) {
142
+ __asm__ volatile("" ::: "memory");
143
+ *lock = 0;
144
+ }
145
+ /** returns a lock's state (non 0 == Busy). */
146
+ __unused static inline int spn_is_locked(spn_lock_i *lock) {
147
+ __asm__ volatile("" ::: "memory");
148
+ return *lock;
149
+ }
150
+
151
+ #endif /* has atomics */
152
+ #include <stdio.h>
153
+ /** Busy waits for the lock. */
154
+ __unused static inline void spn_lock(spn_lock_i *lock) {
155
+ while (spn_trylock(lock)) {
156
+ reschedule_thread();
157
+ }
158
+ }
159
+
160
+ /* *****************************************************************************
161
+ spnlock.h finished
162
+ */
163
+ #endif
164
+
165
+ #if DEBUG == 1 && !defined(_SPN_LOCK_TEST_REPEAT_COUNT)
166
+
167
+ #define _SPN_LOCK_TEST_REPEAT_COUNT 10000UL
168
+ #define _SPN_LOCK_TEST_THREAD_COUNT 10000UL
169
+ #include <pthread.h>
170
+ #include <stdio.h>
171
+
172
+ __unused static void *test_spn_lock_work(void *arg) {
173
+ static spn_lock_i lck = SPN_LOCK_INIT;
174
+ uint64_t *ip = arg;
175
+ for (size_t i = 0; i < _SPN_LOCK_TEST_REPEAT_COUNT; i++) {
176
+ spn_lock(&lck);
177
+ uint64_t j = *ip;
178
+ j++;
179
+ __asm__ volatile("" ::: "memory", "cc");
180
+ *ip = j;
181
+ spn_unlock(&lck);
182
+ }
183
+ return NULL;
184
+ }
185
+
186
+ __unused static void *test_spn_lock_lockless_work(void *arg) {
187
+ uint64_t *ip = arg;
188
+ for (size_t i = 0; i < _SPN_LOCK_TEST_REPEAT_COUNT; i++) {
189
+ uint64_t j = *ip;
190
+ j++;
191
+ __asm__ volatile("" ::: "memory", "cc");
192
+ *ip = j;
193
+ }
194
+ return NULL;
195
+ }
196
+
197
+ __unused static void spn_lock_test(void) {
198
+ time_t start, end;
199
+ unsigned long num = 0;
200
+ pthread_t *threads = malloc(_SPN_LOCK_TEST_THREAD_COUNT * sizeof(*threads));
201
+ void *tmp;
202
+ start = clock();
203
+ for (size_t i = 0; i < _SPN_LOCK_TEST_THREAD_COUNT; i++) {
204
+ pthread_create(threads + i, NULL, test_spn_lock_lockless_work, &num);
205
+ }
206
+ for (size_t i = 0; i < _SPN_LOCK_TEST_THREAD_COUNT; i++) {
207
+ pthread_join(threads[i], &tmp);
208
+ }
209
+ end = clock();
210
+ fprintf(stderr, "Lockless Num = %lu with %lu CPU cycles.\n", num,
211
+ end - start);
212
+
213
+ num = 0;
214
+
215
+ start = clock();
216
+ for (size_t i = 0; i < _SPN_LOCK_TEST_THREAD_COUNT; i++) {
217
+ if (pthread_create(threads + i, NULL, test_spn_lock_work, &num))
218
+ fprintf(stderr,
219
+ "Failed to create thread number %lu... test will fail to run as "
220
+ "expected.\n",
221
+ i);
222
+ ;
223
+ }
224
+ for (size_t i = 0; i < _SPN_LOCK_TEST_THREAD_COUNT; i++) {
225
+ pthread_join(threads[i], &tmp);
226
+ }
227
+ end = clock();
228
+ free(threads);
229
+ fprintf(stderr, "Locked Num = %lu with %lu CPU cycles.\n", num, end - start);
230
+ fprintf(stderr, "spn_lock test %s\n",
231
+ num == _SPN_LOCK_TEST_THREAD_COUNT * _SPN_LOCK_TEST_REPEAT_COUNT
232
+ ? "passed."
233
+ : "FAILED!");
234
+ }
235
+ #endif /* Test */