├── .gitignore ├── .mvn-compat └── src │ └── main │ └── java │ └── com │ └── stumbleupon │ └── async ├── libs └── slf4j-api-1.6.0.jar ├── THANKS ├── AUTHORS ├── INSTALL ├── README ├── COPYING ├── src ├── CallbackOverflowError.java ├── TimeoutException.java ├── DeferredGroupException.java ├── Callback.java ├── DeferredGroup.java └── Deferred.java ├── pom.xml.in ├── Makefile └── NEWS /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | pom.xml 3 | target/ 4 | -------------------------------------------------------------------------------- /.mvn-compat/src/main/java/com/stumbleupon/async: -------------------------------------------------------------------------------- 1 | ../../../../../../src -------------------------------------------------------------------------------- /libs/slf4j-api-1.6.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenTSDB/async/HEAD/libs/slf4j-api-1.6.0.jar -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | StumbleUpon Async THANKS file 2 | 3 | The following persons contributed to the library by reporting problems, 4 | suggesting improvements, submitting patches or proofreading the code. 5 | 6 | Michael Stack 7 | Nick Telford 8 | Venkata Deepankar Duvvuru (dvdreddy) 9 | Viral Bajaria 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | The SUAsync Authors 2 | ------------------- 3 | 4 | SUAsync was originally written by Benoit Sigoure. 5 | The following organizations and people have contributed code to SUAsync. 6 | (Please keep both lists sorted alphabetically.) 7 | 8 | 9 | StumbleUpon, Inc. 10 | 11 | 12 | Benoit Sigoure 13 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Installation Instructions 2 | ************************* 3 | 4 | Copyright (C) 2010 StumbleUpon, Inc. 5 | 6 | This file is free documentation; StumbleUpon, Inc gives 7 | unlimited permission to copy, distribute and modify it. 8 | 9 | Basic Installation 10 | ================== 11 | 12 | Just type `make', and everything should work as long as you have Java 13 | and GNU make installed in your PATH. On some BSD systems, you may have 14 | to type `gmake' instead. 15 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ,-----------------------------. 2 | | StumbleUpon's Async Library | 3 | `-----------------------------' 4 | 5 | This Java library provides some useful building blocks to build 6 | high-performance multi-threaded asynchronous applications in Java. 7 | Its implementation was inspired by Twisted's asynchronous library 8 | (twisted.internet.defer). 9 | 10 | Deferred allows you to easily build asynchronous processing chains 11 | that must trigger when an asynchronous event (I/O, RPC and whatnot) 12 | completes. It can be used extensively to build an asynchronous API 13 | in a multi-threaded server or client library. 14 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | - Redistributions of source code must retain the above copyright notice, 6 | this list of conditions and the following disclaimer. 7 | - Redistributions in binary form must reproduce the above copyright notice, 8 | this list of conditions and the following disclaimer in the documentation 9 | and/or other materials provided with the distribution. 10 | - Neither the name of the StumbleUpon nor the names of its contributors 11 | may be used to endorse or promote products derived from this software 12 | without specific prior written permission. 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /src/CallbackOverflowError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | /** 29 | * Exception raised when too many callbacks are chained together. 30 | *

31 | * You are not encouraged to catch this exception. That is why it 32 | * inherits from {@link StackOverflowError}, because it generally 33 | * indicates a programming error that would have led to an actual 34 | * stack overflow had the code been written with sequential code 35 | * (without callbacks). 36 | * @since 1.3 37 | */ 38 | public final class CallbackOverflowError extends StackOverflowError { 39 | 40 | public CallbackOverflowError(final String msg) { 41 | super(msg); 42 | } 43 | 44 | private static final long serialVersionUID = 1350030042; 45 | } 46 | -------------------------------------------------------------------------------- /src/TimeoutException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | // This file only exists because java.util.concurrent.TimeoutException 29 | // is a checked exception and checked exceptions are fucking retarded. 30 | 31 | /** 32 | * Exception thrown when there's a timeout. 33 | * @since 1.1 34 | */ 35 | public final class TimeoutException extends RuntimeException { 36 | 37 | /** 38 | * Package-private constructor. 39 | * @param d The Deferred on which we timed out. 40 | * @param timeout The original timeout in milliseconds. 41 | */ 42 | TimeoutException(final Deferred d, final long timeout) { 43 | super("Timed out after " + timeout + "ms when joining " + d); 44 | } 45 | 46 | private static final long serialVersionUID = 1316924542; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/DeferredGroupException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | import java.util.ArrayList; 29 | 30 | /** 31 | * Exception used when one the {@link Deferred}s in a group failed. 32 | *

33 | * You can create a group of {@link Deferred}s using {@link Deferred#group}. 34 | */ 35 | public final class DeferredGroupException extends RuntimeException { 36 | 37 | private final ArrayList results; 38 | 39 | /** 40 | * Constructor. 41 | * @param results All the results of the {@link DeferredGroup}. 42 | * @param first The first exception among those results. 43 | */ 44 | DeferredGroupException(final ArrayList results, 45 | final Exception first) { 46 | super("At least one of the Deferreds failed, first exception:", first); 47 | this.results = results; 48 | } 49 | 50 | /** 51 | * Returns all the results of the group of {@link Deferred}s. 52 | */ 53 | public ArrayList results() { 54 | return results; 55 | } 56 | 57 | private static final long serialVersionUID = 1281980542; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/Callback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | /** 29 | * A simple 1-argument callback interface. 30 | *

31 | * Callbacks are typically created as anonymous classes. In order to make 32 | * debugging easier, it is recommended to override the {@link Object#toString} 33 | * method so that it returns a string that briefly explains what the callback 34 | * does. If you use {@link Deferred} with {@code DEBUG} logging turned on, 35 | * this will be really useful to understand what's in the callback chains. 36 | * @param The return type of the callback. 37 | * @param The argument type of the callback. 38 | */ 39 | public interface Callback { 40 | 41 | /** 42 | * The callback. 43 | * @param arg The argument to the callback. 44 | * @return The return value of the callback. 45 | * @throws Exception any exception. 46 | */ 47 | public R call(T arg) throws Exception; 48 | 49 | /** The identity function (returns its argument). */ 50 | public static final Callback PASSTHROUGH = 51 | new Callback() { 52 | public Object call(final Object arg) { 53 | return arg; 54 | } 55 | public String toString() { 56 | return "passthrough"; 57 | } 58 | }; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /pom.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | com.stumbleupon 5 | async 6 | @spec_version@ 7 | @spec_title@ 8 | 9 | @spec_vendor@ 10 | http://www.stumbleupon.com 11 | 12 | 13 | Building blocks for high-performance multi-threaded asynchronous 14 | applications. Inspired by Twisted's async library (twisted.internet.defer). 15 | 16 | https://github.com/stumbleupon/async 17 | 18 | 19 | BSD 20 | http://www.opensource.org/licenses/BSD-3-Clause 21 | repo 22 | 23 | 24 | 25 | scm:git:git@github.com:stumbleupon/async.git 26 | https://github.com/stumbleupon/async 27 | 28 | 29 | GitHub 30 | http://github.com/stumbleupon/async/issues 31 | 32 | 33 | 34 | tsuna 35 | Benoit "tsuna" Sigoure 36 | tsunanet@gmail.com 37 | 38 | developer 39 | 40 | -8 41 | 42 | 43 | 2010 44 | 45 | jar 46 | 47 | 48 | .mvn-compat/src/main/java 49 | su${project.artifactId}-${project.version} 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 2.3.2 56 | 57 | -Xlint 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-source-plugin 63 | 2.1.2 64 | 65 | 66 | attach-sources 67 | 68 | jar 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-javadoc-plugin 76 | 2.8.1 77 | 78 | 79 | attach-javadocs 80 | 81 | jar 82 | 83 | 84 | 85 | 86 | true 87 | true 88 | 89 | Copyright © {inceptionYear}-{currentYear}, 90 | ${project.organization.name} 91 | 92 | 93 | 94 | 95 | org.apache.maven.plugins 96 | maven-gpg-plugin 97 | 1.4 98 | 99 | 100 | sign-artifacts 101 | verify 102 | 103 | sign 104 | 105 | 106 | 107 | 108 | tsunanet@gmail.com 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | org.slf4j 117 | slf4j-api 118 | [@SLF4J_API_VERSION@,) 119 | 120 | 121 | 122 | 123 | true 124 | UTF-8 125 | 126 | 127 | 128 | org.sonatype.oss 129 | oss-parent 130 | 7 131 | 132 | 133 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without 4 | # modification, are permitted provided that the following conditions are met: 5 | # - Redistributions of source code must retain the above copyright notice, 6 | # this list of conditions and the following disclaimer. 7 | # - Redistributions in binary form must reproduce the above copyright notice, 8 | # this list of conditions and the following disclaimer in the documentation 9 | # and/or other materials provided with the distribution. 10 | # - Neither the name of the StumbleUpon nor the names of its contributors 11 | # may be used to endorse or promote products derived from this software 12 | # without specific prior written permission. 13 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 17 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | # POSSIBILITY OF SUCH DAMAGE. 24 | 25 | all: jar 26 | # TODO(tsuna): Use automake to avoid relying on GNU make extensions. 27 | 28 | top_builddir := build 29 | package := com.stumbleupon.async 30 | spec_title := SUAsync Library 31 | spec_vendor := The SUAsync Authors 32 | # Semantic Versioning (see http://semver.org/). 33 | spec_version := 1.4.1 34 | suasync_SOURCES := \ 35 | src/Callback.java \ 36 | src/CallbackOverflowError.java \ 37 | src/Deferred.java \ 38 | src/DeferredGroupException.java \ 39 | src/DeferredGroup.java \ 40 | src/TimeoutException.java \ 41 | 42 | SLF4J_API_VERSION := 1.6.0 43 | suasync_LIBADD := libs/slf4j-api-$(SLF4J_API_VERSION).jar 44 | AM_JAVACFLAGS := -Xlint 45 | package_dir := $(subst .,/,$(package)) 46 | classes := $(suasync_SOURCES:src/%.java=$(top_builddir)/$(package_dir)/%.class) 47 | jar := $(top_builddir)/suasync-$(spec_version).jar 48 | 49 | jar: $(jar) 50 | 51 | get_dep_classpath = `echo $(suasync_LIBADD) | tr ' ' ':'` 52 | $(top_builddir)/.javac-stamp: $(suasync_SOURCES) 53 | @mkdir -p $(top_builddir) 54 | javac $(AM_JAVACFLAGS) -cp $(get_dep_classpath) \ 55 | -d $(top_builddir) $(suasync_SOURCES) 56 | @touch "$@" 57 | 58 | classes_with_nested_classes = $(classes:$(top_builddir)/%.class=%*.class) 59 | 60 | pkg_version := \ 61 | `git rev-list --pretty=format:%h HEAD --max-count=1 | sed 1d || echo unknown` 62 | $(top_builddir)/manifest: $(top_builddir)/.javac-stamp .git/HEAD 63 | { echo "Specification-Title: $(spec_title)"; \ 64 | echo "Specification-Version: $(spec_version)"; \ 65 | echo "Specification-Vendor: $(spec_vendor)"; \ 66 | echo "Implementation-Title: $(package)"; \ 67 | echo "Implementation-Version: $(pkg_version)"; \ 68 | echo "Implementation-Vendor: $(spec_vendor)"; } >"$@" 69 | 70 | $(jar): $(top_builddir)/manifest $(top_builddir)/.javac-stamp $(classes) 71 | cd $(top_builddir) && jar cfm `basename $(jar)` manifest $(classes_with_nested_classes) \ 72 | || { rv=$$? && rm -f `basename $(jar)` && exit $$rv; } 73 | # ^^^^^^^^^^^^^^^^^^^^^^^ 74 | # I've seen cases where `jar' exits with an error but leaves a partially built .jar file! 75 | 76 | doc: $(top_builddir)/api/index.html 77 | 78 | JDK_JAVADOC := http://docs.oracle.com/javase/6/docs/api 79 | $(top_builddir)/api/index.html: $(suasync_SOURCES) 80 | javadoc -d $(top_builddir)/api -classpath $(get_dep_classpath) \ 81 | -link $(JDK_JAVADOC) $(suasync_SOURCES) 82 | clean: 83 | @rm -f $(top_builddir)/.javac-stamp 84 | rm -f $(top_builddir)/manifest 85 | cd $(top_builddir) || exit 0 && rm -f $(classes_with_nested_classes) 86 | cd $(top_builddir) || exit 0 \ 87 | && test -d $(package_dir) || exit 0 \ 88 | && dir=$(package_dir) \ 89 | && while test x"$$dir" != x"$${dir%/*}"; do \ 90 | rmdir "$$dir" && dir=$${dir%/*} || break; \ 91 | done \ 92 | && rmdir "$$dir" 93 | 94 | distclean: clean 95 | rm -f $(jar) 96 | rm -rf $(top_builddir)/api target 97 | test ! -d $(top_builddir) || rmdir $(top_builddir) 98 | 99 | pom.xml: pom.xml.in Makefile 100 | { \ 101 | echo ''; \ 102 | sed <$< \ 103 | -e 's/@SLF4J_API_VERSION@/$(SLF4J_API_VERSION)/' \ 104 | -e 's/@spec_title@/$(spec_title)/' \ 105 | -e 's/@spec_vendor@/$(spec_vendor)/' \ 106 | -e 's/@spec_version@/$(spec_version)/' \ 107 | ; \ 108 | } >$@-t 109 | mv $@-t $@ 110 | 111 | .PHONY: all jar clean distclean doc check 112 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | The SUAsync Library - User visible changes. 2 | 3 | * Version 1.4.1 (2015-05-20) [???????] 4 | 5 | Noteworthy changes: 6 | - Fixed a deadlock when joining a Deferred with a timeout (#4). 7 | 8 | 9 | * Version 1.4.0 (2013-08-01) [fe17b98] 10 | 11 | New public APIs: 12 | - `groupInOrder(Collection)' was added to provide an alternative to 13 | `group(...)' that preserves the order of the results. 14 | 15 | 16 | * Version 1.3.3 (2013-06-16) [fc509ea] 17 | 18 | Noteworthy changes: 19 | - Fixed the signature of the group() methods so as to preserve the 20 | type T of the Deferreds being grouped, instead of forcing them 21 | to be just Object. This is a compatible API change. 22 | 23 | 24 | * Version 1.3.2 (2013-05-30) [bf284c8] 25 | 26 | Noteworthy changes: 27 | - Fixed a race condition in Deferred.toString() that could cause 28 | a NullPointerException. 29 | 30 | 31 | * Version 1.3.1 (2012-11-03) [aab6920] 32 | 33 | Noteworthy changes: 34 | - Restore the limit on the size of the callback chain to 16383. 35 | 36 | 37 | * Version 1.3.0 (2012-10-28) [05c513b] 38 | 39 | Noteworthy changes: 40 | - Fix the maximum number of callbacks and the check enforcing it. 41 | The maximum number is now 8192 (down from 16383) and an exception 42 | is properly raised when the limit is reached. 43 | - When the limit on the number of callbacks is hit, an exception 44 | of type `CallbackOverflowError' is now raised instead of a 45 | `StackOverflowError'. Because `CallbackOverflowError' inherits 46 | from `StackOverflowError', this change is backward compatible. 47 | 48 | 49 | * Version 1.2.0 (2012-04-08) [640e4e3] 50 | 51 | Noteworthy changes: 52 | - Optimization: the internal state of a Deferred is now captured 53 | in a plain `int' instead of using an enum. 54 | - The maximum number of callbacks allowed on a Deferred has been 55 | significantly increased, from 128 to 16383, to allow for some 56 | use cases where a large number of operations are waiting on a 57 | single asynchronous operation to complete. This implementation 58 | cannot support more than this many callbacks without refactoring. 59 | 60 | 61 | * Version 1.1.0 (2011-09-27) [cf62d8d] 62 | 63 | New public APIs: 64 | - The `join()' and `joinUninterruptibly()' methods now have an 65 | overloaded version that accepts a timeout in milliseconds and 66 | throws the new `TimeoutException' when a timed join times out. 67 | 68 | Noteworthy changes: 69 | - Callback chains are now stored in an array internally, instead of 2 70 | LinkedLists, which dramatically reduces memory usage and the number 71 | of objects created for each callback. 72 | 73 | 74 | * Version 1.0 (2010-10-26) [80c1235] 75 | 76 | Noteworthy changes: 77 | - `MAX_CALLBACK_CHAIN_LENGTH' was increased from 64 to 127. 78 | - Switch from LGPLv3+ to BSD license. 79 | - Fix `join()' and `joinUninterruptibly()' to be safe with multiple 80 | waiting threads. 81 | - Fix a race condition when adding a callback right before the end 82 | of the execution of the current callback chain. 83 | - Optimization: invoke callbacks immediately when added to a Deferred 84 | already in `State.DONE'. 85 | 86 | New public APIs: 87 | - `Deferred.group()' and `DeferredGroupException' were added. 88 | 89 | 90 | * Version 0.1 (2010-08-26) [d955ddb] 91 | 92 | Initial release: 93 | - Deferred with Callback, inspired from Twisted's defer.py. 94 | 95 | ----- 96 | 97 | Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 98 | 99 | Redistribution and use in source and binary forms, with or without 100 | modification, are permitted provided that the following conditions are met: 101 | - Redistributions of source code must retain the above copyright notice, 102 | this list of conditions and the following disclaimer. 103 | - Redistributions in binary form must reproduce the above copyright notice, 104 | this list of conditions and the following disclaimer in the documentation 105 | and/or other materials provided with the distribution. 106 | - Neither the name of the StumbleUpon nor the names of its contributors 107 | may be used to endorse or promote products derived from this software 108 | without specific prior written permission. 109 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 110 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 111 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 112 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 113 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 114 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 115 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 116 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 117 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 118 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 119 | POSSIBILITY OF SUCH DAMAGE. 120 | 121 | Local Variables: 122 | mode: outline 123 | End: 124 | -------------------------------------------------------------------------------- /src/DeferredGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | import java.util.Collection; 29 | import java.util.ArrayList; 30 | 31 | /** 32 | * Groups multiple {@link Deferred}s into a single one. 33 | *

