blob: 7676c423e8e89baefbc077d76be715e45f4a16de [file] [log] [blame]
Ben Claytona74c7d92019-08-08 10:45:25 +01001// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef yarn_condition_variable_hpp
16#define yarn_condition_variable_hpp
17
18#include "Containers.hpp"
19#include "Debug.hpp"
20#include "Scheduler.hpp"
21
22#include <atomic>
23#include <mutex>
24
25namespace yarn {
26
27// ConditionVariable is a synchronization primitive that can be used to block
28// one or more fibers or threads, until another fiber or thread modifies a
29// shared variable (the condition) and notifies the ConditionVariable.
30//
31// If the ConditionVariable is blocked on a thread with a Scheduler bound, the
32// thread will work on other tasks until the ConditionVariable is unblocked.
33class ConditionVariable
34{
35public:
36 // Notifies and potentially unblocks one waiting fiber or thread.
37 inline void notify_one();
38
39 // Notifies and potentially unblocks all waiting fibers and/or threads.
40 inline void notify_all();
41
42 // Blocks the current fiber or thread until the predicate is satisfied
43 // and the ConditionVariable is notified.
44 template <typename Predicate>
45 inline void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
46
47private:
48 std::mutex mutex;
49 containers::vector<Scheduler::Fiber*, 4> waiting;
50 std::condition_variable condition;
51 std::atomic<int> numWaiting = { 0 };
52 std::atomic<int> numWaitingOnCondition = { 0 };
53};
54
55void ConditionVariable::notify_one()
56{
57 if (numWaiting == 0) { return; }
58 std::unique_lock<std::mutex> lock(mutex);
59 if (waiting.size() > 0)
60 {
61 auto fiber = waiting.back();
62 waiting.pop_back();
63 fiber->schedule();
64 }
65 lock.unlock();
66 if (numWaitingOnCondition > 0) { condition.notify_one(); }
67}
68
69void ConditionVariable::notify_all()
70{
71 if (numWaiting == 0) { return; }
72 std::unique_lock<std::mutex> lock(mutex);
73 while (waiting.size() > 0)
74 {
75 auto fiber = waiting.back();
76 waiting.pop_back();
77 fiber->schedule();
78 }
79 lock.unlock();
80 if (numWaitingOnCondition > 0) { condition.notify_all(); }
81}
82
83template <typename Predicate>
84void ConditionVariable::wait(std::unique_lock<std::mutex>& dataLock, Predicate pred)
85{
86 if (pred())
87 {
88 return;
89 }
90 numWaiting++;
91 if (auto fiber = Scheduler::Fiber::current())
92 {
93 // Currently executing on a scheduler fiber.
94 // Yield to let other tasks run that can unblock this fiber.
95 while (!pred())
96 {
97 mutex.lock();
98 waiting.push_back(fiber);
99 mutex.unlock();
100
101 dataLock.unlock();
102 fiber->yield();
103 dataLock.lock();
104 }
105 }
106 else
107 {
108 // Currently running outside of the scheduler.
109 // Delegate to the std::condition_variable.
110 numWaitingOnCondition++;
111 condition.wait(dataLock, pred);
112 numWaitingOnCondition--;
113 }
114 numWaiting--;
115}
116
117} // namespace yarn
118
119#endif // yarn_condition_variable_hpp