26
26
27
27
import java .util .ArrayList ;
28
28
import java .util .Collection ;
29
+ import java .util .Collections ;
29
30
import java .util .HashMap ;
30
31
import java .util .HashSet ;
31
32
import java .util .Iterator ;
32
33
import java .util .List ;
33
34
import java .util .Map ;
34
35
import java .util .Set ;
36
+ import java .util .concurrent .Executor ;
35
37
36
38
/**
37
39
* Execute a set of entry actions in parallel to leverage bulk I/O. The basic idea is to
@@ -51,14 +53,21 @@ public abstract class BulkAction<K, V, R> implements
51
53
AsyncBulkCacheLoader .BulkCallback <K , V >,
52
54
EntryAction .CompletedCallback <K , V , R > {
53
55
56
+ /** Used for executors **/
57
+ private HeapCache heapCache ;
58
+ /**
59
+ * Map with the individual entry actions, the map will not be modified after the
60
+ * bulk operation start, so its save to read from it from different threads.
61
+ */
54
62
private final Map <K , EntryAction <K , V , R >> key2action ;
55
63
private final Collection <EntryAction <K , V , R >> toStart ;
56
64
private final Set <K > toLoad ;
57
65
private final AsyncCacheLoader <K , V > loader ;
58
66
private int completedCount = 0 ;
59
67
60
68
/** Create object and start operation. */
61
- public BulkAction (AsyncCacheLoader <K , V > loader , Set <K > keys , boolean sync ) {
69
+ public BulkAction (HeapCache heapCache , AsyncCacheLoader <K , V > loader , Set <K > keys , boolean sync ) {
70
+ this .heapCache = heapCache ;
62
71
this .loader = loader ;
63
72
key2action = new HashMap <>(keys .size ());
64
73
toLoad = new HashSet <>(keys .size ());
@@ -204,15 +213,15 @@ private void startLoadingSingle() {
204
213
205
214
/**
206
215
* Start loading via calling the async bulk loader. The keys are removed from
207
- * toLoad in the callback to ourselves, we use toLoad additionally to keep track
208
- * of partial completions
216
+ * toLoad in the callback to ourselves since we use toLoad additionally to keep track
217
+ * of partial completions. We need to copy the keys, since we modify the toLoad set
218
+ * if we get partial completions.
209
219
*/
210
220
private void startLoadingBulk () {
211
221
AsyncBulkCacheLoader <K , V > bulkLoader = (AsyncBulkCacheLoader <K , V >) loader ;
212
- Set <Context <K , V >> contextSet = new HashSet <>(toLoad .size ());
213
- for (K key : toLoad ) { contextSet .add (key2action .get (key )); }
222
+ Set <K > keysCopy = Collections .unmodifiableSet (new HashSet <K >(toLoad ));
214
223
try {
215
- bulkLoader .loadAll (toLoad , contextSet , this );
224
+ bulkLoader .loadAll (keysCopy , new MyBulkLoadContext ( keysCopy ) , this );
216
225
} catch (Throwable ouch ) {
217
226
onLoadFailure (ouch );
218
227
}
@@ -311,4 +320,48 @@ protected void bulkOperationCompleted() { }
311
320
312
321
protected abstract EntryAction <K , V , R > createEntryAction (K key , BulkAction <K , V , R > self );
313
322
323
+ private class MyBulkLoadContext implements AsyncBulkCacheLoader .BulkLoadContext <K , V > {
324
+
325
+ private final Set <K > keys ;
326
+ private Set <Context <K , V >> contextSet ;
327
+
328
+ MyBulkLoadContext (Set <K > keys ) {
329
+ this .keys = keys ;
330
+ }
331
+
332
+ /** Lazily create set with entry contexts */
333
+ @ Override
334
+ public Set <Context <K , V >> getContextSet () {
335
+ if (contextSet == null ) {
336
+ contextSet = new HashSet <>(toLoad .size ());
337
+ for (K key : keys ) {
338
+ contextSet .add (key2action .get (key ));
339
+ }
340
+ }
341
+ return contextSet ;
342
+ }
343
+
344
+ @ Override
345
+ public long getStartTime () {
346
+ K firstKey = keys .iterator ().next ();
347
+ return key2action .get (firstKey ).getStartTime ();
348
+ }
349
+
350
+ @ Override
351
+ public Set <K > getKeys () {
352
+ return keys ;
353
+ }
354
+
355
+ @ Override
356
+ public Executor getExecutor () {
357
+ return heapCache .getExecutor ();
358
+ }
359
+
360
+ @ Override
361
+ public Executor getLoaderExecutor () {
362
+ return heapCache .getLoaderExecutor ();
363
+ }
364
+
365
+ }
366
+
314
367
}
0 commit comments