2017
[folly.git] / folly / fibers / Semaphore.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "Semaphore.h"
17
18 namespace folly {
19 namespace fibers {
20
21 bool Semaphore::signalSlow() {
22   // If we signalled a release, notify the waitlist
23   SYNCHRONIZED(waitList_) {
24     auto testVal = tokens_.load(std::memory_order_acquire);
25     if (testVal != 0) {
26       return false;
27     }
28
29     if (waitList_.empty()) {
30       // If the waitlist is now empty, ensure the token count increments
31       // No need for CAS here as we will always be under the mutex
32       CHECK(tokens_.compare_exchange_strong(
33           testVal, testVal + 1, std::memory_order_relaxed));
34     } else {
35       // trigger waiter if there is one
36       waitList_.front()->post();
37       waitList_.pop();
38     }
39   } // SYNCHRONIZED(waitList_)
40   return true;
41 }
42
43 void Semaphore::signal() {
44   auto oldVal = tokens_.load(std::memory_order_acquire);
45   do {
46     if (oldVal == 0) {
47       if (signalSlow()) {
48         break;
49       }
50     }
51   } while (!tokens_.compare_exchange_weak(
52       oldVal,
53       oldVal + 1,
54       std::memory_order_release,
55       std::memory_order_acquire));
56 }
57
58 bool Semaphore::waitSlow() {
59   // Slow path, create a baton and acquire a mutex to update the wait list
60   folly::fibers::Baton waitBaton;
61
62   SYNCHRONIZED(waitList_) {
63     auto testVal = tokens_.load(std::memory_order_acquire);
64     if (testVal != 0) {
65       return false;
66     }
67     // prepare baton and add to queue
68     waitList_.push(&waitBaton);
69   }
70   // If we managed to create a baton, wait on it
71   // This has to be done here so the mutex has been released
72   waitBaton.wait();
73   return true;
74 }
75
76 void Semaphore::wait() {
77   auto oldVal = tokens_.load(std::memory_order_acquire);
78   do {
79     if (oldVal == 0) {
80       // If waitSlow fails it is because the token is non-zero by the time
81       // the lock is taken, so we can just continue round the loop
82       if (waitSlow()) {
83         break;
84       }
85     }
86   } while (!tokens_.compare_exchange_weak(
87       oldVal,
88       oldVal - 1,
89       std::memory_order_release,
90       std::memory_order_acquire));
91 }
92
93 size_t Semaphore::getCapacity() const {
94   return capacity_;
95 }
96
97 } // namespace fibers
98 } // namespace folly