1 /* Copyright 2019 Google LLC. All Rights Reserved.
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 
16 #ifndef RUY_RUY_WAIT_H_
17 #define RUY_RUY_WAIT_H_
18 
19 #include <condition_variable>  // NOLINT(build/c++11)
20 #include <functional>
21 #include <mutex>  //  NOLINT(build/c++11)
22 
23 #include "ruy/time.h"
24 
25 namespace ruy {
26 
27 // Waits until some evaluation of `condition` has returned true.
28 //
29 // There is no guarantee that calling `condition` again after this function
30 // has returned would still return true. The only
31 // contract is that at some point during the execution of that function,
32 // `condition` has returned true.
33 //
34 // First does some spin-waiting for the specified `spin_duration`,
35 // then falls back to passive waiting for the given condvar, guarded
36 // by the given mutex. At this point it will try to acquire the mutex lock,
37 // around the waiting on the condition variable.
38 // Therefore, this function expects that the calling thread hasn't already
39 // locked the mutex before calling it.
40 // This function will always release the mutex lock before returning.
41 //
42 // The idea of doing some initial spin-waiting is to help get
43 // better and more consistent multithreading benefits for small GEMM sizes.
44 // Spin-waiting help ensuring that if we need to wake up soon after having
45 // started waiting, then we can wake up quickly (as opposed to, say,
46 // having to wait to be scheduled again by the OS). On the other hand,
47 // we must still eventually revert to passive waiting for longer waits
48 // (e.g. worker threads having finished a GEMM and waiting until the next GEMM)
49 // so as to avoid permanently spinning.
50 //
51 // In situations where other threads might have more useful things to do with
52 // these CPU cores than our spin-waiting, it may be best to reduce the value
53 // of `spin_duration`. Setting it to zero disables the spin-waiting entirely.
54 //
55 // There is a risk that the std::function used here might use a heap allocation
56 // to store its context. The expected usage pattern is that these functions'
57 // contexts will consist of a single pointer value (typically capturing only
58 // [this]), and that in this case the std::function implementation will use
59 // inline storage, avoiding a heap allocation. However, we can't effectively
60 // guard that assumption, and that's not a big concern anyway because the
61 // latency of a small heap allocation is probably low compared to the intrinsic
62 // latency of what this Wait function does.
63 void Wait(const std::function<bool()>& condition, const Duration& spin_duration,
64           std::condition_variable* condvar, std::mutex* mutex);
65 
66 }  // namespace ruy
67 
68 #endif  // RUY_RUY_WAIT_H_
69