TorchCraftAI
A bot for machine learning research on StarCraft: Brood War
prioritymutex.h
1 /*
2  * Copyright (c) 2017-present, Facebook, Inc.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #pragma once
9 #include <array>
10 #include <condition_variable>
11 #include <mutex>
12 #include <thread>
13 
14 #include <torch/torch.h>
15 
16 namespace cpid {
17 
18 /// This exactly a unique lock that doesn't unlock on delete
19 template <typename Mutex_t>
21  Mutex_t* m = nullptr;
22  bool locked = false;
23 
24  public:
25  permanent_lock() = default;
26  explicit permanent_lock(Mutex_t& m_) : m(&m_), locked(true) {
27  m->lock();
28  }
29  permanent_lock(const permanent_lock&) = delete;
30  permanent_lock& operator=(const permanent_lock&) = delete;
32 
33  void operator=(permanent_lock&& o);
34  void lock();
35  void unlock();
36  bool owns_lock();
37 };
38 
39 /** This class implements a mutex that offers some control over the
40  * priority of the waiting threads.
41  * If several threads are waiting to obtain the lock, it is guaranteed that the
42  * one with the highest priority will get it first. If there are several threads
43  * with the same priority level, then the outcome is up to the pthread
44  * implementation. Note that if there are always high priority threads in the
45  * queue, it will create starvation on the lower priority ones.
46  * If used inside a lock from the standard library, this will default to locking
47  * with the lowest priority
48  */
50  public:
51  /// Constructs a mutex
52  /// @param maxPrio is the maximal priority level accepted
53  priority_mutex(int maxPrio) : maxPrio_(maxPrio) {
54  queueCount_.resize(maxPrio_ + 1, 0);
55  }
56  priority_mutex(const priority_mutex&) = delete;
57  priority_mutex& operator=(const priority_mutex&) = delete;
58  priority_mutex(priority_mutex&&) = delete;
59 
60  void lock(int prio = 0) {
61  if (prio < 0 || prio > maxPrio_) {
62  throw std::runtime_error("Invalid priority level");
63  }
64  {
65  std::lock_guard lock(queueMutex_);
66  queueCount_[prio]++;
67  }
68  permanent_lock<std::mutex> lock(dataMutex_);
69  queueCV_.wait(lock, [this, prio]() { return canGo(prio); });
70 
71  {
72  std::lock_guard lock(queueMutex_);
73  queueCount_[prio]--;
74  }
75  }
76 
77  bool try_lock(int prio = 0) {
78  if (prio < 0 || prio > maxPrio_) {
79  throw std::runtime_error("Invalid priority level");
80  }
81  // we can ignore the prio, because if there is a thread holding the
82  // dataMutex, we can't lock, no matter the priority
83  return dataMutex_.try_lock();
84  }
85 
86  void unlock() {
87  dataMutex_.unlock();
88  queueCV_.notify_all();
89  }
90 
91  protected:
92  bool canGo(int prio) {
93  std::lock_guard lock(queueMutex_);
94  for (int i = prio + 1; i <= maxPrio_; ++i) {
95  if (queueCount_[i] != 0)
96  return false;
97  }
98  return true;
99  }
100 
101  std::mutex queueMutex_, dataMutex_;
102  std::condition_variable_any queueCV_;
103  std::vector<int> queueCount_;
104 
105  int maxPrio_;
106 };
107 
108 /// This is exactly an unique_lock without automatic lock, except that the lock
109 /// functions accepts a priority
111  priority_mutex* m = nullptr;
112  bool locked = false;
113  int default_prio_ = 0;
114 
115  public:
116  priority_lock() = default;
117  explicit priority_lock(priority_mutex& m, int default_prio = 0)
118  : m(&m), locked(false), default_prio_(default_prio) {}
119  priority_lock(const priority_lock&) = delete;
120  priority_lock& operator=(const priority_lock&) = delete;
121 
123  if (locked) {
124  m->unlock();
125  }
126  }
127 
129  if (owns_lock())
130  m->unlock();
131  m = o.m;
132  locked = o.locked;
133  o.m = nullptr;
134  o.locked = false;
135  }
136 
137  void lock(int prio) {
138  m->lock(prio);
139  locked = true;
140  }
141 
142  void lock() {
143  lock(default_prio_);
144  }
145 
146  void unlock() {
147  m->unlock();
148  locked = false;
149  }
150 
151  bool try_lock(int prio) {
152  if (m->try_lock(prio)) {
153  locked = true;
154  return true;
155  }
156  return false;
157  }
158 
159  bool owns_lock() {
160  return locked;
161  }
162 };
163 
164 template <typename Mutex_t>
166  if (owns_lock())
167  m->unlock();
168  m = o.m;
169  locked = o.locked;
170  o.m = nullptr;
171  o.locked = false;
172 }
173 
174 template <typename Mutex_t>
176  m->lock();
177  locked = true;
178 }
179 
180 template <typename Mutex_t>
182  m->unlock();
183  locked = false;
184 }
185 
186 template <typename Mutex_t>
188  return locked;
189 }
190 
191 } // namespace cpid
This is exactly an unique_lock without automatic lock, except that the lock functions accepts a prior...
Definition: prioritymutex.h:110
~permanent_lock()
Definition: prioritymutex.h:31
std::vector< int > queueCount_
Definition: prioritymutex.h:103
void unlock()
Definition: prioritymutex.h:146
bool owns_lock()
Definition: prioritymutex.h:159
bool try_lock(int prio)
Definition: prioritymutex.h:151
permanent_lock & operator=(const permanent_lock &)=delete
std::condition_variable_any queueCV_
Definition: prioritymutex.h:102
This class implements a mutex that offers some control over the priority of the waiting threads...
Definition: prioritymutex.h:49
priority_mutex(int maxPrio)
Constructs a mutex.
Definition: prioritymutex.h:53
void lock(int prio=0)
Definition: prioritymutex.h:60
void operator=(priority_lock &&o)
Definition: prioritymutex.h:128
permanent_lock(Mutex_t &m_)
Definition: prioritymutex.h:26
std::mutex queueMutex_
Definition: prioritymutex.h:101
~priority_lock()
Definition: prioritymutex.h:122
bool canGo(int prio)
Definition: prioritymutex.h:92
int maxPrio_
Definition: prioritymutex.h:105
void lock()
Definition: prioritymutex.h:142
bool try_lock(int prio=0)
Definition: prioritymutex.h:77
void lock(int prio)
Definition: prioritymutex.h:137
bool owns_lock()
Definition: prioritymutex.h:187
permanent_lock()=default
priority_lock(priority_mutex &m, int default_prio=0)
Definition: prioritymutex.h:117
The TorchCraftAI training library.
Definition: batcher.cpp:15
void unlock()
Definition: prioritymutex.h:86
This exactly a unique lock that doesn&#39;t unlock on delete.
Definition: prioritymutex.h:20
void unlock()
Definition: prioritymutex.h:181
void lock()
Definition: prioritymutex.h:175