lmdb 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,7 +20,6 @@
20
20
  #include <stdlib.h>
21
21
  #include <errno.h>
22
22
  #include <sys/types.h>
23
- #include <assert.h>
24
23
  #include "midl.h"
25
24
 
26
25
  /** @defgroup internal MDB Internals
@@ -150,7 +149,7 @@ int mdb_midl_need( MDB_IDL *idp, unsigned num )
150
149
  num = (num + num/4 + (256 + 2)) & -256;
151
150
  if (!(ids = realloc(ids-1, num * sizeof(MDB_ID))))
152
151
  return ENOMEM;
153
- *ids++ = num -= 2;
152
+ *ids++ = num - 2;
154
153
  *idp = ids;
155
154
  }
156
155
  return 0;
@@ -306,7 +305,6 @@ int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
306
305
  unsigned x, i;
307
306
 
308
307
  x = mdb_mid2l_search( ids, id->mid );
309
- assert( x > 0 );
310
308
 
311
309
  if( x < 1 ) {
312
310
  /* internal error */
@@ -1,4 +1,5 @@
1
1
  #include "lmdb_ext.h"
2
+ #include "ruby/thread.h"
2
3
 
3
4
  static void check(int code) {
4
5
  if (!code)
@@ -110,6 +111,13 @@ static void transaction_finish(VALUE self, int commit) {
110
111
  else
111
112
  mdb_txn_abort(transaction->txn);
112
113
 
114
+ long i;
115
+ for (i=0; i<RARRAY_LEN(transaction->cursors); i++) {
116
+ VALUE cursor = RARRAY_AREF(transaction->cursors, i);
117
+ cursor_close(cursor);
118
+ }
119
+ rb_ary_clear(transaction->cursors);
120
+
113
121
  // Mark child transactions as closed
114
122
  p = environment_active_txn(transaction->env);
115
123
  while (p != self) {
@@ -141,11 +149,58 @@ static VALUE call_with_transaction(VALUE venv, VALUE self, const char* name, int
141
149
  return with_transaction(venv, call_with_transaction_helper, (VALUE)&arg, flags);
142
150
  }
143
151
 
152
+ static void *call_txn_begin(void *arg) {
153
+ TxnArgs *txn_args = arg;
154
+ txn_args->result = mdb_txn_begin(txn_args->env,
155
+ txn_args->parent, txn_args->flags, txn_args->htxn);
156
+ return (void *)NULL;
157
+ }
158
+
159
+ static void stop_txn_begin(void *arg)
160
+ {
161
+ TxnArgs *txn_args = arg;
162
+ // There's no way to stop waiting for mutex:
163
+ // http://www.cognitus.net/faq/pthread/pthreadSemiFAQ_6.html
164
+ // However, we can (and must) release the mutex as soon as we get it:
165
+ txn_args->stop = 1;
166
+ }
167
+
144
168
  static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flags) {
145
169
  ENVIRONMENT(venv, environment);
146
170
 
147
171
  MDB_txn* txn;
148
- check(mdb_txn_begin(environment->env, active_txn(venv), flags, &txn));
172
+ TxnArgs txn_args;
173
+
174
+ retry:
175
+ txn = NULL;
176
+
177
+ txn_args.env = environment->env;
178
+ txn_args.parent = active_txn(venv);
179
+ txn_args.flags = flags;
180
+ txn_args.htxn = &txn;
181
+ txn_args.result = 0;
182
+ txn_args.stop = 0;
183
+
184
+ if (flags & MDB_RDONLY) {
185
+ call_txn_begin(&txn_args);
186
+ }
187
+ else {
188
+ rb_thread_call_without_gvl2(
189
+ call_txn_begin, &txn_args,
190
+ stop_txn_begin, &txn_args);
191
+
192
+ if (txn_args.stop || !txn) {
193
+ // !txn is when rb_thread_call_without_gvl2
194
+ // returns before calling txn_begin
195
+ if (txn) {
196
+ mdb_txn_abort(txn);
197
+ }
198
+ rb_thread_check_ints();
199
+ goto retry; // in what cases do we get here?
200
+ }
201
+ }
202
+
203
+ check(txn_args.result);
149
204
 
150
205
  Transaction* transaction;
151
206
  VALUE vtxn = Data_Make_Struct(cTransaction, Transaction, transaction_mark, transaction_free, transaction);
@@ -153,6 +208,7 @@ static VALUE with_transaction(VALUE venv, VALUE(*fn)(VALUE), VALUE arg, int flag
153
208
  transaction->env = venv;
154
209
  transaction->txn = txn;
155
210
  transaction->thread = rb_thread_current();
211
+ transaction->cursors = rb_ary_new();
156
212
  environment_set_active_txn(venv, transaction->thread, vtxn);
157
213
 
158
214
  int exception;
@@ -855,10 +911,13 @@ static VALUE cursor_close(VALUE self) {
855
911
 
856
912
  /**
857
913
  * @overload cursor
858
- * Create a cursor to iterate through a database.
914
+ * Create a cursor to iterate through a database. Uses current
915
+ * transaction, if any. Otherwise, if called with a block,
916
+ * creates a new transaction for the scope of the block.
917
+ * Otherwise, fails.
859
918
  *
860
919
  * @see Cursor
861
- * @yield [cursor] A block to be executed with the cursor
920
+ * @yield [cursor] A block to be executed with the cursor.
862
921
  * @yieldparam cursor [Cursor] The cursor to be used to iterate
863
922
  * @example
864
923
  * db = env.database "abc"
@@ -869,8 +928,12 @@ static VALUE cursor_close(VALUE self) {
869
928
  */
870
929
  static VALUE database_cursor(VALUE self) {
871
930
  DATABASE(self, database);
872
- if (!active_txn(database->env))
931
+ if (!active_txn(database->env)) {
932
+ if (!rb_block_given_p()) {
933
+ rb_raise(cError, "Must call with block or active transaction.");
934
+ }
873
935
  return call_with_transaction(database->env, self, "cursor", 0, 0, 0);
936
+ }
874
937
 
875
938
  MDB_cursor* cur;
876
939
  check(mdb_cursor_open(need_txn(database->env), database->dbi, &cur));
@@ -890,6 +953,16 @@ static VALUE database_cursor(VALUE self) {
890
953
  cursor_close(vcur);
891
954
  return ret;
892
955
  }
956
+ else {
957
+ VALUE vtxn = environment_active_txn(database->env);
958
+ if (NIL_P(vtxn)) {
959
+ rb_fatal("Internal error: transaction finished unexpectedly.");
960
+ }
961
+ else {
962
+ TRANSACTION(vtxn, txn);
963
+ rb_ary_push(txn->cursors, vcur);
964
+ }
965
+ }
893
966
 
894
967
  return vcur;
895
968
  }
@@ -24,6 +24,11 @@
24
24
  # endif
25
25
  #endif
26
26
 
27
+ // Ruby 2.0 compatibility
28
+ #ifndef RARRAY_AREF
29
+ # define RARRAY_AREF(ary,n) (RARRAY_PTR(ary)[n])
30
+ #endif
31
+
27
32
  #define ENVIRONMENT(var, var_env) \
28
33
  Environment* var_env; \
29
34
  Data_Get_Struct(var, Environment, var_env); \
@@ -46,6 +51,7 @@ typedef struct {
46
51
  VALUE env;
47
52
  VALUE parent;
48
53
  VALUE thread;
54
+ VALUE cursors;
49
55
  MDB_txn* txn;
50
56
  } Transaction;
51
57
 
@@ -80,6 +86,15 @@ typedef struct {
80
86
  size_t mapsize;
81
87
  } EnvironmentOptions;
82
88
 
89
+ typedef struct {
90
+ MDB_env *env;
91
+ MDB_txn *parent;
92
+ unsigned int flags;
93
+ MDB_txn **htxn;
94
+ int result;
95
+ int stop;
96
+ } TxnArgs;
97
+
83
98
  static VALUE cEnvironment, cDatabase, cTransaction, cCursor, cError;
84
99
 
85
100
  #define ERROR(name) static VALUE cError_##name;
@@ -1,3 +1,3 @@
1
1
  module LMDB
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.2'
3
3
  end
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.test_files = `git ls-files -- spec/*`.split("\n")
20
20
  s.require_paths = ['lib']
21
21
 
22
- s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'rake', "~> 10.0"
23
23
  s.add_development_dependency 'rake-compiler', '<=0.8.2'
24
- s.add_development_dependency 'rspec'
24
+ s.add_development_dependency 'rspec', "~> 3.0"
25
25
  end
@@ -24,5 +24,7 @@ end
24
24
  RSpec.configure do |c|
25
25
  c.include LMDB::SpecHelper
26
26
  c.after { FileUtils.rm_rf TEMP_ROOT }
27
+ c.expect_with :rspec do |cc|
28
+ cc.syntax = :should
29
+ end
27
30
  end
28
-
@@ -25,20 +25,20 @@ describe LMDB do
25
25
  describe 'new' do
26
26
  it 'returns environment' do
27
27
  env = LMDB::Environment.new(path)
28
- env.should be_instance_of(described_class::Environment)
28
+ env.should be_instance_of(described_class)
29
29
  env.close
30
30
  end
31
31
 
32
32
  it 'accepts block' do
33
33
  LMDB::Environment.new(path) do |env|
34
- env.should be_instance_of(described_class::Environment)
34
+ env.should be_instance_of(described_class)
35
35
  42
36
36
  end.should == 42
37
37
  end
38
38
 
39
39
  it 'accepts options' do
40
40
  env = LMDB::Environment.new(path, :nosync => true, :mode => 0777, :maxreaders => 777, :mapsize => 111111, :maxdbs => 666)
41
- env.should be_instance_of(described_class::Environment)
41
+ env.should be_instance_of(described_class)
42
42
  env.info[:maxreaders].should == 777
43
43
  env.info[:mapsize].should == 111111
44
44
  env.flags.should include(:nosync)
@@ -93,14 +93,14 @@ describe LMDB do
93
93
  subject.flags.should_not include(:nosync)
94
94
  end
95
95
 
96
- describe 'transaction' do
96
+ describe LMDB::Transaction do
97
97
  subject { env}
98
98
 
99
99
  it 'should create transactions' do
100
100
  subject.active_txn.should == nil
101
101
  subject.transaction do |txn|
102
102
  subject.active_txn.should == txn
103
- txn.should be_instance_of(described_class::Transaction)
103
+ txn.should be_instance_of(described_class)
104
104
  txn.abort
105
105
  subject.active_txn.should == nil
106
106
  end
@@ -111,7 +111,7 @@ describe LMDB do
111
111
  subject.active_txn.should == nil
112
112
  subject.transaction(true) do |txn|
113
113
  subject.active_txn.should == txn
114
- txn.should be_instance_of(described_class::Transaction)
114
+ txn.should be_instance_of(described_class)
115
115
  txn.abort
116
116
  subject.active_txn.should == nil
117
117
  end
@@ -287,5 +287,15 @@ describe LMDB do
287
287
  c.set_range('\x00').should == ['key1', 'value1']
288
288
  end
289
289
  end
290
+
291
+ it 'should raise without block or txn' do
292
+ proc { db.cursor.next }.should raise_error(LMDB::Error)
293
+ end
294
+
295
+ it 'should raise outside txn' do
296
+ c = nil
297
+ env.transaction { c = db.cursor }
298
+ proc { c.next }.should raise_error(LMDB::Error)
299
+ end
290
300
  end
291
301
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lmdb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-08 00:00:00.000000000 Z
11
+ date: 2014-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '10.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '10.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake-compiler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -42,16 +42,16 @@ dependencies:
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.0'
55
55
  description: lmdb is a Ruby binding to OpenLDAP Lightning MDB.
56
56
  email: mail@daniel-mendler.de
57
57
  executables: []
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  version: '0'
112
112
  requirements: []
113
113
  rubyforge_project:
114
- rubygems_version: 2.2.0
114
+ rubygems_version: 2.4.1
115
115
  signing_key:
116
116
  specification_version: 4
117
117
  summary: Ruby bindings to Lightning MDB