|
| 1 | +/* |
| 2 | + +----------------------------------------------------------------------+ |
| 3 | + | HipHop for PHP | |
| 4 | + +----------------------------------------------------------------------+ |
| 5 | + | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) | |
| 6 | + +----------------------------------------------------------------------+ |
| 7 | + | This source file is subject to version 3.01 of the PHP license, | |
| 8 | + | that is bundled with this package in the file LICENSE, and is | |
| 9 | + | available through the world-wide-web at the following url: | |
| 10 | + | http://www.php.net/license/3_01.txt | |
| 11 | + | If you did not receive a copy of the PHP license and are unable to | |
| 12 | + | obtain it through the world-wide-web, please send a note to | |
| 13 | + | license@php.net so we can mail you a copy immediately. | |
| 14 | + +----------------------------------------------------------------------+ |
| 15 | +*/ |
| 16 | + |
| 17 | +#ifndef incl_HPHP_EXTERN_WORKER_DETAIL_H_ |
| 18 | +#error "extern-worker-detail.h should only be included by extern-worker.h" |
| 19 | +#endif |
| 20 | + |
| 21 | +#include "hphp/util/assertions.h" |
| 22 | +#include "hphp/util/blob-encoder.h" |
| 23 | + |
| 24 | +#include <folly/String.h> |
| 25 | +#include <folly/portability/Filesystem.h> |
| 26 | + |
| 27 | +#include <chrono> |
| 28 | +#include <string> |
| 29 | +#include <tuple> |
| 30 | +#include <type_traits> |
| 31 | +#include <vector> |
| 32 | + |
| 33 | +/* |
| 34 | + * Implementation details for extern_worker that don't need to be |
| 35 | + * exposed to users. |
| 36 | + */ |
| 37 | + |
| 38 | +namespace HPHP::extern_worker { |
| 39 | + |
| 40 | +////////////////////////////////////////////////////////////////////// |
| 41 | + |
| 42 | +// This header gets included before any others, so these need to be |
| 43 | +// forward declared. |
| 44 | +template <typename T> struct Opt; |
| 45 | +template <typename T> struct Variadic; |
| 46 | +template <typename... Ts> struct Multi; |
| 47 | + |
| 48 | +template <typename T> struct Ref; |
| 49 | + |
| 50 | +extern int main(int, char**); |
| 51 | + |
| 52 | +////////////////////////////////////////////////////////////////////// |
| 53 | + |
| 54 | +namespace detail { |
| 55 | + |
| 56 | +////////////////////////////////////////////////////////////////////// |
| 57 | + |
| 58 | +// Scoped timing in debug builds. The emitted messages are supplied |
| 59 | +// with lambdas to avoid overhead when the code is compiled out. |
| 60 | +struct Timer { |
| 61 | + Timer() : m_msg{nullptr} { |
| 62 | + ONTRACE(2, [this] { m_begin = std::chrono::steady_clock::now(); }()); |
| 63 | + } |
| 64 | + |
| 65 | + explicit Timer(const char* msg) : m_msg{msg} { |
| 66 | + ONTRACE(2, [this] { m_begin = std::chrono::steady_clock::now(); }()); |
| 67 | + } |
| 68 | + |
| 69 | + template <typename F> |
| 70 | + explicit Timer(const F& f) { |
| 71 | + ONTRACE(2, [&] { |
| 72 | + m_str = f(); |
| 73 | + m_msg = m_str.c_str(); |
| 74 | + m_begin = std::chrono::steady_clock::now(); |
| 75 | + }()); |
| 76 | + } |
| 77 | + |
| 78 | + // Stop the timer early before destruction with the given message. |
| 79 | + template<typename F> |
| 80 | + void stopWithMessage(const F& f) { |
| 81 | + ONTRACE(2, [&] { |
| 82 | + m_msg = nullptr; |
| 83 | + FTRACE(2, "{} (took {})\n", f(), elapsed()); |
| 84 | + }()); |
| 85 | + } |
| 86 | + |
| 87 | + ~Timer() { |
| 88 | + ONTRACE(2, [this] { |
| 89 | + if (!m_msg) return; |
| 90 | + FTRACE(2, "{} took {}\n", m_msg, elapsed()); |
| 91 | + }()); |
| 92 | + } |
| 93 | + |
| 94 | +private: |
| 95 | + std::string elapsed() const { |
| 96 | + auto const d = std::chrono::duration_cast<std::chrono::duration<double>>( |
| 97 | + std::chrono::steady_clock::now() - m_begin |
| 98 | + ).count(); |
| 99 | + return folly::prettyPrint(d, folly::PRETTY_TIME, false); |
| 100 | + } |
| 101 | + |
| 102 | + std::chrono::steady_clock::time_point m_begin; |
| 103 | + const char* m_msg; |
| 104 | + std::string m_str; |
| 105 | + |
| 106 | + TRACE_SET_MOD(extern_worker); |
| 107 | +}; |
| 108 | + |
| 109 | +// Execute the given lambda, wrapping it with a Timer. |
| 110 | +template <typename F> auto time(const char* msg, const F& f) { |
| 111 | + Timer _{msg}; |
| 112 | + return f(); |
| 113 | +} |
| 114 | + |
| 115 | +template <typename F1, typename F2> auto time(const F1& f1, const F2& f2) { |
| 116 | + Timer _{f1}; |
| 117 | + return f2(); |
| 118 | +} |
| 119 | + |
| 120 | +////////////////////////////////////////////////////////////////////// |
| 121 | + |
| 122 | +// Read/write to a file |
| 123 | +extern std::string readFile(const folly::fs::path&); |
| 124 | +extern void writeFile(const folly::fs::path&, const char*, size_t); |
| 125 | + |
| 126 | +////////////////////////////////////////////////////////////////////// |
| 127 | + |
| 128 | +// Matchers for the special "marker" classes |
| 129 | + |
| 130 | +template <typename T> |
| 131 | +struct IsVariadic : std::false_type {}; |
| 132 | +template <typename T> |
| 133 | +struct IsVariadic<Variadic<T>> : std::true_type {}; |
| 134 | + |
| 135 | +template <typename T> |
| 136 | +struct IsOpt : std::false_type {}; |
| 137 | +template <typename T> |
| 138 | +struct IsOpt<Opt<T>> : std::true_type {}; |
| 139 | + |
| 140 | +template <typename... Ts> |
| 141 | +struct IsMulti : std::false_type {}; |
| 142 | +template <typename... Ts> |
| 143 | +struct IsMulti<Multi<Ts...>> : std::true_type {}; |
| 144 | + |
| 145 | +template <typename T> |
| 146 | +using IsMarker = std::disjunction<IsVariadic<T>, IsOpt<T>, IsMulti<T>>; |
| 147 | + |
| 148 | +////////////////////////////////////////////////////////////////////// |
| 149 | + |
| 150 | +// Matchers for special non-marker classes |
| 151 | + |
| 152 | +template <typename T> |
| 153 | +struct IsRef : std::false_type {}; |
| 154 | +template <typename T> |
| 155 | +struct IsRef<Ref<T>> : std::true_type {}; |
| 156 | + |
| 157 | +template <typename T> |
| 158 | +struct IsVector : std::false_type {}; |
| 159 | +template <typename T> |
| 160 | +struct IsVector<std::vector<T>> : std::true_type {}; |
| 161 | + |
| 162 | +template <typename T> |
| 163 | +struct IsOptional : std::false_type {}; |
| 164 | +template <typename T> |
| 165 | +struct IsOptional<Optional<T>> : std::true_type {}; |
| 166 | + |
| 167 | +template <typename T> |
| 168 | +struct IsTuple : std::false_type {}; |
| 169 | +template <typename... Ts> |
| 170 | +struct IsTuple<std::tuple<Ts...>> : std::true_type {}; |
| 171 | + |
| 172 | +////////////////////////////////////////////////////////////////////// |
| 173 | + |
| 174 | +// Given a callable, Params<>::type is the callable's parameter types |
| 175 | +// as a tuple. |
| 176 | +template <typename> struct Params; |
| 177 | +template <typename R, typename... P> |
| 178 | +struct Params<R(P...)> { |
| 179 | + using type = std::tuple< |
| 180 | + typename std::remove_cv<typename std::remove_reference<P>::type>::type... |
| 181 | + >; |
| 182 | +}; |
| 183 | + |
| 184 | +// Given a callable, Return<>::type is the callable's return type. |
| 185 | +template <typename> struct Return; |
| 186 | +template <typename R, typename... P> |
| 187 | +struct Return<R(P...)> { |
| 188 | + using type = |
| 189 | + typename std::remove_cv<typename std::remove_reference<R>::type>::type; |
| 190 | +}; |
| 191 | + |
| 192 | +////////////////////////////////////////////////////////////////////// |
| 193 | + |
| 194 | +// Given a type T, ToRef<T>::type is equivalent Ref type. For most |
| 195 | +// types this is just Ref<T>, but the variadic marker becomes a |
| 196 | +// std::vector of Refs, and an optional marker becomes an Optional |
| 197 | +// Ref. |
| 198 | +template <typename T> struct ToRef { |
| 199 | + using type = Ref<T>; |
| 200 | +}; |
| 201 | +template <typename T> struct ToRef<Variadic<T>> { |
| 202 | + using type = std::vector<typename ToRef<T>::type>; |
| 203 | +}; |
| 204 | +template <typename T> struct ToRef<Opt<T>> { |
| 205 | + using type = Optional<typename ToRef<T>::type>; |
| 206 | +}; |
| 207 | + |
| 208 | +////////////////////////////////////////////////////////////////////// |
| 209 | + |
| 210 | +// Given a tuple of types, ToRefTuple<T>::type is a tuple with every |
| 211 | +// type converted to its ToRef equivalent. |
| 212 | +template <typename> struct ToRefTuple {}; |
| 213 | +template <typename... Ts> struct ToRefTuple<std::tuple<Ts...>> { |
| 214 | + using type = std::tuple<typename ToRef<Ts>::type...>; |
| 215 | +}; |
| 216 | + |
| 217 | +// Same as ToRefTuple, except meant for return types. The only |
| 218 | +// difference is that a return type of the Multi marker becomes a |
| 219 | +// tuple. |
| 220 | +template <typename T> struct ToRefReturn { |
| 221 | + using type = typename ToRef<T>::type; |
| 222 | +}; |
| 223 | +template <typename... Ts> struct ToRefReturn<Multi<Ts...>> { |
| 224 | + using type = std::tuple<typename ToRef<Ts>::type...>; |
| 225 | +}; |
| 226 | + |
| 227 | +////////////////////////////////////////////////////////////////////// |
| 228 | + |
| 229 | +// Given a class (presumed to have static functions called init and |
| 230 | +// run), return their parameters or return values, transformed with |
| 231 | +// ToRef. |
| 232 | +template <typename C> struct ConfigRefs { |
| 233 | + using type = |
| 234 | + typename ToRefTuple<typename Params<decltype(C::init)>::type>::type; |
| 235 | +}; |
| 236 | + |
| 237 | +template <typename C> struct InputRefs { |
| 238 | + using type = |
| 239 | + typename ToRefTuple<typename Params<decltype(C::run)>::type>::type; |
| 240 | +}; |
| 241 | + |
| 242 | +template <typename C> struct ReturnRefs { |
| 243 | + using type = |
| 244 | + typename ToRefReturn<typename Return<decltype(C::run)>::type>::type; |
| 245 | +}; |
| 246 | + |
| 247 | +////////////////////////////////////////////////////////////////////// |
| 248 | + |
| 249 | +// Iterate over a tuple. Just a wrapper around folly::for_each, which |
| 250 | +// doesn't work on empty tuples for some reason. |
| 251 | +template <typename F, typename... Ts> |
| 252 | +void for_each(std::tuple<Ts...>&& tuple, F&& f) { |
| 253 | + if constexpr (sizeof...(Ts) != 0) { |
| 254 | + folly::for_each(std::move(tuple), std::forward<F>(f)); |
| 255 | + } |
| 256 | +} |
| 257 | +template <typename F, typename... Ts> |
| 258 | +void for_each(std::tuple<Ts...>& tuple, F&& f) { |
| 259 | + if constexpr (sizeof...(Ts) != 0) { |
| 260 | + folly::for_each(tuple, std::forward<F>(f)); |
| 261 | + } |
| 262 | +} |
| 263 | +template <typename F, typename... Ts> |
| 264 | +void for_each(const std::tuple<Ts...>& tuple, F&& f) { |
| 265 | + if constexpr (sizeof...(Ts) != 0) { |
| 266 | + folly::for_each(tuple, std::forward<F>(f)); |
| 267 | + } |
| 268 | +} |
| 269 | + |
| 270 | +////////////////////////////////////////////////////////////////////// |
| 271 | + |
| 272 | +// Empty class wrapping a Type. Meant to allow passing the type from |
| 273 | +// typesToValues as an argument. |
| 274 | +template <typename T> struct Tag { using Type = T; }; |
| 275 | + |
| 276 | +////////////////////////////////////////////////////////////////////// |
| 277 | + |
| 278 | +// Instantiated on a tuple, and given a callable, call the callable |
| 279 | +// for each type in that tuple. The callable is called with the tuple |
| 280 | +// index as the first parameter, and Tag<T> as the second (where T is |
| 281 | +// the type at that tuple index). |
| 282 | +template <typename Tuple, typename F, size_t... Is> |
| 283 | +auto typesToValuesImpl(F&& f, |
| 284 | + std::index_sequence<Is...>) { |
| 285 | + return std::make_tuple(f(Is, Tag<std::tuple_element_t<Is, Tuple>>{})...); |
| 286 | +} |
| 287 | + |
| 288 | +template <typename Tuple, typename F> |
| 289 | +auto typesToValues(F&& f) { |
| 290 | + return typesToValuesImpl<Tuple>( |
| 291 | + std::forward<F>(f), |
| 292 | + std::make_index_sequence<std::tuple_size<Tuple>{}>{} |
| 293 | + ); |
| 294 | +} |
| 295 | + |
| 296 | +////////////////////////////////////////////////////////////////////// |
| 297 | + |
| 298 | +// Base class for Jobs. This provide a consistent interface to invoke |
| 299 | +// through. |
| 300 | +struct JobBase { |
| 301 | + const std::string& name() const { return m_name; } |
| 302 | +protected: |
| 303 | + explicit JobBase(const std::string& name); |
| 304 | + virtual ~JobBase() = default; |
| 305 | + |
| 306 | + template <typename T> static T deserialize(const folly::fs::path&); |
| 307 | + template <typename T> static void serialize(const T&, |
| 308 | + size_t, |
| 309 | + const folly::fs::path&); |
| 310 | + |
| 311 | +private: |
| 312 | + virtual void init(const folly::fs::path&) const = 0; |
| 313 | + virtual void fini() const = 0; |
| 314 | + virtual void run(const folly::fs::path&, const folly::fs::path&) const = 0; |
| 315 | + |
| 316 | + std::string m_name; |
| 317 | + |
| 318 | + friend int HPHP::extern_worker::main(int, char**); |
| 319 | +}; |
| 320 | + |
| 321 | +////////////////////////////////////////////////////////////////////// |
| 322 | + |
| 323 | +}} |
0 commit comments