Skip to content

Commit 0a47bc0

Browse files
committed
Minor simplification of the pending Future<?> system.
1 parent 99a7f9e commit 0a47bc0

4 files changed

Lines changed: 42 additions & 177 deletions

File tree

src/main/java/com/googlecode/objectify/cache/Pending.java

Lines changed: 0 additions & 115 deletions
This file was deleted.
Lines changed: 34 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,63 @@
11
package com.googlecode.objectify.cache;
22

3+
import java.util.concurrent.ConcurrentHashMap;
34
import java.util.concurrent.Future;
5+
import java.util.logging.Level;
6+
import java.util.logging.Logger;
47

58
/**
6-
* <p>This bit of appengine magic hooks into the ApiProxy and does the heavy lifting of
7-
* making the TriggerFuture<?> work.</p>
8-
*
99
* <p>This class maintains a thread local list of all the outstanding Future<?> objects
1010
* that have pending triggers. When a Future<?> is done and its trigger is executed,
11-
* it is removed from the list. At various times (anytime an API call is made) the registered
12-
* futures are checked for doneness and processed.</p>
13-
*
14-
* <p>The AsyncCacheFilter is necessary to guarantee that any pending triggers are processed
15-
* at the end of the request. A future GAE SDK which allows us to hook into the Future<?>
16-
* creation process might make this extra Filter unnecessary.</p>
11+
* it is removed from the list.</p>
1712
*
1813
* @author Jeff Schnitzer <jeff@infohazard.org>
1914
*/
2015
public class PendingFutures
2116
{
22-
/** The thread local value will be removed (null) if there are none pending */
23-
private static ThreadLocal<Pending> pending = new ThreadLocal<>();
24-
17+
/** */
18+
private static final Logger log = Logger.getLogger(PendingFutures.class.getName());
19+
20+
/**
21+
* We use ConcurrentHashMap not for concurrency but because it doesn't throw
22+
* ConcurrentModificationException. We need to be able to iterate while Futures remove
23+
* themselves from the set. A Set is just a Map of key to key.
24+
*/
25+
private static ThreadLocal<ConcurrentHashMap<Future<?>, Future<?>>> pending = new ThreadLocal<ConcurrentHashMap<Future<?>, Future<?>>>() {
26+
@Override
27+
protected ConcurrentHashMap<Future<?>, Future<?>> initialValue() {
28+
return new ConcurrentHashMap<>(64, 0.75f, 1);
29+
}
30+
};
2531

2632
/**
2733
* Register a pending Future that has a callback.
2834
* @param future must have at least one callback
2935
*/
30-
public static void addPending(Future<?> future)
31-
{
32-
Pending pend = pending.get();
33-
if (pend == null)
34-
{
35-
pend = new Pending();
36-
pending.set(pend);
37-
}
38-
39-
pend.add(future);
36+
public static void addPending(Future<?> future) {
37+
pending.get().put(future, future);
4038
}
4139

4240
/**
4341
* Deregister a pending Future that had a callback.
4442
*/
45-
public static void removePending(Future<?> future)
46-
{
47-
Pending pend = pending.get();
48-
if (pend != null)
49-
{
50-
pend.remove(future);
51-
52-
// When the last one is gone, we don't need this thread local anymore
53-
if (pend.isEmpty())
54-
pending.remove();
55-
}
43+
public static void removePending(Future<?> future) {
44+
pending.get().remove(future);
5645
}
5746

58-
// /**
59-
// * If any futures are pending, check if they are done. This will process their callbacks.
60-
// * Don't use this method - see comments on Pending.checkPendingFutures()
61-
// */
62-
// @Deprecated
63-
// public static void checkPendingFutures()
64-
// {
65-
// Pending pend = pending.get();
66-
// if (pend != null)
67-
// pend.checkPendingFutures();
68-
// }
69-
7047
/**
7148
* Iterate through all pending futures and get() them, forcing any callbacks to be called.
72-
* This is used only by the AsyncCacheFilter because we don't have a proper hook otherwise.
49+
* This is used only by the AsyncCacheFilter (if using cache without Objectify) or ObjectifyFilter
50+
* (if using Objectify normally) because we don't have a proper hook otherwise.
7351
*/
74-
public static void completeAllPendingFutures()
75-
{
76-
Pending pend = pending.get();
77-
if (pend != null)
78-
pend.completeAllPendingFutures();
52+
public static void completeAllPendingFutures() {
53+
// This will cause done Futures to fire callbacks and remove themselves
54+
for (Future<?> fut: pending.get().keySet()) {
55+
try {
56+
fut.get();
57+
}
58+
catch (Exception e) {
59+
log.log(Level.SEVERE, "Error cleaning up pending Future: " + fut, e);
60+
}
61+
}
7962
}
8063
}

src/main/java/com/googlecode/objectify/cache/TriggerFuture.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
*
2626
* <p>Use the AsyncCacheFilter for normal requests. For situations where a filter is not appropriate
2727
* (ie, the remote api) be sure to call PendingFutures.completeAllPendingFutures() manually.</p>
28+
*
29+
* <p>Note that if you are using this with Objectify, you probably want to use ObjectifyFilter.complete()
30+
* rather than PendingFutures or AsyncCacheFilter static methods.</p>
2831
*
2932
* @author Jeff Schnitzer <jeff@infohazard.org>
3033
*/
@@ -37,8 +40,7 @@ abstract public class TriggerFuture<T> implements Future<T>
3740
boolean triggered = false;
3841

3942
/** Wrap a normal Future<?> */
40-
public TriggerFuture(Future<T> raw)
41-
{
43+
public TriggerFuture(Future<T> raw) {
4244
this.raw = raw;
4345

4446
// We now need to register ourself so that we'll get checked at future API calls
@@ -55,9 +57,7 @@ public TriggerFuture(Future<T> raw)
5557
* @see java.util.concurrent.Future#cancel(boolean)
5658
*/
5759
@Override
58-
public boolean cancel(boolean mayInterruptIfRunning)
59-
{
60-
//return this.raw.cancel(mayInterruptIfRunning);
60+
public boolean cancel(boolean mayInterruptIfRunning) {
6161
throw new UnsupportedOperationException("This makes my head spin. Don't do it.");
6262
}
6363

@@ -77,12 +77,10 @@ public boolean isCancelled()
7777
* @see java.util.concurrent.Future#isDone()
7878
*/
7979
@Override
80-
public boolean isDone()
81-
{
80+
public boolean isDone() {
8281
boolean done = this.raw.isDone();
8382

84-
if (!triggered && done)
85-
{
83+
if (!triggered && done) {
8684
this.triggered = true;
8785
PendingFutures.removePending(this);
8886

src/main/java/com/googlecode/objectify/cache/TriggerSuccessFuture.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ public TriggerSuccessFuture(Future<T> raw)
3232
/* (non-Javadoc)
3333
* @see com.googlecode.objectify.cache.NotifyFuture#execute()
3434
*/
35-
protected final void trigger()
36-
{
35+
protected final void trigger() {
3736
try {
3837
this.success(this.get());
3938
} catch (Exception ex) {

0 commit comments

Comments
 (0)