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