Skip to content

Commit 2132503

Browse files
author
Philip Stutz
committed
Cursors get their own key and value buffers
This allows to concurrently use the same read transaction with multiple cursors (related to conversation in lmdbjava#35).
1 parent 2b2f239 commit 2132503

File tree

5 files changed

+107
-60
lines changed

5 files changed

+107
-60
lines changed

src/main/java/org/lmdbjava/Cursor.java

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
import static java.util.Objects.requireNonNull;
2424
import jnr.ffi.Pointer;
2525
import jnr.ffi.byref.NativeLongByReference;
26+
import jnr.ffi.provider.MemoryManager;
27+
import static org.lmdbjava.BufferProxy.MDB_VAL_STRUCT_SIZE;
2628
import static org.lmdbjava.Dbi.KeyNotFoundException.MDB_NOTFOUND;
2729
import static org.lmdbjava.Env.SHOULD_CHECK;
2830
import static org.lmdbjava.Library.LIB;
31+
import static org.lmdbjava.Library.RUNTIME;
2932
import static org.lmdbjava.MaskedFlag.mask;
3033
import static org.lmdbjava.PutFlags.MDB_RESERVE;
3134
import static org.lmdbjava.ResultCodeMapper.checkRc;
@@ -41,15 +44,62 @@
4144
*/
4245
public final class Cursor<T> implements AutoCloseable {
4346

47+
private static final MemoryManager MEM_MGR = RUNTIME.getMemoryManager();
4448
private boolean closed;
4549
private final Pointer ptrCursor;
4650
private Txn<T> txn;
4751

52+
private T k;
53+
private final BufferProxy<T> proxy;
54+
private final Pointer ptrKey;
55+
private final long ptrKeyAddr;
56+
private final Pointer ptrVal;
57+
private final long ptrValAddr;
58+
private T v;
59+
4860
Cursor(final Pointer ptr, final Txn<T> txn) {
4961
requireNonNull(ptr);
5062
requireNonNull(txn);
5163
this.ptrCursor = ptr;
5264
this.txn = txn;
65+
66+
this.proxy = txn.proxy;
67+
this.k = proxy.allocate();
68+
this.v = proxy.allocate();
69+
ptrKey = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
70+
ptrKeyAddr = ptrKey.address();
71+
ptrVal = MEM_MGR.allocateTemporary(MDB_VAL_STRUCT_SIZE, false);
72+
ptrValAddr = ptrVal.address();
73+
}
74+
75+
void keyIn(final T key) {
76+
proxy.in(key, ptrKey, ptrKeyAddr);
77+
}
78+
79+
T keyOut() {
80+
k = proxy.out(k, ptrKey, ptrKeyAddr);
81+
return k;
82+
}
83+
84+
Pointer pointerKey() {
85+
return ptrKey;
86+
}
87+
88+
Pointer pointerVal() {
89+
return ptrVal;
90+
}
91+
92+
void valIn(final T val) {
93+
proxy.in(val, ptrVal, ptrValAddr);
94+
}
95+
96+
void valIn(final int size) {
97+
proxy.in(v, size, ptrVal, ptrValAddr);
98+
}
99+
100+
T valOut() {
101+
v = proxy.out(v, ptrVal, ptrValAddr);
102+
return v;
53103
}
54104

55105
/**
@@ -68,6 +118,8 @@ public void close() {
68118
txn.checkReady();
69119
}
70120
LIB.mdb_cursor_close(ptrCursor);
121+
proxy.deallocate(k);
122+
proxy.deallocate(v);
71123
closed = true;
72124
}
73125

@@ -131,26 +183,25 @@ public boolean get(final T key, final GetOp op) {
131183
checkNotClosed();
132184
txn.checkReady();
133185
}
134-
txn.keyIn(key);
186+
keyIn(key);
135187

136-
final int rc = LIB.mdb_cursor_get(ptrCursor, txn.pointerKey(), txn.
137-
pointerVal(), op.getCode());
188+
final int rc = LIB.mdb_cursor_get(ptrCursor, pointerKey(), pointerVal(), op.getCode());
138189

139190
if (rc == MDB_NOTFOUND) {
140191
return false;
141192
}
142193

143194
checkRc(rc);
144-
txn.keyOut();
145-
txn.valOut();
195+
keyOut();
196+
valOut();
146197
return true;
147198
}
148199

149200
/**
150201
* @return the key that the cursor is located at.
151202
*/
152203
public T key() {
153-
return txn.key();
204+
return k;
154205
}
155206

156207
/**
@@ -199,13 +250,12 @@ public void put(final T key, final T val, final PutFlags... op) {
199250
txn.checkReady();
200251
txn.checkWritesAllowed();
201252
}
202-
txn.keyIn(key);
203-
txn.valIn(val);
253+
keyIn(key);
254+
valIn(val);
204255
final int flags = mask(op);
205-
checkRc(LIB.mdb_cursor_put(ptrCursor, txn.pointerKey(), txn.pointerVal(),
206-
flags));
207-
txn.keyOut();
208-
txn.valOut();
256+
checkRc(LIB.mdb_cursor_put(ptrCursor, pointerKey(), pointerVal(), flags));
257+
keyOut();
258+
valOut();
209259
}
210260

211261
/**
@@ -256,13 +306,12 @@ public T reserve(final T key, final int size, final PutFlags... op) {
256306
txn.checkReady();
257307
txn.checkWritesAllowed();
258308
}
259-
txn.keyIn(key);
260-
txn.valIn(size);
309+
keyIn(key);
310+
valIn(size);
261311
final int flags = mask(op) | MDB_RESERVE.getMask();
262-
checkRc(LIB.mdb_cursor_put(ptrCursor, txn.pointerKey(), txn.pointerVal(),
263-
flags));
264-
txn.valOut();
265-
return txn.val();
312+
checkRc(LIB.mdb_cursor_put(ptrCursor, pointerKey(), pointerVal(), flags));
313+
valOut();
314+
return val();
266315
}
267316

268317
/**
@@ -278,25 +327,23 @@ public boolean seek(final SeekOp op) {
278327
txn.checkReady();
279328
}
280329

281-
final int rc = LIB.mdb_cursor_get(ptrCursor, txn.pointerKey(), txn.
282-
pointerVal(),
283-
op.getCode());
330+
final int rc = LIB.mdb_cursor_get(ptrCursor, pointerKey(), pointerVal(), op.getCode());
284331

285332
if (rc == MDB_NOTFOUND) {
286333
return false;
287334
}
288335

289336
checkRc(rc);
290-
txn.keyOut();
291-
txn.valOut();
337+
keyOut();
338+
valOut();
292339
return true;
293340
}
294341

295342
/**
296343
* @return the value that the cursor is located at.
297344
*/
298345
public T val() {
299-
return txn.val();
346+
return v;
300347
}
301348

302349
private void checkNotClosed() throws ClosedException {

src/main/java/org/lmdbjava/Txn.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@
4444
public final class Txn<T> implements AutoCloseable {
4545

4646
private static final MemoryManager MEM_MGR = RUNTIME.getMemoryManager();
47+
final BufferProxy<T> proxy;
4748
private T k;
4849
private final Txn<T> parent;
49-
private final BufferProxy<T> proxy;
5050
private final Pointer ptr;
5151
private final Pointer ptrKey;
5252
private final long ptrKeyAddr;

src/test/java/org/lmdbjava/CursorParamTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,33 +121,33 @@ public final void execute(final TemporaryFolder tmp) {
121121
// check MDB_SET operations
122122
final T key3 = set(3);
123123
assertThat(c.get(key3, MDB_SET_KEY), is(true));
124-
assertThat(get(txn.key()), is(3));
125-
assertThat(get(txn.val()), is(4));
124+
assertThat(get(c.key()), is(3));
125+
assertThat(get(c.val()), is(4));
126126
final T key6 = set(6);
127127
assertThat(c.get(key6, MDB_SET_RANGE), is(true));
128-
assertThat(get(txn.key()), is(7));
128+
assertThat(get(c.key()), is(7));
129129
if (!(this instanceof ByteArrayRunner)) {
130-
assertThat(get(txn.val()), is(8));
130+
assertThat(get(c.val()), is(8));
131131
}
132132
final T key999 = set(999);
133133
assertThat(c.get(key999, MDB_SET_KEY), is(false));
134134

135135
// check MDB navigation operations
136136
assertThat(c.seek(MDB_LAST), is(true));
137-
final int mdb1 = get(txn.key());
138-
final int mdb2 = get(txn.val());
137+
final int mdb1 = get(c.key());
138+
final int mdb2 = get(c.val());
139139

140140
assertThat(c.seek(MDB_PREV), is(true));
141-
final int mdb3 = get(txn.key());
142-
final int mdb4 = get(txn.val());
141+
final int mdb3 = get(c.key());
142+
final int mdb4 = get(c.val());
143143

144144
assertThat(c.seek(MDB_NEXT), is(true));
145-
final int mdb5 = get(txn.key());
146-
final int mdb6 = get(txn.val());
145+
final int mdb5 = get(c.key());
146+
final int mdb6 = get(c.val());
147147

148148
assertThat(c.seek(MDB_FIRST), is(true));
149-
final int mdb7 = get(txn.key());
150-
final int mdb8 = get(txn.val());
149+
final int mdb7 = get(c.key());
150+
final int mdb8 = get(c.val());
151151

152152
// assert afterwards to ensure memory address from LMDB
153153
// are valid within same txn and across cursor movement

src/test/java/org/lmdbjava/CursorTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -131,21 +131,21 @@ public void cursorFirstLastNextPrev() {
131131
c.put(bb(7), bb(8));
132132

133133
assertThat(c.first(), is(true));
134-
assertThat(txn.key().getInt(0), is(1));
135-
assertThat(txn.val().getInt(0), is(2));
134+
assertThat(c.key().getInt(0), is(1));
135+
assertThat(c.val().getInt(0), is(2));
136136

137137
assertThat(c.last(), is(true));
138-
assertThat(txn.key().getInt(0), is(7));
139-
assertThat(txn.val().getInt(0), is(8));
138+
assertThat(c.key().getInt(0), is(7));
139+
assertThat(c.val().getInt(0), is(8));
140140

141141
assertThat(c.prev(), is(true));
142-
assertThat(txn.key().getInt(0), is(5));
143-
assertThat(txn.val().getInt(0), is(6));
142+
assertThat(c.key().getInt(0), is(5));
143+
assertThat(c.val().getInt(0), is(6));
144144

145145
assertThat(c.first(), is(true));
146146
assertThat(c.next(), is(true));
147-
assertThat(txn.key().getInt(0), is(3));
148-
assertThat(txn.val().getInt(0), is(4));
147+
assertThat(c.key().getInt(0), is(3));
148+
assertThat(c.val().getInt(0), is(4));
149149
}
150150
}
151151

@@ -157,12 +157,12 @@ public void delete() {
157157
c.put(bb(1), bb(2), MDB_NOOVERWRITE);
158158
c.put(bb(3), bb(4));
159159
assertThat(c.seek(MDB_FIRST), is(true));
160-
assertThat(txn.key().getInt(), is(1));
161-
assertThat(txn.val().getInt(), is(2));
160+
assertThat(c.key().getInt(), is(1));
161+
assertThat(c.val().getInt(), is(2));
162162
c.delete();
163163
assertThat(c.seek(MDB_FIRST), is(true));
164-
assertThat(txn.key().getInt(), is(3));
165-
assertThat(txn.val().getInt(), is(4));
164+
assertThat(c.key().getInt(), is(3));
165+
assertThat(c.val().getInt(), is(4));
166166
c.delete();
167167
assertThat(c.seek(MDB_FIRST), is(false));
168168
}

src/test/java/org/lmdbjava/TutorialTest.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -252,17 +252,17 @@ public void tutorial3() throws IOException {
252252

253253
// We can read from the Cursor by key.
254254
c.get(key, MDB_SET);
255-
assertThat(UTF_8.decode(txn.key()).toString(), is("ccc"));
255+
assertThat(UTF_8.decode(c.key()).toString(), is("ccc"));
256256

257257
// Let's see that LMDB provides the keys in appropriate order....
258258
c.seek(MDB_FIRST);
259-
assertThat(UTF_8.decode(txn.key()).toString(), is("aaa"));
259+
assertThat(UTF_8.decode(c.key()).toString(), is("aaa"));
260260

261261
c.seek(MDB_LAST);
262-
assertThat(UTF_8.decode(txn.key()).toString(), is("zzz"));
262+
assertThat(UTF_8.decode(c.key()).toString(), is("zzz"));
263263

264264
c.seek(MDB_PREV);
265-
assertThat(UTF_8.decode(txn.key()).toString(), is("ccc"));
265+
assertThat(UTF_8.decode(c.key()).toString(), is("ccc"));
266266

267267
// Cursors can also delete the current key.
268268
c.delete();
@@ -388,13 +388,13 @@ public void tutorial5() throws IOException {
388388

389389
// Let's position the Cursor. Note sorting still works.
390390
c.seek(MDB_FIRST);
391-
assertThat(UTF_8.decode(txn.val()).toString(), is("kkk"));
391+
assertThat(UTF_8.decode(c.val()).toString(), is("kkk"));
392392

393393
c.seek(MDB_LAST);
394-
assertThat(UTF_8.decode(txn.val()).toString(), is("xxx"));
394+
assertThat(UTF_8.decode(c.val()).toString(), is("xxx"));
395395

396396
c.seek(MDB_PREV);
397-
assertThat(UTF_8.decode(txn.val()).toString(), is("lll"));
397+
assertThat(UTF_8.decode(c.val()).toString(), is("lll"));
398398

399399
c.close();
400400
txn.commit();
@@ -436,11 +436,11 @@ public void tutorial6() throws IOException {
436436
c.put(key, val);
437437

438438
c.seek(MDB_FIRST);
439-
assertThat(txn.key().getStringWithoutLengthUtf8(0, env.getMaxKeySize()),
439+
assertThat(c.key().getStringWithoutLengthUtf8(0, env.getMaxKeySize()),
440440
startsWith("ggg"));
441441

442442
c.seek(MDB_LAST);
443-
assertThat(txn.key().getStringWithoutLengthUtf8(0, env.getMaxKeySize()),
443+
assertThat(c.key().getStringWithoutLengthUtf8(0, env.getMaxKeySize()),
444444
startsWith("yyy"));
445445

446446
// DirectBuffer has no notion of a position. Often you don't want to store
@@ -454,8 +454,8 @@ public void tutorial6() throws IOException {
454454
assertThat(key.capacity(), is(keyLen));
455455
c.put(key, val);
456456
c.seek(MDB_FIRST);
457-
assertThat(txn.key().capacity(), is(keyLen));
458-
assertThat(txn.key().getStringWithoutLengthUtf8(0, txn.key().capacity()),
457+
assertThat(c.key().capacity(), is(keyLen));
458+
assertThat(c.key().getStringWithoutLengthUtf8(0, c.key().capacity()),
459459
is("12characters"));
460460

461461
// If we want to store bigger values again, just wrap our original buffer.

0 commit comments

Comments
 (0)