private static void changeHandlerThread(InvocationHandler handler, Thread oldThread, Thread newThread) { if (handler instanceof ThreadConfinedProxy) { ((ThreadConfinedProxy) handler).changeThread(oldThread, newThread); } if (handler instanceof DelegatingInvocationHandler) { changeThread(((DelegatingInvocationHandler) handler).getDelegate(), oldThread, newThread); } }
/** * Wraps a callable in a new callable that assigns ownership to the thread running the callable, then passes ownership back to * the thread that called this method. */ public static <T> Callable<T> threadLendingCallable(final Object proxy, final Callable<T> callable) { if (proxy == null) { return callable; } final Thread parent = Thread.currentThread(); return () -> { Thread child = Thread.currentThread(); changeThread(proxy, parent, child); try { return callable.call(); } finally { changeThread(proxy, child, parent); } }; }
/** * Explicitly passes the given ThreadConfinedProxy to a new thread. If the proxy passed in is not a ThreadConfinedProxy, but is a * different type of proxy that also uses a {@linkplain DelegatingInvocationHandler}, this method will recursively apply to the * delegate. This means that this method can handle arbitrarily nested DelegatingInvocationHandlers, including nested * ThreadConfinedProxy objects. * */ public static void changeThread(Object proxy, Thread oldThread, Thread newThread) { Validate.notNull(proxy, "Proxy argument must not be null"); if (Proxy.isProxyClass(proxy.getClass())) { InvocationHandler handler = Proxy.getInvocationHandler(proxy); changeHandlerThread(handler, oldThread, newThread); } else if (proxy instanceof Delegator) { changeThread(((Delegator) proxy).getDelegate(), oldThread, newThread); } }
@Test public void testChildThreadCanDelegateBackToMainThread() throws InterruptedException { final AtomicReference<List<String>> inputReference = new AtomicReference<List<String>>(null); final AtomicInteger outputReference = new AtomicInteger(0); final Thread mainThread = Thread.currentThread(); Thread childThread = new Thread(() -> { outputReference.compareAndSet(0, 1); List<String> subjectInChildThread = inputReference.get(); ThreadConfinedProxy.changeThread(subjectInChildThread, mainThread, Thread.currentThread()); subjectInChildThread.add(testString); if (Iterables.getOnlyElement(subjectInChildThread).equals(testString)) { outputReference.compareAndSet(1, 2); } ThreadConfinedProxy.changeThread(subjectInChildThread, Thread.currentThread(), mainThread); }); @SuppressWarnings("unchecked") List<String> subject = ThreadConfinedProxy.newProxyInstance(List.class, new ArrayList<String>(), ThreadConfinedProxy.Strictness.VALIDATE); inputReference.set(subject); childThread.start(); childThread.join(10000); assertEquals(2, outputReference.get()); // We got delegated back, so we can use subject again assertEquals(testString, Iterables.getOnlyElement(subject)); }
outputReference.compareAndSet(0, 1); List<String> subjectInChildThread = inputReference.get(); ThreadConfinedProxy.changeThread(subjectInChildThread, mainThread, Thread.currentThread()); subjectInChildThread.add(testString); if (Iterables.getOnlyElement(subjectInChildThread).equals(testString)) { outputReference.compareAndSet(3, 4); List<String> subjectInChildThread = inputReference.get(); ThreadConfinedProxy.changeThread(subjectInChildThread, mainThread, Thread.currentThread()); outputReference.compareAndSet(4, 5); }); ThreadConfinedProxy.changeThread(subject, mainThread, otherThread); fail(); } catch (Exception e) {
outputReference.compareAndSet(0, 1); List<String> subjectInChildThread = inputReference.get(); ThreadConfinedProxy.changeThread(subjectInChildThread, mainThread, Thread.currentThread()); subjectInChildThread.add(testString); if (Iterables.getOnlyElement(subjectInChildThread).equals(testString)) { outputReference.compareAndSet(1, 2); ThreadConfinedProxy.changeThread(subjectInChildThread, Thread.currentThread(), mainThread); });
private static void changeHandlerThread(InvocationHandler handler, Thread oldThread, Thread newThread) { if (handler instanceof ThreadConfinedProxy) { ((ThreadConfinedProxy) handler).changeThread(oldThread, newThread); } if (handler instanceof DelegatingInvocationHandler) { changeThread(((DelegatingInvocationHandler) handler).getDelegate(), oldThread, newThread); } }
/** * Wraps a callable in a new callable that assigns ownership to the thread running the callable, then passes ownership back to * the thread that called this method. */ public static <T> Callable<T> threadLendingCallable(final Object proxy, final Callable<T> callable) { if (proxy == null) { return callable; } final Thread parent = Thread.currentThread(); return () -> { Thread child = Thread.currentThread(); changeThread(proxy, parent, child); try { return callable.call(); } finally { changeThread(proxy, child, parent); } }; }
/** * Explicitly passes the given ThreadConfinedProxy to a new thread. If the proxy passed in is not a ThreadConfinedProxy, but is a * different type of proxy that also uses a {@linkplain DelegatingInvocationHandler}, this method will recursively apply to the * delegate. This means that this method can handle arbitrarily nested DelegatingInvocationHandlers, including nested * ThreadConfinedProxy objects. * */ public static void changeThread(Object proxy, Thread oldThread, Thread newThread) { Validate.notNull(proxy, "Proxy argument must not be null"); if (Proxy.isProxyClass(proxy.getClass())) { InvocationHandler handler = Proxy.getInvocationHandler(proxy); changeHandlerThread(handler, oldThread, newThread); } else if (proxy instanceof Delegator) { changeThread(((Delegator) proxy).getDelegate(), oldThread, newThread); } }