|
28 | 28 | import java.nio.ByteBuffer;
|
29 | 29 | import static java.nio.ByteBuffer.allocateDirect;
|
30 | 30 | import static java.nio.charset.StandardCharsets.UTF_8;
|
| 31 | +import java.util.ArrayList; |
31 | 32 | import static java.util.Collections.nCopies;
|
32 | 33 | import java.util.Comparator;
|
33 | 34 | import java.util.List;
|
34 | 35 | import java.util.Random;
|
| 36 | +import java.util.concurrent.ExecutionException; |
| 37 | +import java.util.concurrent.ExecutorService; |
| 38 | +import java.util.concurrent.Executors; |
| 39 | +import java.util.concurrent.Future; |
| 40 | +import static java.util.concurrent.TimeUnit.SECONDS; |
| 41 | +import java.util.concurrent.TimeoutException; |
| 42 | +import java.util.concurrent.atomic.AtomicBoolean; |
| 43 | +import static java.util.stream.Collectors.toList; |
| 44 | +import static java.util.stream.IntStream.range; |
35 | 45 | import org.agrona.concurrent.UnsafeBuffer;
|
36 | 46 | import static org.hamcrest.CoreMatchers.is;
|
37 | 47 | import static org.hamcrest.CoreMatchers.not;
|
38 | 48 | import static org.hamcrest.CoreMatchers.notNullValue;
|
39 | 49 | import static org.hamcrest.CoreMatchers.nullValue;
|
40 | 50 | import static org.hamcrest.MatcherAssert.assertThat;
|
| 51 | +import org.hamcrest.Matchers; |
41 | 52 | import static org.hamcrest.Matchers.hasSize;
|
42 | 53 | import static org.hamcrest.collection.IsEmptyCollection.empty;
|
43 | 54 | import org.junit.After;
|
|
47 | 58 | import org.junit.Rule;
|
48 | 59 | import org.junit.Test;
|
49 | 60 | import org.junit.rules.TemporaryFolder;
|
| 61 | +import static org.lmdbjava.ByteBufferProxy.PROXY_OPTIMAL; |
50 | 62 | import org.lmdbjava.Dbi.DbFullException;
|
51 | 63 | import static org.lmdbjava.DbiFlags.MDB_CREATE;
|
52 | 64 | import static org.lmdbjava.DbiFlags.MDB_DUPSORT;
|
@@ -81,7 +93,7 @@ public void before() throws IOException {
|
81 | 93 | final File path = tmp.newFile();
|
82 | 94 | env = create()
|
83 | 95 | .setMapSize(MEBIBYTES.toBytes(64))
|
84 |
| - .setMaxReaders(1) |
| 96 | + .setMaxReaders(2) |
85 | 97 | .setMaxDbs(2)
|
86 | 98 | .open(path, MDB_NOSUBDIR);
|
87 | 99 | }
|
@@ -127,6 +139,51 @@ public void dbOpenMaxDatabases() {
|
127 | 139 | env.openDbi("db3 fails", MDB_CREATE);
|
128 | 140 | }
|
129 | 141 |
|
| 142 | + @Test |
| 143 | + public void dbiWithComparatorThreadSafety() { |
| 144 | + final Dbi<ByteBuffer> db = env.openDbi(DB_1, PROXY_OPTIMAL::compare, |
| 145 | + MDB_CREATE); |
| 146 | + |
| 147 | + final List<Integer> keys = range(0, 1000).boxed().collect(toList()); |
| 148 | + |
| 149 | + final ExecutorService pool = Executors.newCachedThreadPool(); |
| 150 | + final AtomicBoolean proceed = new AtomicBoolean(true); |
| 151 | + final Future<?> reader = pool.submit(() -> { |
| 152 | + while (proceed.get()) { |
| 153 | + try (Txn<ByteBuffer> txn = env.txnRead()) { |
| 154 | + db.get(txn, bb(50)); |
| 155 | + } |
| 156 | + } |
| 157 | + }); |
| 158 | + |
| 159 | + for (final Integer key : keys) { |
| 160 | + try (Txn<ByteBuffer> txn = env.txnWrite()) { |
| 161 | + db.put(txn, bb(key), bb(3)); |
| 162 | + txn.commit(); |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + try (Txn<ByteBuffer> txn = env.txnRead()) { |
| 167 | + final CursorIterator<ByteBuffer> iter = db.iterate(txn); |
| 168 | + |
| 169 | + final List<Integer> result = new ArrayList<>(); |
| 170 | + while (iter.hasNext()) { |
| 171 | + result.add(iter.next().key().getInt()); |
| 172 | + } |
| 173 | + |
| 174 | + assertThat(result, Matchers.contains(keys.toArray(new Integer[0]))); |
| 175 | + } |
| 176 | + |
| 177 | + proceed.set(false); |
| 178 | + try { |
| 179 | + reader.get(1, SECONDS); |
| 180 | + pool.shutdown(); |
| 181 | + pool.awaitTermination(1, SECONDS); |
| 182 | + } catch (ExecutionException | InterruptedException | TimeoutException e) { |
| 183 | + throw new IllegalStateException(e); |
| 184 | + } |
| 185 | + } |
| 186 | + |
130 | 187 | @Test
|
131 | 188 | public void drop() {
|
132 | 189 | final Dbi<ByteBuffer> db = env.openDbi(DB_1, MDB_CREATE);
|
|
0 commit comments