1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Copyright 2017 The Fuchsia Authors. All rights reserved.
16 // Use of this source code is governed by a BSD-style license that can be
17 // found in the LICENSE file.
18 
19 #pragma once
20 
21 #include "FunctionInternal.h"
22 #include "UtilityInternal.h"
23 
24 namespace gfxstream::guest {
25 namespace fit {
26 
27 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
28 class FunctionImpl;
29 
30 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
31 class CallbackImpl;
32 
33 // The default size allowance for storing a target inline within a function
34 // object, in bytes.  This default allows for inline storage of targets
35 // as big as two pointers, such as an object pointer and a pointer to a member
36 // function.
37 constexpr size_t kDefaultInlineTargetSize = sizeof(void*) * 2;
38 
39 // A |fit::Function| is a move-only polymorphic function wrapper.
40 //
41 // If you need a class with similar characteristics that also ensures
42 // "run-once" semantics (such as callbacks shared with timeouts, or for
43 // service requests with redundant, failover, or fallback service providers),
44 // see |fit::Callback|.
45 //
46 // |fit::Function<T>| behaves like |std::function<T>| except that it is
47 // move-only instead of copyable, so it can hold targets that cannot be copied,
48 // such as mutable lambdas, and immutable lambdas that capture move-only
49 // objects.
50 //
51 // Targets of up to |inlineTargetSize| bytes in size (rounded up for memory
52 // alignment) are stored inline within the function object without incurring
53 // any heap allocation.  Larger callable objects will be moved to the heap as
54 // required.
55 //
56 // See also |fit::InlineFunction<T, size>| for more control over allocation
57 // behavior.
58 //
59 // SYNOPSIS
60 //
61 // |T| is the function's signature.  e.g. void(int, std::string).
62 //
63 // |inlineTargetSize| is the minimum size of target that is guaranteed to
64 // fit within a function without requiring heap allocation.
65 // Defaults to |kDefaultInlineTargetSize|.
66 //
67 // Class members are documented in |fit::FunctionImpl|, below.
68 //
69 // EXAMPLES
70 //
71 // -
72 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example1.cc
73 // -
74 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/lib/fit/test/examples/function_example2.cc
75 //
76 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
77 using function = FunctionImpl<inlineTargetSize, /*requireInline=*/false, T>;
78 
79 // A move-only callable object wrapper that forces callables to be stored inline
80 // and never performs heap allocation.
81 //
82 // Behaves just like |fit::Function<T, inlineTargetSize>| except that
83 // attempting to store a target larger than |inlineTargetSize| will fail to
84 // compile.
85 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
86 using InlineFunction = FunctionImpl<inlineTargetSize,
87                                     /*requireInline=*/true,
88                                     T>;
89 
90 // Synonym for a function which takes no arguments and produces no result.
91 using closure = function<void()>;
92 
93 // A |fit::Callback| is a move-only polymorphic function wrapper that also
94 // ensures "run-once" semantics (such as callbacks shared with timeouts, or for
95 // service requests with redundant, failover, or fallback service providers).
96 // A |fit::Callback| releases it's resources after the first call, and can be
97 // inspected before calling, so a potential caller can know if it should call
98 // the function, or skip the call because the target was already called.
99 //
100 // If you need a move-only function class with typical function characteristics,
101 // that permits multiple invocations of the same function, see |fit::Function|.
102 //
103 // |fit::Callback<T>| behaves like |std::function<T>| except:
104 //
105 //   1. It is move-only instead of copyable, so it can hold targets that cannot
106 //      be copied, such as mutable lambdas, and immutable lambdas that capture
107 //      move-only objects.
108 //   2. On the first call to invoke a |fit::Callback|, the target function held
109 //      by the |fit::Callback| cannot be called again.
110 //
111 // When a |fit::Callback| is invoked for the first time, the target function is
112 // released and destructed, along with any resources owned by that function
113 // (typically the objects captured by a lambda).
114 //
115 // A |fit::Callback| in the "already called" state has the same state as a
116 // |fit::Callback| that has been assigned to |nullptr|. It can be compared to
117 // |nullptr| (via "==" or "!=", and its "operator bool()" returns false, which
118 // provides a convenient way to gate whether or not the |fit::Callback| should
119 // be called. (Note that invoking an empty |fit::Callback| or |fit::Function|
120 // will cause a program abort!)
121 //
122 // As an example, sharing |fit::Callback| between both a service and a timeout
123 // might look something like this:
124 //
125 //  void service_with_timeout(fit::Callback<void(bool)> cb, uint timeout_ms) {
126 //    service_request([cb = cb.share()]() mutable { if (cb) cb(false); });
127 //    timeout(timeout_ms, [cb = std::move(cb)]() mutable { if (cb) cb(true); });
128 //  }
129 //
130 // Since |fit::Callback| objects are move-only, and not copyable, duplicate
131 // references to the same |fit::Callback| can be obtained via share(), as shown
132 // in the example above. This method converts the |fit::Callback| into a
133 // reference-counted version of the |fit::Callback| and returns a copy of the
134 // reference as another |fit::Callback| with the same target function.
135 //
136 // What is notable about |fit::Callback<T>.share()| is that invoking any shared
137 // copy will "nullify" all shared copies, as shown in the example.
138 //
139 // Note that |fit::Callback| is NOT thread-safe by default. If multi-threaded
140 // support is required, you would need to implement your own mutex, or similar
141 // guard, before checking and calling a |fit::Callback|.
142 //
143 // Targets of up to |inlineTargetSize| bytes in size (rounded up for memory
144 // alignment) are stored inline within the callback object without incurring
145 // any heap allocation.  Larger callable objects will be moved to the heap as
146 // required.
147 //
148 // See also |fit::inline_callback<T, size>| for more control over allocation
149 // behavior.
150 //
151 // SYNOPSIS
152 //
153 // |T| is the callback's signature.  e.g. void(int, std::string).
154 //
155 // |inlineTargetSize| is the minimum size of target that is guaranteed to
156 // fit within a callback without requiring heap allocation.
157 // Defaults to |kDefaultInlineTargetSize|.
158 //
159 // Class members are documented in |fit::CallbackImpl|, below.
160 //
161 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
162 using Callback = CallbackImpl<inlineTargetSize, /*requireInline=*/false, T>;
163 
164 // A move-only, run-once, callable object wrapper that forces callables to be
165 // stored inline and never performs heap allocation.
166 //
167 // Behaves just like |fit::Callback<T, inlineTargetSize>| except that
168 // attempting to store a target larger than |inlineTargetSize| will fail to
169 // compile.
170 template <typename T, size_t inlineTargetSize = kDefaultInlineTargetSize>
171 using InlineCallback = CallbackImpl<inlineTargetSize,
172                                     /*requireInline=*/true,
173                                     T>;
174 
175 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
176 class FunctionImpl<inlineTargetSize, requireInline, Result(Args...)> final
177     : private gfxstream::guest::fit::internal::
178           function_base<inlineTargetSize, requireInline, Result(Args...)> {
179     using Base = gfxstream::guest::fit::internal::
180         function_base<inlineTargetSize, requireInline, Result(Args...)>;
181 
182     // function_base requires private access during share()
183     friend class gfxstream::guest::fit::internal::
184         function_base<inlineTargetSize, requireInline, Result(Args...)>;
185 
186     // supports target() for shared functions
187     friend const void* gfxstream::guest::fit::internal::get_target_type_id<>(
188         const FunctionImpl<inlineTargetSize, requireInline, Result(Args...)>&);
189 
190     template <typename U>
191     using NotSelfType = gfxstream::guest::fit::internal::NotSameType<FunctionImpl, U>;
192 
193     template <typename... Conditions>
194     using RequiresConditions = gfxstream::guest::fit::internal::RequiresConditions<Conditions...>;
195 
196     template <typename... Conditions>
197     using AssignmentRequiresConditions =
198         gfxstream::guest::fit::internal::AssignmentRequiresConditions<FunctionImpl&, Conditions...>;
199 
200 public:
201     // The function's result type.
202     using typename Base::result_type;
203 
204     // Initializes an empty (null) function. Attempting to call an empty
205     // function will abort the program.
206     FunctionImpl() = default;
207 
208     // Creates a function with an empty target (same outcome as the default
209     // constructor).
FunctionImpl(decltype (nullptr))210     FunctionImpl(decltype(nullptr)) : Base(nullptr) {}
211 
212     // Creates a function bound to the specified function pointer.
213     // If target == nullptr, assigns an empty target.
FunctionImpl(Result (* target)(Args...))214     FunctionImpl(Result (*target)(Args...)) : Base(target) {}
215 
216     // Creates a function bound to the specified callable object.
217     // If target == nullptr, assigns an empty target.
218     //
219     // For functors, we need to capture the raw type but also restrict on the
220     // existence of an appropriate operator () to resolve overloads and implicit
221     // casts properly.
222     //
223     // Note that specializations of this template method that take fit::Callback
224     // objects as the target Callable are deleted (see below).
225     template <typename Callable,
226               RequiresConditions<
227                   std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
228                                       result_type>,
229                   NotSelfType<Callable>> = true>
FunctionImpl(Callable && target)230     FunctionImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
231 
232     // Deletes the specializations of FunctionImpl(Callable) that would allow
233     // a |fit::Function| to be constructed from a |fit::Callback|. This prevents
234     // unexpected behavior of a |fit::Function| that would otherwise fail after
235     // one call. To explicitly allow this, simply wrap the |fit::Callback| in a
236     // pass-through lambda before passing it to the |fit::Function|.
237     template <size_t otherInlineTargetSize, bool otherRequireInline>
238     FunctionImpl(gfxstream::guest::fit::CallbackImpl<otherInlineTargetSize,
239                                                     otherRequireInline,
240                                                     Result(Args...)>) = delete;
241 
242     // Creates a function with a target moved from another function,
243     // leaving the other function with an empty target.
FunctionImpl(FunctionImpl && other)244     FunctionImpl(FunctionImpl&& other) : Base(static_cast<Base&&>(other)) {}
245 
246     // Destroys the function, releasing its target.
247     ~FunctionImpl() = default;
248 
249     // Assigns the function to an empty target. Attempting to invoke the
250     // function will abort the program.
decltype(nullptr)251     FunctionImpl& operator=(decltype(nullptr)) {
252         Base::assign(nullptr);
253         return *this;
254     }
255 
256     // Assigns the function to the specified callable object. If target ==
257     // nullptr, assigns an empty target.
258     //
259     // For functors, we need to capture the raw type but also restrict on the
260     // existence of an appropriate operator () to resolve overloads and implicit
261     // casts properly.
262     //
263     // Note that specializations of this template method that take fit::Callback
264     // objects as the target Callable are deleted (see below).
265     template <typename Callable>
266     AssignmentRequiresConditions<
267         std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
268                             result_type>,
269         NotSelfType<Callable>>
270     operator=(Callable&& target) {
271         Base::assign(std::forward<Callable>(target));
272         return *this;
273     }
274 
275     // Deletes the specializations of operator=(Callable) that would allow
276     // a |fit::Function| to be assigned from a |fit::Callback|. This
277     // prevents unexpected behavior of a |fit::Function| that would otherwise
278     // fail after one call. To explicitly allow this, simply wrap the
279     // |fit::Callback| in a pass-through lambda before assigning it to the
280     // |fit::Function|.
281     template <size_t otherInlineTargetSize, bool otherRequireInline>
282     FunctionImpl& operator=(gfxstream::guest::fit::CallbackImpl<otherInlineTargetSize,
283                                                                otherRequireInline,
284                                                                Result(Args...)>) = delete;
285 
286     // Move assignment
287     FunctionImpl& operator=(FunctionImpl&& other) {
288         if (&other == this)
289             return *this;
290         Base::assign(static_cast<Base&&>(other));
291         return *this;
292     }
293 
294     // Swaps the functions' targets.
swap(FunctionImpl & other)295     void swap(FunctionImpl& other) { Base::swap(other); }
296 
297     // Returns a pointer to the function's target.
298     using Base::target;
299 
300     // Returns true if the function has a non-empty target.
301     using Base::operator bool;
302 
303     // Invokes the function's target.
304     // Aborts if the function's target is empty.
operator()305     Result operator()(Args... args) const { return Base::invoke(std::forward<Args>(args)...); }
306 
307     // Returns a new function object that invokes the same target.
308     // The target itself is not copied; it is moved to the heap and its
309     // lifetime is extended until all references have been released.
310     //
311     // Note: This method is not supported on |fit::InlineFunction<>|
312     //       because it may incur a heap allocation which is contrary to
313     //       the stated purpose of |fit::InlineFunction<>|.
share()314     FunctionImpl share() {
315         FunctionImpl copy;
316         Base::template share_with<FunctionImpl>(copy);
317         return copy;
318     }
319 };
320 
321 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
swap(FunctionImpl<inlineTargetSize,requireInline,Result,Args...> & a,FunctionImpl<inlineTargetSize,requireInline,Result,Args...> & b)322 void swap(FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& a,
323           FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
324     a.swap(b);
325 }
326 
327 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
328 bool operator==(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
329                 decltype(nullptr)) {
330     return !f;
331 }
332 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
333 bool operator==(decltype(nullptr),
334                 const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
335     return !f;
336 }
337 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
338 bool operator!=(const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f,
339                 decltype(nullptr)) {
340     return !!f;
341 }
342 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
343 bool operator!=(decltype(nullptr),
344                 const FunctionImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
345     return !!f;
346 }
347 
348 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
349 class CallbackImpl<inlineTargetSize, requireInline, Result(Args...)> final
350     : private gfxstream::guest::fit::internal::
351           function_base<inlineTargetSize, requireInline, Result(Args...)> {
352     using Base = gfxstream::guest::fit::internal::
353         function_base<inlineTargetSize, requireInline, Result(Args...)>;
354 
355     // function_base requires private access during share()
356     friend class gfxstream::guest::fit::internal::
357         function_base<inlineTargetSize, requireInline, Result(Args...)>;
358 
359     // supports target() for shared functions
360     friend const void* gfxstream::guest::fit::internal::get_target_type_id<>(
361         const CallbackImpl<inlineTargetSize, requireInline, Result(Args...)>&);
362 
363     template <typename U>
364     using NotSelfType = gfxstream::guest::fit::internal::NotSameType<CallbackImpl, U>;
365 
366     template <typename... Conditions>
367     using RequiresConditions = gfxstream::guest::fit::internal::RequiresConditions<Conditions...>;
368 
369     template <typename... Conditions>
370     using AssignmentRequiresConditions =
371         gfxstream::guest::fit::internal::AssignmentRequiresConditions<CallbackImpl&, Conditions...>;
372 
373 public:
374     // The callback function's result type.
375     using typename Base::result_type;
376 
377     // Initializes an empty (null) callback. Attempting to call an empty
378     // callback will abort the program.
379     CallbackImpl() = default;
380 
381     // Creates a callback with an empty target (same outcome as the default
382     // constructor).
CallbackImpl(decltype (nullptr))383     CallbackImpl(decltype(nullptr)) : Base(nullptr) {}
384 
385     // Creates a callback bound to the specified function pointer.
386     // If target == nullptr, assigns an empty target.
CallbackImpl(Result (* target)(Args...))387     CallbackImpl(Result (*target)(Args...)) : Base(target) {}
388 
389     // Creates a callback bound to the specified callable object.
390     // If target == nullptr, assigns an empty target.
391     //
392     // For functors, we need to capture the raw type but also restrict on the
393     // existence of an appropriate operator () to resolve overloads and implicit
394     // casts properly.
395     template <typename Callable,
396               RequiresConditions<
397                   std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
398                                       result_type>,
399                   NotSelfType<Callable>> = true>
CallbackImpl(Callable && target)400     CallbackImpl(Callable&& target) : Base(std::forward<Callable>(target)) {}
401 
402     // Creates a callback with a target moved from another callback,
403     // leaving the other callback with an empty target.
CallbackImpl(CallbackImpl && other)404     CallbackImpl(CallbackImpl&& other) : Base(static_cast<Base&&>(other)) {}
405 
406     // Destroys the callback, releasing its target.
407     ~CallbackImpl() = default;
408 
409     // Assigns the callback to an empty target. Attempting to invoke the
410     // callback will abort the program.
decltype(nullptr)411     CallbackImpl& operator=(decltype(nullptr)) {
412         Base::assign(nullptr);
413         return *this;
414     }
415 
416     // Assigns the callback to the specified callable object. If target ==
417     // nullptr, assigns an empty target.
418     //
419     // For functors, we need to capture the raw type but also restrict on the
420     // existence of an appropriate operator () to resolve overloads and implicit
421     // casts properly.
422     template <typename Callable>
423     AssignmentRequiresConditions<
424         std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)),
425                             result_type>,
426         NotSelfType<Callable>>
427     operator=(Callable&& target) {
428         Base::assign(std::forward<Callable>(target));
429         return *this;
430     }
431 
432     // Move assignment
433     CallbackImpl& operator=(CallbackImpl&& other) {
434         if (&other == this)
435             return *this;
436         Base::assign(static_cast<Base&&>(other));
437         return *this;
438     }
439 
440     // Swaps the callbacks' targets.
swap(CallbackImpl & other)441     void swap(CallbackImpl& other) { Base::swap(other); }
442 
443     // Returns a pointer to the callback's target.
444     using Base::target;
445 
446     // Returns true if the callback has a non-empty target.
447     using Base::operator bool;
448 
449     // Invokes the callback's target.
450     // Aborts if the callback's target is empty.
451     // |fit::Callback| must be non-const to invoke. Before the target function
452     // is actually called, the fit::Callback will be set to the default empty
453     // state (== nullptr, and operator bool() will subsequently return |false|).
454     // The target function will then be released after the function is called.
455     // If the callback was shared, any remaining copies will also be cleared.
operator()456     Result operator()(Args... args) {
457         auto temp = std::move(*this);
458         return temp.invoke(std::forward<Args>(args)...);
459     }
460 
461     // Returns a new callback object that invokes the same target.
462     // The target itself is not copied; it is moved to the heap and its
463     // lifetime is extended until all references have been released.
464     // For |fit::Callback| (unlike fit::Function), the first invocation of the
465     // callback will release all references to the target. All callbacks
466     // derived from the same original callback (via share()) will be cleared,
467     // as if set to |nullptr|, and "operator bool()" will return false.
468     //
469     // Note: This method is not supported on |fit::InlineFunction<>|
470     //       because it may incur a heap allocation which is contrary to
471     //       the stated purpose of |fit::InlineFunction<>|.
share()472     CallbackImpl share() {
473         CallbackImpl copy;
474         Base::template share_with<CallbackImpl>(copy);
475         return copy;
476     }
477 };
478 
479 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
swap(CallbackImpl<inlineTargetSize,requireInline,Result,Args...> & a,CallbackImpl<inlineTargetSize,requireInline,Result,Args...> & b)480 void swap(CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& a,
481           CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& b) {
482     a.swap(b);
483 }
484 
485 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
486 bool operator==(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
487                 decltype(nullptr)) {
488     return !f;
489 }
490 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
491 bool operator==(decltype(nullptr),
492                 const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
493     return !f;
494 }
495 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
496 bool operator!=(const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f,
497                 decltype(nullptr)) {
498     return !!f;
499 }
500 template <size_t inlineTargetSize, bool requireInline, typename Result, typename... Args>
501 bool operator!=(decltype(nullptr),
502                 const CallbackImpl<inlineTargetSize, requireInline, Result, Args...>& f) {
503     return !!f;
504 }
505 
506 // Returns a Callable object that invokes a member function of an object.
507 template <typename R, typename T, typename... Args>
bindMember(T * instance,R (T::* fn)(Args...))508 auto bindMember(T* instance, R (T::*fn)(Args...)) {
509     return [instance, fn](Args... args) { return (instance->*fn)(std::forward<Args>(args)...); };
510 }
511 
512 }  // namespace fit
513 }  // namespace gfxstream::guest
514