1// Copyright 2016 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 sw_MutexLock_hpp
16#define sw_MutexLock_hpp
17
18#include "Thread.hpp"
19
20#if defined(__linux__)
21// Use a pthread mutex on Linux. Since many processes may use SwiftShader
22// at the same time it's best to just have the scheduler overhead.
23#include <pthread.h>
24
25namespace sw
26{
27 class MutexLock
28 {
29 public:
30 MutexLock()
31 {
32 pthread_mutex_init(&mutex, NULL);
33 }
34
35 ~MutexLock()
36 {
37 pthread_mutex_destroy(&mutex);
38 }
39
40 bool attemptLock()
41 {
42 return pthread_mutex_trylock(&mutex) == 0;
43 }
44
45 void lock()
46 {
47 pthread_mutex_lock(&mutex);
48 }
49
50 void unlock()
51 {
52 pthread_mutex_unlock(&mutex);
53 }
54
55 private:
56 pthread_mutex_t mutex;
57 };
58}
59
60#else // !__linux__
61
62#include <atomic>
63
64namespace sw
65{
66 class BackoffLock
67 {
68 public:
69 BackoffLock()
70 {
71 mutex = 0;
72 }
73
74 bool attemptLock()
75 {
76 if(!isLocked())
77 {
78 if(mutex.exchange(true) == false)
79 {
80 return true;
81 }
82 }
83
84 return false;
85 }
86
87 void lock()
88 {
89 int backoff = 1;
90
91 while(!attemptLock())
92 {
93 if(backoff <= 64)
94 {
95 for(int i = 0; i < backoff; i++)
96 {
97 nop();
98 nop();
99 nop();
100 nop();
101 nop();
102
103 nop();
104 nop();
105 nop();
106 nop();
107 nop();
108
109 nop();
110 nop();
111 nop();
112 nop();
113 nop();
114
115 nop();
116 nop();
117 nop();
118 nop();
119 nop();
120
121 nop();
122 nop();
123 nop();
124 nop();
125 nop();
126
127 nop();
128 nop();
129 nop();
130 nop();
131 nop();
132
133 nop();
134 nop();
135 nop();
136 nop();
137 nop();
138 }
139
140 backoff *= 2;
141 }
142 else
143 {
144 Thread::yield();
145
146 backoff = 1;
147 }
148 };
149 }
150
151 void unlock()
152 {
153 mutex.store(false, std::memory_order_release);
154 }
155
156 bool isLocked()
157 {
158 return mutex.load(std::memory_order_acquire);
159 }
160
161 private:
162 struct
163 {
164 // Ensure that the mutex variable is on its own 64-byte cache line to avoid false sharing
165 // Padding must be public to avoid compiler warnings
166 volatile int padding1[16];
167 std::atomic<bool> mutex;
168 volatile int padding2[15];
169 };
170 };
171
172 using MutexLock = BackoffLock;
173}
174
175#endif // !__linux__
176
177class LockGuard
178{
179public:
180 explicit LockGuard(sw::MutexLock &mutex) : mutex(&mutex)
181 {
182 mutex.lock();
183 }
184
185 explicit LockGuard(sw::MutexLock *mutex) : mutex(mutex)
186 {
187 if (mutex) mutex->lock();
188 }
189
190 ~LockGuard()
191 {
192 if (mutex) mutex->unlock();
193 }
194
195protected:
196 sw::MutexLock *mutex;
197};
198
199#endif // sw_MutexLock_hpp
200