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,570 +0,0 @@
1
- /*
2
- copyright: Boaz Segev, 2016-2017
3
- license: MIT
4
-
5
- Feel free to copy, use and enjoy according to the license provided.
6
- */
7
-
8
- #include "rb-libasync.h" // enable this line for Iodine's Ruby
9
-
10
- #ifndef _GNU_SOURCE
11
- #define _GNU_SOURCE
12
- #endif
13
-
14
- #include "libasync.h"
15
-
16
- #include <errno.h>
17
- #include <execinfo.h>
18
- #include <fcntl.h>
19
- #include <pthread.h>
20
- #include <sched.h>
21
- #include <signal.h>
22
- #include <stdio.h>
23
- #include <stdlib.h>
24
- #include <string.h>
25
- #include <sys/mman.h>
26
- #include <unistd.h>
27
-
28
- /* *****************************************************************************
29
- Performance options.
30
- */
31
-
32
- #ifndef ASYNC_TASK_POOL_SIZE
33
- #define ASYNC_TASK_POOL_SIZE 1024
34
- #endif
35
-
36
- /* Spinlock vs. Mutex data protection. */
37
- #ifndef ASYNC_USE_SPINLOCK
38
- #define ASYNC_USE_SPINLOCK 1
39
- #endif
40
-
41
- /* use pipe for wakeup if == 0 else, use nanosleep when no tasks. */
42
- #ifndef ASYNC_NANO_SLEEP
43
- #define ASYNC_NANO_SLEEP 8388608 // 1048576 // 524288 // 16777216
44
- #endif
45
-
46
- /* Sentinal thread to respawn crashed threads - limited crash resistance. */
47
- #ifndef ASYNC_USE_SENTINEL
48
- #define ASYNC_USE_SENTINEL 0
49
- #endif
50
-
51
- /* *****************************************************************************
52
- Forward declarations - used for functions that might be needed before they are
53
- defined.
54
- */
55
-
56
- // the actual working thread
57
- static void *worker_thread_cycle(void *);
58
-
59
- // A thread sentinal (optional - edit the ASYNC_USE_SENTINEL macro to use or
60
- // disable)
61
- static void *sentinal_thread(void *);
62
-
63
- /******************************************************************************
64
- Portability - used to help port this to different frameworks (i.e. Ruby).
65
- */
66
-
67
- #ifndef THREAD_TYPE
68
- #define THREAD_TYPE pthread_t
69
-
70
- static void *join_thread(THREAD_TYPE thr) {
71
- void *ret;
72
- pthread_join(thr, &ret);
73
- return ret;
74
- }
75
-
76
- static int create_thread(THREAD_TYPE *thr, void *(*thread_func)(void *),
77
- void *async) {
78
- return pthread_create(thr, NULL, thread_func, async);
79
- }
80
-
81
- #endif
82
- /******************************************************************************
83
- Data Types
84
- */
85
-
86
- /**
87
- A task
88
- */
89
- typedef struct {
90
- void (*task)(void *);
91
- void *arg;
92
- } task_s;
93
-
94
- /**
95
- A task node
96
- */
97
- typedef struct async_task_ns {
98
- task_s task;
99
- struct async_task_ns *next;
100
- } async_task_ns;
101
-
102
- /* *****************************************************************************
103
- Use spinlocks "spnlock.h".
104
-
105
- For portability, it's possible copy "spnlock.h" directly after this line.
106
- */
107
- #include "spnlock.h"
108
-
109
- /******************************************************************************
110
- Core Data
111
- */
112
-
113
- typedef struct {
114
- #if !defined(ASYNC_USE_SPINLOCK) || ASYNC_USE_SPINLOCK != 1
115
- /* if using mutex */
116
- pthread_mutex_t lock;
117
- #endif
118
-
119
- /* task management*/
120
- async_task_ns memory[ASYNC_TASK_POOL_SIZE];
121
- async_task_ns *pool;
122
- async_task_ns *tasks;
123
- async_task_ns **pos;
124
-
125
- /* thread management*/
126
- size_t thread_count;
127
-
128
- #if ASYNC_NANO_SLEEP == 0
129
- /* when using pipes */
130
- struct {
131
- int in;
132
- int out;
133
- } io;
134
- #endif
135
-
136
- #if defined(ASYNC_USE_SPINLOCK) && ASYNC_USE_SPINLOCK == 1 // use spinlock
137
- /* if using spinlock */
138
- spn_lock_i lock;
139
- #endif
140
-
141
- /* state management*/
142
- struct {
143
- unsigned run : 1;
144
- } flags;
145
-
146
- /** the threads array, must be last */
147
- THREAD_TYPE threads[];
148
- } async_data_s;
149
-
150
- static async_data_s *async;
151
-
152
- /******************************************************************************
153
- Core Data initialization and lock/unlock
154
- */
155
-
156
- #if defined(ASYNC_USE_SPINLOCK) && ASYNC_USE_SPINLOCK == 1 // use spinlock
157
- #define lock_async_init() (spn_unlock(&(async->lock)), 0)
158
- #define lock_async_destroy() ;
159
- #define lock_async() spn_lock(&(async->lock))
160
- #define unlock_async() spn_unlock(&(async->lock))
161
-
162
- #else // Using Mutex
163
- #define lock_async_init() (pthread_mutex_init(&((async)->lock), NULL))
164
- #define lock_async_destroy() (pthread_mutex_destroy(&((async)->lock)))
165
- #define lock_async() (pthread_mutex_lock(&((async)->lock)))
166
- #define unlock_async() (pthread_mutex_unlock(&((async)->lock)))
167
- #endif
168
-
169
- static inline void async_free(void) {
170
- #if ASYNC_NANO_SLEEP == 0
171
- if (async->io.in) {
172
- close(async->io.in);
173
- close(async->io.out);
174
- }
175
- #endif
176
- lock_async_destroy();
177
- munmap(async, (sizeof(async_data_s) +
178
- (sizeof(THREAD_TYPE) * (async->thread_count))));
179
- async = NULL;
180
- }
181
-
182
- static inline void async_alloc(size_t threads) {
183
- async = mmap(NULL, (sizeof(async_data_s) + (sizeof(THREAD_TYPE) * (threads))),
184
- PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS,
185
- -1, 0);
186
- if (async == MAP_FAILED) {
187
- async = NULL;
188
- }
189
- *async = (async_data_s){.flags.run = 1};
190
- async->pos = &async->tasks;
191
-
192
- if (lock_async_init()) {
193
- async_free();
194
- return;
195
- }
196
-
197
- #if ASYNC_NANO_SLEEP == 0 // using pipes?
198
- if (pipe(&async->io.in)) {
199
- async_free();
200
- return;
201
- }
202
- fcntl(async->io.out, F_SETFL, O_NONBLOCK);
203
- #endif
204
-
205
- // initialize pool
206
- for (size_t i = 0; i < ASYNC_TASK_POOL_SIZE - 1; i++) {
207
- async->memory[i].next = async->memory + i + 1;
208
- }
209
- async->memory[ASYNC_TASK_POOL_SIZE - 1].next = NULL;
210
- async->pool = async->memory;
211
- }
212
-
213
- /******************************************************************************
214
- Perfoming tasks
215
- */
216
-
217
- static inline void perform_tasks(void) {
218
- task_s tsk;
219
- async_task_ns *t;
220
- while (async) {
221
- lock_async();
222
- t = async->tasks;
223
- if (t) {
224
- async->tasks = t->next;
225
- if (async->tasks == NULL)
226
- async->pos = &(async->tasks);
227
- tsk = t->task;
228
- if (t >= async->memory &&
229
- (t <= (async->memory + ASYNC_TASK_POOL_SIZE - 1))) {
230
- t->next = async->pool;
231
- async->pool = t;
232
- } else {
233
- free(t);
234
- }
235
- unlock_async();
236
- tsk.task(tsk.arg);
237
- continue;
238
- }
239
- async->pos = &(async->tasks);
240
- unlock_async();
241
- return;
242
- }
243
- }
244
-
245
- /******************************************************************************
246
- Pausing and resuming threads
247
- */
248
-
249
- static inline void pause_thread() {
250
- #if ASYNC_NANO_SLEEP == 0
251
- if (async && async->flags.run) {
252
- uint8_t tmp;
253
- read(async->io.in, &tmp, 1);
254
- }
255
- #else
256
- struct timespec act,
257
- tm = {.tv_sec = 0,
258
- .tv_nsec = ASYNC_NANO_SLEEP * (async ? async->thread_count : 1)};
259
- nanosleep(&tm, &act);
260
- // sched_yield();
261
- #endif
262
- }
263
-
264
- static inline void wake_thread() {
265
- #if ASYNC_NANO_SLEEP == 0
266
- write(async->io.out, async, 1);
267
- #endif
268
- }
269
-
270
- static inline void wake_all_threads() {
271
- #if ASYNC_NANO_SLEEP == 0
272
- write(async->io.out, async, async->thread_count + 16);
273
- #endif
274
- }
275
-
276
- /******************************************************************************
277
- Worker threads
278
- */
279
-
280
- // on thread failure, a backtrace should be printed (if
281
- // using sentinal)
282
- // manage thread error signals
283
- static void on_err_signal(int sig) {
284
- void *array[22];
285
- size_t size;
286
- char **strings;
287
- size_t i;
288
- size = backtrace(array, 22);
289
- strings = backtrace_symbols(array, size);
290
- perror("\nERROR");
291
- fprintf(stderr, "Async: Error signal received"
292
- " - %s (errno %d).\nBacktrace (%zd):\n",
293
- strsignal(sig), errno, size);
294
- for (i = 2; i < size; i++)
295
- fprintf(stderr, "%s\n", strings[i]);
296
- free(strings);
297
- fprintf(stderr, "\n");
298
- // pthread_exit(0); // for testing
299
- pthread_exit((void *)on_err_signal);
300
- }
301
-
302
- // The worker cycle
303
- static void *worker_thread_cycle(void *unused) {
304
- (void)(unused);
305
- // register error signals when using a sentinal
306
- if (ASYNC_USE_SENTINEL) {
307
- signal(SIGSEGV, on_err_signal);
308
- signal(SIGFPE, on_err_signal);
309
- signal(SIGILL, on_err_signal);
310
- #ifdef SIGBUS
311
- signal(SIGBUS, on_err_signal);
312
- #endif
313
- #ifdef SIGSYS
314
- signal(SIGSYS, on_err_signal);
315
- #endif
316
- #ifdef SIGXFSZ
317
- signal(SIGXFSZ, on_err_signal);
318
- #endif
319
- }
320
-
321
- // ignore pipe issues
322
- signal(SIGPIPE, SIG_IGN);
323
-
324
- // pause for signal for as long as we're active.
325
- while (async && async->flags.run) {
326
- perform_tasks();
327
- pause_thread();
328
- }
329
- perform_tasks();
330
- return 0;
331
- }
332
-
333
- // an optional sentinal
334
- static void *sentinal_thread(void *_) {
335
- THREAD_TYPE thr;
336
- while (async != NULL && async->flags.run == 1 &&
337
- create_thread(&thr, worker_thread_cycle, _) == 0)
338
- join_thread(thr);
339
- return 0;
340
- }
341
-
342
- /******************************************************************************
343
- API
344
- */
345
-
346
- /**
347
- Starts running the global thread pool. Use:
348
-
349
- async_start(8);
350
-
351
- */
352
- ssize_t async_start(size_t threads) {
353
- async_alloc(threads);
354
- if (async == NULL)
355
- return -1;
356
- // initialize threads
357
- for (size_t i = 0; i < threads; i++) {
358
- if (create_thread(
359
- async->threads + i,
360
- (ASYNC_USE_SENTINEL ? sentinal_thread : worker_thread_cycle),
361
- NULL) < 0) {
362
- async->flags.run = 0;
363
- wake_all_threads();
364
- async_free();
365
- return -1;
366
- }
367
- ++async->thread_count;
368
- }
369
- signal(SIGPIPE, SIG_IGN);
370
- return 0;
371
- }
372
-
373
- /**
374
- Waits for all the present tasks to complete.
375
-
376
- The thread pool will remain active, waiting for new tasts.
377
-
378
- This function will wait forever or until a signal is
379
- received and all the tasks in the queue have been processed.
380
-
381
- Unline finish (that uses `join`) this is an **active** wait where the waiting
382
- thread acts as a working thread and performs any pending tasks.
383
-
384
- Use:
385
-
386
- Async.wait(async);
387
-
388
- */
389
- void async_perform() { perform_tasks(); }
390
-
391
- /**
392
- Returns TRUE (not 0) if there are any pending tasks.
393
- */
394
- int async_any(void) { return (async && async->tasks); }
395
-
396
- /**
397
- Schedules a task to be performed by the thread pool.
398
-
399
- The Task should be a function such as `void task(void
400
- *arg)`.
401
-
402
- Use:
403
-
404
- void task(void * arg) { printf("%s", arg); }
405
-
406
- char arg[] = "Demo Task";
407
-
408
- async_run(task, arg);
409
-
410
- */
411
- int async_run(void (*task)(void *), void *arg) {
412
- if (async == NULL)
413
- return -1;
414
- async_task_ns *tsk;
415
- lock_async();
416
- tsk = async->pool;
417
- if (tsk) {
418
- async->pool = tsk->next;
419
- } else {
420
- tsk = malloc(sizeof(*tsk));
421
- if (!tsk)
422
- goto error;
423
- }
424
- *tsk = (async_task_ns){.task.task = task, .task.arg = arg};
425
- *(async->pos) = tsk;
426
- async->pos = &(tsk->next);
427
- unlock_async();
428
- wake_thread();
429
- return 0;
430
- error:
431
- unlock_async();
432
- return -1;
433
- }
434
-
435
- /**
436
- Waits for existing tasks to complete and releases the thread
437
- pool and it's
438
- resources.
439
- */
440
- void async_join() {
441
- if (async == NULL)
442
- return;
443
- for (size_t i = 0; i < async->thread_count; i++) {
444
- join_thread(async->threads[i]);
445
- }
446
- perform_tasks();
447
- async_free();
448
- }
449
-
450
- /**
451
- Waits for existing tasks to complete and releases the thread
452
- pool and it's
453
- resources.
454
- */
455
- void async_signal() {
456
- if (async == NULL)
457
- return;
458
- async->flags.run = 0;
459
- wake_all_threads();
460
- }
461
-
462
- /******************************************************************************
463
- Test
464
- */
465
-
466
- #ifdef DEBUG
467
-
468
- #define ASYNC_SPEED_TEST_THREAD_COUNT 120
469
-
470
- static spn_lock_i i_lock = SPN_LOCK_INIT;
471
- static size_t i_count = 0;
472
-
473
- static void sample_task(void *unused) {
474
- (void)(unused);
475
- spn_lock(&i_lock);
476
- i_count++;
477
- spn_unlock(&i_lock);
478
- }
479
-
480
- static void sched_sample_task(void *unused) {
481
- (void)(unused);
482
- for (size_t i = 0; i < 1024; i++) {
483
- async_run(sample_task, async);
484
- }
485
- }
486
-
487
- static void text_task_text(void *unused) {
488
- (void)(unused);
489
- spn_lock(&i_lock);
490
- fprintf(stderr, "this text should print before async_finish returns\n");
491
- spn_unlock(&i_lock);
492
- }
493
-
494
- static void text_task(void *_) {
495
- sleep(2);
496
- async_run(text_task_text, _);
497
- }
498
-
499
- #if ASYNC_USE_SENTINEL == 1
500
- static void evil_task(void *_) {
501
- __asm__ volatile("" ::: "memory");
502
- fprintf(stderr, "EVIL CODE RUNNING!\n");
503
- sprintf(NULL,
504
- "Never write text to a NULL pointer, this is a terrible idea that "
505
- "should segfault.\n");
506
- }
507
- #endif
508
-
509
- void async_test_library_speed(void) {
510
- spn_lock(&i_lock);
511
- i_count = 0;
512
- spn_unlock(&i_lock);
513
- time_t start, end;
514
- fprintf(stderr, "Starting Async testing\n");
515
- if (async_start(ASYNC_SPEED_TEST_THREAD_COUNT) == 0) {
516
- fprintf(stderr, "Thread count test %s %lu/%d\n",
517
- (async->thread_count == ASYNC_SPEED_TEST_THREAD_COUNT ? "PASSED"
518
- : "FAILED"),
519
- async->thread_count, ASYNC_SPEED_TEST_THREAD_COUNT);
520
- start = clock();
521
- for (size_t i = 0; i < 1024; i++) {
522
- async_run(sched_sample_task, async);
523
- }
524
- async_finish();
525
- end = clock();
526
- fprintf(stderr, "Async performance test %lu cycles with i_count = %lu\n",
527
- end - start, i_count);
528
- } else {
529
- fprintf(stderr, "Async test couldn't be initialized\n");
530
- exit(-1);
531
- }
532
- if (async_start(8)) {
533
- fprintf(stderr, "Couldn't start thread pool!\n");
534
- exit(-1);
535
- }
536
- fprintf(stderr, "calling async_perform.\n");
537
- async_run(text_task, NULL);
538
- sleep(1);
539
- async_perform();
540
- fprintf(stderr, "async_perform returned.\n");
541
- fprintf(stderr, "calling finish.\n");
542
- async_run(text_task, NULL);
543
- sleep(1);
544
- async_finish();
545
- fprintf(stderr, "finish returned.\n");
546
-
547
- #if ASYNC_USE_SENTINEL == 1
548
- if (async_start(8)) {
549
- fprintf(stderr, "Couldn't start thread pool!\n");
550
- exit(-1);
551
- }
552
- sleep(1);
553
- fprintf(stderr, "calling evil task.\n");
554
- async_run(evil_task, NULL);
555
- sleep(1);
556
- fprintf(stderr, "calling finish.\n");
557
- async_finish();
558
- #endif
559
-
560
- // async_start(8);
561
- // fprintf(stderr,
562
- // "calling a few tasks and sleeping 12 seconds before finishing
563
- // up...\n"
564
- // "check the processor CPU cycles - are we busy?\n");
565
- // async_run(sched_sample_task, NULL);
566
- // sleep(12);
567
- // async_finish();
568
- }
569
-
570
- #endif