34 | * This is just a helper class, see {@link Deferred#group} for more details. 35 | */ 36 | final class DeferredGroup { 37 | 38 | /** 39 | * The Deferred we'll callback when all Deferreds in the group have been 40 | * called back. 41 | */ 42 | private final Deferred> parent = new Deferred>(); 43 | 44 | /** 45 | * How many results do we expect?. 46 | * Need to acquires this' monitor before changing. 47 | */ 48 | private int nresults; 49 | 50 | /** 51 | * All the results for each Deferred we're grouping. 52 | * Need to acquires this' monitor before changing. 53 | * Each result is either of type T, or an Exception. 54 | */ 55 | private final ArrayList results; 56 | 57 | /** 58 | * Constructor. 59 | * @param deferreds All the {@link Deferred}s we want to group. 60 | * @param ordered If true, the results will be presented in the same order 61 | * as the {@link Deferred}s are in the {@code deferreds} argument. 62 | * If false, results will be presented in the order in which they arrive. 63 | * In other words, assuming that {@code deferreds} is a list of three 64 | * {@link Deferred} objects {@code [A, B, C]}, then if {@code ordered} is 65 | * true, {@code results} will be {@code [result A, result B, result C]} 66 | * whereas if {@code ordered} is false then the order in {@code results} 67 | * is determined by the order in which callbacks fire on A, B, and C. 68 | */ 69 | public DeferredGroup(final Collection> deferreds, 70 | final boolean ordered) { 71 | nresults = deferreds.size(); 72 | results = new ArrayList(nresults); 73 | 74 | if (nresults == 0) { 75 | parent.callback(results); 76 | return; 77 | } 78 | 79 | // Callback used to collect results in the order in which they appear. 80 | final class Notify implements Callback { 81 | public T call(final T arg) { 82 | recordCompletion(arg); 83 | return arg; 84 | } 85 | public String toString() { 86 | return "notify DeferredGroup@" + DeferredGroup.super.hashCode(); 87 | } 88 | }; 89 | 90 | // Callback that preserves the original orders of the Deferreds. 91 | final class NotifyOrdered implements Callback { 92 | private final int index; 93 | NotifyOrdered(int index) { 94 | this.index = index; 95 | } 96 | public T call(final T arg) { 97 | recordCompletion(arg, index); 98 | return arg; 99 | } 100 | public String toString() { 101 | return "notify #" + index + " DeferredGroup@" 102 | + DeferredGroup.super.hashCode(); 103 | } 104 | }; 105 | 106 | if (ordered) { 107 | int i = 0; 108 | for (final Deferred d : deferreds) { 109 | results.add(null); // ensures results.set(i, result) is valid. 110 | // Note: it's important to add the callback after the line above, 111 | // as the callback can fire at any time once it's been added, and 112 | // if it fires before results.set(i, result) is valid, we'll get 113 | // an IndexOutOfBoundsException. 114 | d.addBoth(new NotifyOrdered(i++)); 115 | } 116 | } else { 117 | final Notify notify = new Notify(); 118 | for (final Deferred d : deferreds) { 119 | d.addBoth(notify); 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * Returns the parent {@link Deferred} of the group. 126 | */ 127 | public Deferred> getDeferred() { 128 | return parent; 129 | } 130 | 131 | /** 132 | * Called back when one of the {@link Deferred} in the group completes. 133 | * @param result The result of the deferred. 134 | */ 135 | private void recordCompletion(final Object result) { 136 | int left; 137 | synchronized (this) { 138 | results.add(result); 139 | left = --nresults; 140 | } 141 | if (left == 0) { 142 | done(); 143 | } 144 | } 145 | 146 | /** 147 | * Called back when one of the {@link Deferred} in the group completes. 148 | * @param result The result of the deferred. 149 | * @param index The index of the result. 150 | */ 151 | private void recordCompletion(final Object result, final int index) { 152 | int left; 153 | synchronized (this) { 154 | results.set(index, result); 155 | left = --nresults; 156 | } 157 | if (left == 0) { 158 | done(); 159 | } 160 | } 161 | 162 | /** Called once we have obtained all the results of this group. */ 163 | private void done() { 164 | // From this point on, we no longer need to synchronize in order to 165 | // access `results' since we know we're done, so no other thread is 166 | // going to call recordCompletion() again. 167 | for (final Object r : results) { 168 | if (r instanceof Exception) { 169 | parent.callback(new DeferredGroupException(results, (Exception) r)); 170 | return; 171 | } 172 | } 173 | parent.callback(results); 174 | } 175 | 176 | public String toString() { 177 | return "DeferredGroup" 178 | + "(parent=" + parent 179 | + ", # results=" + results.size() + " / " + nresults + " left)"; 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /src/Deferred.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010-2012 The SUAsync Authors. All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * - Redistributions of source code must retain the above copyright notice, 7 | * this list of conditions and the following disclaimer. 8 | * - Redistributions in binary form must reproduce the above copyright notice, 9 | * this list of conditions and the following disclaimer in the documentation 10 | * and/or other materials provided with the distribution. 11 | * - Neither the name of the StumbleUpon nor the names of its contributors 12 | * may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | package com.stumbleupon.async; 27 | 28 | import java.util.ArrayList; 29 | import java.util.Collection; 30 | import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; 31 | 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | 35 | /** 36 | * A thread-safe implementation of a deferred result for easy asynchronous 37 | * processing. 38 | *

39 | * This implementation is based on Twisted's 40 | * Python {@code Deferred} API. 41 | * 42 | * This API is a simple and elegant way of managing asynchronous and dynamic 43 | * "pipelines" (processing chains) without having to explicitly define a 44 | * finite state machine. 45 | * 46 | *

The tl;dr version

47 | * 48 | * We're all busy and don't always have time to RTFM in details. Please pay 49 | * special attention to the invariants you must 50 | * respect. Other than that, here's an executive summary of what 51 | * {@code Deferred} offers: 52 | *
    53 | *
  • A {@code Deferred} is like a {@link java.util.concurrent.Future} with 54 | * a dynamic {@link Callback} chain associated to it.
  • 55 | *
  • When the deferred result becomes available, the callback chain gets 56 | * triggered.
  • 57 | *
  • The result of one callback in the chain is passed on to the next.
  • 58 | *
  • When a callback returns another {@code Deferred}, the next callback in 59 | * the chain doesn't get executed until that other {@code Deferred} result 60 | * becomes available.
  • 61 | *
  • There are actually two callback chains. One is for normal processing, 62 | * the other is for error handling / recovery. A {@link Callback} that handles 63 | * errors is called an "errback".
  • 64 | *
  • {@code Deferred} is an important building block for writing easy-to-use 65 | * asynchronous APIs in a thread-safe fashion.
  • 66 | *
67 | * 68 | *

Understanding the concept of {@code Deferred}

69 | * 70 | * The idea is that a {@code Deferred} represents a result that's not yet 71 | * available. An asynchronous operation (I/O, RPC, whatever) has been started 72 | * and will hand its result (be it successful or not) to the {@code Deferred} 73 | * in the future. The key difference between a {@code Deferred} and a 74 | * {@link java.util.concurrent.Future Future} is that a {@code Deferred} has 75 | * a callback chain associated to it, whereas with just a 76 | * {@code Future} you need get the result manually at some point, which poses 77 | * problems such as: How do you know when the result is available? What if 78 | * the result itself depends on another future? 79 | *

80 | * When you start an asynchronous operation, you typically want to be called 81 | * back when the operation completes. If the operation was successful, you 82 | * want your callback to use its result to carry on what you were doing at 83 | * the time you started the asynchronous operation. If there was an error, 84 | * you want to trigger some error handling code. 85 | *

86 | * But there's more to a {@code Deferred} than a single callback. You can add 87 | * arbitrary number of callbacks, which effectively allows you to easily build 88 | * complex processing pipelines in a really simple and elegant way. 89 | * 90 | *

Understanding the callback chain

91 | * Let's take a typical example. You're writing a client library for others 92 | * to use your simple remote storage service. When your users call the {@code 93 | * get} method in your library, you want to retrieve some piece of data from 94 | * your remote service and hand it back to the user, but you want to do so in 95 | * an asynchronous fashion. 96 | *

97 | * When the user of your client library invokes {@code get}, you assemble a 98 | * request and send it out to the remote server through a socket. Before 99 | * sending it to the socket, you create a {@code Deferred} and you store it 100 | * somewhere, for example in a map, to keep an association between the request 101 | * and this {@code Deferred}. You then return this {@code Deferred} to the 102 | * user, this is how they will access the deferred result as soon as the RPC 103 | * completes. 104 | *

105 | * Sooner or later, the RPC will complete (successfully or not), and your 106 | * socket will become readable (or maybe closed, in the event of a failure). 107 | * Let's assume for now that everything works as expected, and thus the socket 108 | * is readable, so you read the response from the socket. At this point you 109 | * extract the result of the remote {@code get} call, and you hand it out to 110 | * the {@code Deferred} you created for this request (remember, you had to 111 | * store it somewhere, so you could give it the deferred result once you have 112 | * it). The {@code Deferred} then stores this result and triggers any 113 | * callback that may have been added to it. The expectation is that the user 114 | * of your client library, after calling your {@code get} method, will add a 115 | * {@link Callback} to the {@code Deferred} you gave them. This way, when the 116 | * deferred result becomes available, you'll call it with the result in 117 | * argument. 118 | *

119 | * So far what we've explained is nothing more than a {@code Future} with a 120 | * callback associated to it. But there's more to {@code Deferred} than just 121 | * this. Let's assume now that someone else wants to build a caching layer on 122 | * top of your client library, to avoid repeatedly {@code get}ting the same 123 | * value over and over again through the network. Users who want to use the 124 | * cache will invoke {@code get} on the caching library instead of directly 125 | * calling your client library. 126 | *

127 | * Let's assume that the caching library already has a result cached for a 128 | * {@code get} call. It will create a {@code Deferred}, and immediately hand 129 | * it the cached result, and it will return this {@code Deferred} to the user. 130 | * The user will add a {@link Callback} to it, which will be immediately 131 | * invoked since the deferred result is already available. So the entire 132 | * {@code get} call completed virtually instantaneously and entirely from the 133 | * same thread. There was no context switch (no other thread involved, no 134 | * I/O and whatnot), nothing ever blocked, everything just happened really 135 | * quickly. 136 | *

137 | * Now let's assume that the caching library has a cache miss and needs to do 138 | * a remote {@code get} call using the original client library described 139 | * earlier. The RPC is sent out to the remote server and the client library 140 | * returns a {@code Deferred} to the caching library. This is where things 141 | * become exciting. The caching library can then add its own callback to the 142 | * {@code Deferred} before returning it to the user. This callback will take 143 | * the result that came back from the remote server, add it to the cache and 144 | * return it. As usual, the user then adds their own callback to process the 145 | * result. So now the {@code Deferred} has 2 callbacks associated to it: 146 | *

 147 |  *              1st callback       2nd callback
 148 |  *
 149 |  *   Deferred:  add to cache  -->  user callback
 150 |  * 
151 | * When the RPC completes, the original client library will de-serialize the 152 | * result from the wire and hand it out to the {@code Deferred}. The first 153 | * callback will be invoked, which will add the result to the cache of the 154 | * caching library. Then whatever the first callback returns will be passed 155 | * on to the second callback. It turns out that the caching callback returns 156 | * the {@code get} response unchanged, so that will be passed on to the user 157 | * callback. 158 | *

159 | * Now it's very important to understand that the first callback could have 160 | * returned another arbitrary value, and that's what would have been passed 161 | * to the second callback. This may sound weird at first but it's actually 162 | * the key behind {@code Deferred}. 163 | *

164 | * To illustrate why, let's complicate things a bit more. Let's assume the 165 | * remote service that serves those {@code get} requests is a fairly simple 166 | * and low-level storage service (think {@code memcached}), so it only works 167 | * with byte arrays, it doesn't care what the contents is. So the original 168 | * client library is only de-serializing the byte array from the network and 169 | * handing that byte array to the {@code Deferred}. 170 | *

171 | * Now you're writing a higher-level library that uses this storage system 172 | * to store some of your custom objects. So when you get the byte array from 173 | * the server, you need to further de-serialize it into some kind of an object. 174 | * Users of your higher-level library don't care about what kind of remote 175 | * storage system you use, the only thing they care about is {@code get}ting 176 | * those objects asynchronously. Your higher-level library is built on top 177 | * of the original low-level library that does the RPC communication. 178 | *

179 | * When the users of the higher-level library call {@code get}, you call 180 | * {@code get} on the lower-level library, which issues an RPC call and 181 | * returns a {@code Deferred} to the higher-level library. The higher-level 182 | * library then adds a first callback to further de-serialize the byte array 183 | * into an object. Then the user of the higher-level library adds their own 184 | * callback that does something with that object. So now we have something 185 | * that looks like this: 186 | *

 187 |  *              1st callback                    2nd callback
 188 |  *
 189 |  *   Deferred:  de-serialize to an object  -->  user callback
 190 |  * 
191 | *

192 | * When the result comes in from the network, the byte array is de-serialized 193 | * from the socket. The first callback is invoked and its argument is the 194 | * initial result, the byte array. So the first callback 195 | * further de-serializes it into some object that it returns. The second 196 | * callback is then invoked and its argument is the result of the 197 | * previous callback, that is the de-serialized object. 198 | *

199 | * Now back to the caching library, which has nothing to do with the higher 200 | * level library. All it does is, given an object that implements some 201 | * interface with a {@code get} method, it keeps a map of whatever arguments 202 | * {@code get} receives to an {@code Object} that was cached for this 203 | * particular {@code get} call. Thanks to the way the callback chain works, 204 | * it's possible to use the caching library together with the higher-level 205 | * library transparently. Users who want to use caching simply need to 206 | * use the caching library together with the higher level library. Now when 207 | * they call {@code get} on the caching library, and there's a cache miss, 208 | * here's what happens, step by step: 209 | *

    210 | *
  1. The caching library calls {@code get} on the higher-level 211 | * library.
  2. 212 | *
  3. The higher-level library calls {@code get} on the lower-level 213 | * library.
  4. 214 | *
  5. The lower-level library creates a {@code Deferred}, issues out the 215 | * RPC call and returns its {@code Deferred}.
  6. 216 | *
  7. The higher-level library adds its own object de-serialization 217 | * callback to the {@code Deferred} and returns it.
  8. 218 | *
  9. The caching library adds its own cache-updating callback to the 219 | * {@code Deferred} and returns it.
  10. 220 | *
  11. The user gets the {@code Deferred} and adds their own callback 221 | * to do something with the object retrieved from the data store.
  12. 222 | *
223 | *
 224 |  *              1st callback       2nd callback       3rd callback
 225 |  *
 226 |  *   Deferred:  de-serialize  -->  add to cache  -->  user callback
 227 |  *   result: (none available)
 228 |  * 
229 | * Once the response comes back, the first callback is invoked, it 230 | * de-serializes the object, returns it. The current result of the 231 | * {@code Deferred} becomes the de-serialized object. The current state of 232 | * the {@code Deferred} is as follows: 233 | *
 234 |  *              2nd callback       3rd callback
 235 |  *
 236 |  *   Deferred:  add to cache  -->  user callback
 237 |  *   result: de-serialized object
 238 |  * 
239 | * Because there are more callbacks in the chain, the {@code Deferred} invokes 240 | * the next one and gives it the current result (the de-serialized object) in 241 | * argument. The callback adds that object to its cache and returns it 242 | * unchanged. 243 | *
 244 |  *              3rd callback
 245 |  *
 246 |  *   Deferred:  user callback
 247 |  *   result: de-serialized object
 248 |  * 
249 | * Finally, the user's callback is invoked with the object in argument. 250 | *
 251 |  *   Deferred:  (no more callbacks)
 252 |  *   result: (whatever the user's callback returned)
 253 |  * 
254 | * If you think this is becoming interesting, read on, you haven't reached the 255 | * most interesting thing about {@code Deferred} yet. 256 | * 257 | *

Building dynamic processing pipelines with {@code Deferred}

258 | * Let's complicate the previous example a little bit more. Let's assume that 259 | * the remote storage service that serves those {@code get} calls is a 260 | * distributed service that runs on many machines. The data is partitioned 261 | * over many nodes and moves around as nodes come and go (due to machine 262 | * failures and whatnot). In order to execute a {@code get} call, the 263 | * low-level client library first needs to know which server is currently 264 | * serving that piece of data. Let's assume that there's another server, 265 | * which is part of that distributed service, that maintains an index and 266 | * keeps track of where each piece of data is. The low-level client library 267 | * first needs to lookup the location of the data using that first server 268 | * (that's a first RPC), then retrieves it from the storage node (that's 269 | * another RPC). End users don't care that retrieving data involves a 2-step 270 | * process, they just want to call {@code get} and be called back when the 271 | * data (a byte array) is available. 272 | *

273 | * This is where what's probably the most useful feature of {@code Deferred} 274 | * comes in. When the user calls {@code get}, the low-level library will issue 275 | * a first RPC to the index server to locate the piece of data requested by the 276 | * user. When issuing this {@code lookup} RPC, a {@code Deferred} gets 277 | * created. The low-level {@code get} code adds a first callback to process 278 | * the {@code lookup} response and then returns it to the user. 279 | *

 280 |  *              1st callback       2nd callback
 281 |  *
 282 |  *   Deferred:  index lookup  -->  user callback
 283 |  *   result: (none available)
 284 |  * 
285 | * Eventually, the {@code lookup} RPC completes, and the {@code Deferred} is 286 | * given the {@code lookup} response. So before triggering the first 287 | * callback, the {@code Deferred} will be in this state: 288 | *
 289 |  *              1st callback       2nd callback
 290 |  *
 291 |  *   Deferred:  index lookup  -->  user callback
 292 |  *   result: lookup response
 293 |  * 
294 | * The first callback runs and now knows where to find the piece of data 295 | * initially requested. It issues the {@code get} request to the right storage 296 | * node. Doing so creates another {@code Deferred}, let's call it 297 | * {@code (B)}, which is then returned by the {@code index lookup} callback. 298 | * And this is where the magic happens. Now we're in this state: 299 | *
 300 |  *   (A)        2nd callback    |   (B)
 301 |  *                              |
 302 |  *   Deferred:  user callback   |   Deferred:  (no more callbacks)
 303 |  *   result: Deferred (B)       |   result: (none available)
 304 |  * 
305 | * Because a callback returned a {@code Deferred}, we can't invoke the user 306 | * callback just yet, since the user doesn't want their callback receive a 307 | * {@code Deferred}, they want it to receive a byte array. The current 308 | * callback gets paused and stops processing the callback chain. 309 | * This callback chain needs to be resumed whenever the {@code Deferred} of 310 | * the {@code get} call [{@code (B)}] completes. In order to achieve that, a 311 | * callback is added to that other {@code Deferred} that will resume 312 | * the execution of the callback chain. 313 | *
 314 |  *   (A)        2nd callback    |   (B)        1st callback
 315 |  *                              |
 316 |  *   Deferred:  user callback   |   Deferred:  resume (A)
 317 |  *   result: Deferred (B)       |   result: (none available)
 318 |  * 
319 | * Once {@code (A)} added the callback on {@code (B)}, it can return 320 | * immediately, there's no need to wait, block a thread or anything like that. 321 | * So the whole process of receiving the {@code lookup} response and sending 322 | * out the {@code get} RPC happened really quickly, without blocking anything. 323 | *

324 | * Now when the {@code get} response comes back from the network, the RPC 325 | * layer de-serializes the byte array, as usual, and hands it to {@code (B)}: 326 | *

 327 |  *   (A)        2nd callback    |   (B)        1st callback
 328 |  *                              |
 329 |  *   Deferred:  user callback   |   Deferred:  resume (A)
 330 |  *   result: Deferred (B)       |   result: byte array
 331 |  * 
332 | * {@code (B)}'s first and only callback is going to set the result of 333 | * {@code (A)} and resume {@code (A)}'s callback chain. 334 | *
 335 |  *   (A)        2nd callback    |   (B)        1st callback
 336 |  *                              |
 337 |  *   Deferred:  user callback   |   Deferred:  resume (A)
 338 |  *   result: byte array         |   result: byte array
 339 |  * 
340 | * So now {@code (A)} resumes its callback chain, and invokes the user's 341 | * callback with the byte array in argument, which is what they wanted. 342 | *
 343 |  *   (A)                        |   (B)        1st callback
 344 |  *                              |
 345 |  *   Deferred:  (no more cb)    |   Deferred:  resume (A)
 346 |  *   result: (return value of   |   result: byte array
 347 |  *            the user's cb)
 348 |  * 
349 | * Then {@code (B)} moves on to its next callback in the chain, but there are 350 | * none, so {@code (B)} is done too. 351 | *
 352 |  *   (A)                        |   (B)
 353 |  *                              |
 354 |  *   Deferred:  (no more cb)    |   Deferred:  (no more cb)
 355 |  *   result: (return value of   |   result: byte array
 356 |  *            the user's cb)
 357 |  * 
358 | * The whole process of reading the {@code get} response, resuming the initial 359 | * {@code Deferred} and executing the second {@code Deferred} happened all in 360 | * the same thread, sequentially, and without blocking anything (provided that 361 | * the user's callback didn't block, as it must not). 362 | *

363 | * What we've done is essentially equivalent to dynamically building an 364 | * implicit finite state machine to handle the life cycle of the {@code get} 365 | * request. This simple API allows you to build arbitrarily complex 366 | * processing pipelines that make dynamic decisions at each stage of the 367 | * pipeline as to what to do next. 368 | * 369 | *

Handling errors

370 | * A {@code Deferred} has in fact not one but two callback chains. The first 371 | * chain is the "normal" processing chain, and the second is the error 372 | * handling chain. Twisted calls an error handling callback an "errback", so 373 | * we've kept that term here. When the asynchronous processing completes with 374 | * an error, the {@code Deferred} must be given the {@link Exception} that was 375 | * caught instead of giving it the result (or if no {@link Exception} was 376 | * caught, one must be created and handed to the {@code Deferred}). When the 377 | * current result of a {@code Deferred} is an instance of {@link Exception}, 378 | * the next errback is invoked. As for normal callbacks, whatever the errback 379 | * returns becomes the current result. If the current result is still an 380 | * instance of {@link Exception}, the next errback is invoked. If the current 381 | * result is no longer an {@link Exception}, the next callback is invoked. 382 | *

383 | * When a callback or an errback itself throws an exception, it is 384 | * caught by the {@code Deferred} and becomes the current result, which means 385 | * that the next errback in the chain will be invoked with that exception in 386 | * argument. Note that {@code Deferred} will only catch {@link Exception}s, 387 | * not any {@link Throwable} or {@link Error}. 388 | * 389 | * 390 | *

Contract and Invariants

391 | * 392 | * Read this carefully as this is your warranty. 393 | *
    394 | *
  • A {@code Deferred} can receive only one initial result.
  • 395 | *
  • Only one thread at a time is going to execute the callback chain.
  • 396 | *
  • Each action taken by a callback 397 | * happens-before the next 398 | * callback is invoked. In other words, if a callback chain manipulates 399 | * a variable (and no one else manipulates it), no synchronization is 400 | * required.
  • 401 | *
  • The thread that executes the callback chain is the thread that hands 402 | * the initial result to the {@code Deferred}. This class does not 403 | * create or manage any thread or executor.
  • 404 | *
  • As soon as a callback is executed, the {@code Deferred} will lose its 405 | * reference to it.
  • 406 | *
  • Every method that adds a callback to a {@code Deferred} does so in 407 | * {@code O(1)}.
  • 408 | *
  • A {@code Deferred} cannot receive itself as an initial or 409 | * intermediate result, as this would cause an infinite recursion.
  • 410 | *
  • You must not build a cycle of mutually dependant {@code Deferred}s, 411 | * as this would cause an infinite recursion (thankfully, it will 412 | * quickly fail with a {@link CallbackOverflowError}).
  • 413 | *
  • Callbacks and errbacks cannot receive a {@code Deferred} in 414 | * argument. This is because they always receive the result of a 415 | * previous callback, and when the result becomes a {@code Deferred}, 416 | * we suspend the execution of the callback chain until the result of 417 | * that other {@code Deferred} is available.
  • 418 | *
  • Callbacks cannot receive an {@link Exception} in argument. This 419 | * because they're always given to the errbacks.
  • 420 | *
  • Using the monitor of a {@code Deferred} can lead to a deadlock, so 421 | * don't use it. In other words, writing 422 | *
    synchronized (some_deferred) { ... }
    423 | * (or anything equivalent) voids your warranty.
  • 424 | *
425 | * @param The type of the deferred result. 426 | */ 427 | public final class Deferred { 428 | 429 | // I apologize in advance for all the @SuppressWarnings("unchecked") in this 430 | // class. I feel like I'm cheating the type system but, to be honest, Java's 431 | // type erasure makes all this type checking mostly worthless anyway. It's 432 | // really the first time (for me) where type erasure comes in handy though, 433 | // as it makes it easy to "adjust" on a Deferred as Callbacks are added. 434 | 435 | private static final Logger LOG = LoggerFactory.getLogger(Deferred.class); 436 | 437 | /** 438 | * Maximum length of the callback chain we allow. 439 | * Set this to some arbitrary limit to quickly detect problems in code 440 | * that creates potentially infinite chains. I can't imagine a practical 441 | * case that would require a chain with more callbacks than this. 442 | */ 443 | private static final short MAX_CALLBACK_CHAIN_LENGTH = (1 << 14) - 1; 444 | // NOTE: The current implementation cannot support more than this many 445 | // callbacks because indexes used to access the `callbacks' array are 446 | // of type `short' to save memory. 447 | 448 | /** 449 | * How many entries do we create in the callback+errback chain by default. 450 | * Because a callback is always accompanied by a corresponding errback, this 451 | * value must be even and must be greater than or equal to 2. Based on the 452 | * observation that most of the time, chains have on average 2 callbacks, 453 | * pre-allocating 4 entries means that no reallocation will occur. 454 | */ 455 | private static final byte INIT_CALLBACK_CHAIN_SIZE = 4; 456 | 457 | /** 458 | * The state of this {@link Deferred}. 459 | *

460 | * The FSM (Finite State Machine) is as follows: 461 | *

 462 |    *                ,---------------------,
 463 |    *                |   ,-------,         |
 464 |    *                V   v       |         |
 465 |    *   PENDING --> RUNNING --> DONE     PAUSED
 466 |    *                  |                   ^
 467 |    *                  `-------------------'
 468 |    * 
469 | * A Deferred starts in the PENDING state, unless it's given a result when 470 | * it's instantiated in which case it starts in the DONE state. 471 | * When {@link #callback()} is invoked, the Deferred enters the RUNNING 472 | * state and starts to execute its callback chain. 473 | * Once the callback chain is exhausted, the Deferred enters the state DONE. 474 | * 475 | * While we're executing callbacks, if one of them returns another Deferred, 476 | * then we enter the state PAUSED, and we add a callback on the other 477 | * Deferred so we can resume execution (and return to the RUNNING state) 478 | * once that other Deferred is DONE. 479 | * 480 | * When we're DONE, if additional callbacks are added, we're going to 481 | * execute them immediately (since we already have the deferred result 482 | * available), which means we're going to return to the RUNNING state. 483 | */ 484 | private static final byte PENDING = 0; 485 | private static final byte RUNNING = 1; 486 | private static final byte PAUSED = 2; 487 | private static final byte DONE = 3; 488 | 489 | /** 490 | * The current state of this Deferred. 491 | * All state transitions must be done atomically. Since this attribute is 492 | * volatile, a single assignment is atomic. But if you need to do a state 493 | * transition with a test first ("if state is A, move to state B") then you 494 | * must use {@link #casState} to do an atomic CAS (Compare And Swap). 495 | */ 496 | private volatile int state; 497 | // Technically ^^^ this could be a byte, but unfortunately !@#$% Java only 498 | // supports CAS on 3 things: references, long, and int. Even through Unsafe. 499 | 500 | /** 501 | * The current result. This reference is either of type T or Exception. 502 | * 503 | * This reference doesn't need to be volatile because it's guaranteed that 504 | * only 1 thread at a time will access this reference, and all accesses 505 | * happen in between a volatile access on the {@link #state} variable. 506 | * 507 | * This reference isn't touched until the initial call to {@link #callback} 508 | * occurs. The thread that invokes {@link #callback} is going to run through 509 | * the callback chain in {@link #runCallbacks}, but it will first set the 510 | * state to RUNNING (volatile access). When the method returns, the state 511 | * is changed either to DONE or PAUSED (volatile access). When going out of 512 | * PAUSED, the state is set back to RUNNING before updating the result 513 | * (volatile access). Going from state DONE to RUNNING again also involves 514 | * a volatile access. 515 | * 516 | * The new Java memory model thus guarantees that accesses to this reference 517 | * will always be consistent, since they always happen after a volatile 518 | * access to the state associated with this reference. See also the FAQ on 519 | * JSR 133 (Java Memory Model). 520 | */ 521 | private Object result; 522 | 523 | /** 524 | * The current callback and errback chains (can be null). 525 | * Invariants: 526 | * - Let i = 2 * n. The ith entry is a callback, and (i+1)th is the 527 | * corresponding errback. In other words, even entries are callbacks 528 | * and odd entries are errbacks of the callback right before them. 529 | * - When not null, the length is between {@link #INIT_CALLBACK_CHAIN_SIZE} 530 | * and {@link #MAX_CALLBACK_CHAIN_LENGTH} * 2 (both limits inclusive). 531 | * - This array is only grown, never shrunk. 532 | * - If state is PENDING, this list may be null. 533 | * - If state is DONE, this list must be null. 534 | * - All accesses to this list must be done while synchronizing on `this'. 535 | * Technically, this array could be used as a circular buffer to save RAM. 536 | * However because the typical life of a Deferred is to accumulate callbacks 537 | * and then run through them all in one shot, this would mostly lead to more 538 | * complicated code with little practical gains. Circular buffer would help 539 | * when item removals and insertions are interspersed, but this is uncommon. 540 | * @see #next_callback 541 | * @see #last_callback 542 | */ 543 | private Callback[] callbacks; 544 | 545 | /** 546 | * Index in {@link #callbacks} of the next callback to invoke. 547 | * Invariants: 548 | * - When entering DONE, this value is reset to 0. 549 | * - All the callbacks prior to this index are null. 550 | * - If `callbacks' isn't null, the callback at this index is not null 551 | * unless {@code next_callback == last_callback}. 552 | * - All accesses to this value must be done while synchronizing on `this'. 553 | */ 554 | private short next_callback; 555 | 556 | /** 557 | * Index in {@link #callbacks} past the last callback to invoke. 558 | * Invariants: 559 | * - When entering DONE, this value is reset to 0. 560 | * - All the callbacks at and after this index are null. 561 | * - This value might be equal to {@code callbacks.length}. 562 | * - All accesses to this value must be done while synchronizing on `this'. 563 | */ 564 | private short last_callback; 565 | 566 | /** Helper for atomic CAS on the state. */ 567 | private static final AtomicIntegerFieldUpdater stateUpdater = 568 | AtomicIntegerFieldUpdater.newUpdater(Deferred.class, "state"); 569 | 570 | /** 571 | * Atomically compares and swaps the state of this Deferred. 572 | * @param cmp The expected state to compare against. 573 | * @param val The new state to transition to if the comparison is successful. 574 | * @return {@code true} if the CAS succeeded, {@code false} otherwise. 575 | */ 576 | private boolean casState(final int cmp, final int val) { 577 | return stateUpdater.compareAndSet(this, cmp, val); 578 | } 579 | 580 | /** Constructor. */ 581 | public Deferred() { 582 | state = PENDING; 583 | } 584 | 585 | /** Private constructor used when the result is already available. */ 586 | private Deferred(final Object result) { 587 | this.result = result; 588 | state = DONE; 589 | } 590 | 591 | /** 592 | * Constructs a {@code Deferred} with a result that's readily available. 593 | *

594 | * This is equivalent to writing: 595 | *

{@literal
 596 |    *   Deferred d = new Deferred();
 597 |    *   d.callback(result);
 598 |    * }
599 | * Callbacks added to this {@code Deferred} will be immediately called. 600 | * @param result The "deferred" result. 601 | * @return a new {@code Deferred}. 602 | */ 603 | public static Deferred fromResult(final T result) { 604 | return new Deferred(result); 605 | } 606 | 607 | /** 608 | * Constructs a {@code Deferred} with an error that's readily available. 609 | *

610 | * This is equivalent to writing: 611 | *

{@literal
 612 |    *   Deferred d = new Deferred();
 613 |    *   d.callback(error);
 614 |    * }
615 | * Errbacks added to this {@code Deferred} will be immediately called. 616 | * @param error The error to use as a result. 617 | */ 618 | public static Deferred fromError(final Exception error) { 619 | return new Deferred(error); 620 | } 621 | 622 | /** 623 | * Registers a callback and an "errback". 624 | *

625 | * If the deferred result is already available, the callback or the errback 626 | * (depending on the nature of the result) is executed immediately from this 627 | * thread. 628 | * @param cb The callback to register. 629 | * @param eb Th errback to register. 630 | * @return {@code this} with an "updated" type. 631 | * @throws CallbackOverflowError if there are too many callbacks in this chain. 632 | * The maximum number of callbacks allowed in a chain is set by the 633 | * implementation. The limit is high enough that you shouldn't have to worry 634 | * about this exception (which is why it's an {@link Error} actually). If 635 | * you hit it, you probably did something wrong. 636 | */ 637 | @SuppressWarnings("unchecked") 638 | public Deferred addCallbacks(final Callback cb, 639 | final Callback eb) { 640 | if (cb == null) { 641 | throw new NullPointerException("null callback"); 642 | } else if (eb == null) { 643 | throw new NullPointerException("null errback"); 644 | } 645 | // We need to synchronize on `this' first before the CAS, to prevent 646 | // runCallbacks from switching our state from RUNNING to DONE right 647 | // before we add another callback. 648 | synchronized (this) { 649 | // If we're DONE, switch to RUNNING atomically. 650 | if (state == DONE) { 651 | // This "check-then-act" sequence is safe as this is the only code 652 | // path that transitions from DONE to RUNNING and it's synchronized. 653 | state = RUNNING; 654 | } else { 655 | // We get here if weren't DONE (most common code path) 656 | // -or- 657 | // if we were DONE and another thread raced with us to change the 658 | // state and we lost the race (uncommon). 659 | if (callbacks == null) { 660 | callbacks = new Callback[INIT_CALLBACK_CHAIN_SIZE]; 661 | } 662 | // Do we need to grow the array? 663 | else if (last_callback == callbacks.length) { 664 | final int oldlen = callbacks.length; 665 | if (oldlen == MAX_CALLBACK_CHAIN_LENGTH * 2) { 666 | throw new CallbackOverflowError("Too many callbacks in " + this 667 | + " (size=" + (oldlen / 2) + ") when attempting to add cb=" 668 | + cb + '@' + cb.hashCode() + ", eb=" + eb + '@' + eb.hashCode()); 669 | } 670 | final int len = Math.min(oldlen * 2, MAX_CALLBACK_CHAIN_LENGTH * 2); 671 | final Callback[] newcbs = new Callback[len]; 672 | System.arraycopy(callbacks, next_callback, // Outstanding callbacks. 673 | newcbs, 0, // Move them to the beginning. 674 | last_callback - next_callback); // Number of items. 675 | last_callback -= next_callback; 676 | next_callback = 0; 677 | callbacks = newcbs; 678 | } 679 | callbacks[last_callback++] = cb; 680 | callbacks[last_callback++] = eb; 681 | return (Deferred) ((Deferred) this); 682 | } 683 | } // end of synchronized block 684 | 685 | if (!doCall(result instanceof Exception ? eb : cb)) { 686 | // While we were executing the callback, another thread could have 687 | // added more callbacks. If doCall returned true, it means we're 688 | // PAUSED, so we won't reach this point, because the Deferred we're 689 | // waiting on will call us back later. But if we're still in state 690 | // RUNNING, we'll get to here, and we must check to see if any new 691 | // callbacks were added while we were executing doCall, because if 692 | // there are, we must execute them immediately, because no one else 693 | // is going to execute them for us otherwise. 694 | boolean more; 695 | synchronized (this) { 696 | more = callbacks != null && next_callback != last_callback; 697 | } 698 | if (more) { 699 | runCallbacks(); // Will put us back either in DONE or in PAUSED. 700 | } else { 701 | state = DONE; 702 | } 703 | } 704 | return (Deferred) ((Object) this); 705 | } 706 | 707 | /** 708 | * Registers a callback. 709 | *

710 | * If the deferred result is already available and isn't an exception, the 711 | * callback is executed immediately from this thread. 712 | * If the deferred result is already available and is an exception, the 713 | * callback is discarded. 714 | * If the deferred result is not available, this callback is queued and will 715 | * be invoked from whichever thread gives this deferred its initial result 716 | * by calling {@link #callback}. 717 | * @param cb The callback to register. 718 | * @return {@code this} with an "updated" type. 719 | */ 720 | public Deferred addCallback(final Callback cb) { 721 | return addCallbacks(cb, Callback.PASSTHROUGH); 722 | } 723 | 724 | /** 725 | * Registers a callback. 726 | *

727 | * This has the exact same effect as {@link #addCallback}, but keeps the type 728 | * information "correct" when the callback to add returns a {@code Deferred}. 729 | * @param cb The callback to register. 730 | * @return {@code this} with an "updated" type. 731 | */ 732 | @SuppressWarnings("unchecked") 733 | public > 734 | Deferred addCallbackDeferring(final Callback cb) { 735 | return addCallbacks((Callback) ((Object) cb), Callback.PASSTHROUGH); 736 | } 737 | 738 | /** 739 | * Registers an "errback". 740 | *

741 | * If the deferred result is already available and is an exception, the 742 | * errback is executed immediately from this thread. 743 | * If the deferred result is already available and isn't an exception, the 744 | * errback is discarded. 745 | * If the deferred result is not available, this errback is queued and will 746 | * be invoked from whichever thread gives this deferred its initial result 747 | * by calling {@link #callback}. 748 | * @param eb The errback to register. 749 | * @return {@code this} with an "updated" type. 750 | */ 751 | @SuppressWarnings("unchecked") 752 | public Deferred addErrback(final Callback eb) { 753 | return addCallbacks((Callback) ((Object) Callback.PASSTHROUGH), eb); 754 | } 755 | 756 | /** 757 | * Registers a callback both as a callback and as an "errback". 758 | *

759 | * If the deferred result is already available, the callback is executed 760 | * immediately from this thread (regardless of whether or not the current 761 | * result is an exception). 762 | * If the deferred result is not available, this callback is queued and will 763 | * be invoked from whichever thread gives this deferred its initial result 764 | * by calling {@link #callback}. 765 | * @param cb The callback to register. 766 | * @return {@code this} with an "updated" type. 767 | */ 768 | public Deferred addBoth(final Callback cb) { 769 | return addCallbacks(cb, cb); 770 | } 771 | 772 | /** 773 | * Registers a callback both as a callback and as an "errback". 774 | *

775 | * This has the exact same effect as {@link #addBoth}, but keeps the type 776 | * information "correct" when the callback to add returns a {@code Deferred}. 777 | * @param cb The callback to register. 778 | * @return {@code this} with an "updated" type. 779 | */ 780 | @SuppressWarnings("unchecked") 781 | public > 782 | Deferred addBothDeferring(final Callback cb) { 783 | return addCallbacks((Callback) ((Object) cb), 784 | (Callback) ((Object) cb)); 785 | } 786 | 787 | /** 788 | * Chains another {@code Deferred} to this one. 789 | *

790 | * This method simply ensures that whenever the callback chain in 791 | * {@code this} is run, then the callback chain in {@code other} 792 | * gets run too. The result handed to {@code other} is whatever 793 | * result {@code this} currently has. 794 | *

795 | * One case where this is particularly useful is when you want to multiplex 796 | * a result to multiple {@code Deferred}s, that is if multiple "listeners" 797 | * want to have their callback chain triggered when a single event completes: 798 | *

{@literal
 799 |    * public class ResultMultiplexer {
 800 |    *   private Deferred listeners = new Deferred();
 801 |    *
 802 |    *   public void addListener(Deferred d) {
 803 |    *     listeners.chain(d);
 804 |    *   }
 805 |    *
 806 |    *   public void emitResult(Foo event) {
 807 |    *     listeners.callback(event);
 808 |    *     // Remember that a Deferred is a one-time callback chain.
 809 |    *     // Once emitResult is called, everyone interested in getting the
 810 |    *     // next event would need to call addListener again.  This isn't a
 811 |    *     // pub-sub system, it's just showing how to multiplex a result.
 812 |    *     listeners = new Deferred();
 813 |    *   }
 814 |    * }
 815 |    * }
816 | * @param other The {@code Deferred} to chain to this one. 817 | * @return {@code this}, always. 818 | * @throws AssertionError if {@code this == other}. 819 | */ 820 | public Deferred chain(final Deferred other) { 821 | if (this == other) { 822 | throw new AssertionError("A Deferred cannot be chained to itself." 823 | + " this=" + this); 824 | } 825 | final Chain cb = new Chain(other); 826 | return addCallbacks(cb, cb); 827 | } 828 | 829 | /** 830 | * Callback used to chain a deferred with another one. 831 | */ 832 | private static final class Chain implements Callback { 833 | private final Deferred other; 834 | 835 | /** 836 | * Constructor. 837 | * @param other The other deferred to chain with. 838 | */ 839 | public Chain(final Deferred other) { 840 | this.other = other; 841 | } 842 | 843 | public T call(final T arg) { 844 | other.callback(arg); 845 | return arg; 846 | } 847 | 848 | public String toString() { 849 | return "chain with Deferred@" + other.hashCode(); 850 | } 851 | }; 852 | 853 | /** 854 | * Groups multiple {@code Deferred}s together in a single one. 855 | *

856 | * Conceptually, this does the opposite of {@link #chain}, in the sense that 857 | * it demultiplexes multiple {@code Deferred}s into a single one, so that you 858 | * can easily take an action once all the {@code Deferred}s in the group have 859 | * been called back. 860 | *

{@literal
 861 |    * public class ResultDemultiplexer {
 862 |    *   private ArrayList> deferreds = new ArrayList>();
 863 |    *
 864 |    *   public void addDeferred(final Deferred d) {
 865 |    *     deferreds.add(d);
 866 |    *   }
 867 |    *
 868 |    *   public Deferred> demultiplex() {
 869 |    *     Deferred> demultiplexed = Deferred.group(deferreds);
 870 |    *     deferreds.clear();
 871 |    *     return demultiplexed;
 872 |    *   }
 873 |    * }
 874 |    * }
875 | * In the example above, any number of {@code Deferred} can be added to 876 | * the demultiplexer, and once {@code demultiplex()} is invoked, the 877 | * {@code Deferred} returned will be invoked once all the {@code Deferred}s 878 | * added to the demultiplexer have been called back. 879 | * @param deferreds All the {@code Deferred}s to group together. 880 | * @return A new {@code Deferred} that will be called back once all the 881 | * {@code Deferred}s given in argument have been called back. Each element 882 | * in the list will be either of type {@code } or an {@link Exception}. 883 | * If any of the elements in the list is an {@link Exception}, 884 | * the errback of the {@code Deferred} returned will be invoked 885 | * with a {@link DeferredGroupException} in argument. 886 | *

887 | * There's no guarantee on the order of the results in the deferred list 888 | * returned, it depends on the order in which the {@code Deferred}s in the 889 | * group complete. If you want to preserve the order, use 890 | * {@link #groupInOrder(Collection)} instead. 891 | */ 892 | public static 893 | Deferred> group(final Collection> deferreds) { 894 | return new DeferredGroup(deferreds, false).getDeferred(); 895 | } 896 | 897 | /** 898 | * Groups multiple {@code Deferred}s together in a single one. 899 | *

900 | * This is the same thing as {@link #group(Collection)} except that we 901 | * guarantee we preserve the order of the {@code Deferred}s. 902 | * @param deferreds All the {@code Deferred}s to group together. 903 | * @return A new {@code Deferred} that will be called back once all the 904 | * {@code Deferred}s given in argument have been called back. 905 | * @see #group(Collection) 906 | * @since 1.4 907 | */ 908 | public static 909 | Deferred> groupInOrder(final Collection> deferreds) { 910 | return new DeferredGroup(deferreds, true).getDeferred(); 911 | } 912 | 913 | /** 914 | * Groups two {@code Deferred}s together in a single one. 915 | *

916 | * This is semantically equivalent to: 917 | *

{@link #group(Collection) group}({@link java.util.Arrays#asList
 918 |    * Arrays.asList}(d1, d2));
except that it's type safe as it doesn't 919 | * involve an unchecked generic array creation of type {@code Deferred} 920 | * for the varargs parameter passed to {@code asList}. 921 | * @param d1 The first {@code Deferred} to put in the group. 922 | * @param d2 The second {@code Deferred} to put in the group. 923 | * @return A new {@code Deferred} that will be called back once both 924 | * {@code Deferred}s given in argument have been called back. 925 | * @see #group(Collection) 926 | */ 927 | public static 928 | Deferred> group(final Deferred d1, final Deferred d2) { 929 | final ArrayList> tmp = new ArrayList>(2); 930 | tmp.add(d1); 931 | tmp.add(d2); 932 | return new DeferredGroup(tmp, false).getDeferred(); 933 | } 934 | 935 | /** 936 | * Groups three {@code Deferred}s together in a single one. 937 | *

938 | * This is semantically equivalent to: 939 | *

{@link #group(Collection) group}({@link java.util.Arrays#asList
 940 |    * Arrays.asList}(d1, d2, d3));
except that it's type safe as it doesn't 941 | * involve an unchecked generic array creation of type {@code Deferred} 942 | * for the varargs parameter passed to {@code asList}. 943 | * @param d1 The first {@code Deferred} to put in the group. 944 | * @param d2 The second {@code Deferred} to put in the group. 945 | * @param d3 The third {@code Deferred} to put in the group. 946 | * @return A new {@code Deferred} that will be called back once all three 947 | * {@code Deferred}s given in argument have been called back. 948 | * @see #group(Collection) 949 | */ 950 | public static 951 | Deferred> group(final Deferred d1, 952 | final Deferred d2, 953 | final Deferred d3) { 954 | final ArrayList> tmp = new ArrayList>(3); 955 | tmp.add(d1); 956 | tmp.add(d2); 957 | tmp.add(d3); 958 | return new DeferredGroup(tmp, false).getDeferred(); 959 | } 960 | 961 | /** 962 | * Starts running the callback chain. 963 | *

964 | * This posts the initial result that will be passed to the first callback 965 | * in the callback chain. If the argument is an {@link Exception} then 966 | * the "errback" chain will be triggered instead. 967 | *

968 | * This method will not let any {@link Exception} thrown by a callback 969 | * propagate. You shouldn't try to catch any {@link RuntimeException} when 970 | * you call this method, as this is unnecessary. 971 | * @param initresult The initial result with which to start the 1st callback. 972 | * The following must be true: 973 | * {@code initresult instanceof T || initresult instanceof }{@link Exception} 974 | * @throws AssertionError if this method was already called on this instance. 975 | * @throws AssertionError if {@code initresult == this}. 976 | */ 977 | public void callback(final Object initresult) { 978 | if (!casState(PENDING, RUNNING)) { 979 | throw new AssertionError("This Deferred was already called!" 980 | + " New result=" + initresult + ", this=" + this); 981 | } 982 | result = initresult; 983 | if (initresult instanceof Deferred) { 984 | // Twisted doesn't allow a callback chain to start with another Deferred 985 | // but I don't see any reason. Maybe it was to help prevent people from 986 | // creating recursive callback chains that would never terminate? We 987 | // already check for the obvious in handleContinuation by preventing 988 | // this Deferred from depending on itself, but there's no way to prevent 989 | // people from building mutually dependant Deferreds or complex cyclic 990 | // chains of Deferreds, unless we keep track in a set of all the 991 | // Deferreds we go through while executing a callback chain, which seems 992 | // like an unnecessary complication for uncommon cases (bad code). Plus, 993 | // when that actually happens and people write buggy code that creates 994 | // cyclic chains, they will quickly get a CallbackOverflowError. 995 | final Deferred d = (Deferred) initresult; 996 | if (this == d) { 997 | throw new AssertionError("A Deferred cannot be given to itself" 998 | + " as a result. this=" + this); 999 | } 1000 | handleContinuation(d, null); 1001 | } 1002 | runCallbacks(); 1003 | } 1004 | 1005 | /** 1006 | * Synchronously waits until this Deferred is called back. 1007 | *

1008 | * This helps do synchronous operations using an asynchronous API. 1009 | * If this Deferred already completed, this method returns (or throws) 1010 | * immediately. Otherwise, the current thread will be blocked 1011 | * and will wait until the Deferred is called back. 1012 | * @return The deferred result, at this point in the callback chain. 1013 | * @throws InterruptedException if this thread was interrupted before the 1014 | * deferred result became available. 1015 | * @throws Exception if the deferred result is an exception, this exception 1016 | * will be thrown. 1017 | */ 1018 | public T join() throws InterruptedException, Exception { 1019 | return doJoin(true, 0); 1020 | } 1021 | 1022 | /** 1023 | * Synchronously waits until this Deferred is called back or a timeout occurs. 1024 | *

1025 | * This helps do synchronous operations using an asynchronous API. 1026 | * If this Deferred already completed, this method returns (or throws) 1027 | * immediately. Otherwise, the current thread will be blocked 1028 | * and will wait until the Deferred is called back or the specified amount 1029 | * of time has elapsed. 1030 | * @param timeout The maximum time to wait in milliseconds. A value of 0 1031 | * means no timeout. 1032 | * @return The deferred result, at this point in the callback chain. 1033 | * @throws InterruptedException if this thread was interrupted before the 1034 | * deferred result became available. 1035 | * @throws IllegalArgumentException If the value of timeout is negative. 1036 | * @throws TimeoutException if there's a timeout. 1037 | * @throws Exception if the deferred result is an exception, this exception 1038 | * will be thrown. 1039 | * @since 1.1 1040 | */ 1041 | public T join(final long timeout) throws InterruptedException, Exception { 1042 | return doJoin(true, timeout); 1043 | } 1044 | 1045 | /** 1046 | * Synchronously waits until this Deferred is called back. 1047 | *

1048 | * This helps do synchronous operations using an asynchronous API. 1049 | * If this Deferred already completed, this method returns (or throws) 1050 | * immediately. Otherwise, the current thread will be blocked 1051 | * and will wait until the Deferred is called back. If the current thread 1052 | * gets interrupted while waiting, it will keep waiting anyway until the 1053 | * callback chain terminates, before returning (or throwing an exception) 1054 | * the interrupted status on the thread will be set again. 1055 | * @return The deferred result, at this point in the callback chain. 1056 | * @throws Exception if the deferred result is an exception, this exception 1057 | * will be thrown. 1058 | */ 1059 | public T joinUninterruptibly() throws Exception { 1060 | try { 1061 | return doJoin(false, 0); 1062 | } catch (InterruptedException e) { 1063 | throw new AssertionError("Impossible"); 1064 | } 1065 | } 1066 | 1067 | /** 1068 | * Synchronously waits until this Deferred is called back or a timeout occurs. 1069 | *

1070 | * This helps do synchronous operations using an asynchronous API. 1071 | * If this Deferred already completed, this method returns (or throws) 1072 | * immediately. Otherwise, the current thread will be blocked 1073 | * and will wait until the Deferred is called back or the specified amount 1074 | * of time has elapsed. If the current thread gets interrupted while 1075 | * waiting, it will keep waiting anyway until the callback chain terminates, 1076 | * before returning (or throwing an exception) the interrupted status on the 1077 | * thread will be set again. 1078 | * @param timeout The maximum time to wait in milliseconds. A value of 0 1079 | * means no timeout. 1080 | * @return The deferred result, at this point in the callback chain. 1081 | * @throws IllegalArgumentException If the value of timeout is negative. 1082 | * @throws TimeoutException if there's a timeout. 1083 | * @throws Exception if the deferred result is an exception, this exception 1084 | * will be thrown. 1085 | * @since 1.1 1086 | */ 1087 | public T joinUninterruptibly(final long timeout) throws Exception { 1088 | try { 1089 | return doJoin(false, timeout); 1090 | } catch (InterruptedException e) { 1091 | throw new AssertionError("Impossible"); 1092 | } 1093 | } 1094 | 1095 | /** 1096 | * Synchronously waits until this Deferred is called back. 1097 | * @param interruptible Whether or not to let {@link InterruptedException} 1098 | * interrupt us. If {@code false} then this method will never throw 1099 | * {@link InterruptedException}, it will handle it and keep waiting. In 1100 | * this case though, the interrupted status on this thread will be set when 1101 | * this method returns. 1102 | * @param timeout The maximum time to wait in milliseconds. A value of 0 1103 | * means no timeout. 1104 | * @return The deferred result, at this point in the callback chain. 1105 | * @throws IllegalArgumentException If the value of timeout is negative. 1106 | * @throws InterruptedException if {@code interruptible} is {@code true} and 1107 | * this thread was interrupted while waiting. 1108 | * @throws Exception if the deferred result is an exception, this exception 1109 | * will be thrown. 1110 | * @throws TimeoutException if there's a timeout. 1111 | */ 1112 | @SuppressWarnings("unchecked") 1113 | private T doJoin(final boolean interruptible, final long timeout) 1114 | throws InterruptedException, Exception { 1115 | if (state == DONE) { // Nothing to join, we're already DONE. 1116 | if (result instanceof Exception) { 1117 | throw (Exception) result; 1118 | } 1119 | return (T) result; 1120 | } 1121 | 1122 | final Signal signal_cb = new Signal(); 1123 | 1124 | // Dealing with InterruptedException properly is a PITA. I highly 1125 | // recommend reading http://goo.gl/aeOOXT to understand how this works. 1126 | boolean interrupted = false; 1127 | try { 1128 | while (true) { 1129 | try { 1130 | boolean timedout = false; 1131 | synchronized (signal_cb) { 1132 | addBoth((Callback) ((Object) signal_cb)); 1133 | if (timeout == 0) { // No timeout, we can use a simple loop. 1134 | // If we get called back immediately, we won't enter the loop. 1135 | while (signal_cb.result == signal_cb) { 1136 | signal_cb.wait(); 1137 | } 1138 | } else if (timeout < 0) { 1139 | throw new IllegalArgumentException("negative timeout: " + timeout); 1140 | } else { // We have a timeout, the loop is a bit more complicated. 1141 | long timeleft = timeout * 1000000L; // Convert to nanoseconds. 1142 | if (timeout > 31556926000L) { // One year in milliseconds. 1143 | // Most likely a programming bug. 1144 | LOG.warn("Timeout (" + timeout + ") is long than 1 year." 1145 | + " this=" + this); 1146 | if (timeleft <= 0) { // Very unlikely. 1147 | throw new IllegalArgumentException("timeout overflow after" 1148 | + " conversion to nanoseconds: " + timeout); 1149 | } 1150 | } 1151 | // If we get called back immediately, we won't enter the loop. 1152 | while (signal_cb.result == signal_cb) { 1153 | // We can't distinguish between a timeout and a spurious wakeup. 1154 | // So we have to time how long we slept to figure out whether we 1155 | // timed out or how long we need to sleep again. There's no 1156 | // better way to do this, that's how it's implemented in the JDK 1157 | // in `AbstractQueuedSynchronizer.ConditionObject.awaitNanos()'. 1158 | long duration = System.nanoTime(); 1159 | final long millis = timeleft / 1000000L; 1160 | final int nanos = (int) (timeleft % 1000000); 1161 | // This API is annoying because it won't let us specify just 1162 | // nanoseconds. The second argument must be less than 1000000. 1163 | signal_cb.wait(millis, nanos); 1164 | duration = System.nanoTime() - duration; 1165 | timeleft -= duration; 1166 | // If we have 0ns or less left, the timeout has expired 1167 | // already. If we have less than 100ns left, there's no 1168 | // point in looping again, as just going through the loop 1169 | // above easily takes 100ns on a modern x86-64 CPU, after 1170 | // JIT compilation. `nanoTime()' is fairly cheap since it's 1171 | // not even a system call, it just reads a hardware counter. 1172 | // But entering `wait' is pretty much guaranteed to make the 1173 | // loop take more than 100ns no matter what. 1174 | if (timeleft < 100) { 1175 | timedout = true; 1176 | break; 1177 | } 1178 | } 1179 | } 1180 | } 1181 | if (timedout && signal_cb.result == signal_cb) { 1182 | // Give up if we timed out *and* we haven't gotten a result yet. 1183 | throw new TimeoutException(this, timeout); 1184 | } else if (signal_cb.result instanceof Exception) { 1185 | throw (Exception) signal_cb.result; 1186 | } 1187 | return (T) signal_cb.result; 1188 | } catch (InterruptedException e) { 1189 | LOG.debug("While joining {}: interrupted", this); 1190 | interrupted = true; 1191 | if (interruptible) { 1192 | throw e; // Let the exception propagate out. 1193 | } 1194 | } 1195 | } 1196 | } finally { 1197 | if (interrupted) { 1198 | Thread.currentThread().interrupt(); // Restore the interrupted status. 1199 | } 1200 | } 1201 | } 1202 | 1203 | /** 1204 | * Callback to wake us up when this Deferred is running. 1205 | * We will wait() on the intrinsic condition of this callback. 1206 | */ 1207 | static final class Signal implements Callback { 1208 | // When the callback triggers, it'll replace the reference in this 1209 | // array to something other than `this' -- since the result cannot 1210 | // possibly be `this'. 1211 | Object result = this; 1212 | 1213 | private final String thread = Thread.currentThread().getName(); 1214 | 1215 | public Object call(final Object arg) { 1216 | synchronized (this) { 1217 | result = arg; 1218 | super.notify(); // Guaranteed to have only 1 thread wait()ing. 1219 | } 1220 | return arg; 1221 | } 1222 | 1223 | public String toString() { 1224 | return "wakeup thread " + thread; 1225 | } 1226 | }; 1227 | 1228 | /** 1229 | * Executes all the callbacks in the current chain. 1230 | */ 1231 | private void runCallbacks() { 1232 | while (true) { 1233 | Callback cb = null; 1234 | Callback eb = null; 1235 | synchronized (this) { 1236 | // Read those into local variables so we can call doCall and invoke the 1237 | // callbacks without holding the lock on `this', which would cause a 1238 | // deadlock if we try to addCallbacks to `this' while a callback is 1239 | // running. 1240 | if (callbacks != null && next_callback != last_callback) { 1241 | cb = callbacks[next_callback++]; 1242 | eb = callbacks[next_callback++]; 1243 | } 1244 | // Also, we may need to atomically change the state to DONE. 1245 | // Otherwise if another thread is blocked in addCallbacks right before 1246 | // we're done processing the last element, we'd enter state DONE and 1247 | // leave this method, and then addCallbacks would add callbacks that 1248 | // would never get called. 1249 | else { 1250 | state = DONE; 1251 | callbacks = null; 1252 | next_callback = last_callback = 0; 1253 | break; 1254 | } 1255 | } 1256 | 1257 | //final long start = System.nanoTime(); 1258 | //LOG.debug("START >>>>>>>>>>>>>>>>>> doCall(" + cb + ", " + eb + ')'); 1259 | if (doCall(result instanceof Exception ? eb : cb)) { 1260 | //LOG.debug("PAUSE ================== doCall(" + cb + ", " + eb 1261 | // + ") in " + (System.nanoTime() - start) / 1000 + "us"); 1262 | break; 1263 | } 1264 | //LOG.debug("DONE <<<<<<<<<<<<<<<<<< doCall(" + cb + ", " + eb 1265 | // + "), result=" + result 1266 | // + " in " + (System.nanoTime() - start) / 1000 + "us"); 1267 | } 1268 | } 1269 | 1270 | /** 1271 | * Executes a single callback, handling continuation if it returns a Deferred. 1272 | * @param cb The callback to execute. 1273 | * @return {@code true} if the callback returned a Deferred and we switched to 1274 | * PAUSED, {@code false} otherwise and we didn't change state. 1275 | */ 1276 | @SuppressWarnings("unchecked") 1277 | private boolean doCall(final Callback cb) { 1278 | try { 1279 | //LOG.debug("doCall(" + cb + '@' + cb.hashCode() + ')' + super.hashCode()); 1280 | result = cb.call(result); 1281 | } catch (Exception e) { 1282 | result = e; 1283 | } 1284 | 1285 | if (result instanceof Deferred) { 1286 | handleContinuation((Deferred) result, cb); 1287 | return true; 1288 | } 1289 | return false; 1290 | } 1291 | 1292 | /** 1293 | * Chains this Deferred with another one we need to wait on. 1294 | * @param d The other Deferred we need to wait on. 1295 | * @param cb The callback that returned that Deferred or {@code null} if we 1296 | * don't know where this Deferred comes from (it was our initial result). 1297 | * @throws AssertionError if {@code this == d} as this would cause an 1298 | * infinite recursion. 1299 | */ 1300 | @SuppressWarnings("unchecked") 1301 | private void handleContinuation(final Deferred d, final Callback cb) { 1302 | if (this == d) { 1303 | final String cb2s = cb == null ? "null" : cb + "@" + cb.hashCode(); 1304 | throw new AssertionError("After " + this + " executed callback=" + cb2s 1305 | + ", the result returned was the same Deferred object. This is illegal" 1306 | + ", a Deferred can't run itself recursively. Something is wrong."); 1307 | } 1308 | // Optimization: if `d' is already DONE, instead of calling 1309 | // adding a callback on `d' to continue when `d' completes, 1310 | // we atomically read the result off of `d' immediately. To 1311 | // do this safely, we need to change `d's state momentarily. 1312 | if (d.casState(DONE, RUNNING)) { 1313 | result = d.result; // No one will change `d.result' now. 1314 | d.state = DONE; 1315 | runCallbacks(); 1316 | return; 1317 | } 1318 | 1319 | // Our `state' was either RUNNING or DONE. 1320 | // If it was RUNNING, we have to suspend the callback chain here and 1321 | // resume when the result of that other Deferred is available. 1322 | // If it was DONE, well we're no longer DONE because we now need to wait 1323 | // on that other Deferred to complete, so we're PAUSED again. 1324 | state = PAUSED; 1325 | d.addBoth(new Continue(d, cb)); 1326 | // If d is DONE and our callback chain is empty, we're now in state DONE. 1327 | // Otherwise we're still in state PAUSED. 1328 | if (LOG.isDebugEnabled() && state == PAUSED) { 1329 | if (cb != null) { 1330 | LOG.debug("callback=" + cb + '@' + cb.hashCode() + " returned " + d 1331 | + ", so the following Deferred is getting paused: " + this); 1332 | } else { 1333 | LOG.debug("The following Deferred is getting paused: " + this 1334 | + " as it received another Deferred as a result: " + d); 1335 | } 1336 | } 1337 | } 1338 | 1339 | /** 1340 | * A {@link Callback} to resume execution after another Deferred. 1341 | */ 1342 | private final class Continue implements Callback { 1343 | private final Deferred d; 1344 | private final Callback cb; 1345 | 1346 | /** 1347 | * Constructor. 1348 | * @param d The other Deferred we need to resume after. 1349 | * @param cb The callback that returned that Deferred or {@code null} if we 1350 | * don't know where this Deferred comes from (it was our initial result). 1351 | */ 1352 | public Continue(final Deferred d, final Callback cb) { 1353 | this.d = d; 1354 | this.cb = cb; 1355 | } 1356 | 1357 | public Object call(final Object arg) { 1358 | if (arg instanceof Deferred) { 1359 | handleContinuation((Deferred) arg, cb); 1360 | } else if (!casState(PAUSED, RUNNING)) { 1361 | final String cb2s = cb == null ? "null" : cb + "@" + cb.hashCode(); 1362 | throw new AssertionError("Tried to resume the execution of " 1363 | + Deferred.this + ") although it's not in state=PAUSED." 1364 | + " This occurred after the completion of " + d 1365 | + " which was originally returned by callback=" + cb2s); 1366 | } 1367 | result = arg; 1368 | runCallbacks(); 1369 | return arg; 1370 | } 1371 | 1372 | public String toString() { 1373 | return "(continuation of Deferred@" + Deferred.super.hashCode() 1374 | + " after " + (cb != null ? cb + "@" + cb.hashCode() : d) + ')'; 1375 | } 1376 | }; 1377 | 1378 | /** 1379 | * Returns a helpful string representation of this {@code Deferred}. 1380 | *

1381 | * The string returned is built in {@code O(N)} where {@code N} is the 1382 | * number of callbacks currently in the chain. The string isn't built 1383 | * entirely atomically, so it can appear to show this {@code Deferred} 1384 | * in a slightly inconsistent state. 1385 | *

1386 | * This method is not cheap. Avoid doing: 1387 | *

{@literal
1388 |    * Deferred d = ..;
1389 |    * LOG.debug("Got " + d);
1390 |    * }
1391 | * The overhead of stringifying the {@code Deferred} can be significant, 1392 | * especially if this is in the fast-path of your application. 1393 | */ 1394 | public String toString() { 1395 | final int state = this.state; // volatile access before reading result. 1396 | final Object result = this.result; 1397 | final String str; 1398 | if (result == null) { 1399 | str = "null"; 1400 | } else if (result instanceof Deferred) { // Nested Deferreds 1401 | str = "Deferred@" + result.hashCode(); // are hard to read. 1402 | } else { 1403 | str = result.toString(); 1404 | } 1405 | 1406 | // We can't easily estimate how much space we'll need for the callback 1407 | // chains, so let's just make sure we have enough space for all the static 1408 | // cruft and the result, and double that to avoid the first re-allocation. 1409 | // If result is a very long string, we may end up wasting some space, but 1410 | // it's not likely to happen and even less likely to be a problem. 1411 | final StringBuilder buf = new StringBuilder((9 + 10 + 7 + 7 1412 | + str.length()) * 2); 1413 | buf.append("Deferred@").append(super.hashCode()) 1414 | .append("(state=").append(stateString(state)) 1415 | .append(", result=").append(str) 1416 | .append(", callback="); 1417 | synchronized (this) { 1418 | if (callbacks == null || next_callback == last_callback) { 1419 | buf.append(", errback="); 1420 | } else { 1421 | for (int i = next_callback; i < last_callback; i += 2) { 1422 | buf.append(callbacks[i]).append(" -> "); 1423 | } 1424 | buf.setLength(buf.length() - 4); // Remove the extra " -> ". 1425 | buf.append(", errback="); 1426 | for (int i = next_callback + 1; i < last_callback; i += 2) { 1427 | buf.append(callbacks[i]).append(" -> "); 1428 | } 1429 | buf.setLength(buf.length() - 4); // Remove the extra " -> ". 1430 | } 1431 | } 1432 | buf.append(')'); 1433 | return buf.toString(); 1434 | } 1435 | 1436 | private static String stateString(final int state) { 1437 | switch (state) { 1438 | case PENDING: return "PENDING"; 1439 | case RUNNING: return "RUNNING"; 1440 | case PAUSED: return "PAUSED"; 1441 | case DONE: return "DONE"; 1442 | } 1443 | throw new AssertionError("Should never be here. WTF: state=" + state); 1444 | } 1445 | 1446 | } 1447 | --------------------------------------------------------------------------------