uThreads  0.3.0
BlockingSync.h
1 /*******************************************************************************
2  * Copyright © 2015, 2016 Saman Barghi
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  *******************************************************************************/
17 
18 #ifndef UTHREADS_BLOKING_SYNC_H
19 #define UTHREADS_BLOKING_SYNC_H
20 
21 #include "../generic/IntrusiveContainers.h"
22 #include <cassert>
23 #include <mutex>
24 #include <iostream>
25 
26 //TODO: implement timeouts for all synchronization and mutual exclusion variables
27 class Mutex;
28 class uThread;
37  friend class uThread;
38  friend class Mutex;
39  friend class OwnerLock;
40  friend class ConditionVariable;
41  friend class Semaphore;
42 
43  IntrusiveList<uThread> queue;
44 
45  //temporary fix to avoid including uThread.h
46  uThread* getCurrentUThread();
47 public:
48  BlockingQueue() = default;
49  ~BlockingQueue() {
50  assert(empty());
51  }
53  BlockingQueue(const BlockingQueue&) = delete;
55  const BlockingQueue& operator=(const BlockingQueue&) = delete;
56 
58  bool empty() const {
59  return queue.empty();
60  }
61 
70  bool suspend(std::mutex& lock);
71 
73  bool suspend(Mutex&);
74 // bool suspend(mutex& lock, mword timeout);
75 
82  bool signal(std::mutex& lock, uThread*& owner); //Used along with Mutex
83 
89  bool signal(std::mutex& lock) {
90  uThread* dummy = nullptr;
91  return signal(lock, dummy);
92  }
93 
99  bool signal(Mutex&);
100 
105  void signalAll(Mutex&);
106 
107  template<typename T>
108  static void postSwitchFunc(void* ut, void* args){
109  assert(args != nullptr);
110  assert(ut != nullptr);
111  uThread* utt = (uThread*)ut;
112 
113  auto bqp = (std::pair<T*, BlockingQueue*>*)args;
114  bqp->second->queue.push_back(*utt);
115  bqp->first->unlock();
116  }
117 };
118 
123 class Mutex {
124  friend class ConditionVariable;
125  friend class Semaphore;
126  friend class BlockingQueue;
127 protected:
128  /*
129  * std::mutex acting as the base lock. Usually this is implemented as a
130  * spinlock, but since we are interested in blocking in user level and this
131  * lock is only held for a small amount of time the possibilities that it
132  * blocks the kThread for a long time is minute, and hence it is safe
133  * to use a mutex as the base lock for now. This might change in the future.
134  */
135  std::mutex lock;
136  /* The blocking queue used for this Mutex to block and unblock uThreads */
137  BlockingQueue bq;
138  /* Owner of the Mutex */
139  uThread* owner;
140 
141  template<bool OL> // OL: owner lock
142  bool internalAcquire(mword timeout) {
143 
144  lock.lock();
145  //TODO: since assert is disabled during production, find another way
146  //to make sure the following is happening
147  //Mutex cannot have recursive locking
148  assert(OL || owner != bq.getCurrentUThread());
149  if(fastpath(owner != nullptr && (!OL || owner != bq.getCurrentUThread()))) {
150  return bq.suspend(lock); // release lock, false: timeout
151  } else {
152  /*
153  * End up here if:
154  * - No one is holding the lock
155  * - Someone is holding the lock and:
156  * + The type is OwnlerLock and the calling thread is holding the lock
157  * Otherwise, block and wait for the lock
158  */
159  owner = bq.getCurrentUThread();
160  lock.unlock();
161  return true;
162  }
163  }
164 
165  void internalRelease() {
166  // try baton-passing, if yes: 'signal' releases lock
167  if (fastpath(bq.signal(lock, owner))) return;
168  owner = nullptr;
169  lock.unlock();
170  }
171 
172 public:
173  Mutex() : owner(nullptr) {}
174 
181  template<bool OL=false>
182  bool acquire() {return internalAcquire<OL>(0);}
183 
184  //TODO: implement tryAcquire after timeouts are implemented
185 // template<bool OL=false>
186 // bool tryAcquire(mword t = 0) { return internalAcquire<true,OL>(t); }
187 
191  void release() {
192  lock.lock();
193  assert(owner == bq.getCurrentUThread()); //Attempt to release lock by non-owner
194  internalRelease();
195  }
196  //temporary solution for the postSwitchFunc
197  void unlock(){release();}
198 };
199 
204 class OwnerLock: protected Mutex {
205  /*
206  * Counter to keep track of how many time this has been acquired
207  * by the owner.
208  */
209  mword counter;
210 public:
215  counter(0) {
216  }
221  mword acquire() {
222  if (internalAcquire<true>(0))
223  return ++counter;
224  else
225  return 0;
226  }
227 // mword tryAcquire(mword t = 0) {
228 // if (internalAcquire<true,true>(t)) return ++counter;
229 // else return 0;
230 // }
236  mword release() {
237  lock.lock();
238  assert(owner == bq.getCurrentUThread());
239  if (--counter == 0)
240  internalRelease();
241  else
242  lock.unlock();
243  return counter;
244  }
245 };
246 
255  BlockingQueue bq;
256 
257 public:
262  void wait(Mutex& mutex) {
263  bq.suspend(mutex);
264  mutex.acquire();
265  }
266 
271  void signal(Mutex& mutex) {
272  if(slowpath(!bq.signal(mutex))) mutex.release();}
273 
278  void signalAll(Mutex& mutex) {bq.signalAll(mutex);}
279 
284  bool empty(){ return bq.empty(); }
285 };
286 
294 class Semaphore {
295  Mutex mutex;
296  BlockingQueue bq;
297  mword counter;
298 
299  template<bool TO>
300  bool internalP(mword timeout) {
301  if(fastpath(counter < 1)) {
302  return bq.suspend(mutex); // release lock, false: timeout
303  } else {
304  counter -= 1;
305  mutex.release();
306  return true;
307  }
308  }
309 
310 public:
315  explicit Semaphore(mword c = 0) : counter(c) {}
316 
321  bool P() {
322  mutex.acquire();
323  return internalP<false>(0);
324  }
325 
326 // bool tryP(mword t = 0, SpinLock* l = nullptr) {
327 // if (l) lock.acquire(*l); else lock.acquire();
328 // return internalP<true>(t);
329 // }
330 
334  void V() {
335  mutex.acquire();
336  // try baton-passing, if yes: 'signal' releases lock
337  if (fastpath(bq.signal(mutex))) return;
338  counter += 1;
339  mutex.release();
340  }
341 };
342 
343 #endif /* UTHREADS_BLOKING_SYNC_H */
an Owner Mutex where owner can recursively acquire the Mutex
Definition: BlockingSync.h:204
void signalAll(Mutex &mutex)
unblock all uThreads waiting on the condition variable
Definition: BlockingSync.h:278
void release()
release the Mutex
Definition: BlockingSync.h:191
const BlockingQueue & operator=(const BlockingQueue &)=delete
BlockingQueue cannot be copied or assigned.
void signalAll(Mutex &)
unblock all blocked uThreads, used for Condition Variable
Definition: BlockingSync.cpp:65
A user-level Semaphore.
Definition: BlockingSync.h:294
bool P()
Decrement the value of the Semaphore.
Definition: BlockingSync.h:321
A user-level Mutex.
Definition: BlockingSync.h:123
OwnerLock()
Create a new OwnerLock.
Definition: BlockingSync.h:214
bool acquire()
acquire the mutex
Definition: BlockingSync.h:182
mword release()
Release the OwnerLock once.
Definition: BlockingSync.h:236
bool empty()
Whether the waiting list is empty or not.
Definition: BlockingSync.h:284
mword acquire()
Acquire the OwnerLock.
Definition: BlockingSync.h:221
void V()
increment the value of the Semaphore
Definition: BlockingSync.h:334
user-level threads (fiber)
Definition: uThread.h:63
bool empty() const
Whether the queue is empty or not.
Definition: BlockingSync.h:58
A queue used to keep track of blocked uThreads.
Definition: BlockingSync.h:36
void wait(Mutex &mutex)
Block uThread on the condition variable using the provided mutex.
Definition: BlockingSync.h:262
bool signal(std::mutex &lock)
unblock one blocked, used for Mutex
Definition: BlockingSync.h:89
A user level condition variable.
Definition: BlockingSync.h:254
bool signal(std::mutex &lock, uThread *&owner)
Unblock one blocked uThread, used for OwnerLock.
Definition: BlockingSync.cpp:38
bool suspend(std::mutex &lock)
Suspends the uThread and add it to the queue.
Definition: BlockingSync.cpp:21
Semaphore(mword c=0)
Create a new Semaphore.
Definition: BlockingSync.h:315
void signal(Mutex &mutex)
Unblock one uThread waiting on the condition variable.
Definition: BlockingSync.h:271