lmdb 0.4.1 → 0.4.2

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.
@@ -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