ilios 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18d160f81c3298017c1e7936a32b3c3668414ebc1c327c59ef24c4ad741aa16a
4
+ data.tar.gz: d664a164d230db6deea79bdf8f80ad6e2225da9f31640a84868a4b6d4a91062f
5
+ SHA512:
6
+ metadata.gz: 8d3634a3a2fab8cf35f514cde6fc741f37fb4aa876d8f96cf277d18e90606c5e52682b32731ffd0347bd7666a04df7496d486a0a7861fc189b622b175b2dce0a
7
+ data.tar.gz: fd1e720c2b4dabe04b973a5db48d993188b630d6ec476bb22710062e11aed5e4fdb42245513b970f9b31508893be13dfa577d30527cb7748c5ccd1368a1c3b9f
data/Dockerfile ADDED
@@ -0,0 +1,9 @@
1
+ FROM ubuntu:22.04
2
+
3
+ RUN apt update && \
4
+ apt install -y tzdata sudo && \
5
+ apt install -y curl cmake make gcc g++ git bzip2 zlib1g-dev libgdbm-dev libreadline-dev libffi-dev libssl-dev libyaml-dev && \
6
+ git clone --depth 1 https://github.com/rbenv/ruby-build.git && \
7
+ cd ruby-build/bin && ./ruby-build 3.0.6 /usr/local
8
+
9
+ WORKDIR /opt/ilios
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in ilios.gemspec
6
+ gemspec
7
+
8
+ gem 'minitest', '~> 5.20'
9
+ gem 'rake', '~> 13.0'
10
+ gem 'rake-compiler', '~> 1.2'
11
+ gem 'rubocop', '~> 1.57'
12
+ gem 'rubocop-minitest', '~> 0.33.0'
13
+ gem 'rubocop-performance', '~> 1.19'
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Ilios
2
+
3
+ Ilios that Cassandra driver written by C language for Ruby using [DataStax C/C++ Driver](https://github.com/datastax/cpp-driver).
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```sh
10
+ $ bundle add ilios
11
+ ```
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ ```sh
16
+ $ gem install ilios
17
+ ```
18
+
19
+ ## Example
20
+ ### Basic usage
21
+
22
+ Create the keyspace in advance using the `cqlsh` command.
23
+
24
+ ```sh
25
+ CREATE KEYSPACE IF NOT EXISTS ilios
26
+ WITH REPLICATION = {
27
+ 'class' : 'SimpleStrategy',
28
+ 'replication_factor' : 1
29
+ };
30
+ ```
31
+
32
+ ```ruby
33
+ require 'ilios'
34
+
35
+ Ilios::Cassandra.config = {
36
+ keyspace: 'ilios',
37
+ hosts: ['127.0.0.1'],
38
+ }
39
+
40
+ # Create the table
41
+ statement = Ilios::Cassandra.session.prepare(<<~CQL)
42
+ CREATE TABLE IF NOT EXISTS ilios.example (
43
+ id bigint,
44
+ message text,
45
+ created_at timestamp,
46
+ PRIMARY KEY (id)
47
+ ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }
48
+ AND gc_grace_seconds = 691200;
49
+ CQL
50
+ Ilios::Cassandra.session.execute(statement)
51
+
52
+ # Insert the records
53
+ statement = Ilios::Cassandra.session.prepare(<<~CQL)
54
+ INSERT INTO ilios.example (
55
+ id,
56
+ message,
57
+ created_at
58
+ ) VALUES (?, ?, ?)
59
+ CQL
60
+
61
+ 100.times do |i|
62
+ statement.bind({
63
+ id: i,
64
+ message: 'Hello World',
65
+ created_at: Time.now,
66
+ })
67
+ Ilios::Cassandra.session.execute(statement)
68
+ end
69
+
70
+ # Select the records
71
+ statement = Ilios::Cassandra.session.prepare(<<~CQL)
72
+ SELECT * FROM ilios.example
73
+ CQL
74
+ statement.page_size = 25
75
+ result = Ilios::Cassandra.session.execute(statement)
76
+ result.each do |row|
77
+ p row
78
+ end
79
+
80
+ while(result.next_page)
81
+ result.each do |row|
82
+ p row
83
+ end
84
+ end
85
+ ```
86
+
87
+ ### Synchronous API
88
+ `Ilios::Cassandra::Session#prepare` and `Ilios::Cassandra::Session#execute` are provided as synchronous API.
89
+
90
+ ```ruby
91
+ statement = Ilios::Cassandra.session.prepare(<<~CQL)
92
+ SELECT * FROM ilios.example
93
+ CQL
94
+ result = Ilios::Cassandra.session.execute(statement)
95
+ ```
96
+
97
+ ### Asynchronous API
98
+ `Ilios::Cassandra::Session#prepare_async` and `Ilios::Cassandra::Session#execute_async` are provided as asynchronous API.
99
+
100
+ ```ruby
101
+ prepare_future = Ilios::Cassandra.session.prepare_async(<<~CQL)
102
+ INSERT INTO ilios.example (
103
+ id,
104
+ message,
105
+ created_at
106
+ ) VALUES (?, ?, ?)
107
+ CQL
108
+
109
+ prepare_future.on_success { |statement|
110
+ statement.bind({
111
+ id: 1,
112
+ message: 'Hello World',
113
+ created_at: Time.now,
114
+ })
115
+ result_future = Ilios::Cassandra.session.execute_async(statement)
116
+ result_future.on_success { |result|
117
+ p result
118
+ p "success"
119
+ }
120
+ result_future.on_failure {
121
+ p "fail"
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Contributing
127
+
128
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Watson1978/ilios.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/extensiontask'
5
+ require 'rake/testtask'
6
+
7
+ task test: :compile
8
+ task default: :test
9
+
10
+ Rake::ExtensionTask.new('ilios') do |ext|
11
+ ext.ext_dir = 'ext/ilios'
12
+ end
13
+
14
+ Rake::TestTask.new do |task|
15
+ task.pattern = 'test/test_*.rb'
16
+ end
@@ -0,0 +1,20 @@
1
+ version: '3'
2
+ services:
3
+ cassandra:
4
+ image: cassandra:4
5
+ ports:
6
+ - 9042:9042
7
+ volumes:
8
+ - cassandra-data:/var/lib/cassandra
9
+ ilios:
10
+ build:
11
+ context: .
12
+ dockerfile: Dockerfile
13
+ depends_on:
14
+ - cassandra
15
+ command: bash -c 'sleep infinity'
16
+ volumes:
17
+ - "./:/opt/ilios"
18
+ volumes:
19
+ cassandra-data:
20
+ driver: local
@@ -0,0 +1,63 @@
1
+ #include "ilios.h"
2
+
3
+ static VALUE cassandra_connect(VALUE self)
4
+ {
5
+ CassandraSession *cassandra_session;
6
+ VALUE cassandra_session_obj;
7
+ VALUE config;
8
+ VALUE hosts;
9
+ VALUE keyspace;
10
+ char last_error[4096] = { 0 };
11
+
12
+ cassandra_session_obj = CREATE_SESSION(cassandra_session);
13
+
14
+ cassandra_session->cluster = cass_cluster_new();
15
+ cass_cluster_set_protocol_version(cassandra_session->cluster, CASS_PROTOCOL_VERSION_V4);
16
+
17
+ config = rb_cvar_get(self, id_cvar_config);
18
+ cass_cluster_set_request_timeout(cassandra_session->cluster, NUM2UINT(rb_hash_aref(config, sym_timeout_ms)));
19
+ cass_cluster_set_constant_speculative_execution_policy(cassandra_session->cluster, NUM2LONG(rb_hash_aref(config, sym_constant_delay_ms)), NUM2INT(rb_hash_aref(config, sym_max_speculative_executions)));
20
+
21
+ keyspace = rb_hash_aref(config, sym_keyspace);
22
+ hosts = rb_hash_aref(config, sym_hosts);
23
+
24
+ Check_Type(hosts, T_ARRAY);
25
+ if (RARRAY_LEN(hosts) == 0) {
26
+ rb_raise(rb_eRuntimeError, "No hosts configured");
27
+ }
28
+ if (RARRAY_LEN(hosts) > 1) {
29
+ // To distribute connections
30
+ hosts = rb_funcall(hosts, id_shuffle, 0);
31
+ }
32
+
33
+ for (int i = 0; i < RARRAY_LEN(hosts); i++) {
34
+ VALUE host = RARRAY_AREF(hosts, i);
35
+
36
+ cass_cluster_set_contact_points(cassandra_session->cluster, ""); // Clear previous contact points
37
+ cass_cluster_set_contact_points(cassandra_session->cluster, StringValueCStr(host));
38
+ cassandra_session->session = cass_session_new();
39
+ cassandra_session->connect_future = cass_session_connect_keyspace(cassandra_session->session, cassandra_session->cluster, StringValueCStr(keyspace));
40
+ nogvl_future_wait(cassandra_session->connect_future);
41
+
42
+ if (cass_future_error_code(cassandra_session->connect_future) == CASS_OK) {
43
+ return cassandra_session_obj;
44
+ }
45
+
46
+ strncpy(last_error, cass_error_desc(cass_future_error_code(cassandra_session->connect_future)), sizeof(last_error) - 1);
47
+ cass_future_free(cassandra_session->connect_future);
48
+ cass_session_free(cassandra_session->session);
49
+ cassandra_session->connect_future = NULL;
50
+ cassandra_session->session = NULL;
51
+ }
52
+
53
+ cass_cluster_free(cassandra_session->cluster);
54
+ cassandra_session->cluster = NULL;
55
+
56
+ rb_raise(eConnectError, "Unable to connect: %s", last_error);
57
+ return Qnil;
58
+ }
59
+
60
+ void Init_cassandra(void)
61
+ {
62
+ rb_define_module_function(mCassandra, "connect", cassandra_connect, 0);
63
+ }
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('../../lib/ilios/version', __dir__)
4
+ require 'fileutils'
5
+ require 'mini_portile2'
6
+ require 'mkmf'
7
+
8
+ have_func('malloc_usable_size')
9
+ have_func('malloc_size')
10
+
11
+ CASSANDRA_CPP_DRIVER_INSTALL_PATH = File.expand_path('cpp-driver')
12
+ LIBUV_INSTALL_PATH = File.expand_path('libuv')
13
+
14
+ unless find_executable('cmake')
15
+ puts '--------------------------------------------------'
16
+ puts 'Error: cmake is required to build this gem'
17
+ puts '--------------------------------------------------'
18
+ raise
19
+ end
20
+
21
+ unless File.exist?(LIBUV_INSTALL_PATH)
22
+ class LibuvRecipe < MiniPortileCMake
23
+ def configure_prefix
24
+ "-DCMAKE_INSTALL_PREFIX=#{LIBUV_INSTALL_PATH}"
25
+ end
26
+ end
27
+
28
+ libuv_recipe = LibuvRecipe.new('libuv', Ilios::LIBUV_VERSION, make_command: 'make -j 3')
29
+ libuv_recipe.files << {
30
+ url: "https://github.com/libuv/libuv/archive/v#{Ilios::LIBUV_VERSION}.tar.gz"
31
+ }
32
+ libuv_recipe.cook
33
+ if RUBY_PLATFORM.include?('darwin')
34
+ unless find_executable('install_name_tool')
35
+ puts '------------------------------------------------------'
36
+ puts 'Error: install_name_tool is required to build this gem'
37
+ puts '------------------------------------------------------'
38
+ raise
39
+ end
40
+ xsystem("install_name_tool -id #{LIBUV_INSTALL_PATH}/lib/libuv.1.dylib #{LIBUV_INSTALL_PATH}/lib/libuv.1.dylib")
41
+ end
42
+ end
43
+
44
+ unless File.exist?(CASSANDRA_CPP_DRIVER_INSTALL_PATH)
45
+ class CassandraRecipe < MiniPortileCMake
46
+ def initialize(name, version, **kwargs)
47
+ ENV['LIBUV_ROOT_DIR'] = LIBUV_INSTALL_PATH
48
+ super(name, version, **kwargs)
49
+ end
50
+
51
+ def configure_prefix
52
+ "-DCMAKE_INSTALL_PREFIX=#{CASSANDRA_CPP_DRIVER_INSTALL_PATH}"
53
+ end
54
+ end
55
+
56
+ cassandra_recipe = CassandraRecipe.new('cpp-driver', Ilios::CASSANDRA_CPP_DRIVER_VERSION, make_command: 'make -j 3')
57
+ cassandra_recipe.files << {
58
+ url: "https://github.com/datastax/cpp-driver/archive/#{Ilios::CASSANDRA_CPP_DRIVER_VERSION}.tar.gz"
59
+ }
60
+ cassandra_recipe.cook
61
+ if RUBY_PLATFORM.include?('darwin')
62
+ xsystem("install_name_tool -id #{CASSANDRA_CPP_DRIVER_INSTALL_PATH}/lib/libcassandra.2.dylib #{CASSANDRA_CPP_DRIVER_INSTALL_PATH}/lib/libcassandra.2.dylib")
63
+ end
64
+ end
65
+
66
+ FileUtils.rm_rf('ports')
67
+ FileUtils.rm_rf('tmp')
68
+
69
+ $CPPFLAGS += " -I#{CASSANDRA_CPP_DRIVER_INSTALL_PATH}/include -I#{LIBUV_INSTALL_PATH}/include"
70
+ $LDFLAGS += " -L#{CASSANDRA_CPP_DRIVER_INSTALL_PATH}/lib -Wl,-rpath,#{CASSANDRA_CPP_DRIVER_INSTALL_PATH}/lib -lcassandra"
71
+ $LDFLAGS += " -L#{LIBUV_INSTALL_PATH}/lib -Wl,-rpath,#{LIBUV_INSTALL_PATH}/lib -luv"
72
+
73
+ create_makefile('ilios')
@@ -0,0 +1,269 @@
1
+ #include "ilios.h"
2
+
3
+ #define THREAD_MAX 5
4
+
5
+ typedef struct
6
+ {
7
+ VALUE thread[THREAD_MAX];
8
+ VALUE queue;
9
+ } future_thread_pool;
10
+
11
+ static future_thread_pool thread_pool_prepare;
12
+ static future_thread_pool thread_pool_execute;
13
+
14
+ static VALUE future_result_yielder_thread(void *arg);
15
+ static void future_mark(void *ptr);
16
+ static void future_destroy(void *ptr);
17
+ static size_t future_memsize(const void *ptr);
18
+ static void future_compact(void *ptr);
19
+
20
+ const rb_data_type_t cassandra_future_data_type = {
21
+ "Ilios::Cassandra::Future",
22
+ {
23
+ future_mark,
24
+ future_destroy,
25
+ future_memsize,
26
+ future_compact,
27
+ },
28
+ 0, 0,
29
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
30
+ };
31
+
32
+ static void future_thread_pool_init(future_thread_pool *pool)
33
+ {
34
+ pool->queue = rb_funcall(cQueue, id_new, 0);
35
+ for (int i = 0; i < THREAD_MAX; i++) {
36
+ pool->thread[i] = rb_thread_create(future_result_yielder_thread, (void*)pool);
37
+ }
38
+ }
39
+
40
+ static inline future_thread_pool *future_thread_pool_get(CassandraFuture *cassandra_future)
41
+ {
42
+ future_thread_pool *pool = NULL;
43
+
44
+ switch (cassandra_future->kind) {
45
+ case prepare_async:
46
+ pool = &thread_pool_prepare;
47
+ break;
48
+ case execute_async:
49
+ pool = &thread_pool_execute;
50
+ break;
51
+ }
52
+ return pool;
53
+ }
54
+
55
+ static inline void future_queue_push(future_thread_pool *pool, VALUE future)
56
+ {
57
+ rb_funcall(pool->queue, id_push, 1, future);
58
+ }
59
+
60
+ static inline VALUE future_queue_pop(future_thread_pool *pool)
61
+ {
62
+ return rb_funcall(pool->queue, id_pop, 0);
63
+ }
64
+
65
+ static void future_result_success_yield(CassandraFuture *cassandra_future)
66
+ {
67
+ VALUE obj;
68
+
69
+ if (cassandra_future->on_success_block) {
70
+ if (rb_proc_arity(cassandra_future->on_success_block)) {
71
+ switch (cassandra_future->kind) {
72
+ case prepare_async:
73
+ {
74
+ CassandraStatement *cassandra_statement;
75
+ VALUE cassandra_statement_obj;
76
+
77
+ cassandra_statement_obj = CREATE_STATEMENT(cassandra_statement);
78
+ cassandra_statement->prepared = cass_future_get_prepared(cassandra_future->future);
79
+ cassandra_statement->statement = cass_prepared_bind(cassandra_statement->prepared);
80
+ cassandra_statement->session_obj = cassandra_future->session_obj;
81
+
82
+ statement_default_config(cassandra_statement);
83
+
84
+ obj = cassandra_statement_obj;
85
+ }
86
+ break;
87
+ case execute_async:
88
+ {
89
+ CassandraResult *cassandra_result;
90
+ VALUE cassandra_result_obj;
91
+
92
+ cassandra_result_obj = CREATE_RESULT(cassandra_result);
93
+ cassandra_result->result = cass_future_get_result(cassandra_future->future);
94
+ cassandra_result->statement_obj = cassandra_future->statement_obj;
95
+
96
+ obj = cassandra_result_obj;
97
+ }
98
+ break;
99
+ }
100
+
101
+ rb_proc_call_with_block(cassandra_future->on_success_block, 1, &obj, Qnil);
102
+ } else {
103
+ rb_proc_call_with_block(cassandra_future->on_success_block, 0, NULL, Qnil);
104
+ }
105
+ }
106
+ }
107
+
108
+ static void future_result_failure_yield(CassandraFuture *cassandra_future)
109
+ {
110
+ if (cassandra_future->on_failure_block) {
111
+ rb_proc_call_with_block(cassandra_future->on_failure_block, 0, NULL, Qnil);
112
+ }
113
+ }
114
+
115
+ static VALUE future_result_yielder_synchronize(VALUE arg)
116
+ {
117
+ CassandraFuture *cassandra_future;
118
+
119
+ GET_FUTURE(arg, cassandra_future);
120
+
121
+ if (cass_future_error_code(cassandra_future->future) == CASS_OK) {
122
+ future_result_success_yield(cassandra_future);
123
+ } else {
124
+ future_result_failure_yield(cassandra_future);
125
+ }
126
+ return Qnil;
127
+ }
128
+
129
+ static VALUE future_result_yielder(VALUE arg)
130
+ {
131
+ CassandraFuture *cassandra_future;
132
+
133
+ GET_FUTURE(arg, cassandra_future);
134
+
135
+ nogvl_future_wait(cassandra_future->future);
136
+ return rb_mutex_synchronize(cassandra_future->proc_mutex, future_result_yielder_synchronize, arg);
137
+ }
138
+
139
+ static VALUE future_result_yielder_thread(void *arg)
140
+ {
141
+ future_thread_pool *pool = (future_thread_pool *)arg;
142
+
143
+ while (1) {
144
+ VALUE future = future_queue_pop(pool);
145
+ future_result_yielder(future);
146
+ }
147
+ return Qnil;
148
+ }
149
+
150
+ static VALUE future_on_success(VALUE self)
151
+ {
152
+ CassandraFuture *cassandra_future;
153
+ bool wakeup_thread = false;
154
+
155
+ GET_FUTURE(self, cassandra_future);
156
+
157
+ if (cassandra_future->on_success_block) {
158
+ rb_raise(eExecutionError, "It should not call twice");
159
+ }
160
+
161
+ if (rb_block_given_p()) {
162
+ rb_mutex_lock(cassandra_future->proc_mutex);
163
+
164
+ if (!cassandra_future->on_failure_block) {
165
+ // Invoke the callback with thread pool only once
166
+ wakeup_thread = true;
167
+ }
168
+
169
+ cassandra_future->on_success_block = rb_block_proc();
170
+
171
+ if (cass_future_ready(cassandra_future->future)) {
172
+ rb_mutex_unlock(cassandra_future->proc_mutex);
173
+ if (cass_future_error_code(cassandra_future->future) == CASS_OK) {
174
+ future_result_success_yield(cassandra_future);
175
+ }
176
+ return self;
177
+ }
178
+
179
+ if (wakeup_thread) {
180
+ future_queue_push(future_thread_pool_get(cassandra_future), self);
181
+ }
182
+ rb_mutex_unlock(cassandra_future->proc_mutex);
183
+ }
184
+ return self;
185
+ }
186
+
187
+ static VALUE future_on_failure(VALUE self)
188
+ {
189
+ CassandraFuture *cassandra_future;
190
+ bool wakeup_thread = false;
191
+
192
+ GET_FUTURE(self, cassandra_future);
193
+
194
+ if (cassandra_future->on_failure_block) {
195
+ rb_raise(eExecutionError, "It should not call twice");
196
+ }
197
+
198
+ if (rb_block_given_p()) {
199
+ rb_mutex_lock(cassandra_future->proc_mutex);
200
+
201
+ if (!cassandra_future->on_success_block) {
202
+ // Invoke the callback with thread pool only once
203
+ wakeup_thread = true;
204
+ }
205
+
206
+ cassandra_future->on_failure_block = rb_block_proc();
207
+
208
+ if (cass_future_ready(cassandra_future->future)) {
209
+ rb_mutex_unlock(cassandra_future->proc_mutex);
210
+ if (cass_future_error_code(cassandra_future->future) != CASS_OK) {
211
+ future_result_failure_yield(cassandra_future);
212
+ }
213
+ return self;
214
+ }
215
+
216
+ if (wakeup_thread) {
217
+ future_queue_push(future_thread_pool_get(cassandra_future), self);
218
+ }
219
+ rb_mutex_unlock(cassandra_future->proc_mutex);
220
+ }
221
+ return self;
222
+ }
223
+
224
+ static void future_mark(void *ptr)
225
+ {
226
+ CassandraFuture *cassandra_future = (CassandraFuture *)ptr;
227
+ rb_gc_mark_movable(cassandra_future->session_obj);
228
+ rb_gc_mark_movable(cassandra_future->statement_obj);
229
+ rb_gc_mark_movable(cassandra_future->on_success_block);
230
+ rb_gc_mark_movable(cassandra_future->on_failure_block);
231
+ rb_gc_mark_movable(cassandra_future->proc_mutex);
232
+ }
233
+
234
+ static void future_destroy(void *ptr)
235
+ {
236
+ CassandraFuture *cassandra_future = (CassandraFuture *)ptr;
237
+
238
+ if (cassandra_future->future) {
239
+ cass_future_free(cassandra_future->future);
240
+ }
241
+ xfree(cassandra_future);
242
+ }
243
+
244
+ static size_t future_memsize(const void *ptr)
245
+ {
246
+ return sizeof(CassandraFuture);
247
+ }
248
+
249
+ static void future_compact(void *ptr)
250
+ {
251
+ CassandraFuture *cassandra_future = (CassandraFuture *)ptr;
252
+
253
+ cassandra_future->session_obj = rb_gc_location(cassandra_future->session_obj);
254
+ cassandra_future->statement_obj = rb_gc_location(cassandra_future->statement_obj);
255
+ cassandra_future->on_success_block = rb_gc_location(cassandra_future->on_success_block);
256
+ cassandra_future->on_failure_block = rb_gc_location(cassandra_future->on_failure_block);
257
+ cassandra_future->proc_mutex = rb_gc_location(cassandra_future->proc_mutex);
258
+ }
259
+
260
+ void Init_future(void)
261
+ {
262
+ rb_undef_alloc_func(cFuture);
263
+
264
+ rb_define_method(cFuture, "on_success", future_on_success, 0);
265
+ rb_define_method(cFuture, "on_failure", future_on_failure, 0);
266
+
267
+ future_thread_pool_init(&thread_pool_prepare);
268
+ future_thread_pool_init(&thread_pool_execute);
269
+ }