1/* Copyright 2019 Google LLC. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations 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
25namespace 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.
63void 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