gem5  v21.0.1.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
uncontended_mutex.hh
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Google, Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met: redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer;
8  * redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution;
11  * neither the name of the copyright holders nor the names of its
12  * contributors may be used to endorse or promote products derived from
13  * this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #ifndef __BASE_UNCONTENDED_MUTEX_HH__
29 #define __BASE_UNCONTENDED_MUTEX_HH__
30 
31 #include <atomic>
32 #include <condition_variable>
33 #include <mutex>
34 
35 /*
36  * The std::mutex implementation is slower than expected because of many mode
37  * checking and legacy support.
38  *
39  * The UncontendedMutex uses an atomic flag to check if we really need to
40  * obtain a mutex lock. For most cases without multi-threads event queues,
41  * e.g. non-KVM simulation, this avoid the usage of mutex and speed up the
42  * simulation.
43  */
45 {
46  private:
47  /*
48  * A flag to record the current status:
49  * 0: no one has the lock
50  * 1: exactly one thread has the lock
51  * >1: one or more threads are waiting for the lock
52  */
53  std::atomic<int> flag;
54  std::mutex m;
55  std::condition_variable cv;
56 
57  bool
58  testAndSet(int expected, int desired)
59  {
60  return flag.compare_exchange_strong(expected, desired);
61  }
62 
63  public:
65 
66  void
67  lock()
68  {
69  /*
70  * Here we use 'flag' to check if we are the first thread to get the
71  * lock. If not, we try to obtain the real mutex, and use the condition
72  * variable to wait for the thread who has the lock to release it.
73  *
74  * The flag will be updated to more than 1, so the thread with lock
75  * knows that there is another thread waiting for the lock.
76  */
77  while (!testAndSet(0, 1)) {
78  std::unique_lock<std::mutex> ul(m);
79  /*
80  * It is possible that just before we obtain the mutex lock, the
81  * first thread releases the flag and thus flag becomes zero. In
82  * such case, we shouldn't wait for the condition variable because
83  * there is no the other thread to notify us.
84  */
85  if (flag++ == 0)
86  break;
87  cv.wait(ul);
88  }
89  }
90 
91  void
93  {
94  /* In case there are no other threads waiting, we will just clear the
95  * flag and return.
96  */
97  if (testAndSet(1, 0))
98  return;
99 
100  /*
101  * Otherwise, clear the flag and notify all the waiting threads. We
102  * need to protect the flag by mutex here so that there won't be
103  * another thread waiting but the flag is already set to 0.
104  */
105  {
106  std::lock_guard<std::mutex> g(m);
107  flag = 0;
108  }
109  /*
110  * It's possible to update the algorithm and use notify_one() here.
111  * However, tests show that notify_one() is much slower than
112  * notify_all() in this case. Here we choose to use notify_all().
113  */
114  cv.notify_all();
115  }
116 };
117 
118 #endif // __BASE_UNCONTENDED_MUTEX_HH__
UncontendedMutex::UncontendedMutex
UncontendedMutex()
Definition: uncontended_mutex.hh:64
UncontendedMutex::flag
std::atomic< int > flag
Definition: uncontended_mutex.hh:53
UncontendedMutex::lock
void lock()
Definition: uncontended_mutex.hh:67
UncontendedMutex::m
std::mutex m
Definition: uncontended_mutex.hh:54
MipsISA::g
Bitfield< 4 > g
Definition: dt_constants.hh:83
UncontendedMutex::unlock
void unlock()
Definition: uncontended_mutex.hh:92
UncontendedMutex::testAndSet
bool testAndSet(int expected, int desired)
Definition: uncontended_mutex.hh:58
UncontendedMutex
Definition: uncontended_mutex.hh:44
UncontendedMutex::cv
std::condition_variable cv
Definition: uncontended_mutex.hh:55
expected
std::vector< SwitchingFiber * > expected({ &a, &b, &a, &a, &a, &b, &c, &a, &c, &c, &c })

Generated on Tue Jun 22 2021 15:28:25 for gem5 by doxygen 1.8.17