|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #ifndef rr_MutexLock_hpp | 
|  | #define rr_MutexLock_hpp | 
|  |  | 
|  | #include "Thread.hpp" | 
|  |  | 
|  | #if defined(__linux__) | 
|  | // Use a pthread mutex on Linux. Since many processes may use Reactor | 
|  | // at the same time it's best to just have the scheduler overhead. | 
|  | #include <pthread.h> | 
|  |  | 
|  | namespace rr | 
|  | { | 
|  | class MutexLock | 
|  | { | 
|  | public: | 
|  | MutexLock() | 
|  | { | 
|  | pthread_mutex_init(&mutex, NULL); | 
|  | } | 
|  |  | 
|  | ~MutexLock() | 
|  | { | 
|  | pthread_mutex_destroy(&mutex); | 
|  | } | 
|  |  | 
|  | bool attemptLock() | 
|  | { | 
|  | return pthread_mutex_trylock(&mutex) == 0; | 
|  | } | 
|  |  | 
|  | void lock() | 
|  | { | 
|  | pthread_mutex_lock(&mutex); | 
|  | } | 
|  |  | 
|  | void unlock() | 
|  | { | 
|  | pthread_mutex_unlock(&mutex); | 
|  | } | 
|  |  | 
|  | private: | 
|  | pthread_mutex_t mutex; | 
|  | }; | 
|  | } | 
|  |  | 
|  | #else   // !__linux__ | 
|  |  | 
|  | #include <atomic> | 
|  |  | 
|  | namespace rr | 
|  | { | 
|  | class BackoffLock | 
|  | { | 
|  | public: | 
|  | BackoffLock() | 
|  | { | 
|  | mutex = 0; | 
|  | } | 
|  |  | 
|  | bool attemptLock() | 
|  | { | 
|  | if(!isLocked()) | 
|  | { | 
|  | if(mutex.exchange(true) == false) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void lock() | 
|  | { | 
|  | int backoff = 1; | 
|  |  | 
|  | while(!attemptLock()) | 
|  | { | 
|  | if(backoff <= 64) | 
|  | { | 
|  | for(int i = 0; i < backoff; i++) | 
|  | { | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  |  | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | nop(); | 
|  | } | 
|  |  | 
|  | backoff *= 2; | 
|  | } | 
|  | else | 
|  | { | 
|  | Thread::yield(); | 
|  |  | 
|  | backoff = 1; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | void unlock() | 
|  | { | 
|  | mutex.store(false, std::memory_order_release); | 
|  | } | 
|  |  | 
|  | bool isLocked() | 
|  | { | 
|  | return mutex.load(std::memory_order_acquire); | 
|  | } | 
|  |  | 
|  | private: | 
|  | struct | 
|  | { | 
|  | // Ensure that the mutex variable is on its own 64-byte cache line to avoid false sharing | 
|  | // Padding must be public to avoid compiler warnings | 
|  | volatile int padding1[16]; | 
|  | std::atomic<bool> mutex; | 
|  | volatile int padding2[15]; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | using MutexLock = BackoffLock; | 
|  | } | 
|  |  | 
|  | #endif   // !__linux__ | 
|  |  | 
|  | #endif   // rr_MutexLock_hpp |