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.
- checksums.yaml +4 -4
- data/.gitignore +3 -2
- data/.travis.yml +23 -2
- data/CHANGELOG.md +9 -2
- data/README.md +232 -179
- data/Rakefile +13 -1
- data/bin/config.ru +63 -0
- data/bin/console +6 -0
- data/bin/echo +42 -32
- data/bin/http-hello +62 -0
- data/bin/http-playground +124 -0
- data/bin/playground +62 -0
- data/bin/poc/Gemfile.lock +23 -0
- data/bin/poc/README.md +37 -0
- data/bin/poc/config.ru +66 -0
- data/bin/poc/gemfile +1 -0
- data/bin/poc/www/index.html +57 -0
- data/bin/raw-rbhttp +35 -0
- data/bin/raw_broadcast +66 -0
- data/bin/test_with_faye +40 -0
- data/bin/ws-broadcast +108 -0
- data/bin/ws-echo +108 -0
- data/exe/iodine +59 -0
- data/ext/iodine/base64.c +264 -0
- data/ext/iodine/base64.h +72 -0
- data/ext/iodine/bscrypt-common.h +109 -0
- data/ext/iodine/bscrypt.h +49 -0
- data/ext/iodine/extconf.rb +41 -0
- data/ext/iodine/hex.c +123 -0
- data/ext/iodine/hex.h +70 -0
- data/ext/iodine/http.c +200 -0
- data/ext/iodine/http.h +128 -0
- data/ext/iodine/http1.c +402 -0
- data/ext/iodine/http1.h +56 -0
- data/ext/iodine/http1_simple_parser.c +473 -0
- data/ext/iodine/http1_simple_parser.h +59 -0
- data/ext/iodine/http_request.h +128 -0
- data/ext/iodine/http_response.c +1606 -0
- data/ext/iodine/http_response.h +393 -0
- data/ext/iodine/http_response_http1.h +374 -0
- data/ext/iodine/iodine_core.c +641 -0
- data/ext/iodine/iodine_core.h +70 -0
- data/ext/iodine/iodine_http.c +615 -0
- data/ext/iodine/iodine_http.h +19 -0
- data/ext/iodine/iodine_websocket.c +430 -0
- data/ext/iodine/iodine_websocket.h +21 -0
- data/ext/iodine/libasync.c +552 -0
- data/ext/iodine/libasync.h +117 -0
- data/ext/iodine/libreact.c +347 -0
- data/ext/iodine/libreact.h +244 -0
- data/ext/iodine/libserver.c +912 -0
- data/ext/iodine/libserver.h +435 -0
- data/ext/iodine/libsock.c +950 -0
- data/ext/iodine/libsock.h +478 -0
- data/ext/iodine/misc.c +181 -0
- data/ext/iodine/misc.h +76 -0
- data/ext/iodine/random.c +193 -0
- data/ext/iodine/random.h +48 -0
- data/ext/iodine/rb-call.c +127 -0
- data/ext/iodine/rb-call.h +60 -0
- data/ext/iodine/rb-libasync.h +79 -0
- data/ext/iodine/rb-rack-io.c +389 -0
- data/ext/iodine/rb-rack-io.h +17 -0
- data/ext/iodine/rb-registry.c +213 -0
- data/ext/iodine/rb-registry.h +33 -0
- data/ext/iodine/sha1.c +359 -0
- data/ext/iodine/sha1.h +85 -0
- data/ext/iodine/sha2.c +825 -0
- data/ext/iodine/sha2.h +138 -0
- data/ext/iodine/siphash.c +136 -0
- data/ext/iodine/siphash.h +15 -0
- data/ext/iodine/spnlock.h +235 -0
- data/ext/iodine/websockets.c +696 -0
- data/ext/iodine/websockets.h +120 -0
- data/ext/iodine/xor-crypt.c +189 -0
- data/ext/iodine/xor-crypt.h +107 -0
- data/iodine.gemspec +25 -18
- data/lib/iodine.rb +57 -58
- data/lib/iodine/http.rb +0 -189
- data/lib/iodine/protocol.rb +36 -245
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +145 -2
- metadata +115 -37
- data/bin/core_http_test +0 -51
- data/bin/em playground +0 -56
- data/bin/hello_world +0 -75
- data/bin/setup +0 -7
- data/lib/iodine/client.rb +0 -5
- data/lib/iodine/core.rb +0 -102
- data/lib/iodine/core_init.rb +0 -143
- data/lib/iodine/http/hpack.rb +0 -553
- data/lib/iodine/http/http1.rb +0 -251
- data/lib/iodine/http/http2.rb +0 -507
- data/lib/iodine/http/rack_support.rb +0 -108
- data/lib/iodine/http/request.rb +0 -462
- data/lib/iodine/http/response.rb +0 -474
- data/lib/iodine/http/session.rb +0 -143
- data/lib/iodine/http/websocket_client.rb +0 -335
- data/lib/iodine/http/websocket_handler.rb +0 -101
- data/lib/iodine/http/websockets.rb +0 -336
- data/lib/iodine/io.rb +0 -56
- data/lib/iodine/logging.rb +0 -46
- data/lib/iodine/settings.rb +0 -158
- data/lib/iodine/ssl_connector.rb +0 -48
- 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 */
|