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 +7 -0
- data/Dockerfile +9 -0
- data/Gemfile +13 -0
- data/README.md +128 -0
- data/Rakefile +16 -0
- data/docker-compose.yml +20 -0
- data/ext/ilios/cassandra.c +63 -0
- data/ext/ilios/extconf.rb +73 -0
- data/ext/ilios/future.c +269 -0
- data/ext/ilios/ilios.c +107 -0
- data/ext/ilios/ilios.h +105 -0
- data/ext/ilios/nogvl.c +53 -0
- data/ext/ilios/result.c +239 -0
- data/ext/ilios/session.c +145 -0
- data/ext/ilios/statement.c +211 -0
- data/ilios.gemspec +35 -0
- data/lib/ilios/version.rb +12 -0
- data/lib/ilios.rb +25 -0
- data/sig/ilios.rbs +4 -0
- metadata +79 -0
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
|
data/docker-compose.yml
ADDED
@@ -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')
|
data/ext/ilios/future.c
ADDED
@@ -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
|
+
}
|