iodine 0.5.2 → 0.6.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 +14 -0
- data/README.md +63 -100
- data/bin/raw-rbhttp +12 -7
- data/examples/config.ru +8 -7
- data/examples/echo.ru +8 -7
- data/examples/info.md +41 -35
- data/examples/pubsub_engine.ru +12 -12
- data/examples/redis.ru +10 -12
- data/examples/shootout.ru +19 -42
- data/exe/iodine +116 -1
- data/ext/iodine/defer.c +1 -1
- data/ext/iodine/facil.c +12 -8
- data/ext/iodine/facil.h +2 -2
- data/ext/iodine/iodine.c +177 -343
- data/ext/iodine/iodine.h +18 -72
- data/ext/iodine/iodine_caller.c +132 -0
- data/ext/iodine/iodine_caller.h +21 -0
- data/ext/iodine/iodine_connection.c +841 -0
- data/ext/iodine/iodine_connection.h +55 -0
- data/ext/iodine/iodine_defer.c +391 -0
- data/ext/iodine/iodine_defer.h +7 -0
- data/ext/iodine/{rb-fiobj2rb.h → iodine_fiobj2rb.h} +6 -6
- data/ext/iodine/iodine_helpers.c +51 -5
- data/ext/iodine/iodine_helpers.h +2 -3
- data/ext/iodine/iodine_http.c +284 -141
- data/ext/iodine/iodine_http.h +2 -2
- data/ext/iodine/iodine_json.c +13 -13
- data/ext/iodine/iodine_json.h +1 -1
- data/ext/iodine/iodine_pubsub.c +573 -823
- data/ext/iodine/iodine_pubsub.h +15 -27
- data/ext/iodine/{rb-rack-io.c → iodine_rack_io.c} +30 -8
- data/ext/iodine/{rb-rack-io.h → iodine_rack_io.h} +1 -0
- data/ext/iodine/iodine_store.c +136 -0
- data/ext/iodine/iodine_store.h +20 -0
- data/ext/iodine/iodine_tcp.c +385 -0
- data/ext/iodine/iodine_tcp.h +9 -0
- data/lib/iodine.rb +73 -171
- data/lib/iodine/connection.rb +34 -0
- data/lib/iodine/pubsub.rb +5 -18
- data/lib/iodine/rack_utils.rb +43 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -182
- metadata +17 -18
- data/ext/iodine/iodine_protocol.c +0 -689
- data/ext/iodine/iodine_protocol.h +0 -13
- data/ext/iodine/iodine_websockets.c +0 -550
- data/ext/iodine/iodine_websockets.h +0 -17
- data/ext/iodine/rb-call.c +0 -156
- data/ext/iodine/rb-call.h +0 -70
- data/ext/iodine/rb-defer.c +0 -124
- data/ext/iodine/rb-registry.c +0 -150
- data/ext/iodine/rb-registry.h +0 -34
- data/lib/iodine/cli.rb +0 -89
- data/lib/iodine/monkeypatch.rb +0 -46
- data/lib/iodine/protocol.rb +0 -42
- data/lib/iodine/websocket.rb +0 -16
@@ -1,17 +0,0 @@
|
|
1
|
-
#ifndef H_IODINE_WEBSOCKETS_H
|
2
|
-
#define H_IODINE_WEBSOCKETS_H
|
3
|
-
/*
|
4
|
-
Copyright: Boaz segev, 2016-2017
|
5
|
-
License: MIT
|
6
|
-
|
7
|
-
Feel free to copy, use and enjoy according to the license provided.
|
8
|
-
*/
|
9
|
-
#include "iodine.h"
|
10
|
-
|
11
|
-
#include "http.h"
|
12
|
-
|
13
|
-
void Iodine_init_websocket(void);
|
14
|
-
|
15
|
-
void iodine_upgrade_websocket(http_s *request, VALUE handler);
|
16
|
-
void iodine_upgrade_sse(http_s *h, VALUE handler);
|
17
|
-
#endif
|
data/ext/iodine/rb-call.c
DELETED
@@ -1,156 +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
|
-
#include "rb-call.h"
|
8
|
-
#include <pthread.h>
|
9
|
-
#include <ruby.h>
|
10
|
-
#include <ruby/thread.h>
|
11
|
-
|
12
|
-
#if __STDC_VERSION__ < 201112L || __STDC_NO_THREADS__
|
13
|
-
#define _Thread_local __thread
|
14
|
-
#endif
|
15
|
-
|
16
|
-
typedef enum {
|
17
|
-
RUBY_TASK,
|
18
|
-
C_TASK,
|
19
|
-
} iodine_task_type_en;
|
20
|
-
|
21
|
-
typedef struct {
|
22
|
-
iodine_task_type_en type;
|
23
|
-
VALUE obj;
|
24
|
-
int argc;
|
25
|
-
VALUE *argv;
|
26
|
-
ID method;
|
27
|
-
int exception;
|
28
|
-
} iodine_rb_task_s;
|
29
|
-
|
30
|
-
typedef struct {
|
31
|
-
iodine_task_type_en type;
|
32
|
-
void *(*func)(void *);
|
33
|
-
void *arg;
|
34
|
-
} iodine_c_task_s;
|
35
|
-
|
36
|
-
// running the actual method call
|
37
|
-
static VALUE iodine_ruby_caller_perform(VALUE tsk_) {
|
38
|
-
switch (*(iodine_task_type_en *)tsk_) {
|
39
|
-
case RUBY_TASK: {
|
40
|
-
iodine_rb_task_s *task = (void *)tsk_;
|
41
|
-
return rb_funcall2(task->obj, task->method, task->argc, task->argv);
|
42
|
-
}
|
43
|
-
case C_TASK: {
|
44
|
-
iodine_c_task_s *task = (void *)tsk_;
|
45
|
-
return (VALUE)task->func(task->arg);
|
46
|
-
}
|
47
|
-
}
|
48
|
-
return Qnil;
|
49
|
-
}
|
50
|
-
|
51
|
-
////////////////////////////////////////////////////////////////////////////
|
52
|
-
// Handling exceptions (printing the backtrace doesn't really work well).
|
53
|
-
static void *handle_exception(void *ignr) {
|
54
|
-
(void)ignr;
|
55
|
-
VALUE exc = rb_errinfo();
|
56
|
-
if (exc != Qnil) {
|
57
|
-
VALUE msg = RubyCaller.call(exc, rb_intern("message"));
|
58
|
-
VALUE exc_class = rb_class_name(CLASS_OF(exc));
|
59
|
-
VALUE bt = RubyCaller.call(exc, rb_intern("backtrace"));
|
60
|
-
if (TYPE(bt) == T_ARRAY) {
|
61
|
-
bt = rb_ary_join(bt, rb_str_new_literal("\n"));
|
62
|
-
fprintf(stderr, "Iodine caught an unprotected exception - %.*s: %.*s\n%s",
|
63
|
-
(int)RSTRING_LEN(exc_class), RSTRING_PTR(exc_class),
|
64
|
-
(int)RSTRING_LEN(msg), RSTRING_PTR(msg), StringValueCStr(bt));
|
65
|
-
} else {
|
66
|
-
fprintf(stderr,
|
67
|
-
"Iodine caught an unprotected exception - %.*s: %.*s\n"
|
68
|
-
"No backtrace available.\n",
|
69
|
-
(int)RSTRING_LEN(exc_class), RSTRING_PTR(exc_class),
|
70
|
-
(int)RSTRING_LEN(msg), RSTRING_PTR(msg));
|
71
|
-
}
|
72
|
-
rb_backtrace();
|
73
|
-
fprintf(stderr, "\n\n");
|
74
|
-
rb_set_errinfo(Qnil);
|
75
|
-
}
|
76
|
-
return (void *)Qnil;
|
77
|
-
}
|
78
|
-
|
79
|
-
/* wrap the function call in the exception handling code */
|
80
|
-
static void *iodine_protected_call(void *tsk_) {
|
81
|
-
int state = 0;
|
82
|
-
VALUE ret = rb_protect(iodine_ruby_caller_perform, (VALUE)(tsk_), &state);
|
83
|
-
if (state) {
|
84
|
-
handle_exception(NULL);
|
85
|
-
}
|
86
|
-
return (void *)ret;
|
87
|
-
}
|
88
|
-
|
89
|
-
////////////////////////////////////////////////////////////////////////////
|
90
|
-
// GVL state.
|
91
|
-
|
92
|
-
// a thread specific global variable that lets us know if we're in the GVL
|
93
|
-
static _Thread_local char in_gvl = 0;
|
94
|
-
static char iodine_rb_check_in_gvl(void) { return in_gvl; }
|
95
|
-
|
96
|
-
static void iodine_rb_set_gvl_state(char state) { in_gvl = state; }
|
97
|
-
|
98
|
-
////////////////////////////////////////////////////////////////////////////
|
99
|
-
// Calling C functions.
|
100
|
-
static inline void *iodine_rb_call_c(void *(*func)(void *), void *arg) {
|
101
|
-
if (in_gvl) {
|
102
|
-
return func(arg);
|
103
|
-
}
|
104
|
-
iodine_c_task_s task = {.type = C_TASK, .func = func, .arg = arg};
|
105
|
-
void *ret;
|
106
|
-
in_gvl = 1;
|
107
|
-
ret = rb_thread_call_with_gvl(iodine_protected_call, &task);
|
108
|
-
in_gvl = 0;
|
109
|
-
return ret;
|
110
|
-
}
|
111
|
-
|
112
|
-
static void *iodine_rb_leave_gvl(void *(*func)(void *), void *arg) {
|
113
|
-
if (!in_gvl) {
|
114
|
-
return func(arg);
|
115
|
-
}
|
116
|
-
in_gvl = 0;
|
117
|
-
void *ret = rb_thread_call_without_gvl(func, arg, NULL, NULL);
|
118
|
-
in_gvl = 1;
|
119
|
-
return ret;
|
120
|
-
}
|
121
|
-
|
122
|
-
////////////////////////////////////////////////////////////////////////////
|
123
|
-
// A heavier (memory) design for when we're passing arguments around.
|
124
|
-
|
125
|
-
// wrapping any API calls for exception management AND GVL entry
|
126
|
-
static VALUE iodin_rb_call_arg(VALUE obj, ID method, int argc, VALUE *argv) {
|
127
|
-
iodine_rb_task_s task = {.type = RUBY_TASK,
|
128
|
-
.obj = obj,
|
129
|
-
.method = method,
|
130
|
-
.argc = argc,
|
131
|
-
.argv = argv};
|
132
|
-
void *ret;
|
133
|
-
if (in_gvl) {
|
134
|
-
return (VALUE)iodine_protected_call(&task);
|
135
|
-
}
|
136
|
-
in_gvl = 1;
|
137
|
-
ret = rb_thread_call_with_gvl(iodine_protected_call, &task);
|
138
|
-
in_gvl = 0;
|
139
|
-
return (VALUE)ret;
|
140
|
-
}
|
141
|
-
|
142
|
-
// wrapping any API calls for exception management AND GVL entry
|
143
|
-
static VALUE iodin_rb_call(VALUE obj, ID method) {
|
144
|
-
return iodin_rb_call_arg(obj, method, 0, NULL);
|
145
|
-
}
|
146
|
-
|
147
|
-
////////////////////////////////////////////////////////////////////////////
|
148
|
-
// the API interface
|
149
|
-
struct _Ruby_Method_Caller_Class_ RubyCaller = {
|
150
|
-
.call = iodin_rb_call,
|
151
|
-
.call2 = iodin_rb_call_arg,
|
152
|
-
.call_c = iodine_rb_call_c,
|
153
|
-
.leave_gvl = iodine_rb_leave_gvl,
|
154
|
-
.in_gvl = iodine_rb_check_in_gvl,
|
155
|
-
.set_gvl_state = iodine_rb_set_gvl_state,
|
156
|
-
};
|
data/ext/iodine/rb-call.h
DELETED
@@ -1,70 +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
|
-
#ifndef RB_CALL_H
|
8
|
-
#define RB_CALL_H
|
9
|
-
#include <ruby.h>
|
10
|
-
|
11
|
-
#define RB_CALL_VERSION "0.2.0"
|
12
|
-
|
13
|
-
/**
|
14
|
-
The Ruby framework manages it's own contextc switching and memory...
|
15
|
-
this means that when we make calls to Ruby (i.e. creating Ruby objects),
|
16
|
-
we risk currupting the Ruby framework. Also, Ruby uses all these signals (for
|
17
|
-
it's context switchings) and long-jumps (i.e. Ruby exceptions) that can drive
|
18
|
-
the C code a little nuts...
|
19
|
-
|
20
|
-
Seperation is reqiured :-)
|
21
|
-
|
22
|
-
This is a simple helper that calls Ruby methods on Ruby objects and C
|
23
|
-
functions that require acess to the Ruby API, while managing the GVL lock
|
24
|
-
status as required.
|
25
|
-
|
26
|
-
To call a Ruby object's method, simply use:
|
27
|
-
|
28
|
-
RubyCaller.call(object, method_id);
|
29
|
-
|
30
|
-
To pass arguments to the ruby object's method, use the `call2` method. i.e.:
|
31
|
-
|
32
|
-
RubyCaller.call2(object, method_id, argc, argv);
|
33
|
-
|
34
|
-
|
35
|
-
This library keeps track of the thread, adjusting the method to be called in
|
36
|
-
case the thread is already within the GVL.
|
37
|
-
|
38
|
-
The library assums that it is within a Ruby thread that is was released of
|
39
|
-
the GVL using `rb_thread_call_without_gvl`. If this isn t the case, use the
|
40
|
-
`RubyCaller.set_gvl_state` function to manually set the GVL state assumption.
|
41
|
-
*/
|
42
|
-
extern struct _Ruby_Method_Caller_Class_ {
|
43
|
-
/** calls a Object's ruby method, adjusting for the GVL if needed */
|
44
|
-
VALUE (*call)(VALUE object, ID method_id);
|
45
|
-
/**
|
46
|
-
* calls a Object's ruby method with arguments, adjusting for the GVL if
|
47
|
-
* needed
|
48
|
-
*/
|
49
|
-
VALUE (*call2)(VALUE obj, ID method, int argc, VALUE *argv);
|
50
|
-
/**
|
51
|
-
* calls a C method that requires the GVL for access to the Ruby API, managing
|
52
|
-
* the GVL state as required.
|
53
|
-
*/
|
54
|
-
void *(*call_c)(void *(*func)(void *), void *arg);
|
55
|
-
/**
|
56
|
-
* Exits the GVL (if the thread is withing the GVL) and calls a C function.
|
57
|
-
*/
|
58
|
-
void *(*leave_gvl)(void *(*func)(void *), void *arg);
|
59
|
-
/**
|
60
|
-
* returns the thread's GVL status (1 if inside a GVL and 0 if free from the
|
61
|
-
* GVL).
|
62
|
-
*/
|
63
|
-
char (*in_gvl)(void);
|
64
|
-
/**
|
65
|
-
* forces a thread's GVL state flag (ignoring the actual GVL state) - careful!
|
66
|
-
*/
|
67
|
-
void (*set_gvl_state)(char state);
|
68
|
-
} RubyCaller;
|
69
|
-
|
70
|
-
#endif /* RB_CALL_H */
|
data/ext/iodine/rb-defer.c
DELETED
@@ -1,124 +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
|
-
// clang-format off
|
8
|
-
#include "rb-registry.h"
|
9
|
-
#include "rb-call.h"
|
10
|
-
#include "iodine.h"
|
11
|
-
#include <ruby.h>
|
12
|
-
#include <ruby/thread.h>
|
13
|
-
|
14
|
-
#include <stdint.h>
|
15
|
-
// clang-format on
|
16
|
-
|
17
|
-
#include <spnlock.inc>
|
18
|
-
#include "defer.h"
|
19
|
-
|
20
|
-
#include <pthread.h>
|
21
|
-
|
22
|
-
/* *****************************************************************************
|
23
|
-
Local helpers
|
24
|
-
***************************************************************************** */
|
25
|
-
/* used to create Ruby threads and pass them the information they need */
|
26
|
-
struct CreateThreadArgs {
|
27
|
-
void *(*thread_func)(void *);
|
28
|
-
void *arg;
|
29
|
-
};
|
30
|
-
|
31
|
-
/* used here but declared elsewhere */
|
32
|
-
void call_async_signal(void *pool) { defer_pool_stop((pool_pt)pool); }
|
33
|
-
|
34
|
-
static void *defer_thread_start(void *args_) {
|
35
|
-
struct CreateThreadArgs *args = args_;
|
36
|
-
void *(*thread_func)(void *) = args->thread_func;
|
37
|
-
void *arg = args->arg;
|
38
|
-
free(args);
|
39
|
-
RubyCaller.set_gvl_state(0);
|
40
|
-
thread_func(arg);
|
41
|
-
return NULL;
|
42
|
-
}
|
43
|
-
|
44
|
-
/* the thread's GVL release */
|
45
|
-
static VALUE defer_thread_inGVL(void *args_) {
|
46
|
-
struct CreateThreadArgs *args = args_;
|
47
|
-
rb_thread_call_without_gvl(defer_thread_start, args_,
|
48
|
-
(void (*)(void *))call_async_signal, args->arg);
|
49
|
-
return Qnil;
|
50
|
-
}
|
51
|
-
|
52
|
-
/* Within the GVL, creates a Ruby thread using an API call */
|
53
|
-
static void *create_ruby_thread_gvl(void *args) {
|
54
|
-
return (void *)Registry.add(rb_thread_create(defer_thread_inGVL, args));
|
55
|
-
}
|
56
|
-
|
57
|
-
/* used during fork */
|
58
|
-
void iodine_start_io_thread(void *a1, void *a2);
|
59
|
-
|
60
|
-
static void *fork_using_ruby(void *ignr) {
|
61
|
-
RubyCaller.call(Iodine, rb_intern("before_fork"));
|
62
|
-
const VALUE ProcessClass = rb_const_get(rb_cObject, rb_intern("Process"));
|
63
|
-
const VALUE rb_pid = RubyCaller.call(ProcessClass, rb_intern("fork"));
|
64
|
-
intptr_t pid = 0;
|
65
|
-
if (rb_pid != Qnil) {
|
66
|
-
pid = NUM2INT(rb_pid);
|
67
|
-
} else {
|
68
|
-
pid = 0;
|
69
|
-
}
|
70
|
-
RubyCaller.set_gvl_state(1); /* enforce GVL state in thread storage */
|
71
|
-
if (!pid) {
|
72
|
-
Registry.on_fork();
|
73
|
-
RubyCaller.call(Iodine, rb_intern("after_fork"));
|
74
|
-
iodine_start_io_thread(NULL, NULL);
|
75
|
-
}
|
76
|
-
return (void *)pid;
|
77
|
-
(void)ignr;
|
78
|
-
}
|
79
|
-
|
80
|
-
/* *****************************************************************************
|
81
|
-
The Defer library overriding functions
|
82
|
-
***************************************************************************** */
|
83
|
-
|
84
|
-
/**
|
85
|
-
OVERRIDE THIS to replace the default pthread implementation.
|
86
|
-
*/
|
87
|
-
void *defer_new_thread(void *(*thread_func)(void *), void *arg) {
|
88
|
-
struct CreateThreadArgs *data = malloc(sizeof(*data));
|
89
|
-
if (!data)
|
90
|
-
return NULL;
|
91
|
-
*data = (struct CreateThreadArgs){
|
92
|
-
.thread_func = thread_func, .arg = arg,
|
93
|
-
};
|
94
|
-
void *thr = RubyCaller.call_c(create_ruby_thread_gvl, data);
|
95
|
-
if (!thr || thr == (void *)Qnil || thr == (void *)Qfalse) {
|
96
|
-
thr = NULL;
|
97
|
-
}
|
98
|
-
return thr;
|
99
|
-
}
|
100
|
-
|
101
|
-
/**
|
102
|
-
OVERRIDE THIS to replace the default pthread implementation.
|
103
|
-
*/
|
104
|
-
int defer_join_thread(void *thr) {
|
105
|
-
if (!thr || (VALUE)thr == Qfalse || (VALUE)thr == Qnil)
|
106
|
-
return -1;
|
107
|
-
RubyCaller.call((VALUE)thr, rb_intern("join"));
|
108
|
-
Registry.remove((VALUE)thr);
|
109
|
-
return 0;
|
110
|
-
}
|
111
|
-
|
112
|
-
// void defer_free_thread(void *thr) { (void)thr; }
|
113
|
-
void defer_free_thread(void *thr) { Registry.remove((VALUE)thr); }
|
114
|
-
|
115
|
-
/**
|
116
|
-
OVERRIDE THIS to replace the default `fork` implementation or to inject hooks
|
117
|
-
into the forking function.
|
118
|
-
|
119
|
-
Behaves like the system's `fork`.
|
120
|
-
*/
|
121
|
-
int facil_fork(void) {
|
122
|
-
intptr_t pid = (intptr_t)RubyCaller.call_c(fork_using_ruby, NULL);
|
123
|
-
return (int)pid;
|
124
|
-
}
|
data/ext/iodine/rb-registry.c
DELETED
@@ -1,150 +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
|
-
#include "rb-registry.h"
|
8
|
-
#include <ruby.h>
|
9
|
-
|
10
|
-
#include "spnlock.inc"
|
11
|
-
|
12
|
-
#define FIO_OVERRIDE_MALLOC 1
|
13
|
-
#include "fio_mem.h"
|
14
|
-
|
15
|
-
#include "fio_hashmap.h"
|
16
|
-
#include <signal.h>
|
17
|
-
|
18
|
-
#ifndef RUBY_REG_DBG
|
19
|
-
#define RUBY_REG_DBG 0
|
20
|
-
#endif
|
21
|
-
|
22
|
-
// the registry state keeper
|
23
|
-
static struct {
|
24
|
-
fio_hash_s store;
|
25
|
-
VALUE owner;
|
26
|
-
spn_lock_i lock;
|
27
|
-
} registry = {.store = {.capa = 0}, .owner = 0, .lock = SPN_LOCK_INIT};
|
28
|
-
|
29
|
-
#define try_lock_registry() spn_trylock(®istry.lock)
|
30
|
-
#define unlock_registry() spn_unlock(®istry.lock)
|
31
|
-
#define lock_registry() spn_lock(®istry.lock)
|
32
|
-
|
33
|
-
/** adds an object to the registry or increases it's reference count. */
|
34
|
-
static VALUE register_object(VALUE ruby_obj) {
|
35
|
-
if (!ruby_obj || ruby_obj == Qnil || ruby_obj == Qfalse)
|
36
|
-
return 0;
|
37
|
-
lock_registry();
|
38
|
-
uintptr_t count = (uintptr_t)fio_hash_find(®istry.store, ruby_obj);
|
39
|
-
#if RUBY_REG_DBG == 1
|
40
|
-
fprintf(stderr, "Ruby Registry: register %p ref: %" PRIu64 " + 1\n",
|
41
|
-
(void *)ruby_obj, (uint64_t)count);
|
42
|
-
#endif
|
43
|
-
fio_hash_insert(®istry.store, (uint64_t)ruby_obj, (void *)(count + 1));
|
44
|
-
unlock_registry();
|
45
|
-
return ruby_obj;
|
46
|
-
}
|
47
|
-
|
48
|
-
/** decreases an object's reference count or removes if from the registry. */
|
49
|
-
static void unregister_object(VALUE ruby_obj) {
|
50
|
-
if (!ruby_obj || ruby_obj == Qnil)
|
51
|
-
return;
|
52
|
-
lock_registry();
|
53
|
-
uintptr_t count = (uintptr_t)fio_hash_find(®istry.store, ruby_obj);
|
54
|
-
#if RUBY_REG_DBG == 1
|
55
|
-
fprintf(stderr, "Ruby Registry: unregister %p ref: %" PRIu64 " - 1\n",
|
56
|
-
(void *)ruby_obj, (uint64_t)count);
|
57
|
-
#endif
|
58
|
-
if (count) {
|
59
|
-
fio_hash_insert(®istry.store, (uint64_t)ruby_obj, (void *)(count - 1));
|
60
|
-
}
|
61
|
-
unlock_registry();
|
62
|
-
}
|
63
|
-
|
64
|
-
/* a callback for the GC (marking active objects) */
|
65
|
-
static void registry_mark(void *ignore) {
|
66
|
-
(void)ignore;
|
67
|
-
#if RUBY_REG_DBG == 1
|
68
|
-
Registry.print();
|
69
|
-
#endif
|
70
|
-
lock_registry();
|
71
|
-
fio_hash_compact(®istry.store);
|
72
|
-
FIO_HASH_FOR_LOOP(®istry.store, pos) {
|
73
|
-
if (pos->obj) {
|
74
|
-
rb_gc_mark((VALUE)pos->key);
|
75
|
-
}
|
76
|
-
}
|
77
|
-
unlock_registry();
|
78
|
-
}
|
79
|
-
|
80
|
-
/* clear the registry (end of lifetime) */
|
81
|
-
static void registry_clear(void *ignore) {
|
82
|
-
(void)ignore;
|
83
|
-
#if RUBY_REG_DBG == 1
|
84
|
-
fprintf(stderr, "Ruby Registry: Clear!!!\n");
|
85
|
-
#endif
|
86
|
-
lock_registry();
|
87
|
-
fio_hash_free(®istry.store);
|
88
|
-
registry.owner = 0;
|
89
|
-
registry.store = (fio_hash_s){.capa = 0};
|
90
|
-
unlock_registry();
|
91
|
-
}
|
92
|
-
|
93
|
-
/*
|
94
|
-
the data-type used to identify the registry
|
95
|
-
this sets the callbacks.
|
96
|
-
*/
|
97
|
-
static struct rb_data_type_struct my_registry_type_struct = {
|
98
|
-
.wrap_struct_name = "RubyReferencesIn_C_Land",
|
99
|
-
.function.dfree = (void (*)(void *))registry_clear,
|
100
|
-
.function.dmark = (void (*)(void *))registry_mark,
|
101
|
-
};
|
102
|
-
|
103
|
-
/* initialize the registry */
|
104
|
-
static void init(VALUE owner) {
|
105
|
-
lock_registry();
|
106
|
-
// only one registry
|
107
|
-
if (registry.owner)
|
108
|
-
goto finish;
|
109
|
-
if (!owner)
|
110
|
-
owner = rb_cObject;
|
111
|
-
registry.owner = owner;
|
112
|
-
VALUE rReferences =
|
113
|
-
rb_define_class_under(owner, "RubyObjectRegistry_for_C_land", rb_cData);
|
114
|
-
VALUE r_registry =
|
115
|
-
TypedData_Wrap_Struct(rReferences, &my_registry_type_struct, ®istry);
|
116
|
-
rb_ivar_set(owner, rb_intern("registry"), r_registry);
|
117
|
-
finish:
|
118
|
-
unlock_registry();
|
119
|
-
}
|
120
|
-
|
121
|
-
static void registry_on_fork(void) { unlock_registry(); }
|
122
|
-
|
123
|
-
/* print data, for testing */
|
124
|
-
static void print(void) {
|
125
|
-
lock_registry();
|
126
|
-
fprintf(stderr, "Registry owner is %lu\n", registry.owner);
|
127
|
-
uintptr_t index = 0;
|
128
|
-
FIO_HASH_FOR_LOOP(®istry.store, pos) {
|
129
|
-
if (pos->obj) {
|
130
|
-
fprintf(stderr, "[%" PRIuPTR " ] => %" PRIuPTR " X obj %p type %d\n",
|
131
|
-
index++, (uintptr_t)pos->obj, (void *)pos->key, TYPE(pos->key));
|
132
|
-
}
|
133
|
-
}
|
134
|
-
fprintf(stderr, "Total of %" PRIuPTR " registered objects being marked\n",
|
135
|
-
index);
|
136
|
-
fprintf(stderr,
|
137
|
-
"Registry uses %" PRIuPTR " Hash bins for %" PRIuPTR " objects\n",
|
138
|
-
registry.store.capa, registry.store.count);
|
139
|
-
unlock_registry();
|
140
|
-
}
|
141
|
-
|
142
|
-
////////////////////////////////////////////
|
143
|
-
// The API gateway
|
144
|
-
struct ___RegistryClass___ Registry = {
|
145
|
-
.init = init,
|
146
|
-
.remove = unregister_object,
|
147
|
-
.add = register_object,
|
148
|
-
.print = print,
|
149
|
-
.on_fork = registry_on_fork,
|
150
|
-
};
|