blob: 81794ab8709d05625ce445c660b333289e4e88f6 [file] [log] [blame]
Ben Claytoned01f2c2019-05-20 10:42:35 +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
Ben Claytonb54838a2019-05-21 12:26:40 +010015// This file contains a number of synchronization primitives for concurrency.
16//
17// You may be tempted to change this code to unlock the mutex before calling
18// std::condition_variable::notify_[one,all]. Please read
19// https://issuetracker.google.com/issues/133135427 before making this sort of
20// change.
21
Ben Claytoned01f2c2019-05-20 10:42:35 +010022#ifndef sw_Synchronization_hpp
23#define sw_Synchronization_hpp
24
Ben Clayton6cd63a22019-05-20 11:06:46 +010025#include <assert.h>
Ben Clayton50c66902019-05-20 10:55:22 +010026#include <chrono>
Ben Claytoned01f2c2019-05-20 10:42:35 +010027#include <condition_variable>
Ben Claytoned01f2c2019-05-20 10:42:35 +010028#include <queue>
29
Ben Clayton377573c2020-04-03 20:36:40 +010030#include "marl/mutex.h"
31
Nicolas Capens157ba262019-12-10 17:49:14 -050032namespace sw {
Ben Claytoned01f2c2019-05-20 10:42:35 +010033
Ben Clayton6d33e8c2019-05-20 11:15:03 +010034// TaskEvents is an interface for notifying when tasks begin and end.
35// Tasks can be nested and/or overlapping.
36// TaskEvents is used for task queue synchronization.
37class TaskEvents
38{
39public:
40 // start() is called before a task begins.
41 virtual void start() = 0;
42 // finish() is called after a task ends. finish() must only be called after
43 // a corresponding call to start().
44 virtual void finish() = 0;
45 // complete() is a helper for calling start() followed by finish().
Ben Clayton595d9112019-12-17 20:37:57 +000046 inline void complete()
47 {
48 start();
49 finish();
50 }
Ben Claytoncaf60312019-05-21 15:31:12 +010051
52protected:
53 virtual ~TaskEvents() = default;
Ben Clayton6d33e8c2019-05-20 11:15:03 +010054};
55
Ben Clayton6cd63a22019-05-20 11:06:46 +010056// WaitGroup is a synchronization primitive that allows you to wait for
57// collection of asynchronous tasks to finish executing.
58// Call add() before each task begins, and then call done() when after each task
59// is finished.
60// At the same time, wait() can be used to block until all tasks have finished.
61// WaitGroup takes its name after Golang's sync.WaitGroup.
Ben Clayton6d33e8c2019-05-20 11:15:03 +010062class WaitGroup : public TaskEvents
Ben Clayton6cd63a22019-05-20 11:06:46 +010063{
64public:
65 // add() begins a new task.
66 void add()
67 {
Ben Clayton377573c2020-04-03 20:36:40 +010068 marl::lock lock(mutex);
Ben Clayton6cd63a22019-05-20 11:06:46 +010069 ++count_;
70 }
71
72 // done() is called when a task of the WaitGroup has been completed.
73 // Returns true if there are no more tasks currently running in the
74 // WaitGroup.
75 bool done()
76 {
Ben Clayton377573c2020-04-03 20:36:40 +010077 marl::lock lock(mutex);
Ben Clayton6cd63a22019-05-20 11:06:46 +010078 assert(count_ > 0);
79 --count_;
80 if(count_ == 0)
81 {
82 condition.notify_all();
83 }
84 return count_ == 0;
85 }
86
87 // wait() blocks until all the tasks have been finished.
88 void wait()
89 {
Ben Clayton377573c2020-04-03 20:36:40 +010090 marl::lock lock(mutex);
91 lock.wait(condition, [this]() REQUIRES(mutex) { return count_ == 0; });
Ben Clayton6cd63a22019-05-20 11:06:46 +010092 }
93
94 // wait() blocks until all the tasks have been finished or the timeout
95 // has been reached, returning true if all tasks have been completed, or
96 // false if the timeout has been reached.
Ben Clayton595d9112019-12-17 20:37:57 +000097 template<class CLOCK, class DURATION>
98 bool wait(const std::chrono::time_point<CLOCK, DURATION> &timeout)
Ben Clayton6cd63a22019-05-20 11:06:46 +010099 {
Ben Clayton377573c2020-04-03 20:36:40 +0100100 marl::lock lock(mutex);
101 return condition.wait_until(lock, timeout, [this]() REQUIRES(mutex) { return count_ == 0; });
Ben Clayton6cd63a22019-05-20 11:06:46 +0100102 }
103
104 // count() returns the number of times add() has been called without a call
105 // to done().
106 // Note: No lock is held after count() returns, so the count may immediately
107 // change after returning.
108 int32_t count()
109 {
Ben Clayton377573c2020-04-03 20:36:40 +0100110 marl::lock lock(mutex);
Ben Clayton6cd63a22019-05-20 11:06:46 +0100111 return count_;
112 }
113
Ben Clayton6d33e8c2019-05-20 11:15:03 +0100114 // TaskEvents compliance
115 void start() override { add(); }
116 void finish() override { done(); }
117
Ben Clayton6cd63a22019-05-20 11:06:46 +0100118private:
Ben Clayton377573c2020-04-03 20:36:40 +0100119 marl::mutex mutex;
120 int32_t count_ GUARDED_BY(mutex) = 0;
Ben Clayton6cd63a22019-05-20 11:06:46 +0100121 std::condition_variable condition;
122};
123
Ben Claytoned01f2c2019-05-20 10:42:35 +0100124// Chan is a thread-safe FIFO queue of type T.
125// Chan takes its name after Golang's chan.
Ben Clayton595d9112019-12-17 20:37:57 +0000126template<typename T>
Ben Claytoned01f2c2019-05-20 10:42:35 +0100127class Chan
128{
129public:
130 Chan();
131
132 // take returns the next item in the chan, blocking until an item is
133 // available.
134 T take();
135
136 // tryTake returns a <T, bool> pair.
137 // If the chan is not empty, then the next item and true are returned.
138 // If the chan is empty, then a default-initialized T and false are returned.
139 std::pair<T, bool> tryTake();
140
141 // put places an item into the chan, blocking if the chan is bounded and
142 // full.
143 void put(const T &v);
144
145 // Returns the number of items in the chan.
146 // Note: that this may change as soon as the function returns, so should
147 // only be used for debugging.
148 size_t count();
149
150private:
Ben Clayton377573c2020-04-03 20:36:40 +0100151 marl::mutex mutex;
152 std::queue<T> queue GUARDED_BY(mutex);
Ben Claytoned01f2c2019-05-20 10:42:35 +0100153 std::condition_variable added;
Ben Claytoned01f2c2019-05-20 10:42:35 +0100154};
155
Ben Clayton595d9112019-12-17 20:37:57 +0000156template<typename T>
157Chan<T>::Chan()
158{}
Ben Claytoned01f2c2019-05-20 10:42:35 +0100159
Ben Clayton595d9112019-12-17 20:37:57 +0000160template<typename T>
Ben Claytoned01f2c2019-05-20 10:42:35 +0100161T Chan<T>::take()
162{
Ben Clayton377573c2020-04-03 20:36:40 +0100163 marl::lock lock(mutex);
Ben Clayton183b8ad2019-05-20 10:45:36 +0100164 // Wait for item to be added.
Ben Clayton377573c2020-04-03 20:36:40 +0100165 lock.wait(added, [this]() REQUIRES(mutex) { return queue.size() > 0; });
Ben Claytoned01f2c2019-05-20 10:42:35 +0100166 T out = queue.front();
167 queue.pop();
Ben Claytoned01f2c2019-05-20 10:42:35 +0100168 return out;
169}
170
Ben Clayton595d9112019-12-17 20:37:57 +0000171template<typename T>
Ben Claytoned01f2c2019-05-20 10:42:35 +0100172std::pair<T, bool> Chan<T>::tryTake()
173{
Ben Clayton377573c2020-04-03 20:36:40 +0100174 marl::lock lock(mutex);
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500175 if(queue.size() == 0)
Ben Claytoned01f2c2019-05-20 10:42:35 +0100176 {
177 return std::make_pair(T{}, false);
178 }
179 T out = queue.front();
180 queue.pop();
Ben Claytoned01f2c2019-05-20 10:42:35 +0100181 return std::make_pair(out, true);
182}
183
Ben Clayton595d9112019-12-17 20:37:57 +0000184template<typename T>
Ben Claytoned01f2c2019-05-20 10:42:35 +0100185void Chan<T>::put(const T &item)
186{
Ben Clayton377573c2020-04-03 20:36:40 +0100187 marl::lock lock(mutex);
Ben Claytoned01f2c2019-05-20 10:42:35 +0100188 queue.push(item);
Ben Claytoned01f2c2019-05-20 10:42:35 +0100189 added.notify_one();
190}
191
Ben Clayton595d9112019-12-17 20:37:57 +0000192template<typename T>
Ben Claytoned01f2c2019-05-20 10:42:35 +0100193size_t Chan<T>::count()
194{
Ben Clayton377573c2020-04-03 20:36:40 +0100195 marl::lock lock(mutex);
Ben Clayton183b8ad2019-05-20 10:45:36 +0100196 return queue.size();
Ben Claytoned01f2c2019-05-20 10:42:35 +0100197}
198
Nicolas Capens157ba262019-12-10 17:49:14 -0500199} // namespace sw
Ben Claytoned01f2c2019-05-20 10:42:35 +0100200
Ben Clayton595d9112019-12-17 20:37:57 +0000201#endif // sw_Synchronization_hpp