From: Hans Fugal Date: Mon, 27 Apr 2015 18:20:04 +0000 (-0700) Subject: rearrange Future.h X-Git-Tag: v0.36.0~3 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=abdd128c348e8c7406745660eda3d22cddf05a1b rearrange Future.h Summary: Move boring preamble stuff to `Future-pre.h` and `folly::futures` and `makeFuture` and pals to `helpers.h`. Test Plan: tests still build and pass Reviewed By: jsedgwick@fb.com Subscribers: exa, folly-diffs@, jsedgwick, yfeldblum, chalfant, hannesr, davejwatson FB internal diff: D2014330 Signature: t1:2014330:1429941589:1e2c336136f3375f9b96e5df8c06ca5820ba6aeb --- diff --git a/folly/Makefile.am b/folly/Makefile.am index adba4d1e..1c4030cf 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -122,8 +122,10 @@ nobase_follyinclude_HEADERS = \ Format-inl.h \ futures/Deprecated.h \ futures/DrivableExecutor.h \ - futures/Future-inl.h \ + futures/Future-pre.h \ + futures/helpers.h \ futures/Future.h \ + futures/Future-inl.h \ futures/FutureException.h \ futures/InlineExecutor.h \ futures/ManualExecutor.h \ diff --git a/folly/futures/Future-pre.h b/folly/futures/Future-pre.h new file mode 100644 index 00000000..70add42a --- /dev/null +++ b/folly/futures/Future-pre.h @@ -0,0 +1,139 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// included by Future.h, do not include directly. + +namespace folly { + +template struct Promise; + +template +struct isFuture : std::false_type { + typedef T Inner; +}; + +template +struct isFuture> : std::true_type { + typedef T Inner; +}; + +template +struct isTry : std::false_type {}; + +template +struct isTry> : std::true_type {}; + +namespace detail { + +template struct Core; +template struct VariadicContext; +template struct CollectContext; + +template +using resultOf = decltype(std::declval()(std::declval()...)); + +template +struct ArgType; + +template +struct ArgType { + typedef Arg FirstArg; +}; + +template <> +struct ArgType<> { + typedef void FirstArg; +}; + +template +struct argResult { + typedef resultOf Result; +}; + +template +struct callableWith { + template> + static constexpr std::true_type + check(std::nullptr_t) { return std::true_type{}; }; + + template + static constexpr std::false_type + check(...) { return std::false_type{}; }; + + typedef decltype(check(nullptr)) type; + static constexpr bool value = type::value; +}; + +template +struct callableResult { + typedef typename std::conditional< + callableWith::value, + detail::argResult, + typename std::conditional< + callableWith::value, + detail::argResult, + typename std::conditional< + callableWith::value, + detail::argResult, + typename std::conditional< + callableWith&&>::value, + detail::argResult&&>, + detail::argResult&>>::type>::type>::type>::type Arg; + typedef isFuture ReturnsFuture; + typedef Future Return; +}; + +template +struct callableResult { + typedef typename std::conditional< + callableWith::value, + detail::argResult, + typename std::conditional< + callableWith&&>::value, + detail::argResult&&>, + detail::argResult&>>::type>::type Arg; + typedef isFuture ReturnsFuture; + typedef Future Return; +}; + +template +struct Extract : Extract { }; + +template +struct Extract { + typedef isFuture ReturnsFuture; + typedef Future Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType::FirstArg FirstArg; +}; + +template +struct Extract { + typedef isFuture ReturnsFuture; + typedef Future Return; + typedef typename ReturnsFuture::Inner RawReturn; + typedef typename ArgType::FirstArg FirstArg; +}; + +} // detail + + +struct Timekeeper; + +} // namespace diff --git a/folly/futures/Future.h b/folly/futures/Future.h index 683c3cbb..0989873d 100644 --- a/folly/futures/Future.h +++ b/folly/futures/Future.h @@ -32,173 +32,14 @@ #include #include -namespace folly { - -template struct Promise; - -template -struct isFuture : std::false_type { - typedef T Inner; -}; - -template -struct isFuture> : std::true_type { - typedef T Inner; -}; - -template -struct isTry : std::false_type {}; - -template -struct isTry> : std::true_type {}; - -namespace detail { - -template struct Core; -template struct VariadicContext; -template struct CollectContext; - -template -using resultOf = decltype(std::declval()(std::declval()...)); - -template -struct ArgType; - -template -struct ArgType { - typedef Arg FirstArg; -}; - -template <> -struct ArgType<> { - typedef void FirstArg; -}; - -template -struct argResult { - typedef resultOf Result; -}; - -template -struct callableWith { - template> - static constexpr std::true_type - check(std::nullptr_t) { return std::true_type{}; }; - - template - static constexpr std::false_type - check(...) { return std::false_type{}; }; - - typedef decltype(check(nullptr)) type; - static constexpr bool value = type::value; -}; - -template -struct callableResult { - typedef typename std::conditional< - callableWith::value, - detail::argResult, - typename std::conditional< - callableWith::value, - detail::argResult, - typename std::conditional< - callableWith::value, - detail::argResult, - typename std::conditional< - callableWith&&>::value, - detail::argResult&&>, - detail::argResult&>>::type>::type>::type>::type Arg; - typedef isFuture ReturnsFuture; - typedef Future Return; -}; - -template -struct callableResult { - typedef typename std::conditional< - callableWith::value, - detail::argResult, - typename std::conditional< - callableWith&&>::value, - detail::argResult&&>, - detail::argResult&>>::type>::type Arg; - typedef isFuture ReturnsFuture; - typedef Future Return; -}; - -template -struct Extract : Extract { }; +// boring predeclarations and details +#include -template -struct Extract { - typedef isFuture ReturnsFuture; - typedef Future Return; - typedef typename ReturnsFuture::Inner RawReturn; - typedef typename ArgType::FirstArg FirstArg; -}; - -template -struct Extract { - typedef isFuture ReturnsFuture; - typedef Future Return; - typedef typename ReturnsFuture::Inner RawReturn; - typedef typename ArgType::FirstArg FirstArg; -}; - -} // detail - -struct Timekeeper; - -/// This namespace is for utility functions that would usually be static -/// members of Future, except they don't make sense there because they don't -/// depend on the template type (rather, on the type of their arguments in -/// some cases). This is the least-bad naming scheme we could think of. Some -/// of the functions herein have really-likely-to-collide names, like "map" -/// and "sleep". -namespace futures { - /// Returns a Future that will complete after the specified duration. The - /// Duration typedef of a `std::chrono` duration type indicates the - /// resolution you can expect to be meaningful (milliseconds at the time of - /// writing). Normally you wouldn't need to specify a Timekeeper, we will - /// use the global futures timekeeper (we run a thread whose job it is to - /// keep time for futures timeouts) but we provide the option for power - /// users. - /// - /// The Timekeeper thread will be lazily created the first time it is - /// needed. If your program never uses any timeouts or other time-based - /// Futures you will pay no Timekeeper thread overhead. - Future sleep(Duration, Timekeeper* = nullptr); +// not-boring helpers, e.g. all in folly::futures, makeFuture variants, etc. +// Needs to be included after Future-pre.h and before Future-inl.h +#include - /// Create a Future chain from a sequence of callbacks. i.e. - /// - /// f.then(a).then(b).then(c); - /// - /// where f is a Future and the result of the chain is a Future - /// becomes - /// - /// f.then(chain(a, b, c)); - // If anyone figures how to get chain to deduce A and Z, I'll buy you a drink. - template - std::function(Try)> - chain(Callbacks... fns); - - /** - * Set func as the callback for each input Future and return a vector of - * Futures containing the results in the input order. - */ - template ::value_type, - class Result = decltype(std::declval().then(std::declval()))> - std::vector> map(It first, It last, F func); - - // Sugar for the most common case - template - auto map(Collection&& c, F&& func) - -> decltype(map(c.begin(), c.end(), func)) { - return map(c.begin(), c.end(), std::forward(func)); - } - -} +namespace folly { template class Future { @@ -562,181 +403,6 @@ class Future { void setExecutor(Executor* x) { core_->setExecutor(x); } }; -/** - Make a completed Future by moving in a value. e.g. - - string foo = "foo"; - auto f = makeFuture(std::move(foo)); - - or - - auto f = makeFuture("foo"); -*/ -template -Future::type> makeFuture(T&& t); - -/** Make a completed void Future. */ -Future makeFuture(); - -/** Make a completed Future by executing a function. If the function throws - we capture the exception, otherwise we capture the result. */ -template -auto makeFutureWith( - F&& func, - typename std::enable_if< - !std::is_reference::value, bool>::type sdf = false) - -> Future; - -template -auto makeFutureWith( - F const& func) - -> Future; - -/// Make a failed Future from an exception_ptr. -/// Because the Future's type cannot be inferred you have to specify it, e.g. -/// -/// auto f = makeFuture(std::current_exception()); -template -Future makeFuture(std::exception_ptr const& e) DEPRECATED; - -/// Make a failed Future from an exception_wrapper. -template -Future makeFuture(exception_wrapper ew); - -/** Make a Future from an exception type E that can be passed to - std::make_exception_ptr(). */ -template -typename std::enable_if::value, - Future>::type -makeFuture(E const& e); - -/** Make a Future out of a Try */ -template -Future makeFuture(Try&& t); - -/* - * Return a new Future that will call back on the given Executor. - * This is just syntactic sugar for makeFuture().via(executor) - * - * @param executor the Executor to call back on - * - * @returns a void Future that will call back on the given executor - */ -template -Future via(Executor* executor); - -/** When all the input Futures complete, the returned Future will complete. - Errors do not cause early termination; this Future will always succeed - after all its Futures have finished (whether successfully or with an - error). - - The Futures are moved in, so your copies are invalid. If you need to - chain further from these Futures, use the variant with an output iterator. - - This function is thread-safe for Futures running on different threads. But - if you are doing anything non-trivial after, you will probably want to - follow with `via(executor)` because it will complete in whichever thread the - last Future completes in. - - The return type for Future input is a Future>> - */ -template -Future::value_type::value_type>>> -collectAll(InputIterator first, InputIterator last); - -// Sugar for the most common case -template -auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) { - return collectAll(c.begin(), c.end()); -} - -/// This version takes a varying number of Futures instead of an iterator. -/// The return type for (Future, Future, ...) input -/// is a Future, Try, ...>>. -/// The Futures are moved in, so your copies are invalid. -template -typename detail::VariadicContext< - typename std::decay::type::value_type...>::type -collectAll(Fs&&... fs); - -/// Like collectAll, but will short circuit on the first exception. Thus, the -/// type of the returned Future is std::vector instead of -/// std::vector> -template -Future::value_type::value_type ->::result_type> -collect(InputIterator first, InputIterator last); - -// Sugar for the most common case -template -auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) { - return collect(c.begin(), c.end()); -} - -/** The result is a pair of the index of the first Future to complete and - the Try. If multiple Futures complete at the same time (or are already - complete when passed in), the "winner" is chosen non-deterministically. - - This function is thread-safe for Futures running on different threads. - */ -template -Future::value_type::value_type>>> -collectAny(InputIterator first, InputIterator last); - -// Sugar for the most common case -template -auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) { - return collectAny(c.begin(), c.end()); -} - -/** when n Futures have completed, the Future completes with a vector of - the index and Try of those n Futures (the indices refer to the original - order, but the result vector will be in an arbitrary order) - - Not thread safe. - */ -template -Future::value_type::value_type>>>> -collectN(InputIterator first, InputIterator last, size_t n); - -// Sugar for the most common case -template -auto collectN(Collection&& c, size_t n) - -> decltype(collectN(c.begin(), c.end(), n)) { - return collectN(c.begin(), c.end(), n); -} - -template -using MaybeTryArg = typename std::conditional< - detail::callableWith&&>::value, Try, ItT>::type; - -template -using isFutureResult = isFuture::type>; - -/** repeatedly calls func on every result, e.g. - reduce(reduce(reduce(T initial, result of first), result of second), ...) - - The type of the final result is a Future of the type of the initial value. - - Func can either return a T, or a Future - */ -template ::value_type::value_type, - class Arg = MaybeTryArg> -typename std::enable_if::value, Future>::type -reduce(It first, It last, T initial, F func); - -template ::value_type::value_type, - class Arg = MaybeTryArg> -typename std::enable_if::value, Future>::type -reduce(It first, It last, T initial, F func); // Sugar for the most common case template diff --git a/folly/futures/helpers.h b/folly/futures/helpers.h new file mode 100644 index 00000000..138cfd47 --- /dev/null +++ b/folly/futures/helpers.h @@ -0,0 +1,249 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +namespace folly { + +/// This namespace is for utility functions that would usually be static +/// members of Future, except they don't make sense there because they don't +/// depend on the template type (rather, on the type of their arguments in +/// some cases). This is the least-bad naming scheme we could think of. Some +/// of the functions herein have really-likely-to-collide names, like "map" +/// and "sleep". +namespace futures { + /// Returns a Future that will complete after the specified duration. The + /// Duration typedef of a `std::chrono` duration type indicates the + /// resolution you can expect to be meaningful (milliseconds at the time of + /// writing). Normally you wouldn't need to specify a Timekeeper, we will + /// use the global futures timekeeper (we run a thread whose job it is to + /// keep time for futures timeouts) but we provide the option for power + /// users. + /// + /// The Timekeeper thread will be lazily created the first time it is + /// needed. If your program never uses any timeouts or other time-based + /// Futures you will pay no Timekeeper thread overhead. + Future sleep(Duration, Timekeeper* = nullptr); + + /// Create a Future chain from a sequence of callbacks. i.e. + /// + /// f.then(a).then(b).then(c); + /// + /// where f is a Future and the result of the chain is a Future + /// becomes + /// + /// f.then(chain(a, b, c)); + // If anyone figures how to get chain to deduce A and Z, I'll buy you a drink. + template + std::function(Try)> + chain(Callbacks... fns); + + /** + * Set func as the callback for each input Future and return a vector of + * Futures containing the results in the input order. + */ + template ::value_type, + class Result = decltype(std::declval().then(std::declval()))> + std::vector> map(It first, It last, F func); + + // Sugar for the most common case + template + auto map(Collection&& c, F&& func) + -> decltype(map(c.begin(), c.end(), func)) { + return map(c.begin(), c.end(), std::forward(func)); + } + +} + +/** + Make a completed Future by moving in a value. e.g. + + string foo = "foo"; + auto f = makeFuture(std::move(foo)); + + or + + auto f = makeFuture("foo"); +*/ +template +Future::type> makeFuture(T&& t); + +/** Make a completed void Future. */ +Future makeFuture(); + +/** Make a completed Future by executing a function. If the function throws + we capture the exception, otherwise we capture the result. */ +template +auto makeFutureWith( + F&& func, + typename std::enable_if< + !std::is_reference::value, bool>::type sdf = false) + -> Future; + +template +auto makeFutureWith( + F const& func) + -> Future; + +/// Make a failed Future from an exception_ptr. +/// Because the Future's type cannot be inferred you have to specify it, e.g. +/// +/// auto f = makeFuture(std::current_exception()); +template +Future makeFuture(std::exception_ptr const& e) DEPRECATED; + +/// Make a failed Future from an exception_wrapper. +template +Future makeFuture(exception_wrapper ew); + +/** Make a Future from an exception type E that can be passed to + std::make_exception_ptr(). */ +template +typename std::enable_if::value, + Future>::type +makeFuture(E const& e); + +/** Make a Future out of a Try */ +template +Future makeFuture(Try&& t); + +/* + * Return a new Future that will call back on the given Executor. + * This is just syntactic sugar for makeFuture().via(executor) + * + * @param executor the Executor to call back on + * + * @returns a void Future that will call back on the given executor + */ +template +Future via(Executor* executor); + +/** When all the input Futures complete, the returned Future will complete. + Errors do not cause early termination; this Future will always succeed + after all its Futures have finished (whether successfully or with an + error). + + The Futures are moved in, so your copies are invalid. If you need to + chain further from these Futures, use the variant with an output iterator. + + This function is thread-safe for Futures running on different threads. But + if you are doing anything non-trivial after, you will probably want to + follow with `via(executor)` because it will complete in whichever thread the + last Future completes in. + + The return type for Future input is a Future>> + */ +template +Future::value_type::value_type>>> +collectAll(InputIterator first, InputIterator last); + +// Sugar for the most common case +template +auto collectAll(Collection&& c) -> decltype(collectAll(c.begin(), c.end())) { + return collectAll(c.begin(), c.end()); +} + +/// This version takes a varying number of Futures instead of an iterator. +/// The return type for (Future, Future, ...) input +/// is a Future, Try, ...>>. +/// The Futures are moved in, so your copies are invalid. +template +typename detail::VariadicContext< + typename std::decay::type::value_type...>::type +collectAll(Fs&&... fs); + +/// Like collectAll, but will short circuit on the first exception. Thus, the +/// type of the returned Future is std::vector instead of +/// std::vector> +template +Future::value_type::value_type +>::result_type> +collect(InputIterator first, InputIterator last); + +// Sugar for the most common case +template +auto collect(Collection&& c) -> decltype(collect(c.begin(), c.end())) { + return collect(c.begin(), c.end()); +} + +/** The result is a pair of the index of the first Future to complete and + the Try. If multiple Futures complete at the same time (or are already + complete when passed in), the "winner" is chosen non-deterministically. + + This function is thread-safe for Futures running on different threads. + */ +template +Future::value_type::value_type>>> +collectAny(InputIterator first, InputIterator last); + +// Sugar for the most common case +template +auto collectAny(Collection&& c) -> decltype(collectAny(c.begin(), c.end())) { + return collectAny(c.begin(), c.end()); +} + +/** when n Futures have completed, the Future completes with a vector of + the index and Try of those n Futures (the indices refer to the original + order, but the result vector will be in an arbitrary order) + + Not thread safe. + */ +template +Future::value_type::value_type>>>> +collectN(InputIterator first, InputIterator last, size_t n); + +// Sugar for the most common case +template +auto collectN(Collection&& c, size_t n) + -> decltype(collectN(c.begin(), c.end(), n)) { + return collectN(c.begin(), c.end(), n); +} + +template +using MaybeTryArg = typename std::conditional< + detail::callableWith&&>::value, Try, ItT>::type; + +template +using isFutureResult = isFuture::type>; + +/** repeatedly calls func on every result, e.g. + reduce(reduce(reduce(T initial, result of first), result of second), ...) + + The type of the final result is a Future of the type of the initial value. + + Func can either return a T, or a Future + */ +template ::value_type::value_type, + class Arg = MaybeTryArg> +typename std::enable_if::value, Future>::type +reduce(It first, It last, T initial, F func); + +template ::value_type::value_type, + class Arg = MaybeTryArg> +typename std::enable_if::value, Future>::type +reduce(It first, It last, T initial, F func); + +} // namespace folly