ilios 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